@@ -219,4 +219,76 @@ export function getQueueRankingCount(reader: ClickhouseReader) {
219219 } ) ;
220220}
221221
222+ // --- Per-concurrency-key ranking (the queue detail "Concurrency keys" table) ---
223+
224+ const ConcurrencyKeyRankingParams = z . object ( {
225+ organizationId : z . string ( ) ,
226+ projectId : z . string ( ) ,
227+ environmentId : z . string ( ) ,
228+ queueName : z . string ( ) ,
229+ startTime : z . string ( ) ,
230+ endTime : z . string ( ) ,
231+ /** Case-insensitive substring filter on the key ('' = no filter). */
232+ nameContains : z . string ( ) ,
233+ limit : z . number ( ) ,
234+ offset : z . number ( ) ,
235+ } ) ;
236+
237+ const ConcurrencyKeyRankingRow = z . object ( {
238+ concurrency_key : z . string ( ) ,
239+ started : z . coerce . number ( ) ,
240+ peak_backlog : z . coerce . number ( ) ,
241+ peak_running : z . coerce . number ( ) ,
242+ mean_wait_ms : z . coerce . number ( ) ,
243+ ranked_total : z . coerce . number ( ) ,
244+ } ) ;
245+
246+ // The per-key table (queue_metrics_ck_v1) is activity-bound and its ORDER BY starts with the
247+ // tenant + queue, so filtering to one queue prunes to a contiguous index range — the aggregate
248+ // is bounded by real activity, never by total key cardinality. There is no per-key 5m rollup,
249+ // so this reads the 10s tier directly (the pre-existing LIMIT-50 query did the same).
250+ const CK_RANKING_WHERE = `organization_id = {organizationId: String}
251+ AND project_id = {projectId: String}
252+ AND environment_id = {environmentId: String}
253+ AND queue_name = {queueName: String}
254+ AND bucket_start >= {startTime: DateTime}
255+ AND bucket_start < {endTime: DateTime}
256+ AND ({nameContains: String} = '' OR positionCaseInsensitive(concurrency_key, {nameContains: String}) > 0)` ;
257+
258+ /**
259+ * One page of a queue's concurrency keys ranked by peak backlog over the window, with the total
260+ * ranked-key count on every row (window function) so page + count cost a single scan — the same
261+ * shape as getQueueRanking. The `concurrency_key ASC` tiebreak makes OFFSET paging stable across
262+ * keys that share a peak. Range stats (started/peak_backlog/peak_running/mean wait) come back on
263+ * the same rows; live "now" counts are enriched per page from Redis by the caller.
264+ */
265+ export function getConcurrencyKeyRanking ( reader : ClickhouseReader ) {
266+ return reader . query ( {
267+ name : "getConcurrencyKeyRanking" ,
268+ query : `SELECT
269+ concurrency_key,
270+ started,
271+ peak_backlog,
272+ peak_running,
273+ mean_wait_ms,
274+ count() OVER () AS ranked_total
275+ FROM (
276+ SELECT
277+ concurrency_key,
278+ deltaSumTimestampMerge(started_delta) AS started,
279+ max(max_queued) AS peak_backlog,
280+ max(max_running) AS peak_running,
281+ if(sum(wait_ms_count) > 0, round(sum(wait_ms_sum) / sum(wait_ms_count)), 0) AS mean_wait_ms
282+ FROM trigger_dev.queue_metrics_ck_v1
283+ WHERE ${ CK_RANKING_WHERE }
284+ GROUP BY concurrency_key
285+ ORDER BY peak_backlog DESC, concurrency_key ASC
286+ )
287+ LIMIT {limit: UInt32} OFFSET {offset: UInt32}` ,
288+ params : ConcurrencyKeyRankingParams ,
289+ schema : ConcurrencyKeyRankingRow ,
290+ settings : QUEUE_METRICS_CACHE_SETTINGS ,
291+ } ) ;
292+ }
293+
222294// (per-queue detail series is now fetched via TRQL + fillGaps from the metric resource route)
0 commit comments