fix: retry a declined typed-structure save instead of returning stranded bytes - #8
Open
kriszyp wants to merge 1 commit into
Open
fix: retry a declined typed-structure save instead of returning stranded bytes#8kriszyp wants to merge 1 commit into
kriszyp wants to merge 1 commit into
Conversation
…ded bytes Forwarding isCompatible (e3c8e44) made a CAS-ing saveStructures able to decline a typed-structure save on the standalone path, but _saveTypedStructures dropped the return value and _structonEncode returned the encoded bytes regardless. A struct encoding is a bare reference to a typed structure id, so a declined save left the record pointing at an id that was never persisted -- or, when the winning writer had assigned that id to a different shape, at another record's structure, which decodes as the wrong fields. msgpackr's own pack call site handles this by marking the structures uninitialized and re-packing (pack.js:190-199). Do the same here: return the saveStructures result, and on a decline reload the durable dictionary and re-mint once. A second decline throws rather than returning bytes that reference an unpersisted structure. The capped plain-fallback path carries no typed-structure reference, so a decline there is best-effort re-saved (it can only strand previously written struct data, not this record) and does not throw. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the _structonEncode method in index.js to handle cases where saving typed structures is declined due to concurrent writes. It introduces a retry mechanism that reloads the structures and attempts to re-encode, throwing an error if the save is declined twice to prevent returning bytes with unpersisted structure IDs. Additionally, _saveTypedStructures now returns the result of saveStructures, and comprehensive unit tests have been added in tests/test.js to verify this retry and error-handling behavior. There are no review comments, so I have no feedback to provide.
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.
Summary
Completes #6. That PR made
_saveTypedStructuresforwardisCompatibleso a CAS-ingsaveStructurescan decline a save — but nothing acted on the decline._saveTypedStructuresdropped the return value, and_structonEncodereturned the encoded bytes either way.A struct encoding is a bare reference to a typed structure id, so it is only meaningful once that structure is durably saved. With #6 alone, a declined save leaves the record pointing at an id that was never persisted — or, when the winning writer assigned that id to a different shape, at another record's structure, so it decodes as the wrong fields. In other words #6 converted "clobber durable, break the previously written records" into "drop the save, break the record we just returned."
msgpackr's own pack call site already enforces the contract this PR restores: on a
falsereturn it marks the structures uninitialized and re-packs, with the comment "re-packing as-is would re-emit the same record pointing at an unpersisted structure" (pack.js:190-199).Fix
_saveTypedStructuresreturns thesaveStructuresresult._structonEncode's struct path retries once on a decline: reload the durable dictionary (_loadStructures), re-mint, re-save. The retry pass always re-attempts the save — an empty durable leaves our unpersisted mint in place, so "no new structure this pass" does not imply the referenced id is persisted.resultthere carries no typed-structure reference, so a decline can only strand previously written struct data, not this record.Scope
Standalone path only (a base without
SUPPORTS_STRUCT_HOOKS— cbor-x, msgpackr v1). The fast path saves through the base's own pack call site, which already handles the decline._saveTypedStructuresis unreachable on the fast path (the constructor returns before installing theencodewrapper).Test plan
mocha tests/test.js tests/test-cbor-x.js -u tdd— 101 passing (3 new)structon – declined structure save is retried (standalone)with a CAS-ing durable store:declined twice) after exactly one retry, rather than returning stranded bytesindex.jschange reverted, the first test fails with a deep-equal mismatch (the stale writer's record decodes as the other writer's fields — the exact corruption) and the second fails on the missing throw.Note on publishing
structon
mainis one commit (#6) ahead of the published1.0.7. Recommend releasing this together with #6 rather than publishing #6 alone.🤖 Generated by an LLM (Claude Opus 5), with @kriszyp.