@@ -28,7 +28,7 @@ import (
2828 "github.com/coder/coder/v2/testutil"
2929)
3030
31- func TestUpdateStates (t * testing.T ) {
31+ func TestUpdateStats (t * testing.T ) {
3232 t .Parallel ()
3333
3434 var (
@@ -542,6 +542,135 @@ func TestUpdateStates(t *testing.T) {
542542 }
543543 require .True (t , updateAgentMetricsFnCalled )
544544 })
545+
546+ t .Run ("DropStats" , func (t * testing.T ) {
547+ t .Parallel ()
548+
549+ var (
550+ now = dbtime .Now ()
551+ dbM = dbmock .NewMockStore (gomock .NewController (t ))
552+ ps = pubsub .NewInMemory ()
553+
554+ templateScheduleStore = schedule.MockTemplateScheduleStore {
555+ GetFn : func (context.Context , database.Store , uuid.UUID ) (schedule.TemplateScheduleOptions , error ) {
556+ panic ("should not be called" )
557+ },
558+ SetFn : func (context.Context , database.Store , database.Template , schedule.TemplateScheduleOptions ) (database.Template , error ) {
559+ panic ("not implemented" )
560+ },
561+ }
562+ updateAgentMetricsFnCalled = false
563+ tickCh = make (chan time.Time )
564+ flushCh = make (chan int , 1 )
565+ wut = workspacestats .NewTracker (dbM ,
566+ workspacestats .TrackerWithTickFlush (tickCh , flushCh ),
567+ )
568+
569+ req = & agentproto.UpdateStatsRequest {
570+ Stats : & agentproto.Stats {
571+ ConnectionsByProto : map [string ]int64 {
572+ "tcp" : 1 ,
573+ "dean" : 2 ,
574+ },
575+ ConnectionCount : 3 ,
576+ ConnectionMedianLatencyMs : 23 ,
577+ RxPackets : 120 ,
578+ RxBytes : 1000 ,
579+ TxPackets : 130 ,
580+ TxBytes : 2000 ,
581+ SessionCountVscode : 1 ,
582+ SessionCountJetbrains : 2 ,
583+ SessionCountReconnectingPty : 3 ,
584+ SessionCountSsh : 4 ,
585+ Metrics : []* agentproto.Stats_Metric {
586+ {
587+ Name : "awesome metric" ,
588+ Value : 42 ,
589+ },
590+ {
591+ Name : "uncool metric" ,
592+ Value : 0 ,
593+ },
594+ },
595+ },
596+ }
597+ )
598+ api := agentapi.StatsAPI {
599+ AgentFn : func (context.Context ) (database.WorkspaceAgent , error ) {
600+ return agent , nil
601+ },
602+ Workspace : & workspaceAsCacheFields ,
603+ Database : dbM ,
604+ StatsReporter : workspacestats .NewReporter (workspacestats.ReporterOptions {
605+ Database : dbM ,
606+ Pubsub : ps ,
607+ StatsBatcher : nil , // Should not be called.
608+ UsageTracker : wut ,
609+ TemplateScheduleStore : templateScheduleStorePtr (templateScheduleStore ),
610+ UpdateAgentMetricsFn : func (ctx context.Context , labels prometheusmetrics.AgentMetricLabels , metrics []* agentproto.Stats_Metric ) {
611+ updateAgentMetricsFnCalled = true
612+ assert .Equal (t , prometheusmetrics.AgentMetricLabels {
613+ Username : user .Username ,
614+ WorkspaceName : workspace .Name ,
615+ AgentName : agent .Name ,
616+ TemplateName : template .Name ,
617+ }, labels )
618+ assert .Equal (t , req .Stats .Metrics , metrics )
619+ },
620+ DisableDatabaseStorage : true ,
621+ }),
622+ AgentStatsRefreshInterval : 10 * time .Second ,
623+ TimeNowFn : func () time.Time {
624+ return now
625+ },
626+ }
627+ defer wut .Close ()
628+
629+ // We expect an activity bump because ConnectionCount > 0.
630+ dbM .EXPECT ().ActivityBumpWorkspace (gomock .Any (), database.ActivityBumpWorkspaceParams {
631+ WorkspaceID : workspace .ID ,
632+ NextAutostart : time.Time {}.UTC (),
633+ }).Return (nil )
634+
635+ // Workspace last used at gets bumped.
636+ dbM .EXPECT ().BatchUpdateWorkspaceLastUsedAt (gomock .Any (), database.BatchUpdateWorkspaceLastUsedAtParams {
637+ IDs : []uuid.UUID {workspace .ID },
638+ LastUsedAt : now ,
639+ }).Return (nil )
640+
641+ // Ensure that pubsub notifications are sent.
642+ notifyDescription := make (chan struct {})
643+ ps .SubscribeWithErr (wspubsub .WorkspaceEventChannel (workspace .OwnerID ),
644+ wspubsub .HandleWorkspaceEvent (
645+ func (_ context.Context , e wspubsub.WorkspaceEvent , err error ) {
646+ if err != nil {
647+ return
648+ }
649+ if e .Kind == wspubsub .WorkspaceEventKindStatsUpdate && e .WorkspaceID == workspace .ID {
650+ go func () {
651+ notifyDescription <- struct {}{}
652+ }()
653+ }
654+ }))
655+
656+ resp , err := api .UpdateStats (context .Background (), req )
657+ require .NoError (t , err )
658+ require .Equal (t , & agentproto.UpdateStatsResponse {
659+ ReportInterval : durationpb .New (10 * time .Second ),
660+ }, resp )
661+
662+ tickCh <- now
663+ count := <- flushCh
664+ require .Equal (t , 1 , count , "expected one flush with one id" )
665+
666+ ctx := testutil .Context (t , testutil .WaitShort )
667+ select {
668+ case <- ctx .Done ():
669+ t .Error ("timed out while waiting for pubsub notification" )
670+ case <- notifyDescription :
671+ }
672+ require .True (t , updateAgentMetricsFnCalled )
673+ })
545674}
546675
547676func templateScheduleStorePtr (store schedule.TemplateScheduleStore ) * atomic.Pointer [schedule.TemplateScheduleStore ] {
0 commit comments