use peer.Status.LastSeen field to detect stale peer.Status.Connected values

Signed-off-by: Dmitri Dolguikh <dmitri.external@netbird.io>
This commit is contained in:
Dmitri Dolguikh
2026-08-20 16:06:50 +02:00
parent a144e8c144
commit cf4b9f8333
6 changed files with 39 additions and 0 deletions

View File

@@ -423,6 +423,7 @@ func (s *Server) handleUpdates(ctx context.Context, accountID string, peerKey wg
// Create a debouncer for this peer connection
debouncer := NewUpdateDebouncer(1000 * time.Millisecond)
defer debouncer.Stop()
ticker := time.NewTicker(1 * time.Second)
for {
select {
@@ -448,6 +449,8 @@ func (s *Server) handleUpdates(ctx context.Context, accountID string, peerKey wg
}
}
s.accountManager.RefreshPeerLastSeen(ctx, accountID, peer.ID)
// Timer expired - quiet period reached, send pending updates if any
case <-debouncer.TimerChannel():
pendingUpdates := debouncer.GetPendingUpdates()
@@ -462,6 +465,11 @@ func (s *Server) handleUpdates(ctx context.Context, accountID string, peerKey wg
}
}
s.accountManager.RefreshPeerLastSeen(ctx, accountID, peer.ID)
case <-ticker.C:
s.accountManager.RefreshPeerLastSeen(ctx, accountID, peer.ID)
// condition when client <-> server connection has been terminated
case <-srv.Context().Done():
// happens when connection drops, e.g. client disconnects

View File

@@ -64,6 +64,7 @@ type Manager interface {
GetPeers(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
MarkPeerConnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
MarkPeerDisconnected(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
RefreshPeerLastSeen(ctx context.Context, accountId, peerId string) error
DeletePeer(ctx context.Context, accountID, peerID, userID string) error
UpdatePeer(ctx context.Context, accountID, userID string, p *nbpeer.Peer) (*nbpeer.Peer, error)
UpdatePeerIP(ctx context.Context, accountID, userID, peerID string, newIP netip.Addr) error

View File

@@ -1382,6 +1382,20 @@ func (mr *MockManagerMockRecorder) OnPeerDisconnected(ctx, accountID, peerPubKey
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPeerDisconnected", reflect.TypeOf((*MockManager)(nil).OnPeerDisconnected), ctx, accountID, peerPubKey, streamStartTime)
}
// RefreshPeerLastSeen mocks base method.
func (m *MockManager) RefreshPeerLastSeen(ctx context.Context, accountId, peerId string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RefreshPeerLastSeen", ctx, accountId, peerId)
ret0, _ := ret[0].(error)
return ret0
}
// RefreshPeerLastSeen indicates an expected call of RefreshPeerLastSeen.
func (mr *MockManagerMockRecorder) RefreshPeerLastSeen(ctx, accountId, peerId any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshPeerLastSeen", reflect.TypeOf((*MockManager)(nil).RefreshPeerLastSeen), ctx, accountId, peerId)
}
// RegenerateUserInvite mocks base method.
func (m *MockManager) RegenerateUserInvite(ctx context.Context, accountID, initiatorUserID, inviteID string, expiresIn int) (*types.UserInvite, error) {
m.ctrl.T.Helper()

View File

@@ -41,6 +41,7 @@ type MockAccountManager struct {
GetPeersFunc func(ctx context.Context, accountID, userID, nameFilter, ipFilter string) ([]*nbpeer.Peer, error)
MarkPeerConnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64, nmap *types.NetworkMap) error
MarkPeerDisconnectedFunc func(ctx context.Context, peerKey string, accountID string, sessionStartedAt int64) error
RefreshPeerLastSeenFunc func(ctx context.Context, accountId, peerId string) error
SyncAndMarkPeerFunc func(ctx context.Context, accountID string, peerPubKey string, meta nbpeer.PeerSystemMeta, realIP net.IP, syncTime time.Time) (*nbpeer.Peer, *types.NetworkMap, []*posture.Checks, int64, error)
DeletePeerFunc func(ctx context.Context, accountID, peerKey, userID string) error
GetNetworkMapFunc func(ctx context.Context, peerKey string) (*types.NetworkMap, error)
@@ -360,6 +361,13 @@ func (am *MockAccountManager) MarkPeerDisconnected(ctx context.Context, peerKey
return status.Errorf(codes.Unimplemented, "method MarkPeerDisconnected is not implemented")
}
func (am *MockAccountManager) RefreshPeerLastSeen(ctx context.Context, accountId, peerId string) error {
if am.RefreshPeerLastSeenFunc != nil {
return am.RefreshPeerLastSeenFunc(ctx, accountId, peerId)
}
return status.Errorf(codes.Unimplemented, "method RefreshPeerLastSeen is not implemented")
}
// DeleteAccount mock implementation of DeleteAccount from server.AccountManager interface
func (am *MockAccountManager) DeleteAccount(ctx context.Context, accountID, userID string) error {
if am.DeleteAccountFunc != nil {

View File

@@ -136,6 +136,11 @@ func (am *DefaultAccountManager) MarkPeerConnected(ctx context.Context, peerPubK
return nil
}
func (am *DefaultAccountManager) RefreshPeerLastSeen(ctx context.Context, accountId, peerId string) error {
_, err := am.Store.RefreshPeerLastSeen(ctx, accountId, peerId, time.Now())
return err
}
// schedulePeerExpirations reschedules the account's login/inactivity expiration
// timers for an SSO peer that just connected.
func (am *DefaultAccountManager) schedulePeerExpirations(ctx context.Context, accountID string, peer *nbpeer.Peer) error {

View File

@@ -1962,6 +1962,9 @@ func (s *SqlStore) getPeers(ctx context.Context, accountID string) ([]nbpeer.Pee
}
if peerStatusConnected.Valid {
p.Status.Connected = peerStatusConnected.Bool
if peerStatusLastSeen.Valid {
p.Status.Connected = p.Status.Connected && peerStatusLastSeen.Time.Before(time.Now().Add(-5*time.Minute))
}
}
if peerStatusLoginExpired.Valid {
p.Status.LoginExpired = peerStatusLoginExpired.Bool