Skip to content

Streaming Mode

Streaming mode emits events in real time as the simulation runs, rather than collecting them all at the end. Enable it with time: { stream: true } in your generate() call.

Basic Usage

typescript
import { generate, defineJourney } from '@synode/core';
import { CallbackAdapter } from '@synode/core';

const adapter = new CallbackAdapter((event) => {
  console.log(event.name, event.timestamp);
});

await generate(journey, {
  time: { duration: '1h', stream: true },
  simulation: { users: 50, lanes: 4, tick: '5m' },
  adapter,
});

Pacing

Control how fast the simulation clock advances using simulation.pacing:

typescript
// As fast as possible (default)
simulation: { users: 500, lanes: 8, tick: '15m', pacing: 'none' }

// Real-time: wall-clock matches simulated time
simulation: { users: 500, lanes: 8, tick: '15m', pacing: 'realtime' }

// Accelerated: 10x faster than real-time
simulation: { users: 500, lanes: 8, tick: '15m', pacing: { mode: 'realtime', speed: 10 } }

// Fixed delay between each tick
simulation: { users: 500, lanes: 8, tick: '15m', pacing: { mode: 'fixed', delayMs: 100 } }

Live Ingestion Example

Stream generated events directly to a webhook in real time:

typescript
import { generate } from '@synode/core';
import { HttpAdapter } from '@synode/adapter-http';

await generate([browseJourney, purchaseJourney], {
  time: { duration: '24h', stream: true },
  simulation: {
    users: 200,
    lanes: 4,
    tick: '10m',
    pacing: { mode: 'realtime', speed: 60 }, // 1 simulated hour per real minute
  },
  adapter: new HttpAdapter({
    url: 'https://ingest.example.com/events',
    batchSize: 50,
  }),
});

Continuous Simulation

Use time: { stream: true } with an AbortSignal to run an endless simulation that you stop externally:

typescript
import { generate } from '@synode/core';
import { CallbackAdapter } from '@synode/core';

const controller = new AbortController();
setTimeout(() => controller.abort(), 60_000); // stop after 1 minute

await generate(journey, {
  time: { stream: true },
  simulation: {
    users: Infinity,
    lanes: 8,
    tick: '1m',
    pacing: 'realtime',
  },
  adapter: new CallbackAdapter((event) => ingest(event)),
  signal: controller.signal,
});

How It Works

In streaming mode the simulator advances the clock one tick at a time and flushes events from that tick before moving to the next. Events are emitted in simulated-timestamp order within each tick.

Without streaming, the simulator runs to completion and the adapter receives all events at the end (or batched as configured by the adapter itself).