通过事务我的意思是在begin isolation level serializable
块中包含(例如)几个SQL语句。并发事务可以使此事务失败,即回滚。
如何在PostgreSQL中重启失败的事务?
答案 0 :(得分:3)
您需要使用特定于客户端驱动程序的机制来检测事务中的错误。当您看到错误时,您必须回滚事务并重新发布整个事务。你不能只重新做最近的部分 1 。
ROLLBACK
是必需的;如果你不这样做,那么该连接上的任何进一步操作都会因“事务中止”错误而失败。
大多数客户端驱动程序抛出异常(使用支持它们的语言)来指示SQL错误,或者希望您在每次数据库操作后检查错误代码,方法是检查函数返回代码,或者调用特殊函数检查错误。 node-postgres
是异步且非阻塞的,因此不太可能抛出异常;您应该寻找一个函数,让您查询会话中最后一个操作的SQLSTATE
和客户端驱动程序错误状态。
node-postgres
doesn't seem to have abundant documentation因此,您最好的选择是检查node-postgres的源代码,或者查找其他人如何执行此操作的示例。我希望你能找到检查会话错误状态的函数。您正在寻找的是为连接上的最后一次操作获取SQLSTATE
的函数。
可能值得发布一个专门针对如何检测和处理node-postgres中的错误的问题。
1 实际上,SAVEPOINT
和ROLLBACK TO SAVEPOINT
可以让您做到这一点,但它们对序列化错误没有帮助。
答案 1 :(得分:0)
这个答案在3年后被添加,以说明自那以来的变化。尽管最初的答案仍然有效,但这个答案显示了今天使用当时无法使用的正确工具是多么容易。
使用pg-promise很容易,它支持任何嵌套级别的嵌套事务。请参阅Nested Transactions,其中说明顶级交易由您的标准using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class RnDHash
{
static void Main()
{
HashSet<int> rndIndexes = new HashSet<int>();
Random rng = new Random();
int maxNumber;
Console.Write("Please input Max number: ");
maxNumber = int.Parse(Console.ReadLine());
int iter = 0;
while (rndIndexes.Count != maxNumber)
{
int index = rng.Next(maxNumber);
rndIndexes.Add(index);
iter++;
}
Console.WriteLine("Random numbers were found in {0} iterations: ", iter);
foreach (int num in rndIndexes)
{
Console.WriteLine(num);
}
Console.ReadKey();
}
}
}
- &gt; BEGIN
/ COMMIT
表示,所有嵌套交易自动成为{{1} }。
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
});
执行嵌套的事务/保存点,如果失败,则再次替换为顶级的UPDATE
。
这样,即使我们INSERT
失败,我们仍然可以成功完成顶级交易。
如果您希望能够重新启动整个事务,只需将整个事务包装到子事务中,然后您可以根据需要在主事务内重新运行。