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

Commit03453b1

Browse files
authored
feat(coderd): add template app usage to insights (#9138)
Fixes#8658
1 parent4de7de4 commit03453b1

File tree

15 files changed

+740
-117
lines changed

15 files changed

+740
-117
lines changed

‎coderd/apidoc/docs.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/database/dbauthz/dbauthz.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,25 @@ func (q *querier) GetTailnetClientsForAgent(ctx context.Context, agentID uuid.UU
11731173
returnq.db.GetTailnetClientsForAgent(ctx,agentID)
11741174
}
11751175

1176+
func (q*querier)GetTemplateAppInsights(ctx context.Context,arg database.GetTemplateAppInsightsParams) ([]database.GetTemplateAppInsightsRow,error) {
1177+
for_,templateID:=rangearg.TemplateIDs {
1178+
template,err:=q.db.GetTemplateByID(ctx,templateID)
1179+
iferr!=nil {
1180+
returnnil,err
1181+
}
1182+
1183+
iferr:=q.authorizeContext(ctx,rbac.ActionUpdate,template);err!=nil {
1184+
returnnil,err
1185+
}
1186+
}
1187+
iflen(arg.TemplateIDs)==0 {
1188+
iferr:=q.authorizeContext(ctx,rbac.ActionUpdate,rbac.ResourceTemplate.All());err!=nil {
1189+
returnnil,err
1190+
}
1191+
}
1192+
returnq.db.GetTemplateAppInsights(ctx,arg)
1193+
}
1194+
11761195
// Only used by metrics cache.
11771196
func (q*querier)GetTemplateAverageBuildTime(ctx context.Context,arg database.GetTemplateAverageBuildTimeParams) (database.GetTemplateAverageBuildTimeRow,error) {
11781197
iferr:=q.authorizeContext(ctx,rbac.ActionRead,rbac.ResourceSystem);err!=nil {

‎coderd/database/dbfake/dbfake.go

Lines changed: 176 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,19 @@ func (q *FakeQuerier) getWorkspaceAgentsByResourceIDsNoLock(_ context.Context, r
549549
returnworkspaceAgents,nil
550550
}
551551

552+
func (q*FakeQuerier)getWorkspaceAppByAgentIDAndSlugNoLock(_ context.Context,arg database.GetWorkspaceAppByAgentIDAndSlugParams) (database.WorkspaceApp,error) {
553+
for_,app:=rangeq.workspaceApps {
554+
ifapp.AgentID!=arg.AgentID {
555+
continue
556+
}
557+
ifapp.Slug!=arg.Slug {
558+
continue
559+
}
560+
returnapp,nil
561+
}
562+
return database.WorkspaceApp{},sql.ErrNoRows
563+
}
564+
552565
func (q*FakeQuerier)getProvisionerJobByIDNoLock(_ context.Context,id uuid.UUID) (database.ProvisionerJob,error) {
553566
for_,provisionerJob:=rangeq.provisionerJobs {
554567
ifprovisionerJob.ID!=id {
@@ -1966,6 +1979,125 @@ func (*FakeQuerier) GetTailnetClientsForAgent(context.Context, uuid.UUID) ([]dat
19661979
returnnil,ErrUnimplemented
19671980
}
19681981

1982+
func (q*FakeQuerier)GetTemplateAppInsights(ctx context.Context,arg database.GetTemplateAppInsightsParams) ([]database.GetTemplateAppInsightsRow,error) {
1983+
err:=validateDatabaseType(arg)
1984+
iferr!=nil {
1985+
returnnil,err
1986+
}
1987+
1988+
q.mutex.RLock()
1989+
deferq.mutex.RUnlock()
1990+
1991+
typeappKeystruct {
1992+
AccessMethodstring
1993+
SlugOrPortstring
1994+
Slugstring
1995+
DisplayNamestring
1996+
Iconstring
1997+
}
1998+
typeuniqueKeystruct {
1999+
TemplateID uuid.UUID
2000+
UserID uuid.UUID
2001+
AgentID uuid.UUID
2002+
AppKeyappKey
2003+
}
2004+
2005+
appUsageIntervalsByUserAgentApp:=make(map[uniqueKey]map[time.Time]int64)
2006+
for_,s:=rangeq.workspaceAppStats {
2007+
// (was.session_started_at >= ts.from_ AND was.session_started_at < ts.to_)
2008+
// OR (was.session_ended_at > ts.from_ AND was.session_ended_at < ts.to_)
2009+
// OR (was.session_started_at < ts.from_ AND was.session_ended_at >= ts.to_)
2010+
if!(((s.SessionStartedAt.After(arg.StartTime)||s.SessionStartedAt.Equal(arg.StartTime))&&s.SessionStartedAt.Before(arg.EndTime))||
2011+
(s.SessionEndedAt.After(arg.StartTime)&&s.SessionEndedAt.Before(arg.EndTime))||
2012+
(s.SessionStartedAt.Before(arg.StartTime)&& (s.SessionEndedAt.After(arg.EndTime)||s.SessionEndedAt.Equal(arg.EndTime)))) {
2013+
continue
2014+
}
2015+
2016+
w,err:=q.getWorkspaceByIDNoLock(ctx,s.WorkspaceID)
2017+
iferr!=nil {
2018+
returnnil,err
2019+
}
2020+
2021+
app,_:=q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, database.GetWorkspaceAppByAgentIDAndSlugParams{
2022+
AgentID:s.AgentID,
2023+
Slug:s.SlugOrPort,
2024+
})
2025+
2026+
key:=uniqueKey{
2027+
TemplateID:w.TemplateID,
2028+
UserID:s.UserID,
2029+
AgentID:s.AgentID,
2030+
AppKey:appKey{
2031+
AccessMethod:s.AccessMethod,
2032+
SlugOrPort:s.SlugOrPort,
2033+
Slug:app.Slug,
2034+
DisplayName:app.DisplayName,
2035+
Icon:app.Icon,
2036+
},
2037+
}
2038+
ifappUsageIntervalsByUserAgentApp[key]==nil {
2039+
appUsageIntervalsByUserAgentApp[key]=make(map[time.Time]int64)
2040+
}
2041+
2042+
t:=s.SessionStartedAt.Truncate(5*time.Minute)
2043+
ift.Before(arg.StartTime) {
2044+
t=arg.StartTime
2045+
}
2046+
fort.Before(s.SessionEndedAt)&&t.Before(arg.EndTime) {
2047+
appUsageIntervalsByUserAgentApp[key][t]=300// 5 minutes.
2048+
t=t.Add(5*time.Minute)
2049+
}
2050+
}
2051+
2052+
appUsageTemplateIDs:=make(map[appKey]map[uuid.UUID]struct{})
2053+
appUsageUserIDs:=make(map[appKey]map[uuid.UUID]struct{})
2054+
appUsage:=make(map[appKey]int64)
2055+
foruniqueKey,usage:=rangeappUsageIntervalsByUserAgentApp {
2056+
for_,seconds:=rangeusage {
2057+
ifappUsageTemplateIDs[uniqueKey.AppKey]==nil {
2058+
appUsageTemplateIDs[uniqueKey.AppKey]=make(map[uuid.UUID]struct{})
2059+
}
2060+
appUsageTemplateIDs[uniqueKey.AppKey][uniqueKey.TemplateID]=struct{}{}
2061+
ifappUsageUserIDs[uniqueKey.AppKey]==nil {
2062+
appUsageUserIDs[uniqueKey.AppKey]=make(map[uuid.UUID]struct{})
2063+
}
2064+
appUsageUserIDs[uniqueKey.AppKey][uniqueKey.UserID]=struct{}{}
2065+
appUsage[uniqueKey.AppKey]+=seconds
2066+
}
2067+
}
2068+
2069+
varrows []database.GetTemplateAppInsightsRow
2070+
forappKey,usage:=rangeappUsage {
2071+
templateIDs:=make([]uuid.UUID,0,len(appUsageTemplateIDs[appKey]))
2072+
fortemplateID:=rangeappUsageTemplateIDs[appKey] {
2073+
templateIDs=append(templateIDs,templateID)
2074+
}
2075+
slices.SortFunc(templateIDs,func(a,b uuid.UUID)int {
2076+
returnslice.Ascending(a.String(),b.String())
2077+
})
2078+
activeUserIDs:=make([]uuid.UUID,0,len(appUsageUserIDs[appKey]))
2079+
foruserID:=rangeappUsageUserIDs[appKey] {
2080+
activeUserIDs=append(activeUserIDs,userID)
2081+
}
2082+
slices.SortFunc(activeUserIDs,func(a,b uuid.UUID)int {
2083+
returnslice.Ascending(a.String(),b.String())
2084+
})
2085+
2086+
rows=append(rows, database.GetTemplateAppInsightsRow{
2087+
TemplateIDs:templateIDs,
2088+
ActiveUserIDs:activeUserIDs,
2089+
AccessMethod:appKey.AccessMethod,
2090+
SlugOrPort:appKey.SlugOrPort,
2091+
DisplayName: sql.NullString{String:appKey.DisplayName,Valid:appKey.DisplayName!=""},
2092+
Icon: sql.NullString{String:appKey.Icon,Valid:appKey.Icon!=""},
2093+
IsApp:appKey.Slug!="",
2094+
UsageSeconds:usage,
2095+
})
2096+
}
2097+
2098+
returnrows,nil
2099+
}
2100+
19692101
func (q*FakeQuerier)GetTemplateAverageBuildTime(ctx context.Context,arg database.GetTemplateAverageBuildTimeParams) (database.GetTemplateAverageBuildTimeRow,error) {
19702102
iferr:=validateDatabaseType(arg);err!=nil {
19712103
return database.GetTemplateAverageBuildTimeRow{},err
@@ -2093,12 +2225,15 @@ func (q *FakeQuerier) GetTemplateDAUs(_ context.Context, arg database.GetTemplat
20932225
returnrs,nil
20942226
}
20952227

2096-
func (q*FakeQuerier)GetTemplateDailyInsights(_ context.Context,arg database.GetTemplateDailyInsightsParams) ([]database.GetTemplateDailyInsightsRow,error) {
2228+
func (q*FakeQuerier)GetTemplateDailyInsights(ctx context.Context,arg database.GetTemplateDailyInsightsParams) ([]database.GetTemplateDailyInsightsRow,error) {
20972229
err:=validateDatabaseType(arg)
20982230
iferr!=nil {
20992231
returnnil,err
21002232
}
21012233

2234+
q.mutex.RLock()
2235+
deferq.mutex.RUnlock()
2236+
21022237
typedailyStatstruct {
21032238
startTime,endTime time.Time
21042239
userSetmap[uuid.UUID]struct{}
@@ -2133,6 +2268,37 @@ func (q *FakeQuerier) GetTemplateDailyInsights(_ context.Context, arg database.G
21332268
}
21342269
}
21352270

2271+
for_,s:=rangeq.workspaceAppStats {
2272+
// (was.session_started_at >= ts.from_ AND was.session_started_at < ts.to_)
2273+
// OR (was.session_ended_at > ts.from_ AND was.session_ended_at < ts.to_)
2274+
// OR (was.session_started_at < ts.from_ AND was.session_ended_at >= ts.to_)
2275+
if!(((s.SessionStartedAt.After(arg.StartTime)||s.SessionStartedAt.Equal(arg.StartTime))&&s.SessionStartedAt.Before(arg.EndTime))||
2276+
(s.SessionEndedAt.After(arg.StartTime)&&s.SessionEndedAt.Before(arg.EndTime))||
2277+
(s.SessionStartedAt.Before(arg.StartTime)&& (s.SessionEndedAt.After(arg.EndTime)||s.SessionEndedAt.Equal(arg.EndTime)))) {
2278+
continue
2279+
}
2280+
2281+
for_,ds:=rangedailyStats {
2282+
// (was.session_started_at >= ts.from_ AND was.session_started_at < ts.to_)
2283+
// OR (was.session_ended_at > ts.from_ AND was.session_ended_at < ts.to_)
2284+
// OR (was.session_started_at < ts.from_ AND was.session_ended_at >= ts.to_)
2285+
if!(((s.SessionStartedAt.After(arg.StartTime)||s.SessionStartedAt.Equal(arg.StartTime))&&s.SessionStartedAt.Before(arg.EndTime))||
2286+
(s.SessionEndedAt.After(arg.StartTime)&&s.SessionEndedAt.Before(arg.EndTime))||
2287+
(s.SessionStartedAt.Before(arg.StartTime)&& (s.SessionEndedAt.After(arg.EndTime)||s.SessionEndedAt.Equal(arg.EndTime)))) {
2288+
continue
2289+
}
2290+
2291+
w,err:=q.getWorkspaceByIDNoLock(ctx,s.WorkspaceID)
2292+
iferr!=nil {
2293+
returnnil,err
2294+
}
2295+
2296+
ds.userSet[s.UserID]=struct{}{}
2297+
ds.templateIDSet[w.TemplateID]=struct{}{}
2298+
break
2299+
}
2300+
}
2301+
21362302
varresult []database.GetTemplateDailyInsightsRow
21372303
for_,ds:=rangedailyStats {
21382304
templateIDs:=make([]uuid.UUID,0,len(ds.templateIDSet))
@@ -2201,9 +2367,14 @@ func (q *FakeQuerier) GetTemplateInsights(_ context.Context, arg database.GetTem
22012367
slices.SortFunc(templateIDs,func(a,b uuid.UUID)int {
22022368
returnslice.Ascending(a.String(),b.String())
22032369
})
2370+
activeUserIDs:=make([]uuid.UUID,0,len(appUsageIntervalsByUser))
2371+
foruserID:=rangeappUsageIntervalsByUser {
2372+
activeUserIDs=append(activeUserIDs,userID)
2373+
}
2374+
22042375
result:= database.GetTemplateInsightsRow{
2205-
TemplateIDs:templateIDs,
2206-
ActiveUsers:int64(len(appUsageIntervalsByUser)),
2376+
TemplateIDs:templateIDs,
2377+
ActiveUserIDs:activeUserIDs,
22072378
}
22082379
for_,intervals:=rangeappUsageIntervalsByUser {
22092380
for_,interval:=rangeintervals {
@@ -3075,24 +3246,15 @@ func (q *FakeQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.C
30753246
returnagents,nil
30763247
}
30773248

3078-
func (q*FakeQuerier)GetWorkspaceAppByAgentIDAndSlug(_ context.Context,arg database.GetWorkspaceAppByAgentIDAndSlugParams) (database.WorkspaceApp,error) {
3249+
func (q*FakeQuerier)GetWorkspaceAppByAgentIDAndSlug(ctx context.Context,arg database.GetWorkspaceAppByAgentIDAndSlugParams) (database.WorkspaceApp,error) {
30793250
iferr:=validateDatabaseType(arg);err!=nil {
30803251
return database.WorkspaceApp{},err
30813252
}
30823253

30833254
q.mutex.RLock()
30843255
deferq.mutex.RUnlock()
30853256

3086-
for_,app:=rangeq.workspaceApps {
3087-
ifapp.AgentID!=arg.AgentID {
3088-
continue
3089-
}
3090-
ifapp.Slug!=arg.Slug {
3091-
continue
3092-
}
3093-
returnapp,nil
3094-
}
3095-
return database.WorkspaceApp{},sql.ErrNoRows
3257+
returnq.getWorkspaceAppByAgentIDAndSlugNoLock(ctx,arg)
30963258
}
30973259

30983260
func (q*FakeQuerier)GetWorkspaceAppsByAgentID(_ context.Context,id uuid.UUID) ([]database.WorkspaceApp,error) {

‎coderd/database/dbmetrics/dbmetrics.go

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

‎coderd/database/dbmock/dbmock.go

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

‎coderd/database/querier.go

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp