CREATE SCHEMA IF not EXISTS вызывает ошибку повторяющегося ключа

чтобы дать некоторый контекст, команда выдается внутри задачи, и многие задачи могут выдавать одну и ту же команду от нескольких работников одновременно.

каждая задача пытается создать схему 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?

2 ответов


это немного бородавка в реализации IF NOT EXISTS для таблиц и схем. В принципе, это попытка upsert, и PostgreSQL не обрабатывает условия гонки чисто. Это безопасно, но уродливо.

если схема одновременно создается в другом сеансе, но еще не зафиксирована, то она существует и не существует, в зависимости от того, кто вы и как вы выглядите. Другие транзакции не могут "видеть" новую схему в системных каталогах, поскольку это uncommitted, так что это запись в pg_namespace не отображается для других транзакций. Так что CREATE SCHEMA / CREATE TABLE пытается создать его, потому что, насколько это касается, объект не существует.

однако это вставляет строку в таблицу с уникальным ограничением. Уникальные ограничения должны иметь возможность видеть незафиксированные строки, чтобы функционировать. Таким образом, вставка блоков (останавливается) до первой транзакции, которая сделала CREATE фиксации или отката. Если он совершает, вторая транзакция прерывается, поскольку он пытался вставить строку, нарушающую уникальное ограничение. CREATE SCHEMA недостаточно умен, чтобы поймать это дело и заново попробовать.

чтобы правильно исправить этот PostgreSQL, вероятно, потребуется блокировка предикатов, где он может заблокировать потенциал для строки. Это возможно сделать в рамках текущей работы по реализации UPSERT.

для этих конкретных команд PostgreSQL, вероятно, может сделать грязное чтение системы каталоги, где можно увидеть незафиксированные изменения. Затем он может дождаться фиксации или отката незафиксированной транзакции, повторно выполнить грязное чтение, чтобы увидеть, ждет ли кто-то еще, и повторить попытку. Но это будет иметь условие гонки, когда кто-то другой может создать схему между тем, когда вы делаете чтение, чтобы проверить его, и когда вы пытаетесь его создать.

так IF NOT EXISTS варианты должны были бы:

  • проверьте, существует ли схема; если она существует, завершите без делать что угодно.
  • попытка создать таблицу
  • если создание не удается из-за уникальной ошибки ограничения, повторите попытку в начале
  • если создание таблицы успешно, finish

насколько я знаю, никто не реализовал это, или они пытались, и это не было принято. При таком подходе возможны проблемы со скоростью записи идентификатора транзакции и т. д.

Я думаю, что это своего рода ошибка, но это" да, мы знаем " вид ошибки, а не "мы исправим эту ошибку". Не стесняйтесь публиковать в pgsql-ошибки об этом; по крайней мере, в документации следует упомянуть это предостережение о IF NOT EXISTS.

Я не рекомендую делать DDL одновременно, как это.


мне нужно, чтобы обойти это ограничение в приложении, где схемы создаются одновременно. Что сработало для меня, так это добавление

LOCK TABLE pg_catalog.pg_namespace

в сделке, в том числе CREATE SCHEMA. Выглядит как грязная и небезопасная вещь, но помогла мне решить проблему, которая произошла только в тестах.