From 434452782192ce2627dbfb629f5044761605913c Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 18 Mar 2024 20:27:21 +0200 Subject: [PATCH 1/3] feat(coderd/database): rewrite `GetUserLatencyInsights` to use `template_usage_stats` --- coderd/apidoc/docs.go | 6 +++- coderd/apidoc/swagger.json | 6 +++- coderd/database/dbmem/dbmem.go | 3 +- coderd/database/queries.sql.go | 43 ++++++++++++++++------------ coderd/database/queries/insights.sql | 35 ++++++++++++---------- coderd/insights.go | 3 +- coderd/insights_test.go | 3 +- codersdk/insights.go | 3 +- docs/api/insights.md | 3 +- docs/api/schemas.md | 15 ++++++---- site/src/api/typesGenerated.ts | 3 +- 11 files changed, 77 insertions(+), 46 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index c6c8be1d58c0e..bebf9a67fdaed 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8618,9 +8618,13 @@ const docTemplate = `{ "type": "number", "example": 31.312 }, - "p95": { + "p90": { "type": "number", "example": 119.832 + }, + "p99": { + "type": "number", + "example": 432.34 } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 605d9f8c762d2..962d765b83116 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7668,9 +7668,13 @@ "type": "number", "example": 31.312 }, - "p95": { + "p90": { "type": "number", "example": 119.832 + }, + "p99": { + "type": "number", + "example": 432.34 } } }, diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index bb903757b6238..c1c4e9822e6ff 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -4352,7 +4352,8 @@ func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.Get AvatarURL: user.AvatarURL, TemplateIDs: templateIDs, WorkspaceConnectionLatency50: tryPercentile(latencies, 50), - WorkspaceConnectionLatency95: tryPercentile(latencies, 95), + WorkspaceConnectionLatency90: tryPercentile(latencies, 90), + WorkspaceConnectionLatency99: tryPercentile(latencies, 99), } rows = append(rows, row) } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 863f2e724aad7..3faa34a701fd7 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -2474,22 +2474,27 @@ func (q *sqlQuerier) GetUserActivityInsights(ctx context.Context, arg GetUserAct const getUserLatencyInsights = `-- name: GetUserLatencyInsights :many SELECT - workspace_agent_stats.user_id, - users.username, - users.avatar_url, - array_agg(DISTINCT template_id)::uuid[] AS template_ids, - coalesce((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_50, - coalesce((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_95 -FROM workspace_agent_stats -JOIN users ON (users.id = workspace_agent_stats.user_id) -WHERE - workspace_agent_stats.created_at >= $1 - AND workspace_agent_stats.created_at < $2 - AND workspace_agent_stats.connection_median_latency_ms > 0 - AND workspace_agent_stats.connection_count > 0 - AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN template_id = ANY($3::uuid[]) ELSE TRUE END -GROUP BY workspace_agent_stats.user_id, users.username, users.avatar_url -ORDER BY user_id ASC + tus.user_id, + u.username, + u.avatar_url, + array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids, + COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50, + COALESCE((PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_90, + COALESCE((PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_99 +FROM + template_usage_stats tus +JOIN + users u +ON + u.id = tus.user_id +WHERE + tus.start_time >= $1::timestamptz + AND tus.end_time <= $2::timestamptz + AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN tus.template_id = ANY($3::uuid[]) ELSE TRUE END +GROUP BY + tus.user_id, u.username, u.avatar_url +ORDER BY + tus.user_id ASC ` type GetUserLatencyInsightsParams struct { @@ -2504,7 +2509,8 @@ type GetUserLatencyInsightsRow struct { AvatarURL string `db:"avatar_url" json:"avatar_url"` TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"` WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"` - WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"` + WorkspaceConnectionLatency90 float64 `db:"workspace_connection_latency_90" json:"workspace_connection_latency_90"` + WorkspaceConnectionLatency99 float64 `db:"workspace_connection_latency_99" json:"workspace_connection_latency_99"` } // GetUserLatencyInsights returns the median and 95th percentile connection @@ -2526,7 +2532,8 @@ func (q *sqlQuerier) GetUserLatencyInsights(ctx context.Context, arg GetUserLate &i.AvatarURL, pq.Array(&i.TemplateIDs), &i.WorkspaceConnectionLatency50, - &i.WorkspaceConnectionLatency95, + &i.WorkspaceConnectionLatency90, + &i.WorkspaceConnectionLatency99, ); err != nil { return nil, err } diff --git a/coderd/database/queries/insights.sql b/coderd/database/queries/insights.sql index ef6596a54e5ae..47af862b382ce 100644 --- a/coderd/database/queries/insights.sql +++ b/coderd/database/queries/insights.sql @@ -4,22 +4,27 @@ -- template_ids, meaning only user data from workspaces based on those templates -- will be included. SELECT - workspace_agent_stats.user_id, - users.username, - users.avatar_url, - array_agg(DISTINCT template_id)::uuid[] AS template_ids, - coalesce((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_50, - coalesce((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY connection_median_latency_ms)), -1)::FLOAT AS workspace_connection_latency_95 -FROM workspace_agent_stats -JOIN users ON (users.id = workspace_agent_stats.user_id) + tus.user_id, + u.username, + u.avatar_url, + array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids, + COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50, + COALESCE((PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_90, + COALESCE((PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_99 +FROM + template_usage_stats tus +JOIN + users u +ON + u.id = tus.user_id WHERE - workspace_agent_stats.created_at >= @start_time - AND workspace_agent_stats.created_at < @end_time - AND workspace_agent_stats.connection_median_latency_ms > 0 - AND workspace_agent_stats.connection_count > 0 - AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN template_id = ANY(@template_ids::uuid[]) ELSE TRUE END -GROUP BY workspace_agent_stats.user_id, users.username, users.avatar_url -ORDER BY user_id ASC; + tus.start_time >= @start_time::timestamptz + AND tus.end_time <= @end_time::timestamptz + AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN tus.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END +GROUP BY + tus.user_id, u.username, u.avatar_url +ORDER BY + tus.user_id ASC; -- name: GetUserActivityInsights :many -- GetUserActivityInsights returns the ranking with top active users. diff --git a/coderd/insights.go b/coderd/insights.go index 7231cb2f5d516..6e3e6775d9433 100644 --- a/coderd/insights.go +++ b/coderd/insights.go @@ -215,7 +215,8 @@ func (api *API) insightsUserLatency(rw http.ResponseWriter, r *http.Request) { AvatarURL: row.AvatarURL, LatencyMS: codersdk.ConnectionLatency{ P50: row.WorkspaceConnectionLatency50, - P95: row.WorkspaceConnectionLatency95, + P90: row.WorkspaceConnectionLatency90, + P99: row.WorkspaceConnectionLatency99, }, }) } diff --git a/coderd/insights_test.go b/coderd/insights_test.go index 4eca480f279e9..1bc5e4dae0afa 100644 --- a/coderd/insights_test.go +++ b/coderd/insights_test.go @@ -318,7 +318,8 @@ func TestUserLatencyInsights(t *testing.T) { require.Len(t, userLatencies.Report.Users, 1, "want only 1 user") require.Equal(t, userLatencies.Report.Users[0].UserID, user.UserID, "want user id to match") assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P50, float64(0), "want p50 to be greater than 0") - assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P95, float64(0), "want p95 to be greater than 0") + assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P90, float64(0), "want p90 to be greater than 0") + assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P99, float64(0), "want p99 to be greater than 0") } func TestUserLatencyInsights_BadRequest(t *testing.T) { diff --git a/codersdk/insights.go b/codersdk/insights.go index 5166d29d2d1a0..c4aaa69f4c5d2 100644 --- a/codersdk/insights.go +++ b/codersdk/insights.go @@ -98,7 +98,8 @@ type UserActivity struct { // ConnectionLatency shows the latency for a connection. type ConnectionLatency struct { P50 float64 `json:"p50" example:"31.312"` - P95 float64 `json:"p95" example:"119.832"` + P90 float64 `json:"p90" example:"119.832"` + P99 float64 `json:"p99" example:"432.34"` } type UserLatencyInsightsRequest struct { diff --git a/docs/api/insights.md b/docs/api/insights.md index 4b8609ae4ffd3..d7891cca47fa0 100644 --- a/docs/api/insights.md +++ b/docs/api/insights.md @@ -208,7 +208,8 @@ curl -X GET http://coder-server:8080/api/v2/insights/user-latency?before=0&after "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p95": 119.832 + "p90": 119.832, + "p99": 432.34 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 710f7d93fb95b..b34df1c2d6e3c 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1258,7 +1258,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ```json { "p50": 31.312, - "p95": 119.832 + "p90": 119.832, + "p99": 432.34 } ``` @@ -1267,7 +1268,8 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Name | Type | Required | Restrictions | Description | | ----- | ------ | -------- | ------------ | ----------- | | `p50` | number | false | | | -| `p95` | number | false | | | +| `p90` | number | false | | | +| `p99` | number | false | | | ## codersdk.ConvertLoginRequest @@ -6516,7 +6518,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p95": 119.832 + "p90": 119.832, + "p99": 432.34 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", @@ -6546,7 +6549,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p95": 119.832 + "p90": 119.832, + "p99": 432.34 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", @@ -6578,7 +6582,8 @@ If the schedule is empty, the user will be updated to use the default schedule.| "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p95": 119.832 + "p90": 119.832, + "p99": 432.34 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index deb4e0636318c..60831c9e12ca8 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -182,7 +182,8 @@ export interface BuildInfoResponse { // From codersdk/insights.go export interface ConnectionLatency { readonly p50: number; - readonly p95: number; + readonly p90: number; + readonly p99: number; } // From codersdk/users.go From 3b719f8a45e5e6f3b80d2cc465cc2ea18078814a Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 22 Mar 2024 16:16:14 +0000 Subject: [PATCH 2/3] revert p95 -> p90/p99 change --- coderd/apidoc/docs.go | 6 +----- coderd/apidoc/swagger.json | 6 +----- coderd/database/dbmem/dbmem.go | 3 +-- coderd/database/queries.sql.go | 9 +++------ coderd/database/queries/insights.sql | 3 +-- coderd/insights.go | 3 +-- coderd/insights_test.go | 3 +-- codersdk/insights.go | 3 +-- docs/api/insights.md | 3 +-- docs/api/schemas.md | 15 +++++---------- site/src/api/typesGenerated.ts | 3 +-- 11 files changed, 17 insertions(+), 40 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index bebf9a67fdaed..c6c8be1d58c0e 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -8618,13 +8618,9 @@ const docTemplate = `{ "type": "number", "example": 31.312 }, - "p90": { + "p95": { "type": "number", "example": 119.832 - }, - "p99": { - "type": "number", - "example": 432.34 } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 962d765b83116..605d9f8c762d2 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -7668,13 +7668,9 @@ "type": "number", "example": 31.312 }, - "p90": { + "p95": { "type": "number", "example": 119.832 - }, - "p99": { - "type": "number", - "example": 432.34 } } }, diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index c1c4e9822e6ff..bb903757b6238 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -4352,8 +4352,7 @@ func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.Get AvatarURL: user.AvatarURL, TemplateIDs: templateIDs, WorkspaceConnectionLatency50: tryPercentile(latencies, 50), - WorkspaceConnectionLatency90: tryPercentile(latencies, 90), - WorkspaceConnectionLatency99: tryPercentile(latencies, 99), + WorkspaceConnectionLatency95: tryPercentile(latencies, 95), } rows = append(rows, row) } diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 3faa34a701fd7..8bee2470ddee2 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -2479,8 +2479,7 @@ SELECT u.avatar_url, array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids, COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50, - COALESCE((PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_90, - COALESCE((PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_99 + COALESCE((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_95 FROM template_usage_stats tus JOIN @@ -2509,8 +2508,7 @@ type GetUserLatencyInsightsRow struct { AvatarURL string `db:"avatar_url" json:"avatar_url"` TemplateIDs []uuid.UUID `db:"template_ids" json:"template_ids"` WorkspaceConnectionLatency50 float64 `db:"workspace_connection_latency_50" json:"workspace_connection_latency_50"` - WorkspaceConnectionLatency90 float64 `db:"workspace_connection_latency_90" json:"workspace_connection_latency_90"` - WorkspaceConnectionLatency99 float64 `db:"workspace_connection_latency_99" json:"workspace_connection_latency_99"` + WorkspaceConnectionLatency95 float64 `db:"workspace_connection_latency_95" json:"workspace_connection_latency_95"` } // GetUserLatencyInsights returns the median and 95th percentile connection @@ -2532,8 +2530,7 @@ func (q *sqlQuerier) GetUserLatencyInsights(ctx context.Context, arg GetUserLate &i.AvatarURL, pq.Array(&i.TemplateIDs), &i.WorkspaceConnectionLatency50, - &i.WorkspaceConnectionLatency90, - &i.WorkspaceConnectionLatency99, + &i.WorkspaceConnectionLatency95, ); err != nil { return nil, err } diff --git a/coderd/database/queries/insights.sql b/coderd/database/queries/insights.sql index 47af862b382ce..ffced53861cd9 100644 --- a/coderd/database/queries/insights.sql +++ b/coderd/database/queries/insights.sql @@ -9,8 +9,7 @@ SELECT u.avatar_url, array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids, COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50, - COALESCE((PERCENTILE_CONT(0.90) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_90, - COALESCE((PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_99 + COALESCE((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_95 FROM template_usage_stats tus JOIN diff --git a/coderd/insights.go b/coderd/insights.go index 6e3e6775d9433..7231cb2f5d516 100644 --- a/coderd/insights.go +++ b/coderd/insights.go @@ -215,8 +215,7 @@ func (api *API) insightsUserLatency(rw http.ResponseWriter, r *http.Request) { AvatarURL: row.AvatarURL, LatencyMS: codersdk.ConnectionLatency{ P50: row.WorkspaceConnectionLatency50, - P90: row.WorkspaceConnectionLatency90, - P99: row.WorkspaceConnectionLatency99, + P95: row.WorkspaceConnectionLatency95, }, }) } diff --git a/coderd/insights_test.go b/coderd/insights_test.go index 1bc5e4dae0afa..4eca480f279e9 100644 --- a/coderd/insights_test.go +++ b/coderd/insights_test.go @@ -318,8 +318,7 @@ func TestUserLatencyInsights(t *testing.T) { require.Len(t, userLatencies.Report.Users, 1, "want only 1 user") require.Equal(t, userLatencies.Report.Users[0].UserID, user.UserID, "want user id to match") assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P50, float64(0), "want p50 to be greater than 0") - assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P90, float64(0), "want p90 to be greater than 0") - assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P99, float64(0), "want p99 to be greater than 0") + assert.Greater(t, userLatencies.Report.Users[0].LatencyMS.P95, float64(0), "want p95 to be greater than 0") } func TestUserLatencyInsights_BadRequest(t *testing.T) { diff --git a/codersdk/insights.go b/codersdk/insights.go index c4aaa69f4c5d2..5166d29d2d1a0 100644 --- a/codersdk/insights.go +++ b/codersdk/insights.go @@ -98,8 +98,7 @@ type UserActivity struct { // ConnectionLatency shows the latency for a connection. type ConnectionLatency struct { P50 float64 `json:"p50" example:"31.312"` - P90 float64 `json:"p90" example:"119.832"` - P99 float64 `json:"p99" example:"432.34"` + P95 float64 `json:"p95" example:"119.832"` } type UserLatencyInsightsRequest struct { diff --git a/docs/api/insights.md b/docs/api/insights.md index d7891cca47fa0..4b8609ae4ffd3 100644 --- a/docs/api/insights.md +++ b/docs/api/insights.md @@ -208,8 +208,7 @@ curl -X GET http://coder-server:8080/api/v2/insights/user-latency?before=0&after "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p90": 119.832, - "p99": 432.34 + "p95": 119.832 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index b34df1c2d6e3c..710f7d93fb95b 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -1258,8 +1258,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in ```json { "p50": 31.312, - "p90": 119.832, - "p99": 432.34 + "p95": 119.832 } ``` @@ -1268,8 +1267,7 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | Name | Type | Required | Restrictions | Description | | ----- | ------ | -------- | ------------ | ----------- | | `p50` | number | false | | | -| `p90` | number | false | | | -| `p99` | number | false | | | +| `p95` | number | false | | | ## codersdk.ConvertLoginRequest @@ -6518,8 +6516,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p90": 119.832, - "p99": 432.34 + "p95": 119.832 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", @@ -6549,8 +6546,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p90": 119.832, - "p99": 432.34 + "p95": 119.832 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", @@ -6582,8 +6578,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "avatar_url": "http://example.com", "latency_ms": { "p50": 31.312, - "p90": 119.832, - "p99": 432.34 + "p95": 119.832 }, "template_ids": ["497f6eca-6276-4993-bfeb-53cbbbba6f08"], "user_id": "a169451c-8525-4352-b8ca-070dd449a1a5", diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 60831c9e12ca8..deb4e0636318c 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -182,8 +182,7 @@ export interface BuildInfoResponse { // From codersdk/insights.go export interface ConnectionLatency { readonly p50: number; - readonly p90: number; - readonly p99: number; + readonly p95: number; } // From codersdk/users.go From 438d3a96216d80e9ee10443a05d450cf2464d7ba Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Mon, 25 Mar 2024 12:02:06 +0000 Subject: [PATCH 3/3] fix dbmem query logic --- coderd/database/dbmem/dbmem.go | 56 +++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index bb903757b6238..a895e9565b16d 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -4300,27 +4300,44 @@ func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.Get q.mutex.RLock() defer q.mutex.RUnlock() + /* + SELECT + tus.user_id, + u.username, + u.avatar_url, + array_agg(DISTINCT tus.template_id)::uuid[] AS template_ids, + COALESCE((PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_50, + COALESCE((PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY tus.median_latency_ms)), -1)::float AS workspace_connection_latency_95 + FROM + template_usage_stats tus + JOIN + users u + ON + u.id = tus.user_id + WHERE + tus.start_time >= @start_time::timestamptz + AND tus.end_time <= @end_time::timestamptz + AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN tus.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END + GROUP BY + tus.user_id, u.username, u.avatar_url + ORDER BY + tus.user_id ASC; + */ + latenciesByUserID := make(map[uuid.UUID][]float64) - seenTemplatesByUserID := make(map[uuid.UUID]map[uuid.UUID]struct{}) - for _, s := range q.workspaceAgentStats { - if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, s.TemplateID) { - continue - } - if !arg.StartTime.Equal(s.CreatedAt) && (s.CreatedAt.Before(arg.StartTime) || s.CreatedAt.After(arg.EndTime)) { - continue - } - if s.ConnectionCount == 0 { + seenTemplatesByUserID := make(map[uuid.UUID][]uuid.UUID) + for _, stat := range q.templateUsageStats { + if stat.StartTime.Before(arg.StartTime) || stat.EndTime.After(arg.EndTime) { continue } - if s.ConnectionMedianLatencyMS <= 0 { + if len(arg.TemplateIDs) > 0 && !slices.Contains(arg.TemplateIDs, stat.TemplateID) { continue } - latenciesByUserID[s.UserID] = append(latenciesByUserID[s.UserID], s.ConnectionMedianLatencyMS) - if seenTemplatesByUserID[s.UserID] == nil { - seenTemplatesByUserID[s.UserID] = make(map[uuid.UUID]struct{}) + if stat.MedianLatencyMs.Valid { + latenciesByUserID[stat.UserID] = append(latenciesByUserID[stat.UserID], stat.MedianLatencyMs.Float64) } - seenTemplatesByUserID[s.UserID][s.TemplateID] = struct{}{} + seenTemplatesByUserID[stat.UserID] = uniqueSortedUUIDs(append(seenTemplatesByUserID[stat.UserID], stat.TemplateID)) } tryPercentile := func(fs []float64, p float64) float64 { @@ -4333,15 +4350,6 @@ func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.Get var rows []database.GetUserLatencyInsightsRow for userID, latencies := range latenciesByUserID { - sort.Float64s(latencies) - templateIDSet := seenTemplatesByUserID[userID] - templateIDs := make([]uuid.UUID, 0, len(templateIDSet)) - for templateID := range templateIDSet { - templateIDs = append(templateIDs, templateID) - } - slices.SortFunc(templateIDs, func(a, b uuid.UUID) int { - return slice.Ascending(a.String(), b.String()) - }) user, err := q.getUserByIDNoLock(userID) if err != nil { return nil, err @@ -4350,7 +4358,7 @@ func (q *FakeQuerier) GetUserLatencyInsights(_ context.Context, arg database.Get UserID: userID, Username: user.Username, AvatarURL: user.AvatarURL, - TemplateIDs: templateIDs, + TemplateIDs: seenTemplatesByUserID[userID], WorkspaceConnectionLatency50: tryPercentile(latencies, 50), WorkspaceConnectionLatency95: tryPercentile(latencies, 95), }