@@ -613,6 +613,50 @@ export class RunQueue {
613613 return { totalBackloggedKeys, keys } ;
614614 }
615615
616+ /**
617+ * Live "now" stats for a specific set of concurrency keys — the current page of the paginated
618+ * per-key table. Unlike concurrencyKeyBreakdown (which reads the top of the ckIndex), this
619+ * targets exactly the given keys, so the table can enrich its ClickHouse-ranked page without
620+ * scanning the whole index: O(keys) via one pipeline, independent of total key cardinality.
621+ * Keys with no live backlog come back as zeros with a null oldest-enqueue time.
622+ */
623+ public async concurrencyKeyLiveStats (
624+ env : MinimalAuthenticatedEnvironment ,
625+ queue : string ,
626+ concurrencyKeys : string [ ]
627+ ) : Promise < Map < string , { queued : number ; running : number ; oldestEnqueuedAt : number | null } > > {
628+ const result = new Map <
629+ string ,
630+ { queued : number ; running : number ; oldestEnqueuedAt : number | null }
631+ > ( ) ;
632+ if ( concurrencyKeys . length === 0 ) return result ;
633+
634+ const ckIndexKey = this . keys . ckIndexKeyFromQueue ( this . keys . queueKey ( env , queue ) ) ;
635+
636+ const pipeline = this . redis . pipeline ( ) ;
637+ for ( const concurrencyKey of concurrencyKeys ) {
638+ const member = this . keys . queueKey ( env , queue , concurrencyKey ) ;
639+ pipeline . zcard ( member ) ; // queued in this key's subqueue
640+ pipeline . scard ( this . keys . queueCurrentConcurrencyKeyFromQueue ( member ) ) ; // running
641+ pipeline . zscore ( ckIndexKey , member ) ; // oldest-enqueued score (null once the key drains)
642+ }
643+ const res = await pipeline . exec ( ) ;
644+ if ( ! res ) return result ;
645+
646+ concurrencyKeys . forEach ( ( concurrencyKey , i ) => {
647+ const queuedResult = res [ i * 3 ] ;
648+ const runningResult = res [ i * 3 + 1 ] ;
649+ const scoreResult = res [ i * 3 + 2 ] ;
650+ const queued = queuedResult && ! queuedResult [ 0 ] ? ( ( queuedResult [ 1 ] as number ) ?? 0 ) : 0 ;
651+ const running = runningResult && ! runningResult [ 0 ] ? ( ( runningResult [ 1 ] as number ) ?? 0 ) : 0 ;
652+ const rawScore = scoreResult && ! scoreResult [ 0 ] ? scoreResult [ 1 ] : null ;
653+ const oldestEnqueuedAt = rawScore != null ? Number ( rawScore ) : null ;
654+ result . set ( concurrencyKey , { queued, running, oldestEnqueuedAt } ) ;
655+ } ) ;
656+
657+ return result ;
658+ }
659+
616660 public async lengthOfEnvQueue ( env : MinimalAuthenticatedEnvironment ) {
617661 return this . redis . zcard ( this . keys . envQueueKey ( env ) ) ;
618662 }
0 commit comments