mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-16 19:49:56 +00:00
Status(GetFullPeerStatus=true) RPCs trigger a full health probe
(network round-trips to management, signal and the relays). The
desktop UI issues these frequently and concurrently, and a burst of
parallel Get() calls each fired its own probe — the lastProbe guard
was unprotected against concurrent access and only advanced when every
component was healthy, so a sustained unhealthy state (e.g. relay down)
disabled the throttle entirely and let every call re-probe.
Extract the throttle/single-flight policy into probeThrottle:
- single-flight: only one probe runs at a time; concurrent callers
that piled up while it ran share its result instead of each
launching another, even when that probe failed.
- throttle: lastOK only advances on a fully successful probe, so
while anything is unhealthy callers keep probing frequently and
notice recovery quickly (preserved from the original design).
RunHealthProbes now takes a context so a caller that gives up (e.g. a
Status RPC whose client disconnected) cancels the in-flight STUN/TURN
probe instead of letting it run to its per-component timeout. The
engine's own lifetime ctx still applies independently.
58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
package server
|
|
|
|
import (
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
"github.com/netbirdio/netbird/client/proto"
|
|
)
|
|
|
|
// SubscribeStatus pushes a fresh StatusResponse on every connection state
|
|
// change. The first message is the current snapshot, so a re-subscribing
|
|
// client doesn't need to also call Status. Subsequent messages fire when
|
|
// the peer recorder reports any of: connected/disconnected/connecting,
|
|
// management or signal flip, address change, or peers list change.
|
|
//
|
|
// The change channel coalesces bursts to a single tick. If the consumer
|
|
// is slow the daemon drops extras (not blocks), and the next snapshot
|
|
// the consumer pulls already reflects everything.
|
|
func (s *Server) SubscribeStatus(req *proto.StatusRequest, stream proto.DaemonService_SubscribeStatusServer) error {
|
|
subID, ch := s.statusRecorder.SubscribeToStateChanges()
|
|
defer func() {
|
|
s.statusRecorder.UnsubscribeFromStateChanges(subID)
|
|
log.Debug("client unsubscribed from status updates")
|
|
}()
|
|
|
|
log.Debug("client subscribed to status updates")
|
|
|
|
if err := s.sendStatusSnapshot(req, stream); err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case _, ok := <-ch:
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if err := s.sendStatusSnapshot(req, stream); err != nil {
|
|
return err
|
|
}
|
|
case <-stream.Context().Done():
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *Server) sendStatusSnapshot(req *proto.StatusRequest, stream proto.DaemonService_SubscribeStatusServer) error {
|
|
resp, err := s.buildStatusResponse(stream.Context(), req)
|
|
if err != nil {
|
|
log.Warnf("build status snapshot for stream: %v", err)
|
|
return err
|
|
}
|
|
if err := stream.Send(resp); err != nil {
|
|
log.Warnf("send status snapshot to stream: %v", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|