New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Experiment/inject transaction connection #3708
base: master
Are you sure you want to change the base?
WIP: Experiment/inject transaction connection #3708
Conversation
FYI: I don't have any plans to merge this branch. I just created the PR to get initial feedback on the high-level approach. Afterwards, we can go back and implement this more cleanly. |
lib/client.js
Outdated
async function withConnection(next) { | ||
return next(connection); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops -- I thought I deleted this part already. I'll fix this in a little while. (I currently have the tests running on loop so that we can detect unhandled promise rejections)
async function(next) { | ||
return next(connection); | ||
}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: This function is currently duplicated in a few places. We can consolidate it as part of the re-implementation.
const withEverything = pipe([ | ||
withConnection, | ||
InjectTransactionID(this.txid), | ||
]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I'll keep the pipe([...])
function in the re-implementation. It's a functional programming concept that many developers might find mystifying.
This PR builds upon #3707 , which makes the diffs a bit challenging to read. So, here's a high-level overview of the concepts. Originally, all of the async acquireConnection(config, cb) {
// Figure out how the connection should be obtained. Specifically: did the caller
// provide a connection, or does the Transaction need to procure one itself?
const configConnection = config && config.connection;
const connection = configConnection || (await this.client.acquireConnection());
try {
// Do other post-processing on the connection, some of which might be
// implementation-specific
// ....
return await cb(connection);
} finally {
// Do other post-processing on the connection, some of which might be
// implementation-specific
// ....
if(configConnection) {
// Do nothing. The caller passed in a connection explicitly, so it is their
// responsibility to release it.
} else {
// This Transaction instance acquired the connection itself; therefore, it
// is the Transaction's responsibility to release it.
await this.client.releaseConnection(connection);
}
}
} As you can see, the Transaction is doing quite a bit of work to figure out the caller's intentions! With the new code, this logic has been reduced to: async acquireConnection(connection, cb) {
try {
// Do other post-processing on the connection, some of which might be
// implementation-specific
// ....
return await cb(connection);
} finally {
// Do other post-processing on the connection, some of which might be
// implementation-specific
// ....
}
} As you can see, the Transaction no longer needs to figure out if it's responsible for acquiring/releasing the connection. Instead, it can focus on the Dialect-specific details. So, where does the connection come from, then? Short answer: the Transaction delegates control to a
So, let's suppose that we're creating a top-level
In contrast, let's suppose we're creating a nested
Notice that it doesn't release the connection. That's because it is the caller's responsibility to release the connection. Anyway, that's the gist of this refactoring. There's still more work to be done, but it at least extracts this responsibility out of the |
Experimental branch that extracts the connection acquisition logic out of
Transaction
. Instead,Transactions
are provided with awithConnection(..)
function that handles the details.The end-result:
Transactions
no longer need to figure out if the connection should be released.