detached 467a7dd

feat(broker): retain first probe transport in warm pool

commit
467a7dd793706a8f3131b1589736965c91c8877e
author
brice · · 2026-09-22 21:01 UTC
parent
354d6b6
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +133 -2
M internal/app/broker_remote_probe.go +17 -2
@@ -41,6 +41,7 @@ type brokerRemoteProbe struct {
4141 // sharedPhysicals lends the pooled transport of an attached or warm daemon.
4242 type sharedPhysicals interface {
4343 SharedPhysical(ports.BrokerDaemonIdentity, ports.BrokerPolicy) (ports.BrokerPhysicalConnection, bool)
44+ AdoptPhysical(ports.BrokerPhysicalConnection, ports.BrokerPolicy) bool
4445 }
4546
4647 // pooledPhysicals lets an observation probe borrow the broker pool's physical
@@ -53,6 +54,11 @@ type pooledPhysicals struct{ pool atomic.Value }
5354
5455 func (s *pooledPhysicals) share(pool sharedPhysicals) { s.pool.Store(pool) }
5556
57+func (s *pooledPhysicals) adopt(physical ports.BrokerPhysicalConnection, policy ports.BrokerPolicy) bool {
58+ pool, _ := s.pool.Load().(sharedPhysicals)
59+ return pool != nil && pool.AdoptPhysical(physical, policy)
60+}
61+
5662 // observe reports handled=false when the probe must dial its own transport.
5763 func (s *pooledPhysicals) observe(ctx context.Context, identity ports.BrokerDaemonIdentity, policy ports.BrokerPolicy, request ports.BrokerOpenStreamRequest) (ports.BrokerDaemonObservation, bool, error) {
5864 pool, _ := s.pool.Load().(sharedPhysicals)
@@ -101,7 +107,12 @@ func (p *brokerRemoteProbe) Probe(ctx context.Context, registration domain.Remot
101107 if err != nil {
102108 return ports.BrokerDaemonObservation{}, err
103109 }
104- defer physical.Close()
110+ adopted := false
111+ defer func() {
112+ if !adopted {
113+ _ = physical.Close()
114+ }
115+ }()
105116 if (endpoint.ExpectedIdentity.Bound && physical.Identity() != endpoint.ExpectedIdentity.Identity) || physical.Policy() != endpoint.Policy {
106117 return ports.BrokerDaemonObservation{}, errors.New("vev: remote probe authenticated binding mismatch")
107118 }
@@ -109,7 +120,11 @@ func (p *brokerRemoteProbe) Probe(ctx context.Context, registration domain.Remot
109120 if err != nil || identity != physical.Identity() {
110121 return ports.BrokerDaemonObservation{}, errors.New("vev: remote probe could not commit authenticated binding")
111122 }
112- return observeDaemonCatalogue(ctx, physical, request)
123+ observation, err := observeDaemonCatalogue(ctx, physical, request)
124+ if err == nil && ctx.Err() == nil {
125+ adopted = p.shared.adopt(physical, endpoint.Policy)
126+ }
127+ return observation, err
113128 }
114129
115130 // observeDaemonCatalogue observes one already authenticated daemon over its
@@ -41,6 +41,7 @@ type brokerRemoteProbe struct {
41 // sharedPhysicals lends the pooled transport of an attached or warm daemon.41 // sharedPhysicals lends the pooled transport of an attached or warm daemon.
42 type sharedPhysicals interface {42 type sharedPhysicals interface {
43 SharedPhysical(ports.BrokerDaemonIdentity, ports.BrokerPolicy) (ports.BrokerPhysicalConnection, bool)43 SharedPhysical(ports.BrokerDaemonIdentity, ports.BrokerPolicy) (ports.BrokerPhysicalConnection, bool)
44+ AdoptPhysical(ports.BrokerPhysicalConnection, ports.BrokerPolicy) bool
44 }45 }
45 46
46 // pooledPhysicals lets an observation probe borrow the broker pool's physical47 // pooledPhysicals lets an observation probe borrow the broker pool's physical
@@ -53,6 +54,11 @@ type pooledPhysicals struct{ pool atomic.Value }
53 54
54 func (s *pooledPhysicals) share(pool sharedPhysicals) { s.pool.Store(pool) }55 func (s *pooledPhysicals) share(pool sharedPhysicals) { s.pool.Store(pool) }
55 56
57+func (s *pooledPhysicals) adopt(physical ports.BrokerPhysicalConnection, policy ports.BrokerPolicy) bool {
58+ pool, _ := s.pool.Load().(sharedPhysicals)
59+ return pool != nil && pool.AdoptPhysical(physical, policy)
60+}
61+
56 // observe reports handled=false when the probe must dial its own transport.62 // observe reports handled=false when the probe must dial its own transport.
57 func (s *pooledPhysicals) observe(ctx context.Context, identity ports.BrokerDaemonIdentity, policy ports.BrokerPolicy, request ports.BrokerOpenStreamRequest) (ports.BrokerDaemonObservation, bool, error) {63 func (s *pooledPhysicals) observe(ctx context.Context, identity ports.BrokerDaemonIdentity, policy ports.BrokerPolicy, request ports.BrokerOpenStreamRequest) (ports.BrokerDaemonObservation, bool, error) {
58 pool, _ := s.pool.Load().(sharedPhysicals)64 pool, _ := s.pool.Load().(sharedPhysicals)
@@ -101,7 +107,12 @@ func (p *brokerRemoteProbe) Probe(ctx context.Context, registration domain.Remot
101 if err != nil {107 if err != nil {
102 return ports.BrokerDaemonObservation{}, err108 return ports.BrokerDaemonObservation{}, err
103 }109 }
104- defer physical.Close()110+ adopted := false
111+ defer func() {
112+ if !adopted {
113+ _ = physical.Close()
114+ }
115+ }()
105 if (endpoint.ExpectedIdentity.Bound && physical.Identity() != endpoint.ExpectedIdentity.Identity) || physical.Policy() != endpoint.Policy {116 if (endpoint.ExpectedIdentity.Bound && physical.Identity() != endpoint.ExpectedIdentity.Identity) || physical.Policy() != endpoint.Policy {
106 return ports.BrokerDaemonObservation{}, errors.New("vev: remote probe authenticated binding mismatch")117 return ports.BrokerDaemonObservation{}, errors.New("vev: remote probe authenticated binding mismatch")
107 }118 }
@@ -109,7 +120,11 @@ func (p *brokerRemoteProbe) Probe(ctx context.Context, registration domain.Remot
109 if err != nil || identity != physical.Identity() {120 if err != nil || identity != physical.Identity() {
110 return ports.BrokerDaemonObservation{}, errors.New("vev: remote probe could not commit authenticated binding")121 return ports.BrokerDaemonObservation{}, errors.New("vev: remote probe could not commit authenticated binding")
111 }122 }
112- return observeDaemonCatalogue(ctx, physical, request)123+ observation, err := observeDaemonCatalogue(ctx, physical, request)
124+ if err == nil && ctx.Err() == nil {
125+ adopted = p.shared.adopt(physical, endpoint.Policy)
126+ }
127+ return observation, err
113 }128 }
114 129
115 // observeDaemonCatalogue observes one already authenticated daemon over its130 // observeDaemonCatalogue observes one already authenticated daemon over its
M internal/app/broker_remote_probe_shared_test.go +4 -0
@@ -20,6 +20,10 @@ func (f sharedPhysicalsFunc) SharedPhysical(identity ports.BrokerDaemonIdentity,
2020 return f(identity, policy)
2121 }
2222
23+func (sharedPhysicalsFunc) AdoptPhysical(ports.BrokerPhysicalConnection, ports.BrokerPolicy) bool {
24+ return false
25+}
26+
2327 // TestBrokerRemoteProbeBorrowsWarmTransport proves observation reuses the
2428 // broker pool's attached or warm transport instead of bootstrapping SSH/QUIC
2529 // again, and redials only when the borrowed transport has retired.
@@ -20,6 +20,10 @@ func (f sharedPhysicalsFunc) SharedPhysical(identity ports.BrokerDaemonIdentity,
20 return f(identity, policy)20 return f(identity, policy)
21 }21 }
22 22
23+func (sharedPhysicalsFunc) AdoptPhysical(ports.BrokerPhysicalConnection, ports.BrokerPolicy) bool {
24+ return false
25+}
26+
23 // TestBrokerRemoteProbeBorrowsWarmTransport proves observation reuses the27 // TestBrokerRemoteProbeBorrowsWarmTransport proves observation reuses the
24 // broker pool's attached or warm transport instead of bootstrapping SSH/QUIC28 // broker pool's attached or warm transport instead of bootstrapping SSH/QUIC
25 // again, and redials only when the borrowed transport has retired.29 // again, and redials only when the borrowed transport has retired.
M internal/usecase/broker/pool.go +83 -0
@@ -489,6 +489,89 @@ func (p *Pool) idleLocked(e *poolEntry) []context.CancelFunc {
489489 return evicted
490490 }
491491
492+// AdoptPhysical publishes a probe-authenticated transport as a warm entry.
493+// The caller retains ownership when adoption fails and must close it. A
494+// concurrently opened pooled transport wins: probes must not replace it.
495+func (p *Pool) AdoptPhysical(physical ports.BrokerPhysicalConnection, policy ports.BrokerPolicy) bool {
496+ if nilDependency(physical) || physical.Identity().Validate() != nil ||
497+ physical.Incarnation().Validate() != nil || !physical.Policy().Compatible(policy) || p.limits.Warm == 0 {
498+ return false
499+ }
500+ select {
501+ case <-physical.Done():
502+ return false
503+ default:
504+ }
505+ key := poolKey{identity: physical.Identity(), policy: policy}
506+ p.mu.Lock()
507+ if p.closed || len(p.entries)+len(p.pending) >= p.limits.Physical || p.entries[key] != nil {
508+ p.mu.Unlock()
509+ return false
510+ }
511+ ctx, cancel := context.WithCancel(p.ctx)
512+ e := &poolEntry{key: key, ctx: ctx, cancel: cancel, ready: make(chan struct{}), wake: make(chan struct{}, 1), physical: physical}
513+ close(e.ready)
514+ p.entries[key] = e
515+ evicted := p.idleLocked(e)
516+ p.wg.Add(1)
517+ p.mu.Unlock()
518+ for _, evict := range evicted {
519+ evict()
520+ }
521+ go p.watchAdopted(e)
522+ return true
523+}
524+
525+func (p *Pool) watchAdopted(e *poolEntry) {
526+ defer p.wg.Done()
527+ physical := e.physical
528+ var timer ports.Timer
529+ if p.limits.Idle > 0 {
530+ timer = p.clock.NewTimer(p.limits.Idle)
531+ defer stopPoolTimer(timer)
532+ }
533+ for {
534+ p.mu.Lock()
535+ idle := e.refs == 0
536+ p.mu.Unlock()
537+ var expiry <-chan time.Time
538+ if timer != nil {
539+ stopPoolTimer(timer)
540+ if idle {
541+ timer.Reset(p.limits.Idle)
542+ expiry = timer.C()
543+ }
544+ }
545+ select {
546+ case <-e.ctx.Done():
547+ goto retire
548+ case <-physical.Done():
549+ goto retire
550+ case <-e.wake:
551+ continue
552+ case <-expiry:
553+ p.mu.Lock()
554+ if e.refs == 0 {
555+ e.retiring = true
556+ p.mu.Unlock()
557+ goto retire
558+ }
559+ p.mu.Unlock()
560+ }
561+ }
562+retire:
563+ p.mu.Lock()
564+ e.retiring = true
565+ p.mu.Unlock()
566+ e.cancel()
567+ _ = physical.Close()
568+ p.mu.Lock()
569+ if p.entries[e.key] == e {
570+ delete(p.entries, e.key)
571+ }
572+ p.mu.Unlock()
573+}
574+
492575 // SharedPhysical returns the published physical transport the pool holds for
493576 // one authenticated identity and exact policy, active or warm, without
494577 // reserving it. Observation borrows it to avoid a second bootstrap; the
@@ -489,6 +489,89 @@ func (p *Pool) idleLocked(e *poolEntry) []context.CancelFunc {
489 return evicted489 return evicted
490 }490 }
491 491
492+// AdoptPhysical publishes a probe-authenticated transport as a warm entry.
493+// The caller retains ownership when adoption fails and must close it. A
494+// concurrently opened pooled transport wins: probes must not replace it.
495+func (p *Pool) AdoptPhysical(physical ports.BrokerPhysicalConnection, policy ports.BrokerPolicy) bool {
496+ if nilDependency(physical) || physical.Identity().Validate() != nil ||
497+ physical.Incarnation().Validate() != nil || !physical.Policy().Compatible(policy) || p.limits.Warm == 0 {
498+ return false
499+ }
500+ select {
501+ case <-physical.Done():
502+ return false
503+ default:
504+ }
505+ key := poolKey{identity: physical.Identity(), policy: policy}
506+ p.mu.Lock()
507+ if p.closed || len(p.entries)+len(p.pending) >= p.limits.Physical || p.entries[key] != nil {
508+ p.mu.Unlock()
509+ return false
510+ }
511+ ctx, cancel := context.WithCancel(p.ctx)
512+ e := &poolEntry{key: key, ctx: ctx, cancel: cancel, ready: make(chan struct{}), wake: make(chan struct{}, 1), physical: physical}
513+ close(e.ready)
514+ p.entries[key] = e
515+ evicted := p.idleLocked(e)
516+ p.wg.Add(1)
517+ p.mu.Unlock()
518+ for _, evict := range evicted {
519+ evict()
520+ }
521+ go p.watchAdopted(e)
522+ return true
523+}
524+
525+func (p *Pool) watchAdopted(e *poolEntry) {
526+ defer p.wg.Done()
527+ physical := e.physical
528+ var timer ports.Timer
529+ if p.limits.Idle > 0 {
530+ timer = p.clock.NewTimer(p.limits.Idle)
531+ defer stopPoolTimer(timer)
532+ }
533+ for {
534+ p.mu.Lock()
535+ idle := e.refs == 0
536+ p.mu.Unlock()
537+ var expiry <-chan time.Time
538+ if timer != nil {
539+ stopPoolTimer(timer)
540+ if idle {
541+ timer.Reset(p.limits.Idle)
542+ expiry = timer.C()
543+ }
544+ }
545+ select {
546+ case <-e.ctx.Done():
547+ goto retire
548+ case <-physical.Done():
549+ goto retire
550+ case <-e.wake:
551+ continue
552+ case <-expiry:
553+ p.mu.Lock()
554+ if e.refs == 0 {
555+ e.retiring = true
556+ p.mu.Unlock()
557+ goto retire
558+ }
559+ p.mu.Unlock()
560+ }
561+ }
562+retire:
563+ p.mu.Lock()
564+ e.retiring = true
565+ p.mu.Unlock()
566+ e.cancel()
567+ _ = physical.Close()
568+ p.mu.Lock()
569+ if p.entries[e.key] == e {
570+ delete(p.entries, e.key)
571+ }
572+ p.mu.Unlock()
573+}
574+
492 // SharedPhysical returns the published physical transport the pool holds for575 // SharedPhysical returns the published physical transport the pool holds for
493 // one authenticated identity and exact policy, active or warm, without576 // one authenticated identity and exact policy, active or warm, without
494 // reserving it. Observation borrows it to avoid a second bootstrap; the577 // reserving it. Observation borrows it to avoid a second bootstrap; the
M internal/usecase/broker/pool_warm_test.go +29 -0
@@ -194,6 +194,35 @@ func TestPoolWarmReuse(t *testing.T) {
194194 }
195195 }
196196
197+func TestPoolAdoptProbePhysical(t *testing.T) {
198+ for _, tt := range []struct {
199+ name string
200+ warm int
201+ want bool
202+ }{
203+ {name: "warm probe is reused", warm: 2, want: true},
204+ {name: "warm disabled refuses probe", warm: 0},
205+ } {
206+ t.Run(tt.name, func(t *testing.T) {
207+ h := newWarmHarness(t, tt.warm, 0)
208+ physical := &fakePhysical{endpoint: ports.BrokerDialTarget{Policy: poolPolicy(), ExpectedIdentity: ports.BrokerExpectedIdentity{Identity: "alpha", Bound: true}}, done: make(chan struct{})}
209+ require.Equal(t, tt.want, h.pool.AdoptPhysical(physical, poolPolicy()))
210+ if !tt.want {
211+ require.NoError(t, physical.Close())
212+ return
213+ }
214+ shared, ok := h.pool.SharedPhysical("alpha", poolPolicy())
215+ require.True(t, ok)
216+ require.Same(t, physical, shared)
217+ h.visit(t, "alpha")
218+ require.Empty(t, h.physicals("alpha"), "attach must not redial after a first probe")
219+ require.False(t, h.pool.AdoptPhysical(&fakePhysical{endpoint: physical.endpoint, done: make(chan struct{})}, poolPolicy()), "duplicate probe cannot replace the attachment")
220+ require.NoError(t, h.pool.Close())
221+ require.True(t, closed(physical))
222+ })
223+ }
224+}
225+
197226 func TestPoolCloseRetiresWarmTransports(t *testing.T) {
198227 h := newWarmHarness(t, 2, 0)
199228 h.visit(t, "alpha")
@@ -194,6 +194,35 @@ func TestPoolWarmReuse(t *testing.T) {
194 }194 }
195 }195 }
196 196
197+func TestPoolAdoptProbePhysical(t *testing.T) {
198+ for _, tt := range []struct {
199+ name string
200+ warm int
201+ want bool
202+ }{
203+ {name: "warm probe is reused", warm: 2, want: true},
204+ {name: "warm disabled refuses probe", warm: 0},
205+ } {
206+ t.Run(tt.name, func(t *testing.T) {
207+ h := newWarmHarness(t, tt.warm, 0)
208+ physical := &fakePhysical{endpoint: ports.BrokerDialTarget{Policy: poolPolicy(), ExpectedIdentity: ports.BrokerExpectedIdentity{Identity: "alpha", Bound: true}}, done: make(chan struct{})}
209+ require.Equal(t, tt.want, h.pool.AdoptPhysical(physical, poolPolicy()))
210+ if !tt.want {
211+ require.NoError(t, physical.Close())
212+ return
213+ }
214+ shared, ok := h.pool.SharedPhysical("alpha", poolPolicy())
215+ require.True(t, ok)
216+ require.Same(t, physical, shared)
217+ h.visit(t, "alpha")
218+ require.Empty(t, h.physicals("alpha"), "attach must not redial after a first probe")
219+ require.False(t, h.pool.AdoptPhysical(&fakePhysical{endpoint: physical.endpoint, done: make(chan struct{})}, poolPolicy()), "duplicate probe cannot replace the attachment")
220+ require.NoError(t, h.pool.Close())
221+ require.True(t, closed(physical))
222+ })
223+ }
224+}
225+
197 func TestPoolCloseRetiresWarmTransports(t *testing.T) {226 func TestPoolCloseRetiresWarmTransports(t *testing.T) {
198 h := newWarmHarness(t, 2, 0)227 h := newWarmHarness(t, 2, 0)
199 h.visit(t, "alpha")228 h.visit(t, "alpha")

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