Skip to content

Commit df0bbe1

Browse files
committed
feat(clickhouse,webapp): keep 10-second resolution on the env metrics rollup
The env rollup's win comes from dropping the queue dimension, not from coarser buckets: row count is queue-independent (~8640/day/env), so full 10-second granularity stays cheap at any range. Env header tiles and saturation charts now resolve short-range detail exactly like the per-queue charts, and the current-value tiles read the latest 10-second bucket instead of a minute-wide one.
1 parent c28b6cf commit df0bbe1

3 files changed

Lines changed: 31 additions & 16 deletions

File tree

apps/webapp/app/v3/querySchemas.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -809,16 +809,17 @@ export const queueMetricsSchema: TableSchema = {
809809
};
810810

811811
/**
812-
* Schema definition for the env_metrics table (trigger_dev.env_metrics_1m_v1).
813-
* Environment-level rollup of queue_metrics into 1-minute buckets with the queue
814-
* dimension dropped, so header tiles and saturation charts cost the same regardless
815-
* of how many queues the environment has.
812+
* Schema definition for the env_metrics table (trigger_dev.env_metrics_v1).
813+
* Environment-level rollup of queue_metrics with the queue dimension dropped, so
814+
* header tiles and saturation charts cost the same regardless of how many queues
815+
* the environment has. Keeps the full 10-second granularity: row count is
816+
* queue-independent, so even 30-day ranges stay small.
816817
*/
817818
export const envMetricsSchema: TableSchema = {
818819
name: "env_metrics",
819-
clickhouseName: "trigger_dev.env_metrics_1m_v1",
820+
clickhouseName: "trigger_dev.env_metrics_v1",
820821
description:
821-
"Environment-level concurrency, saturation, throttling, and scheduling-delay metrics (1-minute buckets)",
822+
"Environment-level concurrency, saturation, throttling, and scheduling-delay metrics (10-second buckets)",
822823
timeConstraint: "bucket_start",
823824
tenantColumns: {
824825
organizationId: "organization_id",
@@ -846,7 +847,7 @@ export const envMetricsSchema: TableSchema = {
846847
bucket_start: {
847848
name: "bucket_start",
848849
...column("DateTime", {
849-
description: "The start of the 1-minute aggregation bucket",
850+
description: "The start of the 10-second aggregation bucket",
850851
example: "2024-01-15 09:30:00",
851852
coreColumn: true,
852853
}),
@@ -907,6 +908,7 @@ export const envMetricsSchema: TableSchema = {
907908
},
908909
},
909910
timeBucketThresholds: [
911+
{ maxRangeSeconds: 3 * 60 * 60, interval: { value: 10, unit: "SECOND" } },
910912
{ maxRangeSeconds: 12 * 60 * 60, interval: { value: 1, unit: "MINUTE" } },
911913
{ maxRangeSeconds: 2 * 24 * 60 * 60, interval: { value: 5, unit: "MINUTE" } },
912914
{ maxRangeSeconds: 7 * 24 * 60 * 60, interval: { value: 15, unit: "MINUTE" } },

internal-packages/clickhouse/schema/035_create_queue_metrics_v1.sql

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ SELECT
9292
FROM trigger_dev.queue_metrics_raw_v1
9393
GROUP BY organization_id, project_id, environment_id, queue_name, bucket_start;
9494

95-
-- (4) Env-level 1m rollup (no queue dimension) for header tiles/saturation charts.
95+
-- (4) Env-level 10s rollup (no queue dimension) for header tiles/saturation charts.
96+
-- Row count is queue-independent (~8640/day/env), so full granularity stays cheap at any range.
9697
-- No counter deltas on purpose: cross-queue deltaSumTimestamp state merges mix unrelated
9798
-- odometers (env totals must GROUP BY queue then sum). TDigest because an env-level
9899
-- reservoir absorbs every sample in the environment.
99-
CREATE TABLE IF NOT EXISTS trigger_dev.env_metrics_1m_v1
100+
CREATE TABLE IF NOT EXISTS trigger_dev.env_metrics_v1
100101
(
101102
organization_id LowCardinality(String),
102103
project_id LowCardinality(String),
@@ -119,11 +120,11 @@ TTL bucket_start + INTERVAL 30 DAY
119120
SETTINGS ttl_only_drop_parts = 1;
120121

121122
-- (5) MV: raw -> env rollup.
122-
CREATE MATERIALIZED VIEW IF NOT EXISTS trigger_dev.env_metrics_1m_mv_v1
123-
TO trigger_dev.env_metrics_1m_v1 AS
123+
CREATE MATERIALIZED VIEW IF NOT EXISTS trigger_dev.env_metrics_mv_v1
124+
TO trigger_dev.env_metrics_v1 AS
124125
SELECT
125126
organization_id, project_id, environment_id,
126-
toStartOfInterval(event_time, INTERVAL 1 MINUTE) AS bucket_start,
127+
toStartOfInterval(event_time, INTERVAL 10 SECOND) AS bucket_start,
127128
max(env_queued) AS max_env_queued,
128129
max(env_running) AS max_env_running,
129130
max(env_limit) AS max_env_limit,
@@ -197,8 +198,8 @@ GROUP BY organization_id, project_id, environment_id, queue_name, bucket_start;
197198
-- +goose Down
198199
DROP VIEW IF EXISTS trigger_dev.queue_metrics_5m_mv_v1;
199200
DROP TABLE IF EXISTS trigger_dev.queue_metrics_5m_v1;
200-
DROP VIEW IF EXISTS trigger_dev.env_metrics_1m_mv_v1;
201-
DROP TABLE IF EXISTS trigger_dev.env_metrics_1m_v1;
201+
DROP VIEW IF EXISTS trigger_dev.env_metrics_mv_v1;
202+
DROP TABLE IF EXISTS trigger_dev.env_metrics_v1;
202203
DROP VIEW IF EXISTS trigger_dev.queue_metrics_mv_v1;
203204
DROP TABLE IF EXISTS trigger_dev.queue_metrics_v1;
204205
DROP TABLE IF EXISTS trigger_dev.queue_metrics_raw_v1;

internal-packages/clickhouse/src/queueMetrics.test.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ describe("queue_metrics_v1", () => {
215215
);
216216

217217
clickhouseTest(
218-
"5m and env rollups agree with the 10s tier, and cross-queue totals sum per queue",
218+
"5m and env rollups agree with the 10s tier, and env buckets are 10s",
219219
async ({ clickhouseContainer }) => {
220220
const ch = new ClickHouse({ url: clickhouseContainer.getConnectionUrl(), name: "test" });
221221

@@ -226,6 +226,14 @@ describe("queue_metrics_v1", () => {
226226
...counter("started", "roll-b", 3, [500, 600, 700]),
227227
{ ...base("gauge", "roll-a"), running: 4, queued: 9, env_running: 30, env_limit: 50 },
228228
{ ...base("gauge", "roll-b"), running: 2, queued: 1, env_running: 45, env_limit: 50 },
229+
{
230+
...base("gauge", "roll-a"),
231+
event_time: "2026-06-30 12:00:15",
232+
running: 1,
233+
queued: 2,
234+
env_running: 20,
235+
env_limit: 50,
236+
},
229237
].map((row) => ({ ...row, organization_id: rollOrg }));
230238
const [insertError] = await ch.queueMetrics.insertRaw(rows, SYNC);
231239
expect(insertError).toBeNull();
@@ -269,19 +277,23 @@ describe("queue_metrics_v1", () => {
269277
query: `SELECT
270278
max(max_env_running) AS max_env_running,
271279
max(max_env_limit) AS max_env_limit,
280+
uniqExact(bucket_start) AS buckets,
272281
round(quantilesTDigestMerge(0.5, 0.9, 0.95, 0.99)(wait_quantiles)[4]) AS wait_p99
273-
FROM trigger_dev.env_metrics_1m_v1
282+
FROM trigger_dev.env_metrics_v1
274283
WHERE organization_id = {org: String}`,
275284
schema: z.object({
276285
max_env_running: z.coerce.number(),
277286
max_env_limit: z.coerce.number(),
287+
buckets: z.coerce.number(),
278288
wait_p99: z.coerce.number(),
279289
}),
280290
params: z.object({ org: z.string() }),
281291
})({ org: rollOrg });
282292
expect(envError).toBeNull();
283293
expect(envRows![0]!.max_env_running).toBe(45);
284294
expect(envRows![0]!.max_env_limit).toBe(50);
295+
// 12:00:05 and 12:00:15 land in separate 10s env buckets (12:00:00 and 12:00:10).
296+
expect(envRows![0]!.buckets).toBe(2);
285297
expect(envRows![0]!.wait_p99).toBeGreaterThanOrEqual(600);
286298
expect(envRows![0]!.wait_p99).toBeLessThanOrEqual(1000);
287299

0 commit comments

Comments
 (0)