reportLoop programs the source (tailnet.Conn) to send it stats via the callback, then reports them to the dest. It's intended to be called within the larger retry loop that establishes a connection to the agent API, then passes that connection to go routines like this that use it. There is no retr
(ctx context.Context, dest statsDest)
| 75 | // this that use it. There is no retry and we fail on the first error since |
| 76 | // this will be inside a larger retry loop. |
| 77 | func (s *statsReporter) reportLoop(ctx context.Context, dest statsDest) error { |
| 78 | // send an initial, blank report to get the interval |
| 79 | resp, err := dest.UpdateStats(ctx, &proto.UpdateStatsRequest{}) |
| 80 | if err != nil { |
| 81 | return xerrors.Errorf("initial update: %w", err) |
| 82 | } |
| 83 | s.lastInterval = resp.ReportInterval.AsDuration() |
| 84 | s.source.SetConnStatsCallback(s.lastInterval, maxConns, s.callback) |
| 85 | |
| 86 | // use a separate goroutine to monitor the context so that we notice immediately, rather than |
| 87 | // waiting for the next callback (which might never come if we are closing!) |
| 88 | ctxDone := false |
| 89 | go func() { |
| 90 | <-ctx.Done() |
| 91 | s.L.Lock() |
| 92 | defer s.L.Unlock() |
| 93 | ctxDone = true |
| 94 | s.Broadcast() |
| 95 | }() |
| 96 | defer s.logger.Debug(ctx, "reportLoop exiting") |
| 97 | |
| 98 | s.L.Lock() |
| 99 | defer s.L.Unlock() |
| 100 | for { |
| 101 | for !s.unreported && !ctxDone { |
| 102 | s.Wait() |
| 103 | } |
| 104 | if ctxDone { |
| 105 | return nil |
| 106 | } |
| 107 | s.unreported = false |
| 108 | if err = s.reportLocked(ctx, dest, s.networkStats); err != nil { |
| 109 | return xerrors.Errorf("report stats: %w", err) |
| 110 | } |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | func (s *statsReporter) reportLocked( |
| 115 | ctx context.Context, dest statsDest, networkStats map[netlogtype.Connection]netlogtype.Counts, |