开发者

Npgsql does not commit transaction after failed command

开发者 https://www.devze.com 2023-04-07 03:14 出处:网络
I\'m using Npgsql 2.0.11 under .NET 4.0 to modify a PostgreSQL 9.0 database. The program makes many modifications to the database, all within a single transaction.

I'm using Npgsql 2.0.11 under .NET 4.0 to modify a PostgreSQL 9.0 database. The program makes many modifications to the database, all within a single transaction.

Just before committing, I run SELECT statement, which sometimes fails (eg. with a timeout). I swallow the exception and go ahead and commit the transaction anyway. There is no error, so it appears as if everything worked, but in actual fact the database was not modified at all!

My guess is that the failed SELECT rolled back the entire transaction. Can I either prevent th开发者_如何学JAVAis (ie. have the transaction still committed) or at least detect this situation and throw an exception, so the user knows the commit failed?

I know that in this specific case I could just move the SELECT outside the transaction, but I'm more concerned about solving this for the general case. Having a commit not commit is a pretty serious problem and I want to make sure it doesn't go undetected.


I know nothing about Npgsql, but I can speak to the behavior of PostgreSQL. When any error occurs within a PostgreSQL transaction, the transaction is marked invalid until it is closed. (Their term is "aborted", which I think is misleading.) Furthermore, and this is IMHO insane, if you COMMIT an invalid transaction, it "succeeds" but has the same effect as ROLLBACK. You can observe this in the psql REPL; it will print ROLLBACK in response to your COMMIT command, but it won't signal an error.

You can create a SAVEPOINT right before your final SELECT. If it fails, then ROLLBACK to the savepoint name; that will get you out of the invalid state and allow you to commit the previous part of the transaction.


I ended up writing a little wrapper method that tries to execute a trivial statement as part of the transaction right before committing, which is effective in detecting the problem.

    public static void CommitTransaction(NpgsqlConnection conn, NpgsqlTransaction tran)
    {
        using (var command = new NpgsqlCommand("SELECT 1", conn, tran))
        {
            try
            {
                command.ExecuteScalar();
            }
            catch (NpgsqlException ex)
            {
                if (ex.Code == "25P02")
                    throw new Exception("The transaction is invalid...");
                throw;
            }
        }

        tran.Commit();
    }

The fix is either of Morg.'s or Ryan Culpepper's answers: either run the statement outside of the transaction or create a SAVEPOINT beforehand and ROLLBACK to it on error.


Having something fail within a transaction and yet the transaction complete wouldn't be very transactional right ?

So basically, if it may fail and you don't care about it, don't put it in the transaction with that which must not fail.

Use transactions as they're meant to be used and you won't have any issues ;)

0

精彩评论

暂无评论...
验证码 换一张
取 消