@flowcore/data-pump 0.22.1 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mod.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FlowcoreEvent, FlowcoreClient, EventListOutput } from '@flowcore/sdk';
2
- import PromClient from 'prom-client';
2
+ import { Registry, Gauge, Counter, Histogram } from 'prom-client';
3
3
  import * as Nats from 'nats';
4
4
 
5
5
  type FlowcoreDataPumpAuth = {
@@ -89,6 +89,8 @@ declare class FlowcoreDataSource {
89
89
  protected eventTypeIds?: string[];
90
90
  /** Cached time buckets */
91
91
  protected timeBuckets?: string[];
92
+ /** Index for each cached bucket. */
93
+ private timeBucketIndexes;
92
94
  /**
93
95
  * Creates a new FlowcoreDataSource instance
94
96
  * @param options - Configuration options for the data source
@@ -141,6 +143,8 @@ declare class FlowcoreDataSource {
141
143
  * @returns Promise that resolves to an array of time bucket strings
142
144
  */
143
145
  getTimeBuckets(force?: boolean): Promise<string[]>;
146
+ private lowerBoundTimeBucket;
147
+ private upperBoundTimeBucket;
144
148
  /**
145
149
  * Gets the next time bucket after the specified time bucket
146
150
  * @param timeBucket - The reference time bucket
@@ -186,6 +190,8 @@ interface PulseSnapshot {
186
190
  timeBucket: string;
187
191
  eventId: string | undefined;
188
192
  isLive: boolean;
193
+ /** Delivery to the processor is paused. The pump is still fetching and alive. */
194
+ paused: boolean;
189
195
  bufferDepth: number;
190
196
  bufferReserved: number;
191
197
  bufferSizeBytes: number;
@@ -237,6 +243,14 @@ interface FlowcoreDataPumpOptions {
237
243
  notifier?: FlowcoreDataPumpNotifierOptions;
238
244
  logger?: FlowcoreLogger;
239
245
  stopAt?: Date;
246
+ /**
247
+ * Start the pump with delivery already paused.
248
+ *
249
+ * Use this to restore a pause that is stored somewhere durable. Calling `pause()`
250
+ * after `start()` would let the pump deliver events in the gap between the two.
251
+ * Ignored when no `processor` is configured, for the same reason as {@link FlowcoreDataPump.pause}.
252
+ */
253
+ paused?: boolean;
240
254
  baseUrlOverride?: string;
241
255
  noTranslation?: boolean;
242
256
  directMode?: boolean;
@@ -259,7 +273,12 @@ declare class FlowcoreDataPump {
259
273
  private readonly logger?;
260
274
  private nextCursor?;
261
275
  private running;
276
+ private paused;
262
277
  private restartTo?;
278
+ private processLoopGeneration;
279
+ private activeProcessLoopGeneration?;
280
+ private processLoopBackoffGeneration?;
281
+ private processLoopRestartTimer?;
263
282
  private abortController?;
264
283
  private buffer;
265
284
  private bufferState;
@@ -273,30 +292,75 @@ declare class FlowcoreDataPump {
273
292
  private pulledCount;
274
293
  private processLoopRestartAttempts;
275
294
  private mainLoopRestartAttempts;
295
+ private readonly replayObserver;
296
+ private readonly bufferStats;
297
+ private bufferReservedCount;
298
+ private bufferSizeBytes;
299
+ private gaugePublicationScheduled;
276
300
  private constructor();
301
+ /**
302
+ * Whether delivery to the processor is currently paused.
303
+ * A paused pump is still running: it fetches, buffers and pulses.
304
+ */
305
+ get isPaused(): boolean;
277
306
  get isRunning(): boolean;
278
307
  getSnapshot(): PulseSnapshot | null;
279
308
  static create(options: FlowcoreDataPumpOptions, dataSourceOverride?: FlowcoreDataSource): FlowcoreDataPump;
280
309
  start(callback?: (error?: Error) => void): Promise<void>;
281
310
  private startMainLoop;
282
311
  restart(state: FlowcoreDataPumpState, stopAt?: Date | null): void;
312
+ /**
313
+ * Pause delivery to the processor.
314
+ *
315
+ * The fetch loop keeps running and tops the buffer up to `bufferSize`, then blocks on
316
+ * normal backpressure. The buffer, the cursor and the pulse emitter are untouched, so
317
+ * the control plane still sees a live pump. An in-flight batch finishes its handler
318
+ * and acknowledges, so the checkpoint stays accurate and nothing is redelivered
319
+ * needlessly.
320
+ *
321
+ * Idempotent. A paused pump holds up to `bufferSize` events in memory.
322
+ */
323
+ pause(): void;
324
+ /**
325
+ * Resume delivery to the processor from the exact position where {@link pause} stopped it.
326
+ * Idempotent.
327
+ */
328
+ resume(): void;
283
329
  stop(isRestart?: boolean): void;
284
330
  private updateState;
285
331
  private loop;
286
332
  reserve(amount: number): Promise<FlowcoreEvent[]>;
333
+ private reserveInternal;
287
334
  onFinalyFailed(handler: (events: FlowcoreEvent[]) => Promise<void> | void): void;
288
335
  acknowledge(eventIds: string[]): Promise<void>;
289
336
  fail(eventIds: string[]): Promise<void>;
290
337
  private reOpen;
338
+ /**
339
+ * Guarantee a live process loop whenever the pump is running with a
340
+ * processor. Called from the fetch loop, so a delivery loop that exited for
341
+ * any reason — most importantly a restart, which clears `running` while the
342
+ * loop is mid-batch — comes back within one fetch iteration instead of
343
+ * leaving a pump that pulls but never delivers.
344
+ */
345
+ private ensureProcessLoop;
291
346
  private startProcessLoop;
347
+ private isCurrentProcessLoop;
292
348
  private processLoop;
349
+ private addEventsToBuffer;
350
+ private updateReservedStats;
351
+ private removeFromBufferStats;
352
+ private resetBufferStats;
293
353
  private updateMetricsGauges;
354
+ private publishMetricsGauges;
294
355
  private incMetricsCounter;
295
- private waiterEvents?;
356
+ private publicEventWaiter?;
357
+ private readonly processEventWaiters;
358
+ private notifyEventWaiters;
296
359
  private waitForEvents;
297
360
  private waiterBufferThreshold?;
298
361
  private waitForBufferThreshold;
299
362
  private waiterBufferEmpty?;
363
+ private notifyBufferEmpty;
300
364
  private waitForBufferEmpty;
301
365
  }
302
366
 
@@ -321,6 +385,7 @@ declare class FlowcoreDataPumpCluster {
321
385
  private readonly logger?;
322
386
  private running;
323
387
  private isLeader;
388
+ private paused;
324
389
  private pump?;
325
390
  private leaderConnection?;
326
391
  private workers;
@@ -372,6 +437,21 @@ declare class FlowcoreDataPumpCluster {
372
437
  */
373
438
  handleConnection(ws: WebSocket): void;
374
439
  start(): Promise<void>;
440
+ /**
441
+ * Whether delivery is paused across the cluster.
442
+ * The flag lives on the cluster, so it survives a leader change.
443
+ */
444
+ get isPaused(): boolean;
445
+ /**
446
+ * Pause delivery. A leader applies it to its pump immediately. A follower records it,
447
+ * so the pause is re-applied if this instance later becomes leader.
448
+ *
449
+ * The flag is in-memory. It does NOT survive a process restart or a full rolling
450
+ * deploy — persist it in the coordinator or the control plane if you need that.
451
+ */
452
+ pause(): void;
453
+ /** Resume delivery from the position where {@link pause} stopped it. */
454
+ resume(): void;
375
455
  stop(): Promise<void>;
376
456
  private startHeartbeat;
377
457
  private startElectionLoop;
@@ -392,22 +472,73 @@ declare class FlowcoreDataPumpCluster {
392
472
  private handleWorkerMessage;
393
473
  }
394
474
 
395
- declare const dataPumpPromRegistry: PromClient.Registry<"text/plain; version=0.0.4; charset=utf-8">;
475
+ declare const dataPumpPromRegistry: Registry<"text/plain; version=0.0.4; charset=utf-8">;
476
+ declare const REPLAY_DURATION_BUCKETS_SECONDS: readonly [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 20, 30, 60, 120];
477
+ declare const REPLAY_EVENT_COUNT_BUCKETS: readonly [1, 10, 50, 100, 250, 500, 1000, 2000, 5000, 10000];
478
+ declare const REPLAY_BATCH_SIZE_BUCKETS: readonly ["empty", "1", "2-10", "11-100", "101-1000", "1001+"];
479
+ declare const REPLAY_FETCH_RESULTS: readonly ["success", "no_events", "error"];
480
+ declare const REPLAY_STAGE_RESULTS: readonly ["success", "error"];
481
+ declare const REPLAY_IDLE_REASONS: readonly ["no_events", "buffer_full", "waiting_for_events"];
482
+ type ReplayBatchSizeBucket = (typeof REPLAY_BATCH_SIZE_BUCKETS)[number];
483
+ type ReplayIdleReason = (typeof REPLAY_IDLE_REASONS)[number];
484
+ type ReplaySourceLabels = {
485
+ tenant: string;
486
+ data_core: string;
487
+ flow_type: string;
488
+ };
489
+ declare function replayBatchSizeBucket(size: number): ReplayBatchSizeBucket;
490
+ declare function createDataPumpMetrics(registry: Registry, includeDefaultRegistry?: boolean): {
491
+ bufferEventCountGauge: Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
492
+ bufferReservedEventCountGauge: Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
493
+ bufferSizeBytesGauge: Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
494
+ eventsAcknowledgedCounter: Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
495
+ eventsFailedCounter: Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
496
+ eventsPulledSizeBytesCounter: Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
497
+ sdkCommandsCounter: Counter<"command">;
498
+ replayFetchDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result">;
499
+ replayFetchEvents: Histogram<"tenant" | "data_core" | "flow_type">;
500
+ replayHandlerDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result" | "batch_size_bucket">;
501
+ replayAcknowledgementDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result">;
502
+ replayCheckpointDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result">;
503
+ replayIdleDuration: Histogram<"tenant" | "data_core" | "flow_type" | "reason">;
504
+ };
505
+ type DataPumpMetrics = ReturnType<typeof createDataPumpMetrics>;
396
506
  declare const metrics: {
397
- bufferEventCountGauge: PromClient.Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
398
- bufferReservedEventCountGauge: PromClient.Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
399
- bufferSizeBytesGauge: PromClient.Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
400
- eventsAcknowledgedCounter: PromClient.Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
401
- eventsFailedCounter: PromClient.Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
402
- eventsPulledSizeBytesCounter: PromClient.Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
403
- sdkCommandsCounter: PromClient.Counter<"command">;
507
+ bufferEventCountGauge: Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
508
+ bufferReservedEventCountGauge: Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
509
+ bufferSizeBytesGauge: Gauge<"tenant" | "data_core" | "flow_type" | "event_type">;
510
+ eventsAcknowledgedCounter: Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
511
+ eventsFailedCounter: Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
512
+ eventsPulledSizeBytesCounter: Counter<"tenant" | "data_core" | "flow_type" | "event_type">;
513
+ sdkCommandsCounter: Counter<"command">;
514
+ replayFetchDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result">;
515
+ replayFetchEvents: Histogram<"tenant" | "data_core" | "flow_type">;
516
+ replayHandlerDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result" | "batch_size_bucket">;
517
+ replayAcknowledgementDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result">;
518
+ replayCheckpointDuration: Histogram<"tenant" | "data_core" | "flow_type" | "result">;
519
+ replayIdleDuration: Histogram<"tenant" | "data_core" | "flow_type" | "reason">;
404
520
  };
521
+ declare class ReplayStageObserver {
522
+ private readonly stageMetrics;
523
+ private readonly source;
524
+ private readonly now;
525
+ constructor(stageMetrics: DataPumpMetrics, source: ReplaySourceLabels, now?: () => number);
526
+ observeFetch<T extends {
527
+ events: unknown[];
528
+ }>(operation: () => Promise<T>): Promise<T>;
529
+ observeHandler<T>(events: unknown[], operation: () => Promise<T>): Promise<T>;
530
+ observeAcknowledgement<T>(operation: () => T): T;
531
+ observeCheckpoint<T>(operation: () => Promise<T> | T): Promise<T>;
532
+ observeIdle<T>(reason: ReplayIdleReason, operation: () => Promise<T>): Promise<T>;
533
+ private observeResult;
534
+ private elapsedSeconds;
535
+ }
405
536
  declare const clusterMetrics: {
406
- activeWorkersGauge: PromClient.Gauge<string>;
407
- leaderStatusGauge: PromClient.Gauge<string>;
408
- eventsDistributedCounter: PromClient.Counter<string>;
409
- workerAcksCounter: PromClient.Counter<string>;
410
- workerFailsCounter: PromClient.Counter<string>;
537
+ activeWorkersGauge: Gauge<string>;
538
+ leaderStatusGauge: Gauge<string>;
539
+ eventsDistributedCounter: Counter<string>;
540
+ workerAcksCounter: Counter<string>;
541
+ workerFailsCounter: Counter<string>;
411
542
  };
412
543
 
413
544
  declare class NatsConnectionManager {
@@ -514,4 +645,4 @@ declare class DeliveryTracker {
514
645
 
515
646
  declare const noOpLogger: FlowcoreLogger;
516
647
 
517
- export { DeliveryTracker, FlowcoreDataPump, type FlowcoreDataPumpAuth, FlowcoreDataPumpCluster, type FlowcoreDataPumpClusterOptions, type FlowcoreDataPumpCoordinator, type FlowcoreDataPumpDataSource, type FlowcoreDataPumpOptions, type FlowcoreDataPumpProcessor, type FlowcoreDataPumpState, type FlowcoreDataPumpStateManager, FlowcoreDataSource, type FlowcoreDataSourceOptions, type FlowcoreLogger, NatsConnectionManager, NatsDistributionLeader, type NatsDistributionReply, type NatsDistributionRequest, NatsDistributionWorker, type PendingDelivery, PulseEmitter, type PulseEmitterOptions, type PulseLogLevel, type PulseSnapshot, type WsAckMessage, WsConnection, type WsConnectionOptions, type WsEventsMessage, type WsFailMessage, type WsMessage, type WsPingMessage, type WsPongMessage, clusterMetrics, dataPumpPromRegistry, deserializeMessage, metrics, noOpLogger, serializeMessage };
648
+ export { type DataPumpMetrics, DeliveryTracker, FlowcoreDataPump, type FlowcoreDataPumpAuth, FlowcoreDataPumpCluster, type FlowcoreDataPumpClusterOptions, type FlowcoreDataPumpCoordinator, type FlowcoreDataPumpDataSource, type FlowcoreDataPumpOptions, type FlowcoreDataPumpProcessor, type FlowcoreDataPumpState, type FlowcoreDataPumpStateManager, FlowcoreDataSource, type FlowcoreDataSourceOptions, type FlowcoreLogger, NatsConnectionManager, NatsDistributionLeader, type NatsDistributionReply, type NatsDistributionRequest, NatsDistributionWorker, type PendingDelivery, PulseEmitter, type PulseEmitterOptions, type PulseLogLevel, type PulseSnapshot, REPLAY_BATCH_SIZE_BUCKETS, REPLAY_DURATION_BUCKETS_SECONDS, REPLAY_EVENT_COUNT_BUCKETS, REPLAY_FETCH_RESULTS, REPLAY_IDLE_REASONS, REPLAY_STAGE_RESULTS, type ReplayBatchSizeBucket, type ReplayIdleReason, type ReplaySourceLabels, ReplayStageObserver, type WsAckMessage, WsConnection, type WsConnectionOptions, type WsEventsMessage, type WsFailMessage, type WsMessage, type WsPingMessage, type WsPongMessage, clusterMetrics, createDataPumpMetrics, dataPumpPromRegistry, deserializeMessage, metrics, noOpLogger, replayBatchSizeBucket, serializeMessage };