UNPKG

@axiomhq/js

Version:

The official javascript bindings for the Axiom API

149 lines (147 loc) 4.74 kB
const DEFAULT_MAX_BATCH_SIZE = 1000; const DEFAULT_FLUSH_INTERVAL_MS = 1000; /** * Builds a deterministic key for grouping events into a single batch queue. * * The key includes dataset identity and ingest options that influence server * parsing so incompatible payloads do not share the same queue. */ function createBatchKey(id, options) { return `${id}:${options?.timestampField || "-"}:${options?.timestampFormat || "-"}:${options?.csvDelimiter || "-"}`; } /** * In-memory FIFO event queue with time/size-based flushing. * * Events are buffered and flushed either when enough events have accumulated or * when the flush interval elapses. Flushes are serialized to preserve ordering. */ class Batch { ingestFn; id; options; events = []; state = "idle"; activeFlush = Promise.resolve(); nextFlush; lastFlush = new Date(); maxBatchSize; flushIntervalMs; onError; now; /** * Creates a background batch queue for one dataset/options combination. */ constructor(ingestFn, id, options, config) { this.ingestFn = ingestFn; this.id = id; this.options = options; this.maxBatchSize = config?.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE; this.flushIntervalMs = config?.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS; this.onError = config?.onError; this.now = config?.now ?? Date.now; } /** * Appends event(s) to the queue and triggers or schedules a flush. * * This method is intentionally fire-and-forget. Background flush failures are * reported through the configured `onError` callback. */ ingest = (events) => { this.enqueue(events); if (this.shouldFlushNow()) { this.startBackgroundFlush(); return; } this.scheduleFlush(); }; /** * Flushes currently queued events and resolves when this flush finishes. * * Flushes are serialized with any in-flight flush to avoid concurrent sends. */ flush = async () => { const events = this.drainEvents(); this.clearScheduledFlush(); const previousFlush = this.activeFlush; const flushPromise = (async () => { this.state = "flushing"; // Keep flushes serialized but don't allow previous failures to poison // all future flushes. await previousFlush.catch(() => undefined); if (events.length === 0) { this.lastFlush = new Date(this.now()); if (this.state !== "scheduled") { this.state = "idle"; } return; } try { return await this.ingestFn(this.id, events, this.options); } finally { this.lastFlush = new Date(this.now()); if (this.state !== "scheduled") { this.state = "idle"; } } })(); this.activeFlush = flushPromise; return await flushPromise; }; /** * Appends events to the queue while preserving FIFO order. */ enqueue = (events) => { if (Array.isArray(events)) { this.events.push(...events); return; } this.events.push(events); }; /** * Drains the current queue in O(1) by swapping array references. */ drainEvents = () => { const events = this.events; this.events = []; return events; }; /** * Returns true when queue size or elapsed time requires an immediate flush. */ shouldFlushNow = () => { return this.events.length >= this.maxBatchSize || this.lastFlush.getTime() < this.now() - this.flushIntervalMs; }; /** * Schedules a deferred flush to ensure low-volume traffic is eventually sent. */ scheduleFlush = () => { this.clearScheduledFlush(); this.state = "scheduled"; this.nextFlush = setTimeout(() => { this.startBackgroundFlush(); }, this.flushIntervalMs); }; /** * Starts a flush without awaiting it and forwards failures to `onError`. */ startBackgroundFlush = () => { const flushPromise = this.flush(); void flushPromise.catch((error) => { this.onError?.(error); return; }); }; /** * Cancels a pending timer-based flush, if one exists. */ clearScheduledFlush = () => { if (!this.nextFlush) { return; } clearTimeout(this.nextFlush); this.nextFlush = undefined; }; } export { Batch, createBatchKey }; //# sourceMappingURL=batch.js.map