Repository navigation
Conversation
e627e42 to
0d8cfcd
Compare
Code Review by Qodo
1. Failed cleanup makes retries leak pools
|
|
Addressed the review feedback in 9c06c0c:
On "Connect failure still leaks": that path ( |
commit: |
|
Rebased onto current master; CI is re-running. This closes the connection leak when |
9c06c0c to
ef18c8e
Compare
|
This repository was recently restructured into a pnpm workspace, and the If your branch has diverged enough that a full rebase isn't worth it, Sorry for the disruption, and thanks for your patience. |
Reject concurrent initialize() calls while one is already in flight, instead of letting both pass the isInitialized check and connect the driver twice, leaking a connection. Disconnect the driver (and best-effort the cache provider) when queryResultCache.connect() fails, instead of rejecting with the driver still connected and no way to release it.
Guard the cleanup driver.disconnect() so a disconnect failure cannot mask the original initialization error, harden the concurrency test teardown for the not-initialized case, reference the tracked issue in the new regression tests, and document that initialize() rejects concurrent or repeated calls.
ef18c8e to
1f991c7
Compare
alumni
left a comment
There was a problem hiding this comment.
Thanks for this!
I would also suggest implementing a single-flight pattern to initialize the DataSource in your application if you want to handle concurrency.
We removed the deprecated connection manager in 1.0 since TypeORM was doing a few too many things, but if we ever create a separate connection manager package, it would likely be using a single flight pattern to create and initialize them.
Fixes #12705
Description of change
DataSource.initialize()could leak database connections in two ways:isInitializedguard is only set afterdriver.connect()resolves, so two overlappinginitialize()calls both passed the check and each opened a driver connection/pool — one of them leaked.queryResultCache.connect()runs after the driver connects but before the existing cleanuptry/catchbegan, so if the cache failed to connect,initialize()rejected with the driver still connected and no way to release it.New behavior:
isInitializingflag makes an overlappinginitialize()call reject withCannotConnectAlreadyConnectedError— the same error already thrown when callinginitialize()on an initialized DataSource, so no new API surface. The flag is cleared in afinally, so a failed initialize can still be retried, as before.try/catch. On failure beforeisInitializedis set (a cache connect failure), the driver is disconnected directly —destroy()cannot be used on that path because it throwsCannotExecuteNotConnectedErrorwhen the DataSource is not initialized. A best-effortqueryResultCache.disconnect()runs first (errors ignored) to release providers that partially connected before throwing, without masking the original error. On failure afterisInitializedis set, behavior is unchanged:destroy()is called and the error re-thrown.Behavioral note for reviewers: code that previously raced two
initialize()calls and appeared to work (while silently double-connecting the driver) will now get a rejection on the second call. Rejecting is consistent with the existing double-initialize semantics; if you'd rather coalesce (return the in-flight promise so concurrent callers all resolve together), I'm happy to switch — rejecting seemed like the more conservative default.Verification: two new regression tests in
test/functional/data-source/data-source.test.ts, both usingbetter-sqlite3in-memory so they need no external services, and both failing againstmaster:isInitializedstaysfalse.driver.connect()to hold the first call mid-flight, asserts the second call rejects withCannotConnectAlreadyConnectedErrorand thatconnect()was only invoked once.Pull-Request Checklist
masterbranchFixes #NNNN,Closes #NNNN, orResolves #NNNN— Fixes DataSource.initialize() leaks connections on concurrent calls and on cache connection failure #12705tests/**.test.ts)docs/docs/**.md) — noted in the DataSource docs thatinitialize()rejects concurrent or repeated calls and that a failed initialization can be retried