fix(coderd): rewrite template insights query for speed and fix intervals (#9300)

This commit is contained in:
Mathias Fredriksson
2023-08-24 14:38:32 +03:00
committed by GitHub
parent d00817ea4a
commit 04d5e3f54f
8 changed files with 38 additions and 58 deletions

View File

@ -2340,22 +2340,22 @@ func (q *FakeQuerier) GetTemplateInsights(_ context.Context, arg database.GetTem
if appUsageIntervalsByUser[s.UserID] == nil { if appUsageIntervalsByUser[s.UserID] == nil {
appUsageIntervalsByUser[s.UserID] = make(map[time.Time]*database.GetTemplateInsightsRow) appUsageIntervalsByUser[s.UserID] = make(map[time.Time]*database.GetTemplateInsightsRow)
} }
t := s.CreatedAt.Truncate(5 * time.Minute) t := s.CreatedAt.Truncate(time.Minute)
if _, ok := appUsageIntervalsByUser[s.UserID][t]; !ok { if _, ok := appUsageIntervalsByUser[s.UserID][t]; !ok {
appUsageIntervalsByUser[s.UserID][t] = &database.GetTemplateInsightsRow{} appUsageIntervalsByUser[s.UserID][t] = &database.GetTemplateInsightsRow{}
} }
if s.SessionCountJetBrains > 0 { if s.SessionCountJetBrains > 0 {
appUsageIntervalsByUser[s.UserID][t].UsageJetbrainsSeconds = 300 appUsageIntervalsByUser[s.UserID][t].UsageJetbrainsSeconds = 60
} }
if s.SessionCountVSCode > 0 { if s.SessionCountVSCode > 0 {
appUsageIntervalsByUser[s.UserID][t].UsageVscodeSeconds = 300 appUsageIntervalsByUser[s.UserID][t].UsageVscodeSeconds = 60
} }
if s.SessionCountReconnectingPTY > 0 { if s.SessionCountReconnectingPTY > 0 {
appUsageIntervalsByUser[s.UserID][t].UsageReconnectingPtySeconds = 300 appUsageIntervalsByUser[s.UserID][t].UsageReconnectingPtySeconds = 60
} }
if s.SessionCountSSH > 0 { if s.SessionCountSSH > 0 {
appUsageIntervalsByUser[s.UserID][t].UsageSshSeconds = 300 appUsageIntervalsByUser[s.UserID][t].UsageSshSeconds = 60
} }
} }

View File

