CREATE SCHEMA如果不是EXISTS引发重复键错误

时间:2015-04-27 16:11:27

标签: postgresql concurrency ddl catalog

为了给出一些上下文,命令在任务中发出,许多任务可能同时从多个worker发出相同的命令。

每个任务都尝试创建一个postgres架构。我经常收到以下错误:

initial begin
    for (IR = 0; IR <= 65535; IR++);
    $finish;
end

Postgres版本是PostgreSQL 9.4rc1 这是Postgres的一个错误吗?

2 个答案:

答案 0 :(得分:12)

对于表和模式的IF NOT EXISTS的实现,这有点蠢。基本上,他们是一个upsert尝试,而PostgreSQL并没有干净地处理竞争条件。这很安全,但很难看。

如果架构是在另一个会话中同时创建但尚未提交,则它既存在又不存在,具体取决于您是谁以及您的外观。其他交易不可能&#34;参见&#34;系统目录中的新架构是因为它未提交,因此pg_namespace中的条目对其他事务不可见。因此,CREATE SCHEMA / CREATE TABLE会尝试创建它,因为就其而言,该对象并不存在。

但是,将行插入具有唯一约束的表中。唯一约束必须能够查看未提交的行才能运行。因此插入阻塞(停止)直到执行CREATE的第一个事务提交或回滚。如果它提交,则第二个事务中止,因为它试图插入违反唯一约束的行。 CREATE SCHEMA并不够聪明,无法抓住这个案例并重新尝试。

要正确修复此PostgreSQL可能需要谓词锁定,它可以锁定行的潜力。这可能会作为当前正在进行的实施UPSERT的工作的一部分添加。

对于这些特定命令,PostgreSQL可能会执行系统目录的脏读,在那里可以看到未提交的更改。然后它可以等待未提交的事务提交或回滚,重新执行脏读以查看是否有其他人正在等待,然后重试。但这会产生竞争条件,其他人可能会在您执行读取操作以及尝试创建它时之间创建架构。

所以IF NOT EXISTS变种必须:

  • 检查架构是否存在;如果确实如此,则不做任何事情。
  • 尝试创建表格
  • 如果由于唯一约束错误而导致创建失败,请在开始时重试
  • 如果表格创建成功,请完成

据我所知,没有人实施,或者他们尝试过,但它并没有被接受。使用这种方法可能会出现事务ID刻录率等问题。

我认为这是一个各种各样的错误,但它是一个&#34;是的,我们知道&#34;一种错误,而不是一种&#34;我们会正确地解决这个问题&#34;那种虫子。随意发布关于它的pgsql-bugs;至少文档应该提到关于IF NOT EXISTS的这个警告。

我不建议同时做DDL。

答案 1 :(得分:1)

我需要在同时创建模式的应用程序中解决此限制。对我有用的是添加

ws

在包含LOCK TABLE pg_catalog.pg_namespace 的交易中。看起来像一个肮脏和不安全的事情,但帮助我解决了只在测试中发生的问题。