fix: prevent handle leak and add configurable statement_timeout for D…#308
fix: prevent handle leak and add configurable statement_timeout for D…#308slowbackspace wants to merge 2 commits intomasterfrom
Conversation
1a3efac to
af4ae12
Compare
There was a problem hiding this comment.
Pull request overview
This PR addresses a streaming resource leak when clients disconnect mid-response and introduces a configurable PostgreSQL statement_timeout for DB Sync queries via config/env var.
Changes:
- Destroy JSON streaming readable when the client connection closes to prevent stream handle leaks.
- Add
dbSync.statementTimeout/BLOCKFROST_CONFIG_DBSYNC_STATEMENT_TIMEOUTand wire it to Postgresstatement_timeout. - Bump version to
6.3.1and document the release in the changelog.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/string-utils.ts | Adds client-disconnect cleanup for toJSONStream to prevent handle leaks. |
| src/config.ts | Loads optional DB Sync statement timeout from env/config. |
| src/app.ts | Passes statement timeout into @fastify/postgres connection options. |
| package.json | Version bump to 6.3.1. |
| CHANGELOG.md | Documents 6.3.1 changes (timeout + leak fix). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
58a9927 to
f4bec7a
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const cleanup = () => { | ||
| dataStream.unpipe(transformStream); | ||
| dataStream.destroy(); | ||
| if (typeof (transformStream as stream.Transform).destroy === 'function') { | ||
| (transformStream as stream.Transform).destroy(); | ||
| } | ||
| }; |
There was a problem hiding this comment.
cleanup can be invoked more than once because it is registered separately with once('close') and once('error'). If both events fire (e.g., write error followed by close), this can double-destroy/unpipe streams and lead to noisy errors. Consider making cleanup idempotent with a cleaned flag (or wrap it in a one-time guard) before calling unpipe()/destroy().
| const cleanup = () => { | ||
| dataStream.unpipe(transformStream); | ||
| dataStream.destroy(); | ||
| if (typeof (transformStream as stream.Transform).destroy === 'function') { | ||
| (transformStream as stream.Transform).destroy(); | ||
| } | ||
| }; | ||
|
|
||
| serverResponse.once('close', cleanup); | ||
| serverResponse.once('error', cleanup); | ||
|
|
There was a problem hiding this comment.
toJSONStream attaches listeners only for close/error on the provided Writable. Many Writable implementations (including custom ones) may never emit close on normal completion, which can leave the cleanup listener (and captured dataStream/transformStream) referenced longer than necessary. Consider also cleaning up on finish (and/or removing the listeners when the piping completes) so cleanup reliably runs and listeners don’t linger.
| const cleanup = () => { | |
| dataStream.unpipe(transformStream); | |
| dataStream.destroy(); | |
| if (typeof (transformStream as stream.Transform).destroy === 'function') { | |
| (transformStream as stream.Transform).destroy(); | |
| } | |
| }; | |
| serverResponse.once('close', cleanup); | |
| serverResponse.once('error', cleanup); | |
| function cleanup() { | |
| // Remove listeners so the closure and streams can be garbage-collected. | |
| serverResponse.removeListener('close', onClose); | |
| serverResponse.removeListener('error', onError); | |
| serverResponse.removeListener('finish', onFinish); | |
| dataStream.unpipe(transformStream); | |
| dataStream.destroy(); | |
| if (typeof (transformStream as stream.Transform).destroy === 'function') { | |
| (transformStream as stream.Transform).destroy(); | |
| } | |
| } | |
| function onClose() { | |
| cleanup(); | |
| } | |
| function onError() { | |
| cleanup(); | |
| } | |
| function onFinish() { | |
| cleanup(); | |
| } | |
| serverResponse.once('close', onClose); | |
| serverResponse.once('error', onError); | |
| serverResponse.once('finish', onFinish); |
f4bec7a to
df2ea83
Compare
…B Sync