Skip to content

Commit 1171a9d

Browse files
committed
refactor stats with rollups, add tests
1 parent 0bb672a commit 1171a9d

File tree

15 files changed

+720
-165
lines changed

15 files changed

+720
-165
lines changed

coderd/apidoc/docs.go

Lines changed: 7 additions & 3 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: 7 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,10 @@ func New(options *Options) *API {
430430
SignedTokenProvider: api.WorkspaceAppsProvider,
431431
AgentProvider: api.agentProvider,
432432
AppSecurityKey: options.AppSecurityKey,
433-
StatsCollector: workspaceapps.NewStatsCollector(
434-
workspaceAppsLogger.Named("stats_collector"),
435-
workspaceapps.NewStatsDBReporter(options.Database, workspaceapps.DefaultStatsDBReporterBatchSize),
436-
workspaceapps.DefaultStatsCollectorReportInterval,
437-
),
433+
StatsCollector: workspaceapps.NewStatsCollector(workspaceapps.StatsCollectorOptions{
434+
Logger: workspaceAppsLogger.Named("stats_collector"),
435+
Reporter: workspaceapps.NewStatsDBReporter(options.Database, workspaceapps.DefaultStatsDBReporterBatchSize),
436+
}),
438437

439438
DisablePathApps: options.DeploymentValues.DisablePathApps.Value(),
440439
SecureAuthCookie: options.DeploymentValues.SecureAuthCookie.Value(),

coderd/database/dbfake/dbfake.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ type data struct {
135135
workspaceAgentMetadata []database.WorkspaceAgentMetadatum
136136
workspaceAgentLogs []database.WorkspaceAgentLog
137137
workspaceApps []database.WorkspaceApp
138+
workspaceAppStats []database.WorkspaceAppStat
138139
workspaceBuilds []database.WorkspaceBuildTable
139140
workspaceBuildParameters []database.WorkspaceBuildParameter
140141
workspaceResourceMetadata []database.WorkspaceResourceMetadatum
@@ -4266,7 +4267,39 @@ func (q *FakeQuerier) InsertWorkspaceAppStats(ctx context.Context, arg database.
42664267
return err
42674268
}
42684269

4269-
panic("not implemented")
4270+
q.mutex.Lock()
4271+
defer q.mutex.Unlock()
4272+
4273+
nextID := int64(1)
4274+
if len(q.workspaceAppStats) > 0 {
4275+
nextID = q.workspaceAppStats[len(q.workspaceAppStats)-1].ID + 1
4276+
}
4277+
InsertWorkspaceAppStatsLoop:
4278+
for i := 0; i < len(arg.UserID); i++ {
4279+
stat := database.WorkspaceAppStat{
4280+
ID: nextID,
4281+
UserID: arg.UserID[i],
4282+
WorkspaceID: arg.WorkspaceID[i],
4283+
AgentID: arg.AgentID[i],
4284+
AccessMethod: arg.AccessMethod[i],
4285+
SlugOrPort: arg.SlugOrPort[i],
4286+
SessionID: arg.SessionID[i],
4287+
SessionStartedAt: arg.SessionStartedAt[i],
4288+
SessionEndedAt: arg.SessionEndedAt[i],
4289+
Requests: arg.Requests[i],
4290+
}
4291+
for j, s := range q.workspaceAppStats {
4292+
// Check unique constraint for upsert.
4293+
if s.UserID == stat.UserID && s.AgentID == stat.AgentID && s.SessionID == stat.SessionID {
4294+
q.workspaceAppStats[j].SessionEndedAt = stat.SessionEndedAt
4295+
q.workspaceAppStats[j].Requests = stat.Requests
4296+
continue InsertWorkspaceAppStatsLoop
4297+
}
4298+
}
4299+
q.workspaceAppStats = append(q.workspaceAppStats, stat)
4300+
}
4301+
4302+
return nil
42704303
}
42714304

42724305
func (q *FakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) error {

coderd/database/dump.sql

Lines changed: 20 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
CREATE TABLE workspace_app_stats (
2-
id uuid PRIMARY KEY,
2+
id BIGSERIAL PRIMARY KEY,
33
user_id uuid NOT NULL REFERENCES users (id),
44
workspace_id uuid NOT NULL REFERENCES workspaces (id),
55
agent_id uuid NOT NULL REFERENCES workspace_agents (id),
66
access_method text NOT NULL,
77
slug_or_port text NOT NULL,
88
session_id uuid NOT NULL,
9-
session_started_at timestamp with time zone NOT NULL,
10-
session_ended_at timestamp with time zone
9+
session_started_at timestamptz NOT NULL,
10+
session_ended_at timestamptz NOT NULL,
11+
requests integer NOT NULL,
12+
13+
-- Set a unique constraint to allow upserting the session_ended_at
14+
-- and requests fields without risk of collisions.
15+
UNIQUE(user_id, agent_id, session_id)
1116
);
1217

1318
COMMENT ON TABLE workspace_app_stats IS 'A record of workspace app usage statistics';
1419

15-
COMMENT ON COLUMN workspace_app_stats.id IS 'The unique identifier for the workspace app stat record';
20+
COMMENT ON COLUMN workspace_app_stats.id IS 'The ID of the record';
1621
COMMENT ON COLUMN workspace_app_stats.user_id IS 'The user who used the workspace app';
1722
COMMENT ON COLUMN workspace_app_stats.workspace_id IS 'The workspace that the workspace app was used in';
1823
COMMENT ON COLUMN workspace_app_stats.agent_id IS 'The workspace agent that was used';
@@ -21,9 +26,7 @@ COMMENT ON COLUMN workspace_app_stats.slug_or_port IS 'The slug or port used to
2126
COMMENT ON COLUMN workspace_app_stats.session_id IS 'The unique identifier for the session';
2227
COMMENT ON COLUMN workspace_app_stats.session_started_at IS 'The time the session started';
2328
COMMENT ON COLUMN workspace_app_stats.session_ended_at IS 'The time the session ended';
24-
25-
-- Create a unique index to prevent duplicate records (scoped to agent to ensure no collisions).
26-
CREATE UNIQUE INDEX workspace_app_stats_user_agent_session_idx ON workspace_app_stats (agent_id, session_id);
29+
COMMENT ON COLUMN workspace_app_stats.requests IS 'The number of requests made during the session, a number larger than 1 indicates that multiple sessions were rolled up into one';
2730

2831
-- Create index on workspace_id for joining/filtering by templates.
2932
CREATE INDEX workspace_app_stats_workspace_id_idx ON workspace_app_stats (workspace_id);

coderd/database/models.go

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

coderd/database/queries.sql.go

Lines changed: 26 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
-- name: InsertWorkspaceAppStats :exec
22
INSERT INTO
33
workspace_app_stats (
4-
id,
54
user_id,
65
workspace_id,
76
agent_id,
87
access_method,
98
slug_or_port,
109
session_id,
1110
session_started_at,
12-
session_ended_at
11+
session_ended_at,
12+
requests
1313
)
1414
SELECT
15-
unnest(@id::uuid[]) AS id,
1615
unnest(@user_id::uuid[]) AS user_id,
1716
unnest(@workspace_id::uuid[]) AS workspace_id,
1817
unnest(@agent_id::uuid[]) AS agent_id,
1918
unnest(@access_method::text[]) AS access_method,
2019
unnest(@slug_or_port::text[]) AS slug_or_port,
2120
unnest(@session_id::uuid[]) AS session_id,
2221
unnest(@session_started_at::timestamptz[]) AS session_started_at,
23-
unnest(@session_ended_at::nulltimestamptz[]) AS session_ended_at
22+
unnest(@session_ended_at::timestamptz[]) AS session_ended_at,
23+
unnest(@requests::int[]) AS requests
2424
ON CONFLICT
25-
(agent_id, session_id)
25+
(user_id, agent_id, session_id)
2626
DO
2727
UPDATE SET
28-
-- Only session end can be updated.
29-
session_ended_at = EXCLUDED.session_ended_at
28+
session_ended_at = EXCLUDED.session_ended_at,
29+
requests = EXCLUDED.requests
3030
WHERE
31-
workspace_app_stats.agent_id = EXCLUDED.agent_id
31+
workspace_app_stats.user_id = EXCLUDED.user_id
32+
AND workspace_app_stats.agent_id = EXCLUDED.agent_id
3233
AND workspace_app_stats.session_id = EXCLUDED.session_id;

coderd/database/unique_constraint.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)