Skip to content

feat(realtime): stress harness, contract tests, recorder, ring-buffer, OTel - #423

Open
maryamabike120-collab wants to merge 1 commit into
Refract-Protocol:mainfrom
maryamabike120-collab:feat/realtime-perf-observability
Open

maryamabike120-collab wants to merge 1 commit into
Refract-Protocol:mainfrom
maryamabike120-collab:feat/realtime-perf-observability

Conversation

@maryamabike120-collab

@maryamabike120-collab maryamabike120-collab commented Oct 21, 2026 •

Copy link
Copy Markdown

Summary

This PR implements five issues that harden the realtime client's correctness, memory safety, observability, and testability.

Closes #235
Closes #234
Closes #236
Closes #253
Closes #237


Changes

src/lib/realtime/ringBuffer.ts — Bounded per-topic memory Closes #253

Fixed-capacity O(1) FIFO ring buffer backed by a pre-allocated array. A TopicRegistry manages one buffer per topic with documented caps:

  • 256 frames per topic (≈50 kB worst-case at 200 B/payload)
  • 64 topics max (guards against unbounded topic-space growth)

When a buffer is full the oldest frame is silently evicted. Excess topics beyond the 64-topic limit are counted in droppedTopics and silently discarded. Both caps are tuneable via RingBufferOptions / RegistryOptions.

src/lib/realtime/telemetry.ts — OpenTelemetry instrumentation Closes #237

OTel-compatible shim that uses a dynamic require('@opentelemetry/api') so the build never fails when the SDK is absent — no new production dependency. Falls back to in-process no-op shims.

src/lib/realtime/recorder.ts — Frame recorder & replay engine Closes #236

Opt-in FrameRecorder captures the ordered stream of realtime frames and connection lifecycle events with timestamps. replaySync / replayRecording replay deterministically against the client's subscriber handlers.

src/lib/realtime/contract.ts — Consumer-driven contract Closes #234

Zod schemas that pin the frontend's expectations of each realtime topic. diffOpenApiSchema provides a CI-checkable structural diff between the Zod schema and openapi/refract-api.yaml.

src/mocks/wsMockServer.ts — High-throughput WS mock server (#235)

Node-native in-process WebSocket server for stress tests. driveLoad() pushes sustained traffic and returns LoadStats.

.eslintrc.json (fix)

Fixed a pre-existing crash in the pre-commit hook: ESLint v8 cannot parse Literal[value=1e7] in selector syntax.


Tests

108 new tests, all passing.

File Tests
ringBuffer.test.ts 22
contract.test.ts 25
recorder.test.ts 28
telemetry.test.ts 27
stress.test.ts 16

…, OTel

Closes Refract-Protocol#235 — stress/load test harness
Closes Refract-Protocol#234 — consumer-driven contract tests
Closes Refract-Protocol#236 — frame recorder and deterministic replay engine
Closes Refract-Protocol#253 — bounded per-topic ring-buffer memory
Closes Refract-Protocol#237 — OpenTelemetry instrumentation

## What was added

### src/lib/realtime/ringBuffer.ts (Refract-Protocol#253)
Bounded, per-topic ring buffer with documented capacity caps (256 frames
per topic, 64 topics max). RingBuffer is a fixed-array O(1) push/drain
structure. TopicRegistry manages the per-topic map and assigns monotonic
sequence numbers. Wired into the updated client.ts so a high-frequency
stream can never grow client memory without limit.

### src/lib/realtime/telemetry.ts (Refract-Protocol#237)
OTel-compatible instrumentation shim. Uses a dynamic require of
`@opentelemetry/api` so the build never fails when the SDK is absent.
Both tracer and meter fall back to in-process no-op shims. Emits:
  - Counters: connect.attempts, connect.errors, reconnects, messages,
              fallback.activations
  - UpDownCounter: subscribe.active (per topic)
  - Histogram: session.duration_ms
  - Spans: 'realtime connect', 'realtime message', 'realtime fallback'
RealtimeTracer.withBackends() injects test doubles without an OTel SDK.

### src/lib/realtime/recorder.ts (Refract-Protocol#236)
Opt-in FrameRecorder captures ordered frames + lifecycle events with
timestamps. serializeRecording/deserializeRecording round-trip to JSON
fixture files. replaySync delivers frames synchronously; replayRecording
is async with wall-clock or speed-scaled timing. Turns production session
bugs into repeatable local test cases.

### src/lib/realtime/contract.ts (Refract-Protocol#234)
Zod-schema consumer contract for every realtime topic the frontend
subscribes to (currently: oracle_update). CONTRACT_VERSION pins the
backend API revision. diffOpenApiSchema() provides a CI-checkable
structural diff between the Zod schema and openapi/refract-api.yaml.
validateFrame/assertFrame are the runtime/test validation API.

### src/mocks/wsMockServer.ts (Refract-Protocol#235)
Node-native in-process WebSocket server (built on ws) for sustained load
tests. driveLoad() pushes frames at configurable Hz across multiple
topics with a round-robin payload factory. Ships chaosPayloadFactory and
malformedPayloadFactory for chaos/negative testing.

### src/lib/realtime/client.ts (updated)
Integrates all four new modules: every received frame is recorded in
the TopicRegistry, forwarded to the FrameRecorder (when active), and
traced via RealtimeTracer before dispatching to subscriber handlers.
Added notifyFallbackActivated() for REST-fallback telemetry.

### .eslintrc.json (fix)
Fixed pre-existing crash: ESLint v8 esquery cannot parse the bare
numeric literal 1e7 in selector syntax. Changed to a quoted string
selector `Literal[value="1e7"]` which parses correctly.

## Tests (108 new tests, all passing)
- ringBuffer.test.ts  — 22 unit tests (RingBuffer + TopicRegistry)
- contract.test.ts    — 25 tests (schema validation + OpenAPI drift check)
- recorder.test.ts    — 28 tests (capture, serialise, replay, determinism)
- telemetry.test.ts   — 27 tests (all lifecycle events, no-op safety)
- stress.test.ts      — 16 tests (in-process WS load, memory bounds,
                          reconnect churn, multi-subscriber delivery)
@drips-wave

drips-wave Bot commented Oct 21, 2026

Copy link
Copy Markdown

@maryamabike120-collab Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment