Relay: detect the rest of §2.4.2's malformed-track conditions - #106
Merged
Merged
Conversation
The relay ended a track for Object Properties and for an Object after END_OF_GROUP on the same stream. It now also detects, on live subgroup and datagram Objects of any upstream, against the last 32 Groups: - a Subgroup's Publisher Priority changing (item 1); - an Object past a Subgroup's, Group's or Track's end, or two different ends (items 2 to 5). An end is kept as the first missing ID: an END_OF_GROUP or END_OF_TRACK status at M ends the Group at M (END_OF_TRACK the Track too); a FIN (§11.4.3; the Group's too with the END_OF_GROUP header bit, §11.4.2) or a datagram's END_OF_GROUP bit after Object N at N+1. Each end is also checked against the Objects already received, duplicates included (TrackEntry.RecordDuplicate, after the §9.1 check), and the Objects past it are removed from the cache (§2.4.2 MUST NOT be cached), as is a first copy that fails the §9.1 duplicate check. Interpretations, chosen with the user and marked in STATUS.md: - §2.4.2 calls both the status Object at M and Object N the "final Object"; they are one end when M = N+1, as §9.1 lets a relay turn one into the other; - Objects past an end make the track malformed, not dropped; - a Normal Object at an end is past it only if a FIN or bit set it: with status Objects alone it is §9.1's existing-to-not-existing change or §2.1's late Object; a status end at M and a FIN end at M+1 agree for the same reason; - a datagram's END_OF_GROUP bit counts as a Group's end; - item 7 is read per Object (§11.2.1), which the §9.1 check covers. A claimed-but-not-yet-cached race with a later end is documented. ClaimDelivered takes an ObjectInfo; SubgroupEnded is called on a clean inbound FIN via the new sessionHandler.inboundEnded, extracted from runFanout. TestFanout_MultiPublisher_MergesDisjointObjects FIN'd one Subgroup on two streams with different finals (item 3); A now resets. Tests, each verified red first: TestTrackEntry_FinalObjects (priority, ends, their agreement and every order the reviews probed), _RecordDuplicateChecksAgain, _LateEndPurgesCache, TestCheckDuplicatePurgesFirstCopy, six TestRelay_MalformedObjectEndsTrack cases, TestRelay_EndSignalsAgree, TestRelay_DuplicateEndOfGroupEndsGroup. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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
Until now the relay ended a track only for malformed Object Properties, or for an Object after END_OF_GROUP on the same stream. It now detects the rest of §2.4.2's list, on live subgroup and datagram Objects from any upstream, against the last 32 Groups (the same window and lock as the dedup ledger):
What counts as an end. An end is stored as its first missing Object ID:
Other behaviour:
TrackEntry.RecordDuplicate) only after it passes the §9.1 check, and re-checked under the lock.Interpretations (marked per CLAUDE.md; chosen with you)
Not covered (listed in STATUS.md)
Code changes
registry.TrackEntry.ClaimDeliveredtakes anObjectInfo. The newSubgroupEndedis called on a clean inbound FIN, throughsessionHandler.inboundEnded, which I extracted fromrunFanoutto stay under the lint limits.TestFanout_MultiPublisher_MergesDisjointObjectshad two streams FIN the same Subgroup with different final Objects, which item 3 makes malformed. It showed up under-race. Publisher A's stream now ends with a reset.Tests (each verified red first)
TestTrackEntry_FinalObjects: priority, ends, the agreement cases, and every arrival order the reviews probed.TestTrackEntry_RecordDuplicateChecksAgaincovers the race.TestTrackEntry_LateEndPurgesCachecovers Subgroup, Group and END_OF_TRACK, including what must stay cached.TestCheckDuplicatePurgesFirstCopy: the cached first copy goes when a duplicate differs.TestRelay_MalformedObjectEndsTrackcases, each malformed in either stream order.TestRelay_EndSignalsAgree(a status end and a FIN end agree).TestRelay_DuplicateEndOfGroupEndsGroup(datagrams from two publishers).-raceandmake bench-quickall pass, with no allocs/op increase.The moqt-reviewer took four rounds; the last found no order dependence across about 260,000 random interleavings.
🤖 Generated with Claude Code