-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Connection state gets corrupted when an SQL error occurred in nested transactions. #2123
Comments
FYI, here are logs, one is diesel's, which I got by modifying diesel, and the other is PostgreSQL, which shows how nested transactions behave.
I guess the correct way to address this issue is when you get an aborted error, keep it and decrement the depth. |
It looks like the issue here stems from the fact that we are only changing the transaction depth if the query succeeded. I don't think this is in any way limited to nested transactions, I believe you will get similar behavior by returning There are a few options here:
If we do change how we set the depth, we need to make sure that nothing happens if opening the transaction fails. Always performing the depth change is somewhat of a lie, since if Precomputing the depth change is the most "honest" solution I think, but it has the main problem of only working if the nested transaction fails. (e.g. if I'm not sure if there are any consequences to attempting to rollback if committing fails. I'd love to see any PR tackling this (or comments that more strongly demonstrate that we should/shouldn't fix this) address the questions/concerns above, why it's the best option, and what the behavior is in all of the various cases |
Logically yes, but in most cases,
One observation here is you can recover error by rolling back savepoint.
I think 'Attempt to rollback (savepoint) if committing fails' is consistent to the behavior of outer most |
I also think "Attempting to rollback the savepoint if releasing it (commit) does fail" is the best solution as it mirrors how top-level commit failure works (at last in postgres, an I guess in other DBs, too). I also would love it if the transaction manager uses the fn has_broken(&self, conn: &mut Self::Connection) -> bool {
let transaction_depth = <TransactionManager<Self::Connection>>
::get_transaction_depth(conn.transaction_manager());
transaction_depth != 0
} (I think this is currently the only invariant which we can fastly check across all databases/) |
Just to clarify I'm aware that this problem happens independent of r2d2, it's just that with connection pooling the reach such a bug can have is pretty big. |
I experienced the same problem even without the nesting, just as @sgrif expected. I was making a test that was validating that a value should be unique and noticed that the following test was failing. I also reproduced it: // main.rs
use diesel::pg::PgConnection;
use diesel::prelude::*;
fn main() {
let conn = PgConnection::establish("postgres://postgres:password@localhost/main").unwrap();
conn.begin_test_transaction().unwrap();
let ret = conn.execute("insert into foo (bar) values ('hello world')");
println!("{:?}", ret);
match conn.execute("insert into foo (bar) values ('hello world')") {
Ok(_) => panic!("This query should fail"),
Err(e) => println!("This should fail with unique constraint violation: {}", e),
};
let ret = conn.execute("select * from foo where bar = 'hello world'");
println!("{:?}", ret);
} Which gives the following output: $ cargo run
Ok(1)
This should fail with unique constraint violation: duplicate key value violates unique constraint "foo_bar_key"
Err(DatabaseError(__Unknown, "current transaction is aborted, commands ignored until end of transaction block")) |
Rollback to the last savepoint if we fail to release a certain savepoint. This happens if an (syntax) error occurs between the creating and the release of a savepoint. This behaviour mirrors the behaviour of unnested transactions, where we also abort if we cannot commit.
Rollback to the last savepoint if we fail to release a certain savepoint. This happens if an (syntax) error occurs between the creating and the release of a savepoint. This behaviour mirrors the behaviour of unnested transactions, where we also abort if we cannot commit.
Setup
Versions
Feature Flags
"postgres"
Problem Description
When an SQL fails in nested transactions, the connection gets corrupted and all the other transaction running as SERIALIZABLE fails in
AlreadyInTransaction
.What are you trying to accomplish?
Just writing an application. Transaction is nested in an accident
What is the expected output?
Ok(1)
What is the actual output?
Err(AlreadyInTransaction)
Are you seeing any additional errors?
postgresql output:
Steps to reproduce
run code below
Cargo.toml
src/main.rs
Then, gets output
The error occurs with only when you use
.build_transacion()
.However, internally, the connection is unsound:
TransactionManager
's depth is1
which should be0
at that point.Checklist
closed if this is not the case)
The text was updated successfully, but these errors were encountered: