fix(server): race condition on concurrent peer joins, crash-safe teardown, smart unhandled rejection handling

- Add per-peerId serialization lock (peerJoinLocks) to prevent concurrent dedupe races

- Wrap removePeerFromRoom calls in disconnect/leave/reaper with try/catch

- Replace immediate process.exit on unhandledRejection with rate-limited smart exit

- Optimize buildHealthPayload from 3-pass array ops to single for-of loop

- Reset rateLimitDenied counters in stopServerForTests

Release v2.3.1
This commit is contained in:
Timo
2026-06-15 13:14:00 +02:00
parent 6652a06840
commit d76e9195c4
3 changed files with 75 additions and 21 deletions
+11
View File
@@ -4,6 +4,17 @@ All notable changes to the KoalaSync browser extension and relay server.
---
## [v2.3.1] — 2026-06-15
### Fixed
- **Server: Race condition on concurrent peer joins**: Added a per-peerId serialization lock (`peerJoinLocks`) that prevents two connections with the same `peerId` from both passing the deduplication check and simultaneously registering in `peerToSocket`. Previously, rapid reconnects could leave stale mappings that caused cross-room ACK/PING misdelivery.
- **Server: Crash-safe error handling in teardown paths**: Wrapped `removePeerFromRoom` calls in the disconnect, leave, and reaper handlers with try/catch to prevent an unhandled exception in any teardown path from crashing the process.
- **Server: Smart unhandled rejection handling**: Replaced the immediate `process.exit(1)` on any `unhandledRejection` with a rate-limited approach — the server now exits only after 5 unhandled rejections within 60 seconds, surviving transient errors while still failing fast on cascading crashes.
- **Server: Reduced GC pressure in admin health metrics**: Replaced the three-pass `Array.from()` / `map()` / `reduce()` / `filter()` pattern in `buildHealthPayload()` with a single `for-of` loop, eliminating temporary array allocations proportional to the number of active rooms.
- **Server: Test isolation for rate-limit denial counters**: `stopServerForTests()` now resets the `rateLimitDenied` counters between test runs.
---
## [v2.3.0] — 2026-06-14
### Added