-
Notifications
You must be signed in to change notification settings - Fork 559
fix(tree): commit enrichment after transaction abort #25978
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(tree): commit enrichment after transaction abort #25978
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR fixes a bug where aborting a transaction would cause subsequent undo/redo operations to fail with an assertion error. The fix introduces a snapshot/restore mechanism for DetachedFieldIndex instead of replacing the instance, ensuring that the SharedTreeReadonlyChangeEnricher continues to reference the correct index after transaction rollbacks.
Key changes:
- Introduced
ReadOnlyDetachedFieldIndexinterface to reduce the API surface exposed toSharedTreeReadonlyChangeEnricher - Added
createSnapshot()method andDetachedFieldIndexSnapshotinterface to enable snapshotting and restoring index state - Modified
TreeCheckoutto use snapshot/restore instead of instance replacement during transaction rollbacks
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
packages/dds/tree/src/core/tree/detachedFieldIndex.ts |
Adds ReadOnlyDetachedFieldIndex interface and implements snapshot/restore mechanism |
packages/dds/tree/src/shared-tree/treeCheckout.ts |
Changes from replacing _removedRoots instance to using snapshot/restore; exposes readonly getter |
packages/dds/tree/src/shared-tree/sharedTreeChangeEnricher.ts |
Updates to use ReadOnlyDetachedFieldIndex and IForestSubscription with "borrowed" naming convention |
packages/dds/tree/src/test/core/tree/detachedFieldIndex.spec.ts |
Adds comprehensive tests for snapshot/restore functionality |
packages/dds/tree/src/test/shared-tree-core/sharedTreeCore.spec.ts |
Enhances tests to verify undo/redo operations work correctly after transaction aborts |
packages/dds/tree/src/test/shared-tree/sharedTreeChangeEnricher.spec.ts |
Updates property names to use "borrowed" prefix for consistency |
packages/dds/tree/src/core/tree/index.ts |
Exports new ReadOnlyDetachedFieldIndex and DetachedFieldIndexSnapshot types |
packages/dds/tree/src/core/index.ts |
Re-exports new types |
.changeset/spicy-grapes-leave.md |
Documents the bug fix |
packages/dds/tree/src/test/shared-tree-core/sharedTreeCore.spec.ts
Outdated
Show resolved
Hide resolved
packages/dds/tree/src/test/shared-tree-core/sharedTreeCore.spec.ts
Outdated
Show resolved
Hide resolved
packages/dds/tree/src/test/shared-tree-core/sharedTreeCore.spec.ts
Outdated
Show resolved
Hide resolved
| createSnapshot(): DetachedFieldIndexSnapshot; | ||
| } | ||
|
|
||
| export interface DetachedFieldIndexSnapshot { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this type (and the associated API that creates it) a little bit confusing, since usually we use the word snapshot to mean "the actual snapshotted data" but this is not that, it's actually a (tiny) tool that lets you revert some index (that it is implicitly bound to) to some state.
What do you think of renaming createSnapshot to checkpoint and have it return a simple () => void directly, then just document what it does in the @return doc?
Then the code looks like:
const restore = index.checkpoint();
// ...
restore()
Josmithr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a couple nitpicks, otherwise docs look great!
Description
Aborting a transaction used to put the tree in a state that would trigger an assert when sending some undo/redo edits to peers.
This would prevent some undo/redo edits from being sent and would put the tree in a broken state that prevented any further edits.
This issue could not have caused document corruption, so reopening the document was a possible remedy.
After this PR, aborting a transaction no longer puts the tree in such a state, so it is safe to perform undo/redo edits after that.
This bug was due to the fact that
TreeCheckoutoverwrote itsDetachedFieldIndexmember with a new instance when performing a transaction rollback but had no way of informing theSharedTreeReadonlyChangeEnricherthat it should now be reading from the new instance during change enrichment.This PR fixes the bug by ensuring that a given
TreeCheckoutinstance always uses the sameDetachedFieldIndexinstance. This requires introducing a way of snapshotting and (on transaction rollback) restoring theDetachedFieldIndexstate that does not require replacing the instance.This PR also reduces the
DetachedFieldIndexAPI surface made available toSharedTreeReadonlyChangeEnricherby introducing theReadOnlyDetachedFieldIndexinterface. This is not required as part of this change and could be done in a separate PR.Breaking Changes
None
Reviewer Guidance
Let me know if you think it's worth spinning up a separate PR for the introduction of
ReadOnlyDetachedFieldIndex.