first commit
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
# `createHeartbeat`
|
||||
|
||||
## Purpose
|
||||
|
||||
Builds the heartbeat sweep that the daemon installs while running. The sweep periodically calls `PresenceTracker.detectTimedOut()` and forwards every returned id through an `onTimedOut(id)` callback (the daemon disconnects the client). Splitting this out of `gateway.ts` keeps the daemon file under the line cap and lets tests assert sweep behaviour without booting a server.
|
||||
|
||||
## Features
|
||||
|
||||
- Idempotent `start()` / `stop()` — both methods can be called multiple times safely.
|
||||
- `enabled()` gate so the daemon can disable presence (`enablePresence: false`) without paying the sweep cost.
|
||||
- `timer.unref()` keeps the sweep from blocking process exit.
|
||||
- Pure factory — no class state escapes the closure.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { createHeartbeat } from "@suaveplan/websocket-gateway";
|
||||
|
||||
const hb = createHeartbeat(presenceTracker, 30_000, (id) => gateway.disconnect(id), () => true);
|
||||
hb.start();
|
||||
hb.stop();
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { createHeartbeat } from "@suaveplan/websocket-gateway";
|
||||
|
||||
let enabled = true;
|
||||
const hb = createHeartbeat(
|
||||
presenceTracker,
|
||||
1_000,
|
||||
(id) => {
|
||||
gateway.disconnectClient(id, "timeout");
|
||||
},
|
||||
() => enabled
|
||||
);
|
||||
hb.start();
|
||||
// Pause sweeps without stopping the timer (e.g. during maintenance)
|
||||
enabled = false;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createHeartbeat(presence, intervalMs, onTimedOut, enabled): HeartbeatHandle`
|
||||
- `HeartbeatHandle.start(): void`
|
||||
- `HeartbeatHandle.stop(): void`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The handle keeps the timer reference inside the closure so the daemon never needs to introspect or replace it. Using `unref()` (when available) means the heartbeat does not pin the Node process during graceful shutdown — the daemon still calls `stop()` explicitly in its `stop()` path. The `enabled()` predicate is checked inside the timer callback (not at start-time) so consumers can disable sweeps at runtime without restarting the heartbeat.
|
||||
|
||||
The sweep runs synchronously inside the `setInterval` callback. `detectTimedOut()` returns at most `presence.size` entries and the typical workload (a few thousand clients per gateway instance) finishes well within a single millisecond, so the sweep cannot starve other timers. The caller is responsible for handling the actual disconnect — the heartbeat does not own the client map, the room set, or any other state. That separation keeps the unit tests trivial: a `PresenceTracker` plus an array as the sink is enough to assert every branch.
|
||||
|
||||
The interval is bound at construction time and cannot be changed without recreating the handle. This is intentional — runtime interval changes would require re-scheduling logic, and the daemon's stable configuration model has no use case for that. Consumers wanting maintenance-window dynamic behaviour use the `enabled()` predicate to pause without restarting.
|
||||
Reference in New Issue
Block a user