Перейти к основному содержимому

Конфликты

примечание

Эта страница переведена при помощи нейросети GigaChat.

Логическая репликация работает аналогично обычным операциям DML: данные обновляются, даже если они были изменены локально на узле подписчика. Если входящие данные нарушают какие-либо ограничения, репликация останавливается. Это называется конфликтом. При репликации операций UPDATE или DELETE отсутствие данных также рассматривается как конфликт, но не приводит к ошибке, и такие операции просто пропускаются.

Дополнительное логирование запускается, и статистика конфликтов собирается (отображается в представлении pg_stat_subscription_stats) в следующих случаях конфликтов:

insert_exists

Вставка строки, нарушающей уникальное ограничение NOT DEFERRABLE. Обратите внимание, что для логирования сведений об источнике и метке времени фиксации конфликтующего ключа необходимо включить track_commit_timestamp на узле подписчика. В этом случае будет выдана ошибка, пока конфликт не будет разрешен вручную.

update_origin_differs

Обновление строки, которая ранее была изменена другим источником. Обратите внимание, что этот конфликт может быть обнаружен только при включенной функции track_commit_timestamp на подписчике. В настоящее время обновление всегда применяется независимо от источника локальной строки.

update_exists

Обновленное значение строки нарушает уникальное ограничение NOT DEFERRABLE. Обратите внимание, что для регистрации источника и метки времени фиксации конфликтующего ключа необходимо включить track_commit_timestamp на подписчике. В этом случае будет выдана ошибка, пока конфликт не будет разрешен вручную. Обратите внимание, что при обновлении секционированной таблицы, если обновленное значение строки удовлетворяет другому ограничению секционирования, в результате чего строка вставляется в новую секцию, может возникнуть конфликт insert_exists, если новая строка нарушает уникальное ограничение NOT DEFERRABLE.

update_missing

Строка для обновления не найдена. В этом случае обновление будет просто пропущено.

delete_origin_differs

Удаление строки, которая ранее была изменена другим источником. Обратите внимание, что этот конфликт может быть обнаружен только при включенной функции track_commit_timestamp на подписчике. В настоящее время удаление всегда применяется независимо от источника локальной строки.

delete_missing

Строка, которую нужно удалить, не найдена. В этом случае удаление будет просто пропущено.

multiple_unique_conflicts

Вставка или обновление строки нарушает несколько уникальных ограничений NOT DEFERRABLE. Обратите внимание, что для регистрации в журнале сведений об источнике и метке времени фиксации конфликтующих ключей убедитесь, что функция track_commit_timestamp включена на подписчике. В этом случае будет выдана ошибка, пока конфликт не будет разрешен вручную.

Обратите внимание, что существуют и другие сценарии конфликтов, такие как нарушения ограничений исключения. В настоящее время дополнительная информация о них в журнале не предоставляется.

Формат журнала для конфликтов логической репликации следующий:

LOG:  conflict detected on relation "schemaname.tablename": conflict=conflict_type
DETAIL: detailed_explanation.
{detail_values [; ... ]}.

where detail_values is one of:

Key (column_name [, ...])=(column_value [, ...])
existing local row [(column_name [, ...])=](column_value [, ...])
remote row [(column_name [, ...])=](column_value [, ...])
replica identity {(column_name [, ...])=(column_value [, ...]) | full [(column_name [, ...])=](column_value [, ...])}

Журнал содержит следующую информацию:

LOG

  • schemaname.tablename идентифицирует локальное отношение, участвующее в конфликте.
  • conflict_type — это тип произошедшего конфликта (например, insert_exists, update_exists).

DETAIL

  • detailed_explanation включает источник, идентификатор транзакции и метку времени фиксации транзакции, которая изменила существующую локальную строку, если таковые имеются.
  • Раздел Key включает значения ключей локальной строки, нарушившей уникальное ограничение, для конфликтов insert_exists, update_exists или multiple_unique_conflicts.
  • Раздел existing local row включает локальную строку, если ее источник отличается от удаленной строки для конфликтов update_origin_differs или delete_origin_differs, или если значение ключа конфликтует с удаленной строкой для конфликтов insert_exists, update_exists или multiple_unique_conflicts.
  • Раздел remote row включает новую строку из удаленной операции вставки или обновления, вызвавшей конфликт. Обратите внимание, что при операции обновления значение столбца новой строки будет равно null, если значение не изменилось и было удалено.
  • Раздел идентификатора реплики включает значения ключей идентификатора реплики, которые использовались для поиска существующей локальной строки для обновления или удаления. Это может включать полное значение строки, если локальное отношение помечено как REPLICA IDENTITY FULL.
  • column_name — это имя столбца. В случаях существующей локальной строки, удаленной строки и полного идентификатора реплики имена столбцов регистрируются только в том случае, если у пользователя нет прав доступа ко всем столбцам таблицы. Если имена столбцов присутствуют, они отображаются в том же порядке, что и соответствующие значения столбцов.
  • column_value — это значение столбца. Большие значения столбцов обрезаются до 64 байт.
  • Обратите внимание, что в случае конфликта multiple_unique_conflicts будет сгенерировано несколько строк detailed_explanation и detail_values, каждая из которых будет содержать подробную информацию о конфликте, связанном с различными уникальными ограничениями.

Операции логической репликации выполняются с привилегиями роли, которой принадлежит подписка. Сбои в правах доступа к целевым таблицам приведут к конфликтам репликации, как и включенная безопасность на уровне строк для целевых таблиц, на которые распространяется действие подписки, независимо от того, отклоняет ли какая-либо политика обычно операции INSERT, UPDATE, DELETE или TRUNCATE, которые реплицируются. Это ограничение на безопасность на уровне строк может быть снято в будущей версии PostgreSQL.

Конфликт, приводящий к ошибке, остановит репликацию; он должен быть разрешен вручную пользователем. Подробности о конфликте можно найти в журнале сервера подписчика.

Разрешение может быть выполнено либо путем изменения данных или прав доступа на подписчике таким образом, чтобы они не конфликтовали с входящим изменением, либо путем пропуска транзакции, которая конфликтует с существующими данными. Когда конфликт приводит к ошибке, репликация не будет продолжена, и логический обработчик репликации выведет в журнал сервера подписчика следующее сообщение:

ERROR:  conflict detected on relation "public.test": conflict=insert_exists
DETAIL: Key already exists in unique index "t_pkey", which was modified locally in transaction 740 at 2024-06-26 10:47:04.727375+08.
Key (c)=(1); existing local row (1, 'local'); remote row (1, 'remote').
CONTEXT: processing remote data for replication origin "pg_16395" during "INSERT" for replication target relation "public.test" in transaction 725 finished at 0/14C0378

LSN транзакции, содержащей изменение, нарушающее ограничение, и имя источника репликации можно найти в журнале сервера (LSN 0/14C0378 и источник репликации pg_16395 в приведенном выше случае). Транзакцию, вызвавшую конфликт, можно пропустить, используя команду ALTER SUBSCRIPTION ... SKIP с LSN завершения (то есть, LSN 0/14C0378). LSN завершения может быть LSN, в котором транзакция зафиксирована или подготовлена на издателе. В качестве альтернативы, транзакцию также можно пропустить, вызвав функцию pg_replication_origin_advance(). Перед использованием этой функции необходимо временно отключить подписку либо с помощью команды ALTER SUBSCRIPTION ... DISABLE, либо с помощью параметра disable_on_error. Затем можно использовать функцию pg_replication_origin_advance() с именем узла (например, pg_16395) и следующим LSN финишного LSN (например, 0/14C0379). Текущее положение источников можно увидеть в системном представлении pg_replication_origin_status. Обратите внимание, что пропуск всей транзакции включает пропуск изменений, которые могут не нарушать никаких ограничений. Это может легко привести к несогласованности подписчика. Дополнительные сведения о конфликтующих строках, такие как их источник и метка времени фиксации, можно увидеть в строке DETAIL журнала. Но обратите внимание, что эта информация доступна только в том случае, если на подписчике включена функция track_commit_timestamp. Пользователи могут использовать эту информацию, чтобы решить, следует ли сохранить локальное изменение или принять удаленное изменение. Например, строка DETAIL в приведенном выше журнале указывает на то, что существующая строка была изменена локально. Пользователи могут вручную выполнить операцию remote-change-win.

При параллельном режиме потоковой передачи LSN завершения неудачных транзакций может не регистрироваться в журнале. В этом случае может потребоваться включить или выключить режим потоковой передачи и снова вызвать те же конфликты, чтобы LSN завершения неудачной транзакции был записан в журнал сервера. Для получения информации об использовании LSN завершения смотрите ALTER SUBSCRIPTION ... SKIP.