mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-14 09:29:07 +02:00
[management] Record activity for a peer that was never seen
peer_status_last_seen is nullable — Status is an embedded pointer, so a peer stored without one leaves the column NULL — and NULL loses the cutoff comparison, so such a peer was silently skipped forever instead of recording its first activity.
This commit is contained in:
@@ -611,12 +611,14 @@ func (s *SqlStore) ApproveAccountPeers(ctx context.Context, accountID string) (i
|
|||||||
//
|
//
|
||||||
// staleBefore carries the caller's throttle into the same statement, so
|
// staleBefore carries the caller's throttle into the same statement, so
|
||||||
// concurrent requests for one peer collapse into a single write instead of
|
// concurrent requests for one peer collapse into a single write instead of
|
||||||
// each racing on its own stale read.
|
// each racing on its own stale read. The column is nullable — Status is an
|
||||||
|
// embedded pointer, so a peer stored without one leaves it NULL — and NULL
|
||||||
|
// loses every comparison, hence the explicit branch for a peer never seen.
|
||||||
func (s *SqlStore) RefreshPeerLastSeen(ctx context.Context, accountID, peerID string, staleBefore time.Time) (bool, error) {
|
func (s *SqlStore) RefreshPeerLastSeen(ctx context.Context, accountID, peerID string, staleBefore time.Time) (bool, error) {
|
||||||
result := s.db.WithContext(ctx).
|
result := s.db.WithContext(ctx).
|
||||||
Model(&nbpeer.Peer{}).
|
Model(&nbpeer.Peer{}).
|
||||||
Where(accountAndIDQueryCondition, accountID, peerID).
|
Where(accountAndIDQueryCondition, accountID, peerID).
|
||||||
Where("peer_status_last_seen < ?", staleBefore).
|
Where("(peer_status_last_seen IS NULL OR peer_status_last_seen < ?)", staleBefore).
|
||||||
Update("peer_status_last_seen", gorm.Expr("CURRENT_TIMESTAMP"))
|
Update("peer_status_last_seen", gorm.Expr("CURRENT_TIMESTAMP"))
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return false, status.Errorf(status.Internal, "refresh peer last seen: %v", result.Error)
|
return false, status.Errorf(status.Internal, "refresh peer last seen: %v", result.Error)
|
||||||
|
|||||||
@@ -65,6 +65,26 @@ func TestRefreshPeerLastSeenHonoursCutoff(t *testing.T) {
|
|||||||
assert.WithinDuration(t, stored, peer.Status.LastSeen.UTC(), time.Second, "last seen must be left where it was")
|
assert.WithinDuration(t, stored, peer.Status.LastSeen.UTC(), time.Second, "last seen must be left where it was")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRefreshPeerLastSeenRecordsNeverSeenPeer covers the nullable column. Status
|
||||||
|
// is an embedded pointer, so a peer stored without one leaves last seen NULL,
|
||||||
|
// and NULL loses the cutoff comparison — such a peer would never record its
|
||||||
|
// first activity.
|
||||||
|
func TestRefreshPeerLastSeenRecordsNeverSeenPeer(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
store := newActivityTestStore(t)
|
||||||
|
stored := activityPeer(time.Time{})
|
||||||
|
stored.Status = nil
|
||||||
|
require.NoError(t, store.AddPeerToAccount(ctx, stored))
|
||||||
|
|
||||||
|
refreshed, err := store.RefreshPeerLastSeen(ctx, activityAccountID, "activityPeer", time.Now().UTC().Add(-time.Hour))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.True(t, refreshed, "a peer that was never seen must record its first activity")
|
||||||
|
|
||||||
|
peer, err := store.GetPeerByID(ctx, LockingStrengthNone, activityAccountID, "activityPeer")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.WithinDuration(t, time.Now().UTC(), peer.Status.LastSeen.UTC(), time.Minute, "last seen should be stamped at write time")
|
||||||
|
}
|
||||||
|
|
||||||
// TestRefreshPeerLastSeenLeavesSessionStateAlone pins the column boundary: the
|
// TestRefreshPeerLastSeenLeavesSessionStateAlone pins the column boundary: the
|
||||||
// connected flag and the session token belong to the sync stream that owns the
|
// connected flag and the session token belong to the sync stream that owns the
|
||||||
// peer's session, and a blind write here would corrupt its fencing. This is why
|
// peer's session, and a blind write here would corrupt its fencing. This is why
|
||||||
|
|||||||
Reference in New Issue
Block a user