Skip to content

Commit 2bfa3c6

Browse files
committed
feat(run-engine): targeted live stats for a set of concurrency keys (TRI-12438)
1 parent f307b28 commit 2bfa3c6

2 files changed

Lines changed: 52 additions & 0 deletions

File tree

internal-packages/run-engine/src/engine/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,14 @@ export class RunEngine {
16601660
return this.runQueue.concurrencyKeyBreakdown(environment, queue, options);
16611661
}
16621662

1663+
async concurrencyKeyLiveStats(
1664+
environment: MinimalAuthenticatedEnvironment,
1665+
queue: string,
1666+
concurrencyKeys: string[]
1667+
) {
1668+
return this.runQueue.concurrencyKeyLiveStats(environment, queue, concurrencyKeys);
1669+
}
1670+
16631671
async removeEnvironmentQueuesFromMasterQueue({
16641672
runtimeEnvironmentId,
16651673
organizationId,

internal-packages/run-engine/src/run-queue/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)