作为升级过程的一部分,我们的产品脚本会更新触发器的存储过程。有两个守护进程正在运行,其中任何一个都可以更新存储过程。似乎PostgrSQL没有序列化DDL来升级程序。确切的错误是" DB_Cursor:执行中的异常:元组并发更新"。 Google搜索在搜索结果中不会与此错误产生完全匹配。看来我们有竞争条件。避免或阻止此类异常的最佳方法是什么?它会阻止升级过程成功,并且必须重新启动一个或两个进程(守护程序)才能重试升级和恢复。 PostgreSQL存在已知问题吗?我们正在运行PostgreSQL 9.2.5。
答案 0 :(得分:1)
似乎PostgreSQL没有序列化DDL来升级 程序
是。这在pgsql邮件列表中不时被提及,例如最近在这里:
'tuple concurrently updated' error when granting permissions
摘录:
我们对表/索引上的DDL有这样的锁定,但是理论在 过去一直是对物体不值得的麻烦 由单个目录行表示,例如函数或角色。您 不能在这样的行上同时更新来破坏数据库, 你只会得到一个“元组并发更新”错误 首次到达的更新。
如果您同时更换功能体,这显然是您的问题。
建议的解决方案是:
与此同时,您可以考虑使用应用程序管理 如果你真的需要这样的补助来透明地工作,那就提供咨询锁。
答案 1 :(得分:1)
如果设计多个并发客户端可以决定执行DDL,那么你真的应该确保只有其中一个客户端正在执行它。你可以使用咨询锁来做到这一点。
伪代码示例:
function try_upgrade(db) {
if ( ! is_upgrade_needed(db) ) {
// we check it before acquiring a lock to speed up a common case of
// no upgrade available
return UPGRADE_NOT_NEEDED;
}
query_result = db->begin_transaction();
if ( query_result < 0 ) throw Error("begin failed");
query_result = db->query(
"select pg_advisory_xact_lock(?)", MAGIC_NUMBER_UPGRADE_LOCK
);
if ( query_result < 0 ) throw Error("pg_advisory_xact_lock failed");
// another client might have performed upgrade between the previous check
// and acquiring advisory lock
if ( ! is_upgrade_needed(db) ) {
query_result = db->rollback_transaction();
return UPGRADE_NOT_NEEDED;
}
perform_upgrade();
query_result = db->commit_transaction();
if ( query_result < 0 ) throw Error("commit failed");
return UPGRADE_PERFORMED;
}