autotel
Version:
Write Once, Observe Anywhere
1 lines • 10.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/drain-pipeline.ts"],"names":[],"mappings":"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","file":"chunk-7EQ4G4SI.cjs","sourcesContent":["export interface DrainPipelineOptions<T = unknown> {\n batch?: {\n /** Maximum events per batch. @default 50 */\n size?: number;\n /** Max time an event can stay buffered before flush. @default 5000 */\n intervalMs?: number;\n };\n retry?: {\n /** Total attempts including first try. @default 3 */\n maxAttempts?: number;\n /** Delay strategy between attempts. @default 'exponential' */\n backoff?: 'exponential' | 'linear' | 'fixed';\n /** Base delay for first retry. @default 1000 */\n initialDelayMs?: number;\n /** Max delay cap. @default 30000 */\n maxDelayMs?: number;\n /** Add random jitter to delays. @default true */\n jitter?: boolean;\n };\n /** Max buffered events before dropping. @default 1000 */\n maxBufferSize?: number;\n /** Overflow policy. @default 'oldest' */\n dropPolicy?: 'oldest' | 'newest';\n /** Called when events are dropped from overflow or exhausted retries. */\n onDropped?: (events: T[], error?: Error) => void;\n}\n\nexport interface PipelineDrainFn<T> {\n (ctx: T): void;\n /** Flush all buffered events. */\n flush: () => Promise<void>;\n /** Flush and stop scheduling future timer work. */\n shutdown: () => Promise<void>;\n readonly pending: number;\n}\n\nfunction wait(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms);\n timer.unref?.();\n });\n}\n\nexport function createDrainPipeline<T = unknown>(\n options?: DrainPipelineOptions<T>,\n): (drain: (batch: T[]) => void | Promise<void>) => PipelineDrainFn<T> {\n const batchSize = options?.batch?.size ?? 50;\n const intervalMs = options?.batch?.intervalMs ?? 5000;\n const maxBufferSize = options?.maxBufferSize ?? 1000;\n const maxAttempts = options?.retry?.maxAttempts ?? 3;\n const backoff = options?.retry?.backoff ?? 'exponential';\n const initialDelayMs = options?.retry?.initialDelayMs ?? 1000;\n const maxDelayMs = options?.retry?.maxDelayMs ?? 30_000;\n const jitter = options?.retry?.jitter ?? true;\n const dropPolicy = options?.dropPolicy ?? 'oldest';\n const onDropped = options?.onDropped;\n\n if (!Number.isFinite(batchSize) || batchSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}`,\n );\n }\n if (!Number.isFinite(intervalMs) || intervalMs <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}`,\n );\n }\n if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}`,\n );\n }\n if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) {\n throw new Error(\n `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}`,\n );\n }\n\n return (drain: (batch: T[]) => void | Promise<void>): PipelineDrainFn<T> => {\n const buffer: T[] = [];\n let timer: ReturnType<typeof setTimeout> | null = null;\n let activeFlush: Promise<void> | null = null;\n let isShutdown = false;\n\n const clearTimer = () => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n\n const computeDelay = (attempt: number): number => {\n const base =\n backoff === 'fixed'\n ? initialDelayMs\n : backoff === 'linear'\n ? initialDelayMs * attempt\n : initialDelayMs * 2 ** (attempt - 1);\n\n const bounded = Math.min(base, maxDelayMs);\n if (!jitter || bounded <= 0) return bounded;\n const factor = 0.5 + Math.random(); // [0.5, 1.5)\n return Math.max(0, Math.round(bounded * factor));\n };\n\n const sendWithRetry = async (batch: T[]): Promise<void> => {\n let lastError: Error | undefined;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await drain(batch);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempt < maxAttempts) {\n await wait(computeDelay(attempt));\n }\n }\n }\n onDropped?.(batch, lastError);\n };\n\n const drainBuffer = async (): Promise<void> => {\n while (buffer.length > 0) {\n const batch = buffer.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const scheduleFlush = () => {\n if (isShutdown || timer || activeFlush) return;\n timer = setTimeout(() => {\n timer = null;\n startFlush();\n }, intervalMs);\n timer.unref?.();\n };\n\n const startFlush = () => {\n if (activeFlush || isShutdown) return;\n activeFlush = drainBuffer().finally(() => {\n activeFlush = null;\n if (isShutdown) return;\n if (buffer.length >= batchSize) {\n startFlush();\n } else if (buffer.length > 0) {\n scheduleFlush();\n }\n });\n };\n\n const push = (ctx: T) => {\n if (isShutdown) return;\n\n if (buffer.length >= maxBufferSize) {\n if (dropPolicy === 'newest') {\n onDropped?.([ctx]);\n return;\n }\n const dropped = buffer.splice(0, 1);\n onDropped?.(dropped);\n }\n\n buffer.push(ctx);\n if (buffer.length >= batchSize) {\n clearTimer();\n startFlush();\n } else {\n scheduleFlush();\n }\n };\n\n const flush = async (): Promise<void> => {\n clearTimer();\n if (activeFlush) await activeFlush;\n\n const snapshot = buffer.length;\n if (snapshot <= 0) return;\n const toFlush = buffer.splice(0, snapshot);\n while (toFlush.length > 0) {\n const batch = toFlush.splice(0, batchSize);\n await sendWithRetry(batch);\n }\n };\n\n const shutdown = async (): Promise<void> => {\n isShutdown = true;\n await flush();\n };\n\n const fn = push as PipelineDrainFn<T>;\n fn.flush = flush;\n fn.shutdown = shutdown;\n Object.defineProperty(fn, 'pending', {\n enumerable: true,\n get: () => buffer.length,\n });\n return fn;\n };\n}\n"]}