Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7fc6fb5

Browse files
committed
feat(coderd/database): adddbrollup service to rollup insights
1 parent46bc0f0 commit7fc6fb5

File tree

7 files changed

+345
-4
lines changed

7 files changed

+345
-4
lines changed

‎coderd/coderd.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"github.com/coder/coder/v2/coderd/batchstats"
4848
"github.com/coder/coder/v2/coderd/database"
4949
"github.com/coder/coder/v2/coderd/database/dbauthz"
50+
"github.com/coder/coder/v2/coderd/database/dbrollup"
5051
"github.com/coder/coder/v2/coderd/database/dbtime"
5152
"github.com/coder/coder/v2/coderd/database/pubsub"
5253
"github.com/coder/coder/v2/coderd/externalauth"
@@ -179,6 +180,7 @@ type Options struct {
179180

180181
UpdateAgentMetricsfunc(ctx context.Context,labels prometheusmetrics.AgentMetricLabels,metrics []*agentproto.Stats_Metric)
181182
StatsBatcher*batchstats.Batcher
183+
DBRollupInterval time.Duration
182184

183185
WorkspaceAppsStatsCollectorOptions workspaceapps.StatsCollectorOptions
184186

@@ -338,6 +340,9 @@ func New(options *Options) *API {
338340
ifoptions.StatsBatcher==nil {
339341
panic("developer error: options.StatsBatcher is nil")
340342
}
343+
ifoptions.DBRollupInterval==0 {
344+
options.DBRollupInterval=dbrollup.DefaultInterval
345+
}
341346

342347
siteCacheDir:=options.CacheDir
343348
ifsiteCacheDir!="" {
@@ -404,7 +409,14 @@ func New(options *Options) *API {
404409
ctx,
405410
options.Logger.Named("acquirer"),
406411
options.Database,
407-
options.Pubsub),
412+
options.Pubsub,
413+
),
414+
rolluper:dbrollup.New(
415+
ctx,
416+
options.Logger,
417+
options.Database,
418+
options.DBRollupInterval,
419+
),
408420
}
409421

410422
api.AppearanceFetcher.Store(&appearance.DefaultFetcher)
@@ -1178,6 +1190,10 @@ type API struct {
11781190

11791191
statsBatcher*batchstats.Batcher
11801192

1193+
// rolluper rolls up template usage stats from raw agent and app
1194+
// stats. This is used to provide insights in the WebUI.
1195+
rolluper*dbrollup.Rolluper
1196+
11811197
Acquirer*provisionerdserver.Acquirer
11821198
}
11831199

@@ -1190,6 +1206,7 @@ func (api *API) Close() error {
11901206
api.WebsocketWaitGroup.Wait()
11911207
api.WebsocketWaitMutex.Unlock()
11921208

1209+
api.rolluper.Close()
11931210
api.metricsCache.Close()
11941211
ifapi.updateChecker!=nil {
11951212
api.updateChecker.Close()

‎coderd/coderdtest/coderdtest.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ type Options struct {
146146
WorkspaceAppsStatsCollectorOptions workspaceapps.StatsCollectorOptions
147147
AllowWorkspaceRenamesbool
148148
NewTickerfunc(duration time.Duration) (<-chan time.Time,func())
149+
DBRollupInterval time.Duration
149150
}
150151

151152
// New constructs a codersdk client connected to an in-memory API instance.
@@ -454,6 +455,7 @@ func NewOptions(t testing.TB, options *Options) (func(http.Handler), context.Can
454455
WorkspaceAppsStatsCollectorOptions:options.WorkspaceAppsStatsCollectorOptions,
455456
AllowWorkspaceRenames:options.AllowWorkspaceRenames,
456457
NewTicker:options.NewTicker,
458+
DBRollupInterval:options.DBRollupInterval,
457459
}
458460
}
459461

‎coderd/database/dbgen/dbgen.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,38 @@ func WorkspaceApp(t testing.TB, db database.Store, orig database.WorkspaceApp) d
489489
returnresource
490490
}
491491

492+
funcWorkspaceAppStat(t testing.TB,db database.Store,orig database.WorkspaceAppStat) database.WorkspaceAppStat {
493+
// This is not going to be correct, but our query doesn't return the ID.
494+
id,err:=cryptorand.Int63()
495+
require.NoError(t,err,"generate id")
496+
497+
scheme:= database.WorkspaceAppStat{
498+
ID:takeFirst(orig.ID,id),
499+
UserID:takeFirst(orig.UserID,uuid.New()),
500+
WorkspaceID:takeFirst(orig.WorkspaceID,uuid.New()),
501+
AgentID:takeFirst(orig.AgentID,uuid.New()),
502+
AccessMethod:takeFirst(orig.AccessMethod,""),
503+
SlugOrPort:takeFirst(orig.SlugOrPort,""),
504+
SessionID:takeFirst(orig.SessionID,uuid.New()),
505+
SessionStartedAt:takeFirst(orig.SessionStartedAt,dbtime.Now().Add(-time.Minute)),
506+
SessionEndedAt:takeFirst(orig.SessionEndedAt,dbtime.Now()),
507+
Requests:takeFirst(orig.Requests,1),
508+
}
509+
err=db.InsertWorkspaceAppStats(genCtx, database.InsertWorkspaceAppStatsParams{
510+
UserID: []uuid.UUID{scheme.UserID},
511+
WorkspaceID: []uuid.UUID{scheme.WorkspaceID},
512+
AgentID: []uuid.UUID{scheme.AgentID},
513+
AccessMethod: []string{scheme.AccessMethod},
514+
SlugOrPort: []string{scheme.SlugOrPort},
515+
SessionID: []uuid.UUID{scheme.SessionID},
516+
SessionStartedAt: []time.Time{scheme.SessionStartedAt},
517+
SessionEndedAt: []time.Time{scheme.SessionEndedAt},
518+
Requests: []int32{scheme.Requests},
519+
})
520+
require.NoError(t,err,"insert workspace agent stat")
521+
returnscheme
522+
}
523+
492524
funcWorkspaceResource(t testing.TB,db database.Store,orig database.WorkspaceResource) database.WorkspaceResource {
493525
resource,err:=db.InsertWorkspaceResource(genCtx, database.InsertWorkspaceResourceParams{
494526
ID:takeFirst(orig.ID,uuid.New()),

‎coderd/database/dbpurge/dbpurge.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const (
2424
// This is for cleaning up old, unused resources from the database that take up space.
2525
funcNew(ctx context.Context,logger slog.Logger,db database.Store) io.Closer {
2626
closed:=make(chanstruct{})
27+
logger=logger.Named("dbpurge")
2728

2829
ctx,cancelFunc:=context.WithCancel(ctx)
2930
//nolint:gocritic // The system purges old db records without user input.

‎coderd/database/dbrollup/dbrollup.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package dbrollup
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"golang.org/x/sync/errgroup"
8+
9+
"cdr.dev/slog"
10+
11+
"github.com/coder/coder/v2/coderd/database"
12+
"github.com/coder/coder/v2/coderd/database/dbauthz"
13+
)
14+
15+
const (
16+
// DefaultInterval is the default time between rollups.
17+
// Rollups will be synchronized with the clock so that
18+
// they happen 13:00, 13:05, 13:10, etc.
19+
DefaultInterval=5*time.Minute
20+
)
21+
22+
typeRolluperstruct {
23+
cancel context.CancelFunc
24+
closedchanstruct{}
25+
db database.Store
26+
logger slog.Logger
27+
}
28+
29+
// New creates a new DB rollup service that periodically runs rollup queries.
30+
// It is the caller's responsibility to call Close on the returned instance.
31+
//
32+
// This is for e.g. generating insights data (template_usage_stats) from
33+
// raw data (workspace_agent_stats, workspace_app_stats).
34+
funcNew(ctx context.Context,logger slog.Logger,db database.Store,interval time.Duration)*Rolluper {
35+
ctx,cancel:=context.WithCancel(ctx)
36+
37+
r:=&Rolluper{
38+
cancel:cancel,
39+
closed:make(chanstruct{}),
40+
db:db,
41+
logger:logger.Named("dbrollup"),
42+
}
43+
44+
//nolint:gocritic // The system rolls up database tables without user input.
45+
ctx=dbauthz.AsSystemRestricted(ctx)
46+
gor.start(ctx,interval)
47+
48+
returnr
49+
}
50+
51+
func (r*Rolluper)start(ctx context.Context,interval time.Duration) {
52+
deferclose(r.closed)
53+
54+
do:=func() {
55+
vareg errgroup.Group
56+
57+
r.logger.Debug(ctx,"rolling up data")
58+
now:=time.Now()
59+
60+
// Track whether or not we performed a rollup (we got the advisory lock).
61+
templateUsageStats:=false
62+
63+
eg.Go(func()error {
64+
returnr.db.InTx(func(tx database.Store)error {
65+
// Acquire a lock to ensure that only one instance of
66+
// the rollup is running at a time.
67+
ok,err:=tx.TryAcquireLock(ctx,database.LockIDDBRollup)
68+
iferr!=nil {
69+
returnerr
70+
}
71+
if!ok {
72+
returnnil
73+
}
74+
75+
templateUsageStats=true
76+
returntx.UpsertTemplateUsageStats(ctx)
77+
},nil)
78+
})
79+
80+
err:=eg.Wait()
81+
iferr!=nil {
82+
ifdatabase.IsQueryCanceledError(err) {
83+
return
84+
}
85+
r.logger.Error(ctx,"failed to rollup data",slog.Error(err))
86+
}else {
87+
r.logger.Debug(ctx,
88+
"rolled up data",
89+
slog.F("took",time.Since(now)),
90+
slog.F("template_usage_stats",templateUsageStats),
91+
)
92+
}
93+
}
94+
95+
// Perform do immediately and on every tick of the ticker,
96+
// disregarding the execution time of do. This ensure that
97+
// the rollup is performed every interval assuming do does
98+
// not take longer than the interval to execute.
99+
t:=time.NewTicker(time.Microsecond)
100+
defert.Stop()
101+
for {
102+
select {
103+
case<-ctx.Done():
104+
return
105+
case<-t.C:
106+
// Ensure we're on the interval.
107+
now:=time.Now()
108+
next:=now.Add(interval).Truncate(interval)// Ensure we're on the interval and synced with the clock.
109+
d:=next.Sub(now)
110+
// Safety check.
111+
ifd==0 {
112+
d=interval
113+
}
114+
t.Reset(d)
115+
116+
do()
117+
118+
r.logger.Debug(ctx,"next rollup at",slog.F("next",next))
119+
}
120+
}
121+
}
122+
123+
func (r*Rolluper)Close()error {
124+
r.cancel()
125+
<-r.closed
126+
returnnil
127+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp