简体   繁体   English

在PostgreSQL中重新启动失败的事务

[英]Restart a failed transaction in PostgreSQL

By transaction I mean several SQL statements wrapped (for example) in a begin isolation level serializable block. 通过事务,我的意思是在begin isolation level serializable块中包含(例如)几个SQL语句。 A concurrent transaction can make this transaction fail, ie rollback. 并发事务可以使此事务失败,即回滚。

How to restart the failed transaction in PostgreSQL? 如何在PostgreSQL中重启失败的事务?

You need to use client-driver specific mechanisms to detect an error in the transaction. 您需要使用特定于客户端驱动程序的机制来检测事务中的错误。 When you see an error you must rollback the transaction and re-issue the entire transaction . 当您看到错误时,您必须回滚事务重新发出整个事务 You can't just re-do the most recent part 1 . 你不能只重新做最新的第1部分。

The ROLLBACK is required; ROLLBACK是必需的; if you don't do it, any further operations on that connection fail with a "transaction aborted" error. 如果不这样做,该连接上的任何进一步操作都会因“事务中止”错误而失败。

Most client drivers either throw exceptions (in languages that support them) to indicate SQL errors, or expect you to check for an error code after every database operation, either by checking a function return code, or by calling a special function to check for errors. 大多数客户端驱动程序抛出异常(使用支持它们的语言)来指示SQL错误,或者希望您在每次数据库操作后检查错误代码,方法是检查函数返回代码,或者调用特殊函数来检查错误。 node-postgres is asynchronous and non-blocking, so it's not especially likely to throw exceptions; node-postgres是异步和非阻塞的,因此它不太可能抛出异常; you should be looking for a function that lets you query a session for the last operation's resulting SQLSTATE and client driver error status. 您应该查找一个函数,该函数允许您查询会话以查找最后一个操作导致的SQLSTATE和客户端驱动程序错误状态。

node-postgres doesn't seem to have abundant documentation so your best bet may be to examine the source code for node-postgres, or find examples of how other people do this. node-postgres 似乎没有丰富的文档,所以你最好的选择是检查node-postgres的源代码,或找到其他人如何做到这一点的例子。 I expect you'll find functions to check the error state of a session. 我希望你能找到检查会话错误状态的函数。 What you're looking for is a function to get the SQLSTATE for the last operation on the connection. 您正在寻找的是为连接上的最后一个操作获取SQLSTATE的函数。

It may be worth posting a question that's focused specifically on how to detect and handle errors in node-postgres. 可能值得发布一个专门针对如何检测和处理node-postgres中的错误的问题。


1 Actually, SAVEPOINT and ROLLBACK TO SAVEPOINT let you do exactly that, but they won't help with serialization errors. 1实际上, SAVEPOINTROLLBACK TO SAVEPOINT让你做到这一点,但它们无法帮助解决序列化错误。

This answer was added 3 years later, to account for changes since. 这个答案在3年后被添加,以说明自那以来的变化。 And even though the original answer is still valid, this answer shows how easy it is to do today with the right tools that weren't available back then. 尽管最初的答案仍然有效,但这个答案表明,今天使用当时无法使用的正确工具是多么容易。


It is easy to do with pg-promise , which supports nested transactions of any nested level. 使用pg-promise很容易,它支持任何嵌套级别的嵌套事务。 See Nested Transactions where it explains that while the top-level transaction is represented by your standard BEGIN -> COMMIT / ROLLBACK , all nested transactions automatically become SAVEPOINT . 请参阅嵌套事务 ,其中解释了当顶级事务由标准BEGIN - > COMMIT / ROLLBACK ,所有嵌套事务都自动变为SAVEPOINT

db.tx(t => {
    // BEGIN
    // top-level changes cannot be restarted:
    return t.any('UPDATE users SET name=$1 WHERE id=$2', ['Mike', 123])
        .then(() => {
            return t.tx(t1 => {
                // SAVEPOINT
                return t1.none('INSERT log(event) VALUES($1)', 'entry');
            })
                .catch(error => {
                    // ROLLBACK TO SAVEPOINT executed
                    return t.none('UPDATE log SET event = $1');
                });
        });
})
    .then(data => {
        // success, COMMIT executed
    })
    .catch(error => {
        // error, ROLLBACK executed
    });

In the example above we do an UPDATE on the top level, which cannot be restarted for that reason. 在上面的示例中,我们在顶层执行UPDATE ,由于这个原因无法重新启动。 And then we do a nested transaction/savepoint, with an INSERT , which if fails, replaced with an UPDATE on the top level again. 然后我们使用INSERT执行嵌套事务/保存点,如果失败,则再次替换为顶层的UPDATE

This way, even though our SAVEPOINT failed, we can still finish our top-level transaction successfully. 这样,即使我们的SAVEPOINT失败,我们仍然可以成功完成顶级交易。

And if you want to be able to restart the entire transaction, simply wrap the whole thing into a sub-transaction, which you then can re-run inside the main transaction as many times as you want. 如果您希望能够重新启动整个事务,只需将整个事务包装到子事务中,然后可以根据需要在主事务内重新运行。

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

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