Skip to content

Fix upstream deletes across independent peer revision spaces - #185

Open
github-actions[bot] wants to merge 7 commits into
mainfrom
gardener/fix-peer-tombstones-167
Open

github-actions[bot] wants to merge 7 commits into
mainfrom
gardener/fix-peer-tombstones-167

Conversation

@github-actions

@github-actions github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Addresses #167. Requested by #167 (comment).

The existing tombstone guard compares a local node revision with the sender's independent revision counter. Four new integration regressions fail against the original implementation: entry and pack pulls after a high-revision host push, and entry and pack pushes into a higher-revision receiver.

  • On pulls, compare the live node's local revision with this backend's local push watermark. Unpushed edits and recreations remain protected, including after watermark reset.
  • On push receivers, use the committed incoming cursor (in the sender's revision space) to distinguish replays from new authoritative deletes. Thread it through both entry and pack receivers; a failed post-commit settle followed by replay preserves a local recreation.
  • Cover asynchronous/synchronous applies, independent backends, watermark resets, and both transports. Existing pull-only deletion fixtures now complete the echo push before expecting remote deletions to win.

Validation

  • Original implementation: all four new cross-space integration regressions fail.
  • Fixed implementation: 220 dofs sync tests and 99 RPC driver/engine tests pass.
  • RPC typecheck (including dofs build), targeted Biome checks, and git diff --check pass.

No real container was required or exercised. Pulls conservatively retain local versions not yet pushed to the selected backend.


Devin Review

Gardener-Operation: op_5e746d3660443aa9d21345f7e9a13aa41dc4605e4a9a3d8b3210451bd0331dd8:bc1b93cc3ac5de32532ffba0acf0db9b7c51d98bbdbe7fb2097be51daad0fade
@changeset-bot

changeset-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

鈿狅笍 No Changeset found

Latest commit: 7fcd593

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

devin-ai-integration[bot]

This comment was marked as outdated.

@pkg-pr-new

pkg-pr-new Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@cloudflare/computer@185

commit: 7fcd593

@scuffi scuffi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 potential issues.

Comment thread packages/dofs/src/sync/apply.ts Outdated
Comment thread packages/dofs/src/sync/apply.ts Outdated
Comment thread packages/dofs/src/sync/apply.ts Outdated
- Let a remote delete win over a pulled version awaiting its echo push
  by recording revs minted by pull applies (_vfs_upstream_revs).
- Check every node in a deleted directory's subtree, not only the
  directory, so unpushed descendant edits are protected.
- Compare against the full push cursor (rev and path), so same-rev
  paths beyond a partial push are protected.

Gardener-Operation: op_29ef360a754d42b37e0381356c12af6d78734c3a98f528dc18cd8bad69630a27:2fb95d57eb10338caf78fbb6584c0a03f9ecadd85a8ac4c0c72bf4de4f4fa155
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

I reproduced all three findings (each is reported twice, by Devin and again by hand) with failing tests on f78ff4b. The proposed commit fixes them in tombstoneIsStale (packages/dofs/src/sync/apply.ts).

Remote deletes resurrect pulled files: fixed. Pull applies now record the local revs they mint, per backend, in a new _vfs_upstream_revs table. It is created by the baseline DDL, so no migration is needed, and rows are pruned in writePushCursor. A node still at a pulled rev gives way to a later delete from the same backend. A local edit after the pull still protects it.

  • Tests: replay.test.ts "lets a remote delete win over an unechoed pulled file" and "protects a local edit made after a pull". sync-engine-pack.test.ts "a container delete wins over a pulled file awaiting its echo push" covers entries and pack mode, and it fails without the fix.
  • This replaces the PR body's note that pulls keep local versions until they are pushed.

Directory deletes discard unpushed child edits: fixed. The guard now walks the whole subtree being deleted with a recursive CTE. It protects the delete if any node is past the push cursor and was not minted by a pull.

  • Tests: "protects unpushed descendants from a directory delete" and "still deletes a clean directory tree".

Partial pushes lose unshipped same-rev changes: fixed. The guard compares {rev, path} against readPushCursor, capped at the pushRev watermark so a reset stays conservative.

  • Test: "protects same-rev paths beyond a partial push cursor", which uses a renamed directory.

Every replay.test.ts case runs against both applyChanges and applyChangesSync.

Checks run, all passing:

  • dofs vitest: 680 tests (after building dofs for the schema test)
  • rpc vitest: 126 tests
  • rpc tsc --noEmit and dofs build
  • Biome on the changed files
  • git diff --check

devin-ai-integration[bot]

This comment was marked as resolved.

A pulled write to one name of a hardlinked file no longer marks the
shared inode's new rev as upstream while that inode holds an unpushed
local version, so a later remote delete of another name cannot discard
the local link.

Gardener-Operation: op_789a00a9ac036c1a03cabf304c4c0ed4a78f5d010d9a28320a7422d516373113:b1b75f82ac55517fe6e1459d845c0232ecd34eb183e361f5c43e4bdb561d57e4
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Unpushed hardlinks lost after remote writes: fixing in the proposed commit. I reproduced it on 4f13766. A pull that rewrites /x restamps the inode it shares with an unpushed link /y, and records that rev as upstream. A later delete of /y then removed the link. The fix adds rewritesUnpushedHardlink in packages/dofs/src/sync/apply.ts. When a pulled file write lands on an inode that has more than one name and still holds an unpushed local version, the new rev is not recorded as upstream. A partially shipped rev counts as unpushed, because the names have different paths. The trade-off is conservative: the shared inode stays protected from later deletes until it is pushed, which is how it behaved before the pull.

  • replay.test.ts "keeps an unpushed hardlink when a pull rewrites its inode" fails without the fix, in both the async and sync variants.
  • "still deletes a pushed hardlink after a pull rewrites its inode" checks that a clean link still gives way to the delete.

The three duplicate threads by scuffi (remote deletes resurrect pulled files, directory deletes discard child edits, partial pushes lose same-rev changes) repeat Devin's round-1 findings. They were fixed in 4f13766, and Devin has resolved its copies. The round-1 tests still pass, so I made no further change for them.

Checks I ran, all passing:

  • dofs vitest: 684 tests, after building dofs
  • rpc vitest: 126 tests
  • rpc typecheck (tsconfig.build.json)
  • Biome on the changed files
  • git diff --check

A bare tsc --noEmit in rpc reports 5 errors in test files. They are the same with and without this change.

@scuffi scuffi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the open hardlink thread in packages/dofs/src/sync/apply.ts.

A pull that rewrites one name of an inode that has an unpushed hardlink
now records its rev for that name only, in _vfs_upstream_paths. A later
remote delete of that name gives way, and the unpushed link stays
protected.

Gardener-Operation: op_bebe0d1a786b63f31da72f41600299520fd0fd4ece1d8b14dd1b05f00af8e766:c7f80763d215ac2584e07dff8913a127a5a51fdb633be3da208666c62c418819
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Remote deletion of updated hardlink stays blocked: fixed in the proposed commit. When a pull rewrites a name next to an unpushed hardlink, it now records the new rev for that name only, in _vfs_upstream_paths. A later delete of that name wins, and the unpushed link stays protected. New replay.test.ts cases cover both apply variants, and the first one fails without the fix.

Review body: the open hardlink thread in packages/dofs/src/sync/apply.ts is the one addressed above.

Checks run, all passing:

  • dofs vitest: 688 tests, after building dofs
  • rpc vitest: 126 tests
  • rpc typecheck
  • Biome on the changed files
  • git diff --check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

allow-pr Allow a PR to remain open.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant