29.7. Конфликты#
29.7. Конфликты #
Логическая репликация работает аналогично обычным операциям 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_typeDETAIL:detailed_explanation. {detail_values[; ... ]}. wheredetail_valuesis 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[, ...])}
Журнал предоставляет следующую информацию:
LOGschemaname.tablenameопределяет локальное отношение, участвующее в конфликте.conflict_type— это тип возникшего конфликта (например,insert_exists,update_exists).
DETAILdetailed_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, если значение не изменилось и имеет формат toasted.Раздел
replica identityвключает значения ключа идентификации реплики, которые использовались для поиска существующей локальной строки, подлежащей обновлению или удалению. Может включать полное значение строки, если локальное отношение помечено какREPLICA IDENTITY FULL.column_name— это имя столбца. Для разделовexisting local row,remote rowиreplica identity fullимена столбцов записываются в журнал только если у пользователя нет прав доступа ко всем столбцам таблицы. Имена столбцов, если они есть, отображаются в том же порядке, что и соответствующие значения столбцов.column_value— это значение столбца. Большие значения столбцов урезаются до 64 байт.Обратите внимание, что в случае конфликта
multiple_unique_conflictsгенерируются строкиdetailed_explanationиdetail_values, каждая из которых будет содержать информацию по отдельным уникальным ограничениям.
Операции логической репликации выполняются с привилегиями роли, которая владеет подпиской. Ошибки разрешений на целевые таблицы вызовут конфликты репликации, а также включенная строковая защита на уровне строк на целевых таблицах, к которой подписчик имеет доступ, независимо от того, будет ли отклонена INSERT, UPDATE, DELETE или TRUNCATE, которые реплицируются. Ограничение на строковую безопасность может быть снято в будущих версиях Tantor SE.
Конфликт, вызывающий ошибку, остановит репликацию; он должен быть разрешён пользователем вручную. Подробности о конфликте можно найти в логах сервера подписчика.
Разрешение может быть выполнено путем изменения данных или разрешений на подписчике таким образом, чтобы они не конфликтовали с входящим изменением, или путем пропуска транзакции, которая конфликтует с существующими данными. Когда конфликт вызывает ошибку, репликация не продолжается, и рабочий процесс логической репликации выдаст следующее сообщение в журнале сервера подписчика:
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() и передать ей node_name (т.е. pg_16395) и LSN, следующий за LSN завершения (т.е. 0/14C0379). Текущие позиции источников можно посмотреть в системном представлении pg_replication_origin_status. Обратите внимание, что пропуск всей транзакции включает пропуск изменений, которые могли бы не нарушать никаких ограничений. Это может легко привести к несогласованности состояния подписчика.
Дополнительные сведения о конфликтующих строках, такие как их источник и время фиксации, можно увидеть в строке DETAIL журнала. Но обратите внимание, что эта информация доступна только при включённом параметре track_commit_timestamp на подписчике. Пользователи могут использовать эту информацию, чтобы решить, сохранять ли локальное изменение или принять удалённое изменение. Например, строка DETAIL в приведённом выше журнале указывает, что существующая строка была изменена локально. Пользователи могут вручную применить удаленное изменение remote-change-win.
Когда
streaming
режим установлен в parallel, конечный LSN неудачных транзакций
может не быть записан. В этом случае может потребоваться изменить режим потоковой
передачи на on или off и вызвать те же
конфликты снова, чтобы конечный LSN неудачной транзакции был записан
в журнал сервера. Для использования конечного LSN, пожалуйста, обратитесь к ALTER SUBSCRIPTION ...
SKIP.