Skip to content

Sync: an 'unchanged' import rebuilds asset_refs from plan-time rows and can drop a concurrent edit's row #196

Closed

Description

@Madraka

Found while verifying RFC-0045 (#193). Pre-existing on main.

packages/core-ts/src/sync-plan.ts (the import batch builder, around lines 295–310) skips the node delete and insert when the import is planned unchanged. It still always pushes refsRebuild(siteId, changes.rows):

if (!changes.unchanged && !changes.conflict) { /* delete + insert nodes */ }
// The derived reference index follows the node set in the same batch (RFC-0004 §4.4).
batch.push(...refsRebuild(siteId, changes.rows));

changes.rows was read at plan time. When nothing else in the batch writes the node set, the rebuild has no CAS anchor. Here is the race:

  1. A no-op cloudhal push (or pull, or import) of site S plans unchanged.
  2. Concurrently, an editor saves an entry that newly uses asset A. That write inserts an asset_refs row for A.
  3. The import batch runs. Its refsRebuild deletes S's asset_refs and re-inserts them from the plan-time rows, which do not include A's new use.

From then on, A is in use but has no asset_refs row. deleteAsset's NOT EXISTS asset_refs guard (assets.ts) allows deleting it, and the media "unused" filter lists it. A published page can lose its image. The window is small (a no-op import racing an edit), but the result is silent data loss.

Fix: run refsRebuild only when the same batch also writes the node set (!changes.unchanged && !changes.conflict). An unchanged import has nothing to rebuild. Add a shared-suite case: an unchanged import racing an asset-adding edit keeps the edit's row.

RFC-0045 step 0 (asset_refs scoping) should fold this in, and RFC-0045's entry_refs rebuild follows the same rule.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions