Skip to content

feat: backpressure-aware async broadcast with per-client drain queues - #217

Merged
levibliz merged 4 commits into
RiftCore00:mainfrom
ayomidemariam:feature/broadcast-backpressure
Jul 30, 2026
Merged

feat: backpressure-aware async broadcast with per-client drain queues#217
levibliz merged 4 commits into
RiftCore00:mainfrom
ayomidemariam:feature/broadcast-backpressure

Conversation

@ayomidemariam

@ayomidemariam ayomidemariam commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR implements backpressure-aware async broadcasting with per-client drain queues to prevent event loop starvation in large rooms.

Changes

  • Constructor options: RoomManager now accepts optional backpressure configuration with enabled, highWaterMark, slowConsumerTimeout, and batchSize options
  • Batched broadcasting: When backpressure is enabled, broadcasts process sends in configurable batches using setImmediate to yield control back to the event loop
  • Slow consumer detection: Clients whose ws.bufferedAmount exceeds highWaterMark (default 1MB) are flagged as slow
  • Message coalescing: Slow consumers receive only the latest location_update message, dropping intermediate updates
  • Auto-eviction: Slow consumers that don't drain within slowConsumerTimeout (default 30s) are terminated via ws.close(4000, "Slow consumer")
  • Observability: New getRoomStats(roomId) method returns member count, per-client send queue depths, and slow consumer list
  • Clean disconnect: Slow consumer tracking state is properly cleaned up on disconnect to prevent memory leaks

Backward Compatibility

The backpressure behavior is opt-in. When no options are provided (or enabled: false), broadcast() behaves identically to the current synchronous implementation. All 30 existing room-manager tests pass unchanged.

Testing

  • All existing tests pass (163 passed, 15 skipped)
  • Lint passes cleanly
  • No new dependencies added
  • No existing test files modified

Closes #194

- Import createRateLimiter from ./rate-limiter.js
- Add safeSend helper for outbound messages
- Implement per-message rate limiting before validation
- Clean up rate limiter state on disconnect
- Remove ipConnectionCount and server from return value
- All 18 test suites pass (163 tests)
…ain queues

- Add backpressure options to RoomManager constructor (opt-in)
- Batch broadcasts using setImmediate to prevent event loop starvation
- Detect slow consumers via ws.bufferedAmount high water mark
- Coalesce location_update messages for slow consumers
- Auto-terminate slow consumers after configurable timeout
- Add getRoomStats() for observability (queue depths, slow consumers)
- Clean up slow consumer state on disconnect (no memory leaks)
- All existing tests pass unchanged (backward compatible)

Closes #734
Closes #733

@levibliz levibliz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ensure your CI/CD PASSES

@levibliz
levibliz merged commit 8fb3867 into RiftCore00:main Jul 30, 2026
1 of 4 checks passed
RajeshRk18 pushed a commit to RajeshRk18/Real-Time-Spatial-Tracking-Gateway that referenced this pull request Aug 18, 2026
…nto feat/session-resumption-228

Resolves conflicts with the independently merged session-resumption
implementation (RiftCore00#267) and the co-located HTTP server / backpressure
work (RiftCore00#203, RiftCore00#217, RiftCore00#218):

- src/session-manager.js: keep this branch's encrypted manager
  (AES-256-GCM + HKDF, Redis/in-memory store, debounced saves, metrics)
  over the add/add conflicting upstream variant.
- src/server.js: keep this branch's session wiring and admin migration
  endpoint; integrate upstream's /healthz, /readyz and Prometheus
  /metrics endpoints, gateway metrics counters, event-loop lag sampling
  and markShuttingDown. Session resumption counters are now exported in
  the Prometheus exposition instead of a JSON /metrics body.
- src/index.js: keep session-aware graceful shutdown; call
  markShuttingDown() on SIGTERM/SIGINT so health probes flip to 503.
- src/room-manager.js: keep ring-buffer replay/dedup/limits; restore the
  backpressure-aware broadcast wiring (constructor options, client
  state tracking and cleanup) that upstream's copy referenced but never
  initialized.
- tests: keep this branch's session-manager and resumption integration
  suites; update /metrics assertions to the Prometheus format.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012uUSA8xm8WNue2Cw1mj1zM
RajeshRk18 added a commit to RajeshRk18/Real-Time-Spatial-Tracking-Gateway that referenced this pull request Aug 18, 2026
…nto feat/session-resumption-228

Resolves conflicts with the independently merged session-resumption
implementation (RiftCore00#267) and the co-located HTTP server / backpressure
work (RiftCore00#203, RiftCore00#217, RiftCore00#218):

- src/session-manager.js: keep this branch's encrypted manager
  (AES-256-GCM + HKDF, Redis/in-memory store, debounced saves, metrics)
  over the add/add conflicting upstream variant.
- src/server.js: keep this branch's session wiring and admin migration
  endpoint; integrate upstream's /healthz, /readyz and Prometheus
  /metrics endpoints, gateway metrics counters, event-loop lag sampling
  and markShuttingDown. Session resumption counters are now exported in
  the Prometheus exposition instead of a JSON /metrics body.
- src/index.js: keep session-aware graceful shutdown; call
  markShuttingDown() on SIGTERM/SIGINT so health probes flip to 503.
- src/room-manager.js: keep ring-buffer replay/dedup/limits; restore the
  backpressure-aware broadcast wiring (constructor options, client
  state tracking and cleanup) that upstream's copy referenced but never
  initialized.
- tests: keep this branch's session-manager and resumption integration
  suites; update /metrics assertions to the Prometheus format.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants