Skip to content

Commit 0dd942e

Browse files
authored
fix: stop incrementing activity on empty agent stats (#15204)
1 parent cd890aa commit 0dd942e

File tree

8 files changed

+107
-86
lines changed

8 files changed

+107
-86
lines changed

coderd/agentapi/stats_test.go

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ func TestUpdateStates(t *testing.T) {
7070
}
7171
batcher = &workspacestatstest.StatsBatcher{}
7272
updateAgentMetricsFnCalled = false
73+
tickCh = make(chan time.Time)
74+
flushCh = make(chan int, 1)
75+
wut = workspacestats.NewTracker(dbM,
76+
workspacestats.TrackerWithTickFlush(tickCh, flushCh),
77+
)
7378

7479
req = &agentproto.UpdateStatsRequest{
7580
Stats: &agentproto.Stats{
@@ -109,6 +114,7 @@ func TestUpdateStates(t *testing.T) {
109114
Database: dbM,
110115
Pubsub: ps,
111116
StatsBatcher: batcher,
117+
UsageTracker: wut,
112118
TemplateScheduleStore: templateScheduleStorePtr(templateScheduleStore),
113119
UpdateAgentMetricsFn: func(ctx context.Context, labels prometheusmetrics.AgentMetricLabels, metrics []*agentproto.Stats_Metric) {
114120
updateAgentMetricsFnCalled = true
@@ -126,25 +132,26 @@ func TestUpdateStates(t *testing.T) {
126132
return now
127133
},
128134
}
135+
defer wut.Close()
129136

130137
// Workspace gets fetched.
131138
dbM.EXPECT().GetWorkspaceByAgentID(gomock.Any(), agent.ID).Return(workspace, nil)
132139

140+
// User gets fetched to hit the UpdateAgentMetricsFn.
141+
dbM.EXPECT().GetUserByID(gomock.Any(), user.ID).Return(user, nil)
142+
133143
// We expect an activity bump because ConnectionCount > 0.
134144
dbM.EXPECT().ActivityBumpWorkspace(gomock.Any(), database.ActivityBumpWorkspaceParams{
135145
WorkspaceID: workspace.ID,
136146
NextAutostart: time.Time{}.UTC(),
137147
}).Return(nil)
138148

139149
// Workspace last used at gets bumped.
140-
dbM.EXPECT().UpdateWorkspaceLastUsedAt(gomock.Any(), database.UpdateWorkspaceLastUsedAtParams{
141-
ID: workspace.ID,
150+
dbM.EXPECT().BatchUpdateWorkspaceLastUsedAt(gomock.Any(), database.BatchUpdateWorkspaceLastUsedAtParams{
151+
IDs: []uuid.UUID{workspace.ID},
142152
LastUsedAt: now,
143153
}).Return(nil)
144154

145-
// User gets fetched to hit the UpdateAgentMetricsFn.
146-
dbM.EXPECT().GetUserByID(gomock.Any(), user.ID).Return(user, nil)
147-
148155
// Ensure that pubsub notifications are sent.
149156
notifyDescription := make(chan []byte)
150157
ps.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, description []byte) {
@@ -159,6 +166,10 @@ func TestUpdateStates(t *testing.T) {
159166
ReportInterval: durationpb.New(10 * time.Second),
160167
}, resp)
161168

169+
tickCh <- now
170+
count := <-flushCh
171+
require.Equal(t, 1, count, "expected one flush with one id")
172+
162173
batcher.Mu.Lock()
163174
defer batcher.Mu.Unlock()
164175
require.Equal(t, int64(1), batcher.Called)
@@ -211,6 +222,7 @@ func TestUpdateStates(t *testing.T) {
211222
StatsReporter: workspacestats.NewReporter(workspacestats.ReporterOptions{
212223
Database: dbM,
213224
Pubsub: ps,
225+
UsageTracker: workspacestats.NewTracker(dbM),
214226
StatsBatcher: batcher,
215227
TemplateScheduleStore: templateScheduleStorePtr(templateScheduleStore),
216228
// Ignored when nil.
@@ -225,12 +237,6 @@ func TestUpdateStates(t *testing.T) {
225237
// Workspace gets fetched.
226238
dbM.EXPECT().GetWorkspaceByAgentID(gomock.Any(), agent.ID).Return(workspace, nil)
227239

228-
// Workspace last used at gets bumped.
229-
dbM.EXPECT().UpdateWorkspaceLastUsedAt(gomock.Any(), database.UpdateWorkspaceLastUsedAtParams{
230-
ID: workspace.ID,
231-
LastUsedAt: now,
232-
}).Return(nil)
233-
234240
_, err := api.UpdateStats(context.Background(), req)
235241
require.NoError(t, err)
236242
})
@@ -306,6 +312,11 @@ func TestUpdateStates(t *testing.T) {
306312
}
307313
batcher = &workspacestatstest.StatsBatcher{}
308314
updateAgentMetricsFnCalled = false
315+
tickCh = make(chan time.Time)
316+
flushCh = make(chan int, 1)
317+
wut = workspacestats.NewTracker(dbM,
318+
workspacestats.TrackerWithTickFlush(tickCh, flushCh),
319+
)
309320

310321
req = &agentproto.UpdateStatsRequest{
311322
Stats: &agentproto.Stats{
@@ -325,6 +336,7 @@ func TestUpdateStates(t *testing.T) {
325336
StatsReporter: workspacestats.NewReporter(workspacestats.ReporterOptions{
326337
Database: dbM,
327338
Pubsub: ps,
339+
UsageTracker: wut,
328340
StatsBatcher: batcher,
329341
TemplateScheduleStore: templateScheduleStorePtr(templateScheduleStore),
330342
UpdateAgentMetricsFn: func(ctx context.Context, labels prometheusmetrics.AgentMetricLabels, metrics []*agentproto.Stats_Metric) {
@@ -343,6 +355,7 @@ func TestUpdateStates(t *testing.T) {
343355
return now
344356
},
345357
}
358+
defer wut.Close()
346359

347360
// Workspace gets fetched.
348361
dbM.EXPECT().GetWorkspaceByAgentID(gomock.Any(), agent.ID).Return(workspace, nil)
@@ -355,9 +368,9 @@ func TestUpdateStates(t *testing.T) {
355368
}).Return(nil)
356369

357370
// Workspace last used at gets bumped.
358-
dbM.EXPECT().UpdateWorkspaceLastUsedAt(gomock.Any(), database.UpdateWorkspaceLastUsedAtParams{
359-
ID: workspace.ID,
360-
LastUsedAt: now,
371+
dbM.EXPECT().BatchUpdateWorkspaceLastUsedAt(gomock.Any(), database.BatchUpdateWorkspaceLastUsedAtParams{
372+
IDs: []uuid.UUID{workspace.ID},
373+
LastUsedAt: now.UTC(),
361374
}).Return(nil)
362375

363376
// User gets fetched to hit the UpdateAgentMetricsFn.
@@ -369,6 +382,10 @@ func TestUpdateStates(t *testing.T) {
369382
ReportInterval: durationpb.New(15 * time.Second),
370383
}, resp)
371384

385+
tickCh <- now
386+
count := <-flushCh
387+
require.Equal(t, 1, count, "expected one flush with one id")
388+
372389
require.True(t, updateAgentMetricsFnCalled)
373390
})
374391

@@ -392,6 +409,11 @@ func TestUpdateStates(t *testing.T) {
392409
}
393410
batcher = &workspacestatstest.StatsBatcher{}
394411
updateAgentMetricsFnCalled = false
412+
tickCh = make(chan time.Time)
413+
flushCh = make(chan int, 1)
414+
wut = workspacestats.NewTracker(dbM,
415+
workspacestats.TrackerWithTickFlush(tickCh, flushCh),
416+
)
395417

396418
req = &agentproto.UpdateStatsRequest{
397419
Stats: &agentproto.Stats{
@@ -422,6 +444,7 @@ func TestUpdateStates(t *testing.T) {
422444
},
423445
}
424446
)
447+
defer wut.Close()
425448
api := agentapi.StatsAPI{
426449
AgentFn: func(context.Context) (database.WorkspaceAgent, error) {
427450
return agent, nil
@@ -431,6 +454,7 @@ func TestUpdateStates(t *testing.T) {
431454
Database: dbM,
432455
Pubsub: ps,
433456
StatsBatcher: batcher,
457+
UsageTracker: wut,
434458
TemplateScheduleStore: templateScheduleStorePtr(templateScheduleStore),
435459
UpdateAgentMetricsFn: func(ctx context.Context, labels prometheusmetrics.AgentMetricLabels, metrics []*agentproto.Stats_Metric) {
436460
updateAgentMetricsFnCalled = true
@@ -462,8 +486,8 @@ func TestUpdateStates(t *testing.T) {
462486
}).Return(nil)
463487

464488
// Workspace last used at gets bumped.
465-
dbM.EXPECT().UpdateWorkspaceLastUsedAt(gomock.Any(), database.UpdateWorkspaceLastUsedAtParams{
466-
ID: workspace.ID,
489+
dbM.EXPECT().BatchUpdateWorkspaceLastUsedAt(gomock.Any(), database.BatchUpdateWorkspaceLastUsedAtParams{
490+
IDs: []uuid.UUID{workspace.ID},
467491
LastUsedAt: now,
468492
}).Return(nil)
469493

@@ -484,6 +508,10 @@ func TestUpdateStates(t *testing.T) {
484508
ReportInterval: durationpb.New(10 * time.Second),
485509
}, resp)
486510

511+
tickCh <- now
512+
count := <-flushCh
513+
require.Equal(t, 1, count, "expected one flush with one id")
514+
487515
batcher.Mu.Lock()
488516
defer batcher.Mu.Unlock()
489517
require.EqualValues(t, 1, batcher.Called)

coderd/insights_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -700,14 +700,13 @@ func TestTemplateInsights_Golden(t *testing.T) {
700700
connectionCount = 0
701701
}
702702
for createdAt.Before(stat.endedAt) {
703-
err = batcher.Add(createdAt, workspace.agentID, workspace.template.id, workspace.user.(*testUser).sdk.ID, workspace.id, &agentproto.Stats{
703+
batcher.Add(createdAt, workspace.agentID, workspace.template.id, workspace.user.(*testUser).sdk.ID, workspace.id, &agentproto.Stats{
704704
ConnectionCount: connectionCount,
705705
SessionCountVscode: stat.sessionCountVSCode,
706706
SessionCountJetbrains: stat.sessionCountJetBrains,
707707
SessionCountReconnectingPty: stat.sessionCountReconnectingPTY,
708708
SessionCountSsh: stat.sessionCountSSH,
709709
}, false)
710-
require.NoError(t, err, "want no error inserting agent stats")
711710
createdAt = createdAt.Add(30 * time.Second)
712711
}
713712
}
@@ -1599,14 +1598,13 @@ func TestUserActivityInsights_Golden(t *testing.T) {
15991598
connectionCount = 0
16001599
}
16011600
for createdAt.Before(stat.endedAt) {
1602-
err = batcher.Add(createdAt, workspace.agentID, workspace.template.id, workspace.user.(*testUser).sdk.ID, workspace.id, &agentproto.Stats{
1601+
batcher.Add(createdAt, workspace.agentID, workspace.template.id, workspace.user.(*testUser).sdk.ID, workspace.id, &agentproto.Stats{
16031602
ConnectionCount: connectionCount,
16041603
SessionCountVscode: stat.sessionCountVSCode,
16051604
SessionCountJetbrains: stat.sessionCountJetBrains,
16061605
SessionCountReconnectingPty: stat.sessionCountReconnectingPTY,
16071606
SessionCountSsh: stat.sessionCountSSH,
16081607
}, false)
1609-
require.NoError(t, err, "want no error inserting agent stats")
16101608
createdAt = createdAt.Add(30 * time.Second)
16111609
}
16121610
}

coderd/workspaceagentsrpc_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package coderd_test
33
import (
44
"context"
55
"testing"
6+
"time"
67

78
"github.com/stretchr/testify/assert"
89
"github.com/stretchr/testify/require"
@@ -11,6 +12,7 @@ import (
1112
"github.com/coder/coder/v2/coderd/coderdtest"
1213
"github.com/coder/coder/v2/coderd/database"
1314
"github.com/coder/coder/v2/coderd/database/dbfake"
15+
"github.com/coder/coder/v2/coderd/database/dbtime"
1416
"github.com/coder/coder/v2/codersdk/agentsdk"
1517
"github.com/coder/coder/v2/provisionersdk/proto"
1618
"github.com/coder/coder/v2/testutil"
@@ -20,7 +22,12 @@ import (
2022
func TestWorkspaceAgentReportStats(t *testing.T) {
2123
t.Parallel()
2224

23-
client, db := coderdtest.NewWithDatabase(t, nil)
25+
tickCh := make(chan time.Time)
26+
flushCh := make(chan int, 1)
27+
client, db := coderdtest.NewWithDatabase(t, &coderdtest.Options{
28+
WorkspaceUsageTrackerFlush: flushCh,
29+
WorkspaceUsageTrackerTick: tickCh,
30+
})
2431
user := coderdtest.CreateFirstUser(t, client)
2532
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
2633
OrganizationID: user.OrganizationID,
@@ -53,6 +60,10 @@ func TestWorkspaceAgentReportStats(t *testing.T) {
5360
})
5461
require.NoError(t, err)
5562

63+
tickCh <- dbtime.Now()
64+
count := <-flushCh
65+
require.Equal(t, 1, count, "expected one flush with one id")
66+
5667
newWorkspace, err := client.Workspace(context.Background(), r.Workspace.ID)
5768
require.NoError(t, err)
5869

coderd/workspacestats/batcher.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const (
2525
)
2626

2727
type Batcher interface {
28-
Add(now time.Time, agentID uuid.UUID, templateID uuid.UUID, userID uuid.UUID, workspaceID uuid.UUID, st *agentproto.Stats, usage bool) error
28+
Add(now time.Time, agentID uuid.UUID, templateID uuid.UUID, userID uuid.UUID, workspaceID uuid.UUID, st *agentproto.Stats, usage bool)
2929
}
3030

3131
// DBBatcher holds a buffer of agent stats and periodically flushes them to
@@ -139,7 +139,7 @@ func (b *DBBatcher) Add(
139139
workspaceID uuid.UUID,
140140
st *agentproto.Stats,
141141
usage bool,
142-
) error {
142+
) {
143143
b.mu.Lock()
144144
defer b.mu.Unlock()
145145

@@ -176,7 +176,6 @@ func (b *DBBatcher) Add(
176176
b.flushLever <- struct{}{}
177177
b.flushForced.Store(true)
178178
}
179-
return nil
180179
}
181180

182181
// Run runs the batcher.

coderd/workspacestats/batcher_internal_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func TestBatchStats(t *testing.T) {
6363
// Given: a single data point is added for workspace
6464
t2 := t1.Add(time.Second)
6565
t.Logf("inserting 1 stat")
66-
require.NoError(t, b.Add(t2.Add(time.Millisecond), deps1.Agent.ID, deps1.User.ID, deps1.Template.ID, deps1.Workspace.ID, randStats(t), false))
66+
b.Add(t2.Add(time.Millisecond), deps1.Agent.ID, deps1.User.ID, deps1.Template.ID, deps1.Workspace.ID, randStats(t), false)
6767

6868
// When: it becomes time to report stats
6969
// Signal a tick and wait for a flush to complete.
@@ -87,9 +87,9 @@ func TestBatchStats(t *testing.T) {
8787
t.Logf("inserting %d stats", defaultBufferSize)
8888
for i := 0; i < defaultBufferSize; i++ {
8989
if i%2 == 0 {
90-
require.NoError(t, b.Add(t3.Add(time.Millisecond), deps1.Agent.ID, deps1.User.ID, deps1.Template.ID, deps1.Workspace.ID, randStats(t), false))
90+
b.Add(t3.Add(time.Millisecond), deps1.Agent.ID, deps1.User.ID, deps1.Template.ID, deps1.Workspace.ID, randStats(t), false)
9191
} else {
92-
require.NoError(t, b.Add(t3.Add(time.Millisecond), deps2.Agent.ID, deps2.User.ID, deps2.Template.ID, deps2.Workspace.ID, randStats(t), false))
92+
b.Add(t3.Add(time.Millisecond), deps2.Agent.ID, deps2.User.ID, deps2.Template.ID, deps2.Workspace.ID, randStats(t), false)
9393
}
9494
}
9595
}()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy