Summary
RESTORE against a CRDT collection in a non-default database keys the CRDT engine and derives its vShard on the bare collection name, while every ordinary apply keys the same collection as {database_id}/{bare}. The two paths therefore address different documents, and restore fences a vShard that the ordinary writes never use.
Where
nodedb/src/control/server/shared/ddl/neutral/version_history/restore.rs:71-92 — passes the bare collection and states it "feeds vShard routing and the restore-op collection field the admission workflow builds internally, which must stay self-consistent".
nodedb/src/control/crdt_admission.rs — dispatch_crdt_restore_admitted derives its vShard from that string and builds CrdtOp::Apply, CrdtOp::RestoreToVersion and CrdtOp::PreviewApply from it, so the path is internally self-consistent but consistently wrong outside DatabaseId::DEFAULT.
- Contrast with the ordinary apply path in the same file, which routes the canonical key from
engine_key(database_id, collection) = QualifiedCollection::new(db, bare).
Why it matters
The collection registry and every other apply use the qualified key, and the CRDT engine keys its collections by exactly the string it is handed (data/executor/dispatch/crdt.rs, engine/crdt/tenant_state/core.rs). In a non-default database a restore therefore previews and generates a delta from a different document than the write path maintains, so the admission preview fences nothing useful.
Scope note
Pre-existing: the bare form and the from_stored(...) construction predate the current work (e6c2d1e59, an ancestor of main). An earlier comment asserting the bare form was intended has been corrected; the routing itself is unchanged. Split out of the CRDT scope-fix PR deliberately, because canonicalizing it means changing the form the restore caller passes.
Summary
RESTOREagainst a CRDT collection in a non-default database keys the CRDT engine and derives its vShard on the bare collection name, while every ordinary apply keys the same collection as{database_id}/{bare}. The two paths therefore address different documents, and restore fences a vShard that the ordinary writes never use.Where
nodedb/src/control/server/shared/ddl/neutral/version_history/restore.rs:71-92— passes the barecollectionand states it "feeds vShard routing and the restore-op collection field the admission workflow builds internally, which must stay self-consistent".nodedb/src/control/crdt_admission.rs—dispatch_crdt_restore_admittedderives its vShard from that string and buildsCrdtOp::Apply,CrdtOp::RestoreToVersionandCrdtOp::PreviewApplyfrom it, so the path is internally self-consistent but consistently wrong outsideDatabaseId::DEFAULT.engine_key(database_id, collection)=QualifiedCollection::new(db, bare).Why it matters
The collection registry and every other apply use the qualified key, and the CRDT engine keys its collections by exactly the string it is handed (
data/executor/dispatch/crdt.rs,engine/crdt/tenant_state/core.rs). In a non-default database a restore therefore previews and generates a delta from a different document than the write path maintains, so the admission preview fences nothing useful.Scope note
Pre-existing: the bare form and the
from_stored(...)construction predate the current work (e6c2d1e59, an ancestor ofmain). An earlier comment asserting the bare form was intended has been corrected; the routing itself is unchanged. Split out of the CRDT scope-fix PR deliberately, because canonicalizing it means changing the form the restore caller passes.