@@ -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 )
0 commit comments