Hyperlinkv0.8.0-beta.28

Polling

Polling.jitteredconstsrc/Polling.ts:171
(
  interval: Duration.Input,
  options?: { readonly jitter: number }
): Layer.Layer<PollingTag>

Fixed interval with random jitter to prevent thundering herd. Each tick varies by ±jitter fraction of the base interval.

presets
Source src/Polling.ts:17145 lines
export const jittered = (
  interval: Duration.Input,
  options: { readonly jitter: number } = { jitter: 0.1 }
): Layer.Layer<PollingTag> => {
  const baseMs = Duration.toMillis(Duration.fromInputUnsafe(interval));
  const jitterFraction = Math.abs(options.jitter);

  return registerPollingLayer(
    Layer.effect(
      PollingTag,
      Effect.gen(function* () {
        const wakeRef = yield* Ref.make<Deferred.Deferred<void, never>>(
          Deferred.makeUnsafe()
        );

        const awaitNextTick: Effect.Effect<void> = Effect.gen(function* () {
          const d = Deferred.makeUnsafe<void, never>();
          yield* Ref.set(wakeRef, d);
          // Random offset: base +/- jitter%.
          const random = yield* Random.next;
          const offset = (random * 2 - 1) * jitterFraction * baseMs;
          const ms = Math.max(0, baseMs + offset);
          yield* Effect.race(
            Effect.sleep(Duration.millis(ms)),
            Deferred.await(d)
          ).pipe(Effect.asVoid);
        });

        const requestWake = Effect.flatMap(Ref.get(wakeRef), (d) =>
          Deferred.succeed(d, undefined)
        ).pipe(Effect.asVoid);

        return {
          awaitNextTick,
          requestWake,
          resetCadence: requestWake,
          afterTick: Effect.void,
          peekCadence: Effect.succeed(
            Option.some(Duration.fromInputUnsafe(interval))
          ),
        } satisfies PollingService;
      })
    )
  );
};