- Notifications
You must be signed in to change notification settings - Fork 182
Fix operations on ended transactions #230
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
Conversation
Can one of the admins verify this patch? |
2 similar comments
Can one of the admins verify this patch? |
Can one of the admins verify this patch? |
I suppose the id need not be a uuid. Could easily be an incrementing integer. Open to opinions. |
Not sure what was happening with the failed tests on node 4.x and 6.x - all passing here on my end 7.x but have fixed the eslint errors in the test files. |
@jannyHou Any idea what might be causing the failures? Is it something I can fix up? |
Seems to be unrelated to this PR:
What is the connection limit on the build server's postgres? |
var tx = new Transaction(connector, connection); | ||
tx.txId = uuid.v1(); | ||
connection.txId = tx.txId; | ||
cb(null, tx); |
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 think here we should callback connection
instead of tx
?
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.
The goal here is to make the new connection and transaction and say "this connection is currently working on this transaction" allowing for the error if you pass in a transaction with a connection id that doesn't match.
If you callback with connection, loop back connector will make a transaction out of it and pas it up the chain. This means you how no tie between the id of the connection and the current transaction.
@slnode test please |
@zbarbuto In my opinion the expected behaviour is: So if the example use case you described in the issue happens, it looks like a bug of nodejs postgresql driver to me :( @raymondfeng any thought? |
I don't think it's a problem with the driver since the driver doesn't actually handle transactions natively. You're just executing normal transaction sql on a single connection: https://github.com/brianc/node-postgres/wiki/Transactions This assumes you're managing the connection in such a way that no other operations can execute on it until the transaction has resolved. Their example does this by not passing the connection outside the callback chain. In the case of the loop back connector, the connection gets released from the transaction but nothing notifies an ongoing promise chain that the connection being used is no longer part of a transaction. If it doesn't error out creates, they just end up in the database which is very dangerous and can result in orphaned records: ModelA.create(tx) .then(() => ModelB.create(tx)) // Tx times out here .then(() => JoinTable.create({ model a, model b, tx}) If the transaction rolls back, a and b don't his the database (they were part of the transaction) but the join between them does (it was on a connection that no longer has an active transaction). Surely this can't be expected behaviour. |
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.
LGTM looking into the CI failure reason
@slnode test please |
2fd53d9
to 0bf6311
Compare @zbarbuto I think the default poolSize is 10, see https://github.com/brianc/node-postgres/blob/master/lib/defaults.js#L40, while increasing the limit doesn't solve the issue but make it worse... And my commit is testing the limit increase, sorry if that confuses you, feel free to remove it if you don't need. |
Thanks for trying. The test that is failing is one that I added a little while ago so strange that it starts failing now. Just waiting on approval from @raymondfeng? Did you want me to switch from uuid to an incrementing value to avoid unnecessary dependencies? |
lib/postgresql.js Outdated
transaction.connector === this) { | ||
if (transaction && transaction.connector === this) { | ||
if (!transaction.connection) { | ||
return callback(new Error(g.f('Connection does not exist'))); |
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.
Please wrap it into process.nextTick
or setImmediate
.
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.
Done
lib/postgresql.js Outdated
return callback(new Error(g.f('Connection does not exist'))); | ||
} | ||
if (transaction.txId !== transaction.connection.txId) { | ||
return callback(new Error(g.f('Transaction is not active'))); |
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.
Please wrap it into process.nextTick
or setImmediate
.
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.
Done
No further ideas on why the tests might be failing? |
@zbarbuto I am making time to debug why |
385c9c8
to 2876a95
Compare The same failure also happens on master branch, it fails on only one machine and I set up a windows vm using same environment, but could not reproduce the error. I will create another PR just debug why it fails. Sorry for the delay of checking this pr in, it looks good but we have to fix the test :) |
No problems. Thanks for your help @jannyHou |
b139e70
to 224240b
Compare 224240b
to 1361460
Compare @zbarbuto I have one question. What if 'ROLLBACK' query is executed, but the callback hasn't been called. So basically releaseConnection hasn't been called and connection.txId is not null. So at this time, any other query in the same transaction can still go through, right? Shouldn't we move connection.txId in rollback function itself? |
@dhr00v Wh would the callback not be executed if |
@zbarbuto After the |
Hmm - I see what you're saying. Do you have a proposed solution for this? Would you be willing to submit a PR? |
Ok. I will submit a PR for this @zbarbuto |
Fixed in #330, which is released as |
Description
Issue
This fixes a pretty critical issue where operations can occur on non-transactional connections when the user expects them to land in a transaction. Tests added which fail on master but pass here.
Example use case:
Solution:
I've added a uuid to each transaction which exists on the transaction itself and is tagged onto the current connection. Whenever the transaction is committed or rolled back ('finished') the id is removed from the connection but remains on the transaction. Any attempts to execute sql on the transaction checks the transaction id against the specified connection and will fail if the ids do not match.
Related issues
Checklist
guide