Follow-up from #406.
Context
The new PATCH /v1/catalogue/item/:uid endpoint (PR #406) has three hardening layers the existing PUT does not:
- Three-phase Cypher: all MATCHes resolve every referenced node (user, item, supplier, category, detail properties) before any SET/MERGE/DELETE runs. Partial writes on a missing reference are impossible at the query-shape level, independent of transaction rollback.
- Cypher-level lost-update guard:
WHERE item.lastUpdateTime.epochSeconds = \$eps AND item.lastUpdateTime.nanosecond = \$nanos catches concurrent writes that raced past the service-layer Go check, with full nanosecond precision.
- MATCH-before-DELETE swap for supplier/category: an invalid new target UID can't leave the item stripped of its supplier/category relationship.
PUT currently has:
- Only the service-layer Go check for `lastUpdateTime` (TOCTOU-prone).
- The legacy destructive Details-delete loop (unrelated to this issue but worth noting).
- No three-phase guarantee — `SET item.lastUpdateTime` runs before subsequent MATCH failures would be detected.
Proposal
Retrofit UpdateCatalogueItemQuery in `services/catalogue-service/catalogue-db-queries.go` with the same three-phase structure. The audit `action: "UPDATE"` value is already consistent, so no changes to history consumers.
Scope
UpdateCatalogueItemQuery restructured (phase-1 MATCHes → phase-2 writes → phase-3 audit)
UpdateCatalogueItem service maps empty-result to helpers.ERR_CONFLICT (matching PATCH behavior)
- Cypher-level guard on
item.lastUpdateTime.epochSeconds + nanosecond
- Tests mirroring `TestPatchCatalogueItemQuery_Phase1MatchFailure_NoPartialWrite` and `TestPatchCatalogueItemQuery_CypherLockRejectsSubMillisecondRace` but targeted at PUT
Non-goals
- Changing PUT's semantics (still full-replace; details still cleared if omitted — that's the PUT contract)
- Changing the HTTP shape
Risk
Low. Existing PUT consumers (FE item edit form) won't see behavior changes, only stronger consistency under concurrent load.
Follow-up from #406.
Context
The new
PATCH /v1/catalogue/item/:uidendpoint (PR #406) has three hardening layers the existingPUTdoes not:WHERE item.lastUpdateTime.epochSeconds = \$eps AND item.lastUpdateTime.nanosecond = \$nanoscatches concurrent writes that raced past the service-layer Go check, with full nanosecond precision.PUTcurrently has:Proposal
Retrofit
UpdateCatalogueItemQueryin `services/catalogue-service/catalogue-db-queries.go` with the same three-phase structure. The audit `action: "UPDATE"` value is already consistent, so no changes to history consumers.Scope
UpdateCatalogueItemQueryrestructured (phase-1 MATCHes → phase-2 writes → phase-3 audit)UpdateCatalogueItemservice maps empty-result tohelpers.ERR_CONFLICT(matching PATCH behavior)item.lastUpdateTime.epochSeconds + nanosecondNon-goals
Risk
Low. Existing PUT consumers (FE item edit form) won't see behavior changes, only stronger consistency under concurrent load.