test: reproduce duplicate-text deletion swallowing a peer's concurrent insert - #3006
Draft
christianhg wants to merge 1 commit into
Draft
test: reproduce duplicate-text deletion swallowing a peer's concurrent insert#3006christianhg wants to merge 1 commit into
christianhg wants to merge 1 commit into
Conversation
…t insert Red by design; this is a repro branch, not a fix. Deleting one of two exact copies of a string travels as a `diffMatchPatch` whose context anchors are identical at both copies. When the peer's insert reaches the store first, the delete applies against a base shifted by text the deleter never saw, anchors at the wrong copy's tail, and consumes the peer's insert. `applyPatches` reports success and every party converges on the corrupted value: no divergence, no repair, silent loss of the peer's text. Two choreographies pinned in the two-client harness: peer typing at the end of the block and peer typing inside the to-be-deleted copy. Both fail on the final-text assert while the convergence assert passes. Delivery order is load-bearing: with the delete arriving first, both scenarios pass, which is why the existing suite never caught it.
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Contributor
📦 Bundle Stats —
|
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 807.4 KB | - |
| Internal (gzip) | 155.1 KB | - |
| Bundled (raw) | 1.42 MB | - |
| Bundled (gzip) | 319.4 KB | - |
| Import time | 102ms | +1ms, +1.0% |
@portabletext/editor/behaviors
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 467 B | - |
| Internal (gzip) | 207 B | - |
| Bundled (raw) | 424 B | - |
| Bundled (gzip) | 171 B | - |
| Import time | 2ms | -0ms, -2.1% |
@portabletext/editor/plugins
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 2.7 KB | - |
| Internal (gzip) | 894 B | - |
| Bundled (raw) | 2.5 KB | - |
| Bundled (gzip) | 827 B | - |
| Import time | 7ms | +0ms, +2.2% |
@portabletext/editor/selectors
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 82.7 KB | - |
| Internal (gzip) | 15.4 KB | - |
| Bundled (raw) | 78.4 KB | - |
| Bundled (gzip) | 14.3 KB | - |
| Import time | 8ms | +0ms, +2.8% |
@portabletext/editor/traversal
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 28.1 KB | - |
| Internal (gzip) | 5.6 KB | - |
| Bundled (raw) | 28.1 KB | - |
| Bundled (gzip) | 5.5 KB | - |
| Import time | 6ms | +0ms, +3.7% |
@portabletext/editor/utils
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 30.6 KB | - |
| Internal (gzip) | 6.4 KB | - |
| Bundled (raw) | 28.2 KB | - |
| Bundled (gzip) | 6.1 KB | - |
| Import time | 6ms | +0ms, +1.3% |
🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @portabletext/markdown
Compared against main (5ddcc5b4)
| Metric | Value | vs main (5ddcc5b) |
|---|---|---|
| Internal (raw) | 53.8 KB | - |
| Internal (gzip) | 9.8 KB | - |
| Bundled (raw) | 348.9 KB | - |
| Bundled (gzip) | 96.3 KB | - |
| Import time | 41ms | -0ms, -0.2% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Red by design: this is a reproduction, not a fix. During a customer's collaborative session, deleting text that exists as an exact duplicate in the same block corrupted the result. Reproduced deterministically here, and the proven variant is nastier than the report: it is convergent silent data loss.
The mechanism is structural to the wire format. Deleting one of two exact copies travels as a
diffMatchPatchwhose context anchors are identical at both copies. When the peer's concurrent insert reaches the store first, the delete applies against a base shifted by text the deleter never saw, anchors at the wrong copy's tail, and consumes the peer's insert.applyPatchesreports success, and server and both editors converge unanimously on the corrupted value: the peer's typed"hi"is gone, no divergence ever forms, and no repair machinery downstream can see anything to repair. A 30-line kernel probe againstapplyPatches(parsePatch(...))shows the same swallow with no editors involved.Two choreographies are pinned in the two-client harness (
plugin.two-client.browser.test.tsx): peer typing at the end of the block, and peer typing inside the to-be-deleted copy. Both fail on the final-text assert whilesettleAndAssertConvergencepasses. Delivery order is load-bearing: with the delete arriving at the store first, both scenarios pass, which is why the existing convergence suite never caught this.The fix conversation is architectural rather than local:
diffMatchPatchcontext matching is definitionally ambiguous on repeated text, so candidates include tightening the offset hint, rejecting low-confidence fuzzy matches instead of applying them, or a different wire representation for deletions. Whichever design wins turns these tests green. Related: repeated paste under concurrent typing manufactures exactly the duplicate text this mechanism needs, so the two field reports compose.