Skip to content

Commit c87fffc

Browse files
committed
feat: Add a flag to disable storing agent and app stats
This effectively disables template insights, as there will be no data. Prometheus remains unaffected; this only prevents our own storage of the stats.
1 parent 27c3ec0 commit c87fffc

File tree

16 files changed

+425
-67
lines changed

16 files changed

+425
-67
lines changed

cli/testdata/coder_server_--help.golden

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,12 @@ Configure TLS for your SMTP server target.
223223
--email-tls-starttls bool, $CODER_EMAIL_TLS_STARTTLS
224224
Enable STARTTLS to upgrade insecure SMTP connections using TLS.
225225

226+
INTROSPECTION OPTIONS:
227+
Configure logging, tracing, and metrics exporting.
228+
229+
--disable-template-insights bool, $CODER_DISABLE_TEMPLATE_INSIGHTS (default: false)
230+
Disable storage and display of template insights.
231+
226232
INTROSPECTION / HEALTH CHECK OPTIONS:
227233
--health-check-refresh duration, $CODER_HEALTH_CHECK_REFRESH (default: 10m0s)
228234
Refresh interval for healthchecks.

cli/testdata/server-config.yaml.golden

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ autobuildPollInterval: 1m0s
190190
# Interval to poll for hung and pending jobs and automatically terminate them.
191191
# (default: 1m0s, type: duration)
192192
jobHangDetectorInterval: 1m0s
193+
# Configure logging, tracing, and metrics exporting.
193194
introspection:
195+
# Disable storage and display of template insights.
196+
# (default: false, type: bool)
197+
disableTemplateInsights: false
194198
prometheus:
195199
# Serve prometheus metrics on the address defined by prometheus address.
196200
# (default: <unset>, type: bool)

coderd/agentapi/stats_test.go

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

547676
func templateScheduleStorePtr(store schedule.TemplateScheduleStore) *atomic.Pointer[schedule.TemplateScheduleStore] {

coderd/apidoc/docs.go

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -768,14 +768,15 @@ func New(options *Options) *API {
768768
}
769769

770770
api.statsReporter = workspacestats.NewReporter(workspacestats.ReporterOptions{
771-
Database: options.Database,
772-
Logger: options.Logger.Named("workspacestats"),
773-
Pubsub: options.Pubsub,
774-
TemplateScheduleStore: options.TemplateScheduleStore,
775-
StatsBatcher: options.StatsBatcher,
776-
UsageTracker: options.WorkspaceUsageTracker,
777-
UpdateAgentMetricsFn: options.UpdateAgentMetrics,
778-
AppStatBatchSize: workspaceapps.DefaultStatsDBReporterBatchSize,
771+
Database: options.Database,
772+
Logger: options.Logger.Named("workspacestats"),
773+
Pubsub: options.Pubsub,
774+
TemplateScheduleStore: options.TemplateScheduleStore,
775+
StatsBatcher: options.StatsBatcher,
776+
UsageTracker: options.WorkspaceUsageTracker,
777+
UpdateAgentMetricsFn: options.UpdateAgentMetrics,
778+
AppStatBatchSize: workspaceapps.DefaultStatsDBReporterBatchSize,
779+
DisableDatabaseStorage: options.DeploymentValues.DisableTemplateInsights.Value(),
779780
})
780781
workspaceAppsLogger := options.Logger.Named("workspaceapps")
781782
if options.WorkspaceAppsStatsCollectorOptions.Logger == nil {

0 commit comments

Comments
 (0)