@ -1675,32 +1675,22 @@ func (q *sqlQuerier) GetTemplateDailyInsights(ctx context.Context, arg GetTempla
} }
const getTemplateInsights = `-- name: GetTemplateInsights :one const getTemplateInsights = `-- name: GetTemplateInsights :one
WITH ts AS ( WITH agent_stats_by_interval_and_user AS (
SELECT SELECT
d::timestamptz AS from_, date_trunc('minute', was.created_at),
(d::timestamptz + '5 minute'::interval) AS to_,
EXTRACT(epoch FROM '5 minute'::interval) AS seconds
FROM
-- Subtract 1 second from end_time to avoid including the next interval in the results.
generate_series($1::timestamptz, ($2::timestamptz) - '1 second'::interval, '5 minute'::interval) d
), agent_stats_by_interval_and_user AS (
SELECT
ts.from_,
ts.to_,
was.user_id, was.user_id,
array_agg(was.template_id) AS template_ids, array_agg(was.template_id) AS template_ids,
CASE WHEN SUM(was.session_count_vscode) > 0 THEN ts.seconds ELSE 0 END AS usage_vscode_seconds, CASE WHEN SUM(was.session_count_vscode) > 0 THEN 60 ELSE 0 END AS usage_vscode_seconds,
CASE WHEN SUM(was.session_count_jetbrains) > 0 THEN ts.seconds ELSE 0 END AS usage_jetbrains_seconds, CASE WHEN SUM(was.session_count_jetbrains) > 0 THEN 60 ELSE 0 END AS usage_jetbrains_seconds,
CASE WHEN SUM(was.session_count_reconnecting_pty) > 0 THEN ts.seconds ELSE 0 END AS usage_reconnecting_pty_seconds, CASE WHEN SUM(was.session_count_reconnecting_pty) > 0 THEN 60 ELSE 0 END AS usage_reconnecting_pty_seconds,
CASE WHEN SUM(was.session_count_ssh) > 0 THEN ts.seconds ELSE 0 END AS usage_ssh_seconds CASE WHEN SUM(was.session_count_ssh) > 0 THEN 60 ELSE 0 END AS usage_ssh_seconds
FROM ts FROM workspace_agent_stats was
JOIN workspace_agent_stats was ON ( WHERE
was.created_at >= ts.from_ was.created_at >= $1::timestamptz
AND was.created_at < ts.to_ AND was.created_at < $2::timestamptz
AND was.connection_count > 0 AND was.connection_count > 0
AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN was.template_id = ANY($3::uuid[]) ELSE TRUE END AND CASE WHEN COALESCE(array_length($3::uuid[], 1), 0) > 0 THEN was.template_id = ANY($3::uuid[]) ELSE TRUE END
) GROUP BY date_trunc('minute', was.created_at), was.user_id
GROUP BY ts.from_, ts.to_, ts.seconds, was.user_id
), template_ids AS ( ), template_ids AS (
SELECT array_agg(DISTINCT template_id) AS ids SELECT array_agg(DISTINCT template_id) AS ids
FROM agent_stats_by_interval_and_user, unnest(template_ids) template_id FROM agent_stats_by_interval_and_user, unnest(template_ids) template_id

View File

@ -25,32 +25,22 @@ ORDER BY user_id ASC;
-- GetTemplateInsights has a granularity of 5 minutes where if a session/app was -- GetTemplateInsights has a granularity of 5 minutes where if a session/app was
-- in use during a minute, we will add 5 minutes to the total usage for that -- in use during a minute, we will add 5 minutes to the total usage for that
-- session/app (per user). -- session/app (per user).
WITH ts AS ( WITH agent_stats_by_interval_and_user AS (
SELECT SELECT
d::timestamptz AS from_, date_trunc('minute', was.created_at),
(d::timestamptz + '5 minute'::interval) AS to_,
EXTRACT(epoch FROM '5 minute'::interval) AS seconds
FROM
-- Subtract 1 second from end_time to avoid including the next interval in the results.
generate_series(@start_time::timestamptz, (@end_time::timestamptz) - '1 second'::interval, '5 minute'::interval) d
), agent_stats_by_interval_and_user AS (
SELECT
ts.from_,
ts.to_,
was.user_id, was.user_id,
array_agg(was.template_id) AS template_ids, array_agg(was.template_id) AS template_ids,
CASE WHEN SUM(was.session_count_vscode) > 0 THEN ts.seconds ELSE 0 END AS usage_vscode_seconds, CASE WHEN SUM(was.session_count_vscode) > 0 THEN 60 ELSE 0 END AS usage_vscode_seconds,
CASE WHEN SUM(was.session_count_jetbrains) > 0 THEN ts.seconds ELSE 0 END AS usage_jetbrains_seconds, CASE WHEN SUM(was.session_count_jetbrains) > 0 THEN 60 ELSE 0 END AS usage_jetbrains_seconds,
CASE WHEN SUM(was.session_count_reconnecting_pty) > 0 THEN ts.seconds ELSE 0 END AS usage_reconnecting_pty_seconds, CASE WHEN SUM(was.session_count_reconnecting_pty) > 0 THEN 60 ELSE 0 END AS usage_reconnecting_pty_seconds,
CASE WHEN SUM(was.session_count_ssh) > 0 THEN ts.seconds ELSE 0 END AS usage_ssh_seconds CASE WHEN SUM(was.session_count_ssh) > 0 THEN 60 ELSE 0 END AS usage_ssh_seconds
FROM ts FROM workspace_agent_stats was
JOIN workspace_agent_stats was ON ( WHERE
was.created_at >= ts.from_ was.created_at >= @start_time::timestamptz
AND was.created_at < ts.to_ AND was.created_at < @end_time::timestamptz
AND was.connection_count > 0 AND was.connection_count > 0
AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN was.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END AND CASE WHEN COALESCE(array_length(@template_ids::uuid[], 1), 0) > 0 THEN was.template_id = ANY(@template_ids::uuid[]) ELSE TRUE END
) GROUP BY date_trunc('minute', was.created_at), was.user_id
GROUP BY ts.from_, ts.to_, ts.seconds, was.user_id
), template_ids AS ( ), template_ids AS (
SELECT array_agg(DISTINCT template_id) AS ids SELECT array_agg(DISTINCT template_id) AS ids
FROM agent_stats_by_interval_and_user, unnest(template_ids) template_id FROM agent_stats_by_interval_and_user, unnest(template_ids) template_id

View File

@ -760,13 +760,13 @@ func TestTemplateInsights_Golden(t *testing.T) {
sessionCountVSCode: 1, sessionCountVSCode: 1,
sessionCountSSH: 1, sessionCountSSH: 1,
}, },
{ // 12 minutes of usage -> 15 minutes. { // 12 minutes of usage.
startedAt: frozenWeekAgo.AddDate(0, 0, 1), startedAt: frozenWeekAgo.AddDate(0, 0, 1),
endedAt: frozenWeekAgo.AddDate(0, 0, 1).Add(12 * time.Minute), endedAt: frozenWeekAgo.AddDate(0, 0, 1).Add(12 * time.Minute),
sessionCountSSH: 1, sessionCountSSH: 1,
}, },
{ // 2 minutes of usage -> 10 minutes because it crosses the 5 minute interval boundary. { // 1m30s of usage -> 2m rounded.
startedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(4 * time.Minute), startedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(4*time.Minute + 30*time.Second),
endedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(6 * time.Minute), endedAt: frozenWeekAgo.AddDate(0, 0, 2).Add(6 * time.Minute),
sessionCountJetBrains: 1, sessionCountJetBrains: 1,
}, },

View File

@ -31,7 +31,7 @@
"display_name": "JetBrains", "display_name": "JetBrains",
"slug": "jetbrains", "slug": "jetbrains",
"icon": "/icon/intellij.svg", "icon": "/icon/intellij.svg",
"seconds": 600 "seconds": 120
}, },
{ {
"template_ids": [ "template_ids": [
@ -55,7 +55,7 @@
"display_name": "SSH", "display_name": "SSH",
"slug": "ssh", "slug": "ssh",
"icon": "/icon/terminal.svg", "icon": "/icon/terminal.svg",
"seconds": 11700 "seconds": 11520
}, },
{ {
"template_ids": [ "template_ids": [

View File

@ -31,7 +31,7 @@
"display_name": "JetBrains", "display_name": "JetBrains",
"slug": "jetbrains", "slug": "jetbrains",
"icon": "/icon/intellij.svg", "icon": "/icon/intellij.svg",
"seconds": 600 "seconds": 120
}, },
{ {
"template_ids": [ "template_ids": [
@ -55,7 +55,7 @@
"display_name": "SSH", "display_name": "SSH",
"slug": "ssh", "slug": "ssh",
"icon": "/icon/terminal.svg", "icon": "/icon/terminal.svg",
"seconds": 11700 "seconds": 11520
}, },
{ {
"template_ids": [ "template_ids": [

View File

@ -25,7 +25,7 @@
"display_name": "JetBrains", "display_name": "JetBrains",
"slug": "jetbrains", "slug": "jetbrains",
"icon": "/icon/intellij.svg", "icon": "/icon/intellij.svg",
"seconds": 600 "seconds": 120
}, },
{ {
"template_ids": [ "template_ids": [
@ -45,7 +45,7 @@
"display_name": "SSH", "display_name": "SSH",
"slug": "ssh", "slug": "ssh",
"icon": "/icon/terminal.svg", "icon": "/icon/terminal.svg",
"seconds": 8100 "seconds": 7920
}, },
{ {
"template_ids": [ "template_ids": [

View File

@ -27,7 +27,7 @@
"display_name": "JetBrains", "display_name": "JetBrains",
"slug": "jetbrains", "slug": "jetbrains",
"icon": "/icon/intellij.svg", "icon": "/icon/intellij.svg",
"seconds": 600 "seconds": 120
}, },
{ {
"template_ids": [ "template_ids": [
@ -47,7 +47,7 @@
"display_name": "SSH", "display_name": "SSH",
"slug": "ssh", "slug": "ssh",
"icon": "/icon/terminal.svg", "icon": "/icon/terminal.svg",
"seconds": 4500 "seconds": 4320
}, },
{ {
"template_ids": [ "template_ids": [