fix(github): keep every open delivery reachable while another is nested inside it - #759
Conversation
…ed inside it asOneDelivery held the open delivery in a single-slot ThreadLocal and replaced it for the duration of a nested group, so while a group for another pull request held the thread the outer one was unreachable. A route of the outer delivery that landed in that window called deliveryFor and got nothing back, so it settled nothing, and the outer group closed as never delivered — announcing as lost a piece of content the pull request had actually received. The same single-slot comparison split one pull request's delivery in two on an A -> B -> A nesting, because the innermost group could only see the group directly enclosing it. Hold the open deliveries in an ArrayDeque instead, innermost first, and have deliveryFor return the innermost entry whose target matches rather than the top entry when its target matches. Same-target nesting now rejoins the group already open for that pull request wherever it sits on the stack, which is a superset of the reuse it did before; a group for another pull request goes on the stack above the outer one rather than in place of it, keeping #748's fix intact. The finally pops exactly one entry on every exit path, and rejoining pushes nothing, so an exception or an error out of the rejoined routes leaves the delivery they rejoined as they found it. Fixes #756
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
🤖 ThrillhouseBot PR SummaryWhat this PR doesRewrites delivery accounting in GitHubLostWrites so routes of an outer delivery remain resolvable while a group for a different pull request is open inside it: open deliveries are kept on a per-thread deque (innermost first), deliveryFor searches past other-PR groups to find the owning delivery, and same-target nesting rejoins the existing group wherever it sits on the stack instead of opening a second one. Tests cover cross-PR nesting, exceptions and errors out of rejoined groups, and controls for pre-existing behavior. Description vs. ImplementationNo mismatch found between the PR description and the change. Control-Flow Diagram🔀 Show diagramflowchart TD
A["asOneDelivery(target, routes)"] --> B{"deliveryFor(target) != null?"}
B -- "yes: rejoin" --> C["run routes; push nothing"]
B -- "no" --> D["push Delivery onto thread-local deque"]
D --> E["run routes"]
E --> F["finally: pop; drop thread-local when empty"]
F --> G{"refused && !landed?"}
G -- "yes" --> H["remember(target)"]
C --> I["recording(target, send)"]
I --> J{"deliveryFor(target) finds a group?"}
J -- "yes, past any other-PR groups" --> K["mark that delivery refused/landed"]
J -- "no" --> L["standalone post path"]
Changes Overview
Changed Files
Risk Assessment
No new issues found in this PR, but the review cannot be approved until required CI is confirmed green.
|
| Check | Type | Status | Detail |
|---|---|---|---|
| frontend | check-run | ⏳ Pending | - |
| trivy | check-run | ⏳ Pending | - |
| format | check-run | ⏳ Pending | - |
| test | check-run | ⏳ Pending | - |
| dependency-review | check-run | ⏳ Pending | - |
Automated review by ThrillhouseBot. Reply with /review to re-run.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|



What type of PR is this?
Description
GitHubLostWrites.asOneDeliveryheld the open delivery in a single-slotThreadLocalandreplaced it for the duration of a nested group instead of stacking it. While a group for another
pull request held the thread, the outer group was not reachable at all:
deliveryFor(outerTarget), gotnullbecause the slot held the inner target, and was treated as a post of its own — so when itlanded it set nothing, and the outer
Delivery.landedstayedfalse;refused && !landedtrue and announced as lost a piece ofcontent the pull request had actually received. That is A finding rescued by the file-level fallback still posts a false "reply was never posted" notice #729's headline symptom arriving through
the opposite door: a settled delivery whose settlement is invisible.
The same single-slot comparison split one pull request's delivery in two on an
A → B → Anesting,because the innermost group could only see the group directly enclosing it and so opened a second
group for
A.The change
GitHubLostWritesnow holds the deliveries open on the thread in anArrayDeque<Delivery>,innermost first, and
deliveryFor(target)returns the innermost entry whose target matchesrather than the top entry when its target matches.
stack. That is a strict superset of the reuse the immediately-enclosing check gave, so
A → Abehaves exactly as before and
A → B → Ais now one delivery forArather than two.outer one rather than in place of it — so The review body's own fallback still posts the false "reply was never posted" notice #748's fix is intact and the nested caller keeps its
own accounting.
finallypops exactly one entry on every exit path, includingExceptionandError, anddrops the deque once it empties so a thread that only posts ungrouped carries none. Rejoining
pushes nothing, so a throw out of rejoined routes leaves the delivery they rejoined as it was.
Scoped to
GitHubLostWrites: no caller ofasOneCommentand nothing inReviewPublisherchanges.The defect is latent at
73cc33d(bothasOneCommentcallers operate on the current review's pullrequest), which is the posture #748 was in before #751 added a second caller.
Related Issues
Fixes #756
How Has This Been Tested?
Four new behavioural tests in
GitHubLostWritesTest, plus two controls. All four are genuinely redon the unfixed tree at
73cc33dand green with the fix.Red — verbatim, on
73cc33dwith the tests applied and the fix notEach failure is the defect exactly as described: content the pull request received announced as
lost. The four cover, in order, an
Errorout of a group that rejoins an already-open delivery, aroute landing inside a group for another pull request, an
Exceptionout of a rejoining group, andthe
A → B → Asplit.Green — with the fix
Controls (green before and after — not proof of the fix)
anErrorOutOfAGroupOfItsOwnLeavesNoDeliveryBehindOnTheThread— anErrorunwinding through agroup that opened its own entry leaves nothing on the thread, so the next post is accounted for
on its own rather than swallowed.
anExceptionOutOfANestedGroupHandsTheOuterOneBack— a throw out of a nested group for anotherpull request still hands the outer group back.
The other direction — a genuine loss must not be forgotten
The failure mode this fix must not introduce is the opposite one: a write that really was lost
being silently dropped. The existing suite pins that and stays green —
contentNoRouteDeliveredIsAnnouncedExactlyOnce,aDeliveryThatWasRefusedRatherThanThrottledAnnouncesNothing,aCallForAnotherPullRequestIsNotOneOfThisDeliverysRoutes,aDeliveryNestedInsideOneForAnotherPullRequestIsGroupedOnItsOwn,aNestedDeliveryIsAnnouncedOnceAndHandsTheOuterOneBack, plusRescuedReviewBodyLostWriteTestandRescuedFindingLostWriteTestthrough the real clientdefaultmethods. Each genuine-loss pathstill reports exactly once: no route delivered, the fallback itself throttled, a non-throttle
refusal, and a loss on one pull request while another pull request's delivery is open.
Checklist
Screenshots / Logs
Gates on
32599b3:Coverage of the changed main code (jacoco ∩
git diff -U0 73cc33d...HEAD): 0 uncovered lines,0 uncovered branches across every executable changed line in
GitHubLostWrites.java— both sidesof the empty-stack check, the pop-and-drop, the rejoin short-circuit, and the scan's
match / no-match / exhausted outcomes.
Additional Notes
The behavioural change is confined to nesting shapes no caller reaches at
73cc33d, so nothingabout today's posting behaviour moves. The
ThreadLocalholds aDequethat is absent rather thanempty while nothing is open, so an ungrouped post still costs one
ThreadLocal.getreturningnull, exactly as before.