detached 9ff586d

Merge pull request 'fix: CI flakes in render coordinator readiness and quicnettest event order' (#16) from fix/ci-race-and-proxy-event-order into main

commit
9ff586df8173b0dac1c1faf24238b3f4edcda434
author
bnema · · 2026-10-08 08:10 UTC
merge of
f67cb47 + fe90d45 · diff against the first parent
signature
unsigned
2 files · +20 -17
M internal/adapters/quicnettest/proxy.go +11 -8
@@ -468,6 +468,17 @@ func (p *Proxy) ingest(toServer bool, raw []byte, dst *net.UDPAddr) {
468468 counters.reordered.Add(1)
469469 }
470470 due := now.Add(link.BaseLatency + decision.ExtraDelay)
471+ // Emit before scheduling: once enqueued, a zero-latency copy can be
472+ // delivered and answered before this call returns, and the reply's event
473+ // must not be observed ahead of the packet that caused it.
474+ p.emit(Event{
475+ Direction: directionOf(toServer),
476+ Sequence: seq,
477+ Bytes: len(raw),
478+ At: now,
479+ Due: due,
480+ Decision: decision,
481+ })
471482 for copyIndex := 0; copyIndex <= decision.Duplicates; copyIndex++ {
472483 copyDue := due.Add(time.Duration(copyIndex) * link.DuplicateDelay)
473484 if !p.enqueue(packet{
@@ -480,14 +491,6 @@ func (p *Proxy) ingest(toServer bool, raw []byte, dst *net.UDPAddr) {
480491 counters.overflowDrops.Add(1)
481492 }
482493 }
483- p.emit(Event{
484- Direction: directionOf(toServer),
485- Sequence: seq,
486- Bytes: len(raw),
487- At: now,
488- Due: due,
489- Decision: decision,
490- })
491494 }
492495
493496 func (p *Proxy) enqueue(pkt packet) bool {
@@ -468,6 +468,17 @@ func (p *Proxy) ingest(toServer bool, raw []byte, dst *net.UDPAddr) {
468 counters.reordered.Add(1)468 counters.reordered.Add(1)
469 }469 }
470 due := now.Add(link.BaseLatency + decision.ExtraDelay)470 due := now.Add(link.BaseLatency + decision.ExtraDelay)
471+ // Emit before scheduling: once enqueued, a zero-latency copy can be
472+ // delivered and answered before this call returns, and the reply's event
473+ // must not be observed ahead of the packet that caused it.
474+ p.emit(Event{
475+ Direction: directionOf(toServer),
476+ Sequence: seq,
477+ Bytes: len(raw),
478+ At: now,
479+ Due: due,
480+ Decision: decision,
481+ })
471 for copyIndex := 0; copyIndex <= decision.Duplicates; copyIndex++ {482 for copyIndex := 0; copyIndex <= decision.Duplicates; copyIndex++ {
472 copyDue := due.Add(time.Duration(copyIndex) * link.DuplicateDelay)483 copyDue := due.Add(time.Duration(copyIndex) * link.DuplicateDelay)
473 if !p.enqueue(packet{484 if !p.enqueue(packet{
@@ -480,14 +491,6 @@ func (p *Proxy) ingest(toServer bool, raw []byte, dst *net.UDPAddr) {
480 counters.overflowDrops.Add(1)491 counters.overflowDrops.Add(1)
481 }492 }
482 }493 }
483- p.emit(Event{
484- Direction: directionOf(toServer),
485- Sequence: seq,
486- Bytes: len(raw),
487- At: now,
488- Due: due,
489- Decision: decision,
490- })
491 }494 }
492 495
493 func (p *Proxy) enqueue(pkt packet) bool {496 func (p *Proxy) enqueue(pkt packet) bool {
M internal/usecase/daemon/render_coordinator.go +9 -9
@@ -1005,21 +1005,21 @@ func (c *renderCoordinator) fireWithTimerTokenAndLease(token *timerToken, gen ui
10051005 if c.pendingGeneration == 0 {
10061006 c.pendingGeneration = 1
10071007 }
1008- leases := make(map[*attachedClient]*attachmentLease, len(c.leases))
1008+ // lease.ready is guarded by c.mu (markAttachmentReady writes it), so only
1009+ // leases already ready under the lock are probed after unlocking.
1010+ probe := make(map[*attachedClient]*attachmentLease, len(c.leases))
10091011 for ac, lease := range c.leases {
1010- if lease != nil && lease.active {
1011- leases[ac] = lease
1012+ if lease != nil && lease.active && lease.ready {
1013+ probe[ac] = lease
10121014 }
10131015 }
10141016 c.mu.Unlock()
10151017
1016- ready := make(map[*attachmentLease]bool, len(leases))
1017- for ac, lease := range leases {
1018+ ready := make(map[*attachmentLease]bool, len(probe))
1019+ for ac, lease := range probe {
10181020 // Welcome holds attachment.sendMu while its transport Send is in
10191021 // flight. Do not probe an incarnation before it is ready.
1020- if lease.ready {
1021- ready[lease] = c.attachmentAckReady(ac)
1022- }
1022+ ready[lease] = c.attachmentAckReady(ac)
10231023 }
10241024
10251025 // The readiness callback can detach, replace, or publish a sync batch.
@@ -1041,7 +1041,7 @@ func (c *renderCoordinator) fireWithTimerTokenAndLease(token *timerToken, gen ui
10411041
10421042 // Re-snapshot after every external readiness probe. A replacement may have
10431043 // retired one of the leases we probed while the callback was unlocked.
1044- leases = make(map[*attachedClient]*attachmentLease, len(c.leases))
1044+ leases := make(map[*attachedClient]*attachmentLease, len(c.leases))
10451045 for ac, lease := range c.leases {
10461046 if lease != nil && lease.active {
10471047 leases[ac] = lease
@@ -1005,21 +1005,21 @@ func (c *renderCoordinator) fireWithTimerTokenAndLease(token *timerToken, gen ui
1005 if c.pendingGeneration == 0 {1005 if c.pendingGeneration == 0 {
1006 c.pendingGeneration = 11006 c.pendingGeneration = 1
1007 }1007 }
1008- leases := make(map[*attachedClient]*attachmentLease, len(c.leases))1008+ // lease.ready is guarded by c.mu (markAttachmentReady writes it), so only
1009+ // leases already ready under the lock are probed after unlocking.
1010+ probe := make(map[*attachedClient]*attachmentLease, len(c.leases))
1009 for ac, lease := range c.leases {1011 for ac, lease := range c.leases {
1010- if lease != nil && lease.active {1012+ if lease != nil && lease.active && lease.ready {
1011- leases[ac] = lease1013+ probe[ac] = lease
1012 }1014 }
1013 }1015 }
1014 c.mu.Unlock()1016 c.mu.Unlock()
1015 1017
1016- ready := make(map[*attachmentLease]bool, len(leases))1018+ ready := make(map[*attachmentLease]bool, len(probe))
1017- for ac, lease := range leases {1019+ for ac, lease := range probe {
1018 // Welcome holds attachment.sendMu while its transport Send is in1020 // Welcome holds attachment.sendMu while its transport Send is in
1019 // flight. Do not probe an incarnation before it is ready.1021 // flight. Do not probe an incarnation before it is ready.
1020- if lease.ready {1022+ ready[lease] = c.attachmentAckReady(ac)
1021- ready[lease] = c.attachmentAckReady(ac)
1022- }
1023 }1023 }
1024 1024
1025 // The readiness callback can detach, replace, or publish a sync batch.1025 // The readiness callback can detach, replace, or publish a sync batch.
@@ -1041,7 +1041,7 @@ func (c *renderCoordinator) fireWithTimerTokenAndLease(token *timerToken, gen ui
1041 1041
1042 // Re-snapshot after every external readiness probe. A replacement may have1042 // Re-snapshot after every external readiness probe. A replacement may have
1043 // retired one of the leases we probed while the callback was unlocked.1043 // retired one of the leases we probed while the callback was unlocked.
1044- leases = make(map[*attachedClient]*attachmentLease, len(c.leases))1044+ leases := make(map[*attachedClient]*attachmentLease, len(c.leases))
1045 for ac, lease := range c.leases {1045 for ac, lease := range c.leases {
1046 if lease != nil && lease.active {1046 if lease != nil && lease.active {
1047 leases[ac] = lease1047 leases[ac] = lease

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close