fix(table): dedupe transaction requirements by semantics#1328
fix(table): dedupe transaction requirements by semantics#1328fallintoplace wants to merge 2 commits into
Conversation
tanmayrauth
left a comment
There was a problem hiding this comment.
Thanks for the PR, confirmed this is a real concurrency fix, not cleanup, the old GetType() key dropped every ref assertion after the first across apply calls. Two small non-blocking things inline.
| } | ||
|
|
||
| func requirementSemanticKey(r Requirement) (string, error) { | ||
| data, err := json.Marshal(r) |
There was a problem hiding this comment.
The dedupe key relies on encoding/json being deterministic. That holds for all current requirement types, fields serialize in declaration order and none contain maps — but a future requirement that adds a map field would silently break dedupe (Go sorts map keys, so it'd still be stable... actually that's fine — the real risk is any field whose marshaling isn't canonical). Could you add a one-line doc comment on requirementSemanticKey noting it assumes canonical/deterministic marshaling, so the constraint is visible to whoever adds the next Requirement type?
| return txn | ||
| } | ||
|
|
||
| func requireContainsRequirement(t *testing.T, requirements []Requirement, expected Requirement) { |
There was a problem hiding this comment.
requireContainsRequirement asserts membership via requirementSemanticKey — the same function the production change uses. If that function had a bug, both sides would agree and the test would still pass.
Comparing on the concrete fields (ref + snapshot id) instead would make the assertion independent of the code under test.
Summary
Why
Transaction.apply currently dedupes requirements by GetType() only. That drops semantically distinct requirements that share a type, especially AssertRefSnapshotID checks for different refs. Operations such as expiring snapshots can build multiple ref assertions, and collapsing them weakens optimistic concurrency control by letting commits proceed without validating every referenced branch or tag.
Using the full requirement payload as the dedupe key keeps equivalent duplicates collapsed while preserving distinct assertions.
Testing