简体   繁体   English

命令失败后,Npgsql不提交事务

[英]Npgsql does not commit transaction after failed command

I'm using Npgsql 2.0.11 under .NET 4.0 to modify a PostgreSQL 9.0 database. 我在.NET 4.0下使用Npgsql 2.0.11来修改PostgreSQL 9.0数据库。 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). 在提交之前,我运行SELECT语句,有时会失败(例如,超时)。 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. 我的猜测是失败的SELECT回滚整个事务。 Can I either prevent this (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. 我知道在这个特定的情况下我可以在事务之外移动SELECT,但是我更关心的是解决这个问题。 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. 我对Npgsql一无所知,但我可以谈谈PostgreSQL的行为。 When any error occurs within a PostgreSQL transaction, the transaction is marked invalid until it is closed. 当PostgreSQL事务中发生任何错误时,事务将被标记为无效,直到它被关闭。 (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 . (他们的术语是“中止”,我认为这是误导性的。)此外,这是恕我直言的疯狂,如果你COMMIT一个无效的交易,它“成功”但具有与ROLLBACK相同的效果。 You can observe this in the psql REPL; 您可以在psql REPL中观察到这一点; it will print ROLLBACK in response to your COMMIT command, but it won't signal an error. 它将打印ROLLBACK以响应您的COMMIT命令,但它不会发出错误信号。

You can create a SAVEPOINT right before your final SELECT . 您可以在最终SELECT之前创建SAVEPOINT If it fails, then ROLLBACK to the savepoint name; 如果失败,则ROLLBACK到保存点名称; 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. 解决方案是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. 或者Ryan Culpepper的答案:要么在事务之外运行语句,要么事先创建一个SAVEPOINT,并在出错时对其进行ROLLBACK。

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 ;) 使用交易,因为它们意味着使用,你不会有任何问题;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM