import { type MetricsConnection } from './connection'; export interface TierStats { keys: number; /** Sum of `MEMORY USAGE` over this tier's keys, in bytes. */ bytes: number; } export interface HistoryQueueStats { /** Queue name, or `__global__` for the cross-queue rollup. */ queue: string; keys: number; bytes: number; /** Recorded minute buckets, the tier that drives storage size. */ minutes: number; /** Days covered by a minute or hour hash, ascending. */ days: string[]; /** Where this queue's bytes actually sit, so a footprint can be diagnosed. */ tiers: Record; } export interface HistoryStats { keys: number; bytes: number; minutes: number; oldestDay: string | null; newestDay: string | null; tiers: Record; queues: HistoryQueueStats[]; } export interface PurgeOptions { /** Limit the purge to one queue. Omit to purge every queue plus the global rollup. */ queue?: string; /** Only drop days strictly before this date (UTC). Omit to drop everything in scope. */ before?: Date | string; } export interface PurgeResult { keysDeleted: number; /** Day fields removed from totals hashes. */ fieldsDeleted: number; } export interface MetricsHistoryAdminOptions { connection: MetricsConnection; /** Must match the recorder's. See `MetricsRecorderOptions.prefix`. */ prefix?: string; } export type HistoryTier = 'minute' | 'hour' | 'day'; interface ParsedKey { queue: string; metric: string; tier: HistoryTier; /** ISO day the key covers, `null` for the daily totals hash. */ day: string | null; } /** * Parses the three key shapes from the right, because queue names may themselves contain * colons: * * ::: minute buckets * :::hour: hourly rollup * :::totals daily totals * * The metric segment is checked against the known set, so a queue named `hour` or one * ending in `:completed` still resolves correctly. Anything that doesn't fit returns null * and is then reported but never deleted, so a stray key can't be destroyed by accident. */ export declare function parseHistoryKey(key: string, namespace: string): ParsedKey | null; /** * Inspection and cleanup for the Redis keys written by `MetricsRecorder`. * * Every operation is confined to the recorder's namespace and driven by SCAN, so it never * blocks Redis and never touches BullMQ's own keys. Deletes use UNLINK. */ export declare class MetricsHistoryAdmin { private readonly redis; private readonly keys; private readonly ownsRedis; constructor(opts: MetricsHistoryAdminOptions); disconnect(): void; /** * Per-queue footprint of the stored history. * * Every key has to be measured individually, since only `MEMORY USAGE` knows what a hash * really costs. Issuing those one at a time would mean a round trip per key, which at a * 90-day retention across a dozen queues runs into the thousands, so the measurements go * out in pipelined batches instead. Still an ops-scale call rather than a hot path: it * reads the whole namespace, so it belongs behind a debug endpoint, not a poll. */ stats(): Promise; /** * Size and entry count for each key, in pipelined batches so the cost is a handful of * round trips rather than one per key. A key that expires between the scan and the * measurement simply reads as zero rather than failing the whole call. */ private measure; /** * Deletes recorded history. Purging a single queue also subtracts that queue's minutes * from the global rollup, so the cross-queue chart stays correct instead of keeping the * removed queue's throughput folded into it forever. * * That correction covers the counter metrics only. The global runtime, waittime and * queueage rollups keep the purged queue's contribution until their own retention drops * it: a packed bucket vector cannot be decremented field by field, and a max gauge has no * record of which queue produced the maximum, so there is nothing to subtract. The * per-queue keys are still deleted either way. See SUMMABLE_METRICS. */ purge(opts?: PurgeOptions): Promise; /** * Removes one queue's buckets from the matching global key, so the cross-queue series * reflects the queues that are left rather than keeping the removed queue folded in. * Each tier is corrected from its own source key, because the tiers have independent * retention and the minute hash may already be gone while the hourly one survives. * Fields that drain to zero are dropped: the recorder never writes a zero bucket, so a * leftover zero would read as recorded-but-idle instead of not recorded. * Returns the number of global keys it deleted. * * Only the summable metrics are touched; the latency ones are skipped outright rather than * silently producing a no-op subtraction of their packed values. See SUMMABLE_METRICS. */ private subtractDayFromGlobal; /** * Same idea for the daily rollup: the global totals hash is the sum of the per-queue * totals hashes, so it is corrected from those rather than re-derived from day hashes, * which may already have expired. Returns the number of global fields it removed. * * Skips the latency metrics for the same reason as subtractDayFromGlobal. */ private subtractTotalsFromGlobal; /** * SCAN over the namespace, once per master: SCAN carries no key, so a cluster client has * no slot to route by and would answer from one arbitrary node. SCAN may also hand back the * same key on more than one cursor iteration, which would double-count in `stats()`, so * emissions are de-duped here. The set is bounded by queues x metrics x retention days. */ private scan; } export {};