fix(trade): resolve WebSocket connection-pool memory leak (#158)#178
Open
Itodo-S wants to merge 2 commits into
Open
fix(trade): resolve WebSocket connection-pool memory leak (#158)#178Itodo-S wants to merge 2 commits into
Itodo-S wants to merge 2 commits into
Conversation
…ETAIL-BOYS#158) The TradeGateway broadcast to clients but never tracked or tore them down, so dropped/ghost sockets and their listeners were retained on the heap — the source of the gradual OOM under connection churn. Changes: - Track every connection in an explicit pool (Map keyed by socket id) via OnGatewayConnection/OnGatewayDisconnect; the entry (and all references to the socket) is deleted on disconnect. - Add a per-socket heartbeat: a 30s interval that forcibly disconnects sockets that are no longer connected or stop proving liveness. Liveness is refreshed from engine-level pongs (no client changes needed) and an optional app-level 'heartbeat:pong'. - Enable engine.io ping/pong (pingInterval 25s / pingTimeout 20s) so dead transports are terminated by the protocol itself. - On disconnect, clear the per-socket interval with clearInterval and detach every listener that was attached (packet/heartbeat:pong/error); clear all timers on module destroy. - Expose getConnectionCount() for health checks and leak assertions. - Fix a latent bad import: OnModuleInit comes from @nestjs/common, not @nestjs/websockets. Verification: - src/trade/trade.gateway.spec.ts: 9 tests covering connect/disconnect cleanup, heartbeat ping, ghost/dead-transport termination, listener detachment, timer clearing on destroy, and a 500-client connect/disconnect churn that leaves the pool empty (no leaked references). - scripts/ws-load-test.js: churns 500 clients x N rounds against a running server and prints rss/heapUsed each round (with --expose-gc) to confirm memory plateaus rather than growing.
Contributor
|
@Itodo-S CI build and test failed please fix it |
CI failed at `npm ci` and the build/test steps could not run. Resolve the pre-existing breakages that block the pipeline: - Align @nestjs/websockets and @nestjs/platform-socket.io to ^10.4.22 (matching the rest of @nestjs). The pinned ^10.0.0 peers reflect-metadata@^0.1.12, which conflicts with the project's reflect-metadata@^0.2.2 and made `npm ci` fail with ERESOLVE. Regenerated package-lock.json so the install resolves cleanly. - Fix InvoicesModule (src/dynamic/dynamic.module.ts): it imported ./invoices.controller and ./pdf.service, which do not exist. Point it at the classes that do (NetworkController, PdfService in ./dynamic.serivice). - Type CustomLogger.formatMessage's logLevel param as LogLevel (not string) so the override matches ConsoleLogger and compiles. - Fix the pools apy-history integration test: the endpoint returns the ApyHistoryPoint[] array directly (per its return type / Swagger isArray), and there is no global response-envelope interceptor, so assert the array shape instead of a {status,data} envelope. After this: `npm ci`, `npm run build`, and `npm test` (17/17) all pass.
Author
|
Thanks @AlAfiz — fixed and pushed. The pipeline was failing at
Verified locally: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #158.
The
TradeGatewaybroadcast trade updates to clients but never tracked connections or tore them down. Dropped/ghost sockets (and the listeners closing over them) stayed referenced on the heap, which is the gradual leak that triggers the ~48h OOM under connection churn.Changes (
src/trade/trade.gateway.ts)Map(keyed by id) on connect viaOnGatewayConnection, and the entry — and all references to the socket — is deleted on disconnect viaOnGatewayDisconnect.disconnect(true)s sockets that are no longer connected or stop proving liveness. Liveness is refreshed from engine-level pongs (no client changes required) plus an optional app-levelheartbeat:pong.pingInterval: 25s/pingTimeout: 20sso dead transports are terminated by engine.io itself.clearIntervaland every attached listener is detached (packet/heartbeat:pong/error);onModuleDestroyclears all remaining timers.getConnectionCount()exposed for health checks / leak assertions.OnModuleInitcomes from@nestjs/common, not@nestjs/websockets).Acceptance criteria
close(disconnect)/errorpaths now remove the socket from the pool and detach listeners; nothing keeps a dropped socket alive.clearIntervalinhandleDisconnect(andonModuleDestroy).src/trade/trade.gateway.spec.tschurns 500 connect/disconnect clients and asserts the pool returns to 0 (no leaked references).scripts/ws-load-test.jsreproduces this against a running server with heap/RSS logging (--expose-gc) to confirm memory plateaus.Testing
Covers: connect/disconnect cleanup, heartbeat ping, responsive client stays alive, ghost + dead-transport termination, listener detachment, timer clearing on destroy, and the 500-client churn leak check.