【问题标题】:CREATE SCHEMA IF NOT EXISTS raises duplicate key errorCREATE SCHEMA IF NOT EXISTS 引发重复键错误
【发布时间】:2015-04-27 16:11:26
【问题描述】:

为了给出一些上下文,命令是在任务内部发出的,许多任务可能同时从多个工作人员发出相同的命令。

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

IntegrityError: (IntegrityError) duplicate key value violates unique constraint "pg_namespace_nspname_index"
DETAIL:  Key (nspname)=(9621584361) already exists.
 'CREATE SCHEMA IF NOT EXISTS "9621584361"'

Postgres 版本是 PostgreSQL 9.4rc1。
这是 Postgres 中的错误吗?

【问题讨论】:

  • 看起来 IF EXISTS 仅在提交后有效,而不是在尚未提交的事务期间。
  • CREATE SCHEMA 自 Postgres 9.3 以来就有 IF NOT EXISTS 子句,所以我不希望您的 9.4rc1 版本与它有任何关系。但你还是应该upgrade to the latest stable release, currently 9.4.1

标签: postgresql concurrency ddl catalog


【解决方案1】:

这在为表和模式实现IF NOT EXISTS 时有点麻烦。基本上,它们是一种 upsert 尝试,而 PostgreSQL 并没有干净地处理竞争条件。安全,但丑陋。

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

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

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

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

所以IF NOT EXISTS 变体必须:

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

据我所知,没有人实施过,或者他们尝试过但没有被接受。使用这种方法可能会出现交易 ID 消耗率等问题。

我认为这是一种错误,但它是一种“是的,我们知道”的错误,而不是“我们会马上修复”的那种错误。随意张贴到 pgsql-bugs ;至少文档应该提到这个关于IF NOT EXISTS的警告。

我不建议这样同时进行 DDL。

【讨论】:

  • 这不会同时发生。我使用不同时运行测试的 mocha。
  • @PositiveGuy 那么这可能是一个错误。如果您可以将其重现为一个简单的可重复测试用例,请将其报告给邮件列表。
【解决方案2】:

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

LOCK TABLE pg_catalog.pg_namespace

在交易中包括CREATE SCHEMA。看起来像一件肮脏和不安全的事情,但帮助我解决了无论如何只在测试中出现的问题。

【讨论】:

  • 很好,但我的用户无权锁定pg_catalog.pg_namespace 在架构存在之前我还能锁定什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-25
  • 2021-07-06
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 2015-05-16
相关资源
最近更新 更多