mirror of
https://github.com/coder/coder.git
synced 2025-07-18 14:17:22 +00:00
fix(coderd): rewrite template insights query for speed and fix intervals (#9300)
This commit is contained in:
committed by
GitHub
parent
d00817ea4a
commit
04d5e3f54f
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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": [
|
||||||
|
@ -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": [
|
||||||
|
@ -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": [
|
||||||
|
@ -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": [
|
||||||
|
Reference in New Issue
Block a user