UNPKG

autotel

Version:
146 lines (144 loc) 4.57 kB
'use strict'; // src/drain-pipeline.ts function wait(ms) { return new Promise((resolve) => { const timer = setTimeout(resolve, ms); timer.unref?.(); }); } function createDrainPipeline(options) { const batchSize = options?.batch?.size ?? 50; const intervalMs = options?.batch?.intervalMs ?? 5e3; const maxBufferSize = options?.maxBufferSize ?? 1e3; const maxAttempts = options?.retry?.maxAttempts ?? 3; const backoff = options?.retry?.backoff ?? "exponential"; const initialDelayMs = options?.retry?.initialDelayMs ?? 1e3; const maxDelayMs = options?.retry?.maxDelayMs ?? 3e4; const jitter = options?.retry?.jitter ?? true; const dropPolicy = options?.dropPolicy ?? "oldest"; const onDropped = options?.onDropped; if (!Number.isFinite(batchSize) || batchSize <= 0) { throw new Error( `[autotel/drain-pipeline] batch.size must be a positive finite number, got: ${batchSize}` ); } if (!Number.isFinite(intervalMs) || intervalMs <= 0) { throw new Error( `[autotel/drain-pipeline] batch.intervalMs must be a positive finite number, got: ${intervalMs}` ); } if (!Number.isFinite(maxBufferSize) || maxBufferSize <= 0) { throw new Error( `[autotel/drain-pipeline] maxBufferSize must be a positive finite number, got: ${maxBufferSize}` ); } if (!Number.isFinite(maxAttempts) || maxAttempts <= 0) { throw new Error( `[autotel/drain-pipeline] retry.maxAttempts must be a positive finite number, got: ${maxAttempts}` ); } return (drain) => { const buffer = []; let timer = null; let activeFlush = null; let isShutdown = false; const clearTimer = () => { if (timer) { clearTimeout(timer); timer = null; } }; const computeDelay = (attempt) => { const base = backoff === "fixed" ? initialDelayMs : backoff === "linear" ? initialDelayMs * attempt : initialDelayMs * 2 ** (attempt - 1); const bounded = Math.min(base, maxDelayMs); if (!jitter || bounded <= 0) return bounded; const factor = 0.5 + Math.random(); return Math.max(0, Math.round(bounded * factor)); }; const sendWithRetry = async (batch) => { let lastError; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { await drain(batch); return; } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt < maxAttempts) { await wait(computeDelay(attempt)); } } } onDropped?.(batch, lastError); }; const drainBuffer = async () => { while (buffer.length > 0) { const batch = buffer.splice(0, batchSize); await sendWithRetry(batch); } }; const scheduleFlush = () => { if (isShutdown || timer || activeFlush) return; timer = setTimeout(() => { timer = null; startFlush(); }, intervalMs); timer.unref?.(); }; const startFlush = () => { if (activeFlush || isShutdown) return; activeFlush = drainBuffer().finally(() => { activeFlush = null; if (isShutdown) return; if (buffer.length >= batchSize) { startFlush(); } else if (buffer.length > 0) { scheduleFlush(); } }); }; const push = (ctx) => { if (isShutdown) return; if (buffer.length >= maxBufferSize) { if (dropPolicy === "newest") { onDropped?.([ctx]); return; } const dropped = buffer.splice(0, 1); onDropped?.(dropped); } buffer.push(ctx); if (buffer.length >= batchSize) { clearTimer(); startFlush(); } else { scheduleFlush(); } }; const flush = async () => { clearTimer(); if (activeFlush) await activeFlush; const snapshot = buffer.length; if (snapshot <= 0) return; const toFlush = buffer.splice(0, snapshot); while (toFlush.length > 0) { const batch = toFlush.splice(0, batchSize); await sendWithRetry(batch); } }; const shutdown = async () => { isShutdown = true; await flush(); }; const fn = push; fn.flush = flush; fn.shutdown = shutdown; Object.defineProperty(fn, "pending", { enumerable: true, get: () => buffer.length }); return fn; }; } exports.createDrainPipeline = createDrainPipeline; //# sourceMappingURL=chunk-7EQ4G4SI.cjs.map //# sourceMappingURL=chunk-7EQ4G4SI.cjs.map