Skip to content

Commit f82b5b6

Browse files
committed
fix(agent): prevent goroutine pile up in reportMetadataLoop
In the prior implementation, calls to DoChan would stack up because we weren't updating lastCollectedAts until collectMetadata finished. This wasn't a true leak, instead, it meant that there would be up to ~ (collectionRuntime / baseInterval) outstanding goroutines. So, for example, if `sleep 60s` was the metadata script there would be up to 60 goroutines waiting at peak.
1 parent ccadd0f commit f82b5b6

File tree

1 file changed

+17
-5
lines changed

1 file changed

+17
-5
lines changed

agent/agent.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import (
3535
"go.uber.org/atomic"
3636
gossh "golang.org/x/crypto/ssh"
3737
"golang.org/x/exp/slices"
38-
"golang.org/x/sync/singleflight"
3938
"golang.org/x/xerrors"
4039
"tailscale.com/net/speedtest"
4140
"tailscale.com/tailcfg"
@@ -264,6 +263,21 @@ type metadataResultAndKey struct {
264263
key string
265264
}
266265

266+
type trySingleflight struct {
267+
m sync.Map
268+
}
269+
270+
func (t *trySingleflight) Do(key string, fn func()) {
271+
_, loaded := t.m.LoadOrStore(key, struct{}{})
272+
if !loaded {
273+
// There is already a goroutine running for this key.
274+
return
275+
}
276+
277+
defer t.m.Delete(key)
278+
fn()
279+
}
280+
267281
func (a *agent) reportMetadataLoop(ctx context.Context) {
268282
baseInterval := adjustIntervalForTests(1)
269283

@@ -276,7 +290,7 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
276290
)
277291
defer baseTicker.Stop()
278292

279-
var flight singleflight.Group
293+
var flight trySingleflight
280294

281295
for {
282296
select {
@@ -348,7 +362,7 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
348362
// We send the result to the channel in the goroutine to avoid
349363
// sending the same result multiple times. So, we don't care about
350364
// the return values.
351-
flight.DoChan(md.Key, func() (interface{}, error) {
365+
go flight.Do(md.Key, func() {
352366
timeout := md.Timeout
353367
if timeout == 0 {
354368
timeout = md.Interval
@@ -360,13 +374,11 @@ func (a *agent) reportMetadataLoop(ctx context.Context) {
360374

361375
select {
362376
case <-ctx.Done():
363-
return 0, nil
364377
case metadataResults <- metadataResultAndKey{
365378
key: md.Key,
366379
result: a.collectMetadata(ctx, md),
367380
}:
368381
}
369-
return 0, nil
370382
})
371383
}
372384
}

0 commit comments

Comments
 (0)