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

psql_logical_slot_rewind. Создание логического слота репликации со сдвигом в прошлое

Версия: 1.0.

В исходном дистрибутиве установлено по умолчанию: нет

Связанные компоненты: отсутствуют

Схема размещения: ext

к сведению

Разработано СУБД Pangolin.

Расширение psql_logical_slot_rewind предназначено для создания логического слота репликации со сдвигом в прошлое на указанный LSN. Оно решает проблему повторного вычитывания данных из слота логической репликации в случае их потери на тракте передачи до применения на приемнике.

В стандартном PostgreSQL при потере данных, уже вычитанных из слота логической репликации, отсутствует возможность повторного чтения этих данных средствами самой репликации. Восстановление вручную является трудоемкой задачей. Расширение обеспечивает возможность создать слот логической репликации с любого уникального идентификатора позиции в WAL-журналах (LSN) в прошлом, при условии, что соответствующие WAL-файлы доступны на диске либо могут быть восстановлены из резервной копии.

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

Описание механизма

Поиск консистентной точки

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

Чтение начинается с найденного restart_lsn, после чего определяется консистентная точка для построения catalog snapshot. Значение confirmed_flush устанавливается равным указанному LSN. Если консистентная точка не найдена, процесс завершается ошибкой и слот не создается.

Ограничения на операции

Для слота со сдвигом в прошлое запрещены следующие операции над реплицируемыми таблицами:

  • TRUNCATE;
  • VACUUM FULL;
  • CLUSTER;
  • DROP;
  • ALTER TABLE (кроме ATTACH PARTITION и DETACH PARTITION, если отсоединенная партиция не участвует в репликации).
Сведения

Партиции могут участвовать в репликации как самостоятельные объекты. В этом случае на них распространяются все ограничения, действующие для реплицируемых таблиц.

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

Отслеживание DDL-операций

После установки расширение psql_logical_slot_rewind отслеживает DDL-команды при включенном параметре psql_logical_slot_rewind.enable_object_monitor (по умолчанию включен).

При выполнении запрещенных операций в таблицу psql_logical_slot_rewind_schema_internal.object_monitor записываются OID объекта и LSN операции. Хранятся данные за последние 256 Тбайт WAL-записей, а более старые записи удаляются.

Если параметр enable_object_monitor отключен (например, для повышения производительности), информация о DDL не собирается. В этом случае создание слота со сдвигом в прошлое завершится ошибкой при force = false.

Для корректного отслеживания DDL расширение должно быть создано и добавлено в shared_preload_libraries ранее LSN, на который создается слот. Если расширение не было создано к этому моменту или параметр enable_object_monitor отключался после указанного LSN, проверка на запрещенные операции завершится ошибкой.

Определение списка реплицируемых объектов

Список реплицируемых таблиц определяется по системной таблице расширения psql_logical_slot_rewind_schema.replicated_tables.

Записи добавляются суперпользователем или владельцем схемы расширения до вызова функции создания слота. Для разных слотов возможны разные списки объектов.

Используются функции:

  • psql_logical_slot_rewind_schema.add_replicated_table(slot_name, full_table_name) — добавление таблицы;
  • psql_logical_slot_rewind_schema.clean_replicated_tables_of_slot(slot_name) — удаление записей для слота.

Если в replicated_tables отсутствуют записи для слота, отслеживаются все существующие таблицы.

В этом случае создание слота завершится ошибкой, если по любой таблице выполнялась запрещенная операция. При force = true консистентность и корректность декодирования не гарантируются.

Если в таблице заданы конкретные реплицируемые таблицы, слот не создается при обнаружении запрещенных операций по этим таблицам. Использование force = true может привести к невозможности декодирования данных и отсутствию изменений по таким таблицам.

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

Флаг слота расширения

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

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

Проверка наличия флага выполняется функцией is_logical_slot_rewind_slot.

Инициализация таблицы object_monitor

При создании расширения или при обнаружении отключенного состояния параметра enable_object_monitor таблица psql_logical_slot_rewind_schema_internal.object_monitor очищается, после чего в нее добавляется запись (InvalidOid, LSN).

Снимок при создании слота

При создании слота в таблице psql_logical_slot_rewind_schema_internal.slot_creation_snapshot сохраняется информация, необходимая для построения cнимка на момент создания.

При чтении из слота, если исторический снепшот предшествует снепшоту создания, выполняется его временная подмена для корректного сопоставления relfilenode → reloid → relname.

Если проверка на запрещенные операции выполнена, информация по реплицируемым таблицам, полученная при подмене снепшота, соответствует данным исторического снепшота при условии, что relfilenode не изменялся.

Объекты расширения

Создаются следующие новые объекты БД после создания расширения:

  • Схемы:

    • psql_logical_slot_rewind_schema (схема для таблиц расширения);
    • psql_logical_slot_rewind_schema_internal (схема для таблиц расширения).
  • Таблицы:

    • psql_logical_slot_rewind_schema.replicated_tables;
    • psql_logical_slot_rewind_schema_internal.object_monitor;
    • psql_logical_slot_rewind_schema_internal.slot_creation_snapshot.
  • Функции:

    • pg_create_logical_replication_slot_lsn();
    • psql_logical_slot_rewind_schema.clean_replicated_tables_of_slot();
    • psql_logical_slot_rewind_schema.add_replicated_table()
    • is_logical_slot_rewind_slot().

Таблицы расширения

psql_logical_slot_rewind_schema.replicated_tables

В таблицу передается список реплицируемых таблиц слота.

Имя столбцаТипОписание
slot_namenameИмя слота, для которого добавляются реплицируемые таблицы
full_table_nametextПолное название таблицы со схемой в формате schema_name.table_name

psql_logical_slot_rewind_schema_internal.object_monitor

В таблицу записывается информация о запрещенных операциях.

Имя столбцаТипОписание
reloidoidOID объекта, по отношению к которому была выполнена запрещенная операция. Строка с reloid=0 означает начало записи
lsnpg_lsnМомент LSN, во время которого для объекта была запрещенная операция

psql_logical_slot_rewind_schema_internal.slot_creation_snapshot

В таблицу записывается информация для создания снимка (снепшота).

Имя столбцаТипОписание
slot_namenameИмя слота, для которого сохранена информация о снимке при создании
xmin_horizonbigintГоризонт на момент создания слота
oldest_running_xidbigintСамая «старая» запущенная транзакция на момент создания слота

Функции расширения

pg_create_logical_replication_slot_lsn()

При помощи функции pg_create_logical_replication_slot_lsn() есть возможность создать слот логической репликации с любого уникального идентификатора позиции в WAL-журналах (LSN) в прошлом.

Права доступа:

Функцию pg_create_logical_replication_slot_lsn может вызывать суперпользователь или пользователь с правом REPLICATION.

Входные параметры:

Название поляТип значенияОписание
slot_namenameИмя создаваемого слота
pluginnameИмя плагина из списка поддерживаемых выходных плагинов
restart_lsnpg_lsnуникальный идентификатор позиции в WAL-файле (LSN), с которого начинается хранение данных в слоте логической репликации

Необязательные входные параметры:

Название поляТип значенияОписание
temporarybooleanМетка временного слота. Если true, слот не будет постоянно храниться на диске. По умолчанию будет задано false
forcebooleanПринудительное создание слота логической репликации без проверки на запрещенные операции. По умолчанию будет задано false
publication_namenameИмя создаваемой публикации, в которой хранится список реплицируемых объектов

Возвращаемое значение:

Название поляТип значенияОписание
slot_namenameИмя создаваемого слота
restart_lsnpg_lsnУникальный идентификатор позиции в WAL-файле (LSN), с которого начинается хранение данных в слоте логической репликации

psql_logical_slot_rewind_schema.add_replicated_table()

Добавляет реплицируемую таблицу для слота slot_name в psql_logical_slot_rewind_schema.replicated_tables.

Входные параметры:

Название параметраТип значенияОписание
slot_namenameНазвание слота, для которого будет добавлена реплицируемая таблица
full_table_nametextПолное название таблицы со схемой в формате schema_name.table_name

Возвращаемое значение:

Отсутствуют.

psql_logical_slot_rewind_schema.clean_replicated_tables_of_slot()

Удаляет из psql_logical_slot_rewind_schema.replicated_tables все записи для слота slot_name.

Входные параметры:

Название параметраТип значенияОписание
slot_namenameНазвание слота, для которого будут очищены все реплицируемые таблицы

Возвращаемое значение:

Отсутствуют.

is_logical_slot_rewind_slot()

Возвращает текущее состояние слота.

Входные параметры:

Название параметраТип значенияОписание
slot_namenameНазвание слота, для которого будет определяться состояние

Возвращаемое значение:

Булево значение.

Если функция возвращает:

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

Ограничения

  • После удаления слота логической репликации со сдвигом в прошлое таблица psql_logical_slot_rewind_schema.replicated_tables не очищается автоматически. Записи, относящиеся к слоту, необходимо удалить вручную:

    SELECT psql_logical_slot_rewind_schema.clean_replicated_tables_of_slot('old_slot');
  • Если в доступных WAL-сегментах отсутствует консистентная точка для построения catalog snapshot ранее целевого LSN, создание слота завершается ошибкой.

  • Если невозможно собрать информацию о DDL-командах по реплицируемым объектам на целевом LSN (например, enable_object_monitor был отключен, расширение не создано или не добавлено в shared_preload_libraries), создание слота завершается ошибкой при force = false:

    ERROR:  There is a missing information in "psql_logical_slot_rewind_schema_internal.object_monitor"
    DETAIL: enable_object_monitor was disabled, or extension was not created, or it was not in shared_preload_libraries
    HINT: you can ignore this error with force = true

    При force = true слот создается с предупреждением, проверка потери данных пропускается:

    WARNING:  "force" parameter enabled, data loss check skipped
  • При чтении из слота, созданного с force = true, возможны:

    • пропуски изменений;
    • ошибки сопоставления relfilenodeOID;
    • невозможность декодирования данных.
  • Если при чтении возникает ошибка:

    • и список реплицируемых таблиц не задан – чтение завершается ошибкой;
    • и ошибка связана с нереплицируемой таблицей — выводится предупреждение, изменения пропускаются;
    • и ошибка связана с таблицей из списка реплицируемых — выполнение завершается ошибкой.
  • Возможна потеря данных реплицируемой таблицы при force = true, если после указанного LSN выполнялись запрещенные операции, изменился relfilenode, а соответствующая проверка была пропущена.

  • При force = false и успешном создании слота:

    • изменения реплицируемых таблиц не пропускаются;

    • для нереплицируемых таблиц возможны предупреждения и пропуск изменений при структурных изменениях:

      WARNING:  could not map filenode "base/16384/16455" to relation OID. Error and slot changes were skipped
  • Функциональность протестирована только для плагинов test_decoding и pgoutput. Для других плагинов (например, wal2json) тестирование не проводилось.

  • Функцию pg_create_logical_replication_slot_lsn может вызывать суперпользователь или пользователь с правом REPLICATION.

  • Читать из слота может суперпользователь или пользователь с правом REPLICATION.

  • При logical_replication_permission_policy = protected и включенной защите от привилегированных пользователей чтение репликации разрешено только защищенным ролям.

Установка

  1. Проверьте/установите значение конфигурационного параметра wal_level не ниже чем logical.

  2. Пропишите расширение в конфигурационный параметр предзагружаемых библиотек:

    shared_preload_libraries = 'psql_logical_slot_rewind'
  3. Активируйте расширение. Установите расширение в схему ext:

    CREATE EXTENSION psql_logical_slot_rewind schema ext;

Дополнительно для проверки:

  1. Файл psql_logical_slot_rewind.so должен находиться в директории /usr/pangolin-{version}/lib/:
ls -l /usr/pangolin-6.7/lib/ | grep psql_logical_slot_rewind
-r--r----- 1 postgres pangolin_users 76176 Dec 12 12:46 psql_logical_slot_rewind.so
  1. Файлы с расширением .sql и .control должны находиться в директории /usr/pangolin-{version}/share/extension/:

    ls -l /usr/pangolin-6.7/share/extension/ | grep psql_logical_slot_rewind
    -rw------- 1 postgres postgres 663 Dec 12 12:46 psql_logical_slot_rewind--1.0.sql
    -rw------- 1 postgres postgres 198 Dec 12 12:46 psql_logical_slot_rewind.control

Настройка

Конфигурационные параметры

Параметры конфигурации предоставляемые расширением:

ПараметрТип значенияЗначение по умолчаниюОписание
psql_logical_slot_rewind.enable_object_monitorbooleanonВключает работу хуков, собирающих информацию о запрещенных операциях
psql_logical_slot_rewind.max_wal_segments_searchinteger0Ограничение на количество сегментов WAL, на которых будет осуществляться поиск ближайшего restart_lsn при создании слота

Отключение

Для отключения расширения необходимо его удалить.

  1. Исключить упоминание psql_logical_slot_rewind из параметра shared_preload_libraries.

  2. Выполните отключение расширения с помощью команды:

    DROP EXTENSION psql_logical_slot_rewind;

Диагностика

Возможные ошибки:

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

    ERROR:  There was restricted operation for relation with OID 151134
  • При выключенном параметре psql_logical_slot_rewind.enable_object_monitor попытка создания слота репликации заканчивается ошибкой при force = false:

    SELECT * FROM pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', false, pg_lsn('8/B0814AC0'));
    ERROR:  There is a missing information in "psql_logical_slot_rewind_schema_internal.object_monitor"
    DETAIL: enable_object_monitor is disabled
    HINT: you can ignore this error with force = true
  • Попытка создать слот репликации при помощи функции pg_create_logical_replication_slot_lsn при значении wal_level ниже уровня logical заканчивается ошибкой:

    ERROR:  logical decoding requires wal_level >= logical
  • Попытка пользователя без прав суперпользователя либо без права REPLICATION создать слот логической репликации при помощи функции pg_create_logical_replication_slot_lsn заканчивается ошибкой:

    ERROR:  must be superuser or replication role to use replication slots
  • После удаления расширения вычитка из слота логической репликации, имеющего специальный флаг, указывающий на то, что слот создан расширением psql_logical_slot_rewind, заканчивается ошибкой:

    SELECT lsn, data FROM pg_logical_slot_peek_changes('old_slot', NULL, NULL, 'include-timestamp', 'on');

    ERROR: schema "psql_logical_slot_rewind_schema_internal" not found

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

Использование модуля

Создание логического слота репликации со сдвигом в прошлое

Список реплицируемых объектов для слота хранится в системной таблице расширения psql_logical_slot_rewind_schema.replicated_tables. Проверить наличие записей о реплицируемых объектах для слота логической репликации можно запросом SELECT:

SELECT * FROM psql_logical_slot_rewind_schema.replicated_tables WHERE slot_name = 'old_slot';

slot_name | replication_objects
-----------+-------------------------
old_slot | test_sch.test_table
old_slot | test_sch.test_table_part
(2 rows)

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

SELECT * FROM pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', false, pg_lsn('8/B0814968'));
WARNING: Replicated tables for "old_slot" were not found. All tables are used.

Добавить реплицируемые таблицы в системную таблицу psql_logical_slot_rewind_schema.replicated_tables для слота можно запросом INSERT (до создания слота). Необходимо указывать название реплицируемой таблицы со схемой (через точку), если не указать название схемы, то по умолчанию к названию таблицы будет добавлена схема public. В одной записи хранится имя только одной таблицы:

INSERT INTO psql_logical_slot_rewind_schema.replicated_tables VALUES('old_slot', 'test_sch.test_table');
INSERT INTO psql_logical_slot_rewind_schema.replicated_tables VALUES('old_slot', 'test_sch.test_table_pаrt');

Вставка объектов для слота также доступна с помощью функции psql_logical_slot_rewind_schema.add_replicated_table.

Удаление реплицируемых таблиц для слота репликации со сдвигом в прошлое доступно вывозом функции psql_logical_slot_rewind_schema.clean_replicated_tables_of_slot:

SELECT psql_logical_slot_rewind_schema.clean_replicated_tables_of_slot('old_slot');

SELECT full_table_name FROM psql_logical_slot_rewind_schema.replicated_tables WHERE slot_name='old_slot';

full_table_name
-----------------
(0 rows)

Удалять записи можно по одной с помощью запроса DELETE:

DELETE FROM psql_logical_slot_rewind_schema.replicated_tables WHERE slot_name='old_slot';

Создать слот логической репликации при помощи функции pg_create_logical_replication_slot_lsn с любого LSN в прошлом:

SELECT * FROM pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', false, pg_lsn('8/B0814968'));

slot_name | lsn
-----------+------------
old_slot | 8/B0814968
(1 row)

Также добавить список реплицируемых объектов для слота в системную таблицу psql_logical_slot_rewind_schema.replicated_tables через публикацию, указав ее имя при создании слота репликации:

CREATE PUBLICATION test_slot_publication FOR TABLE test_sch.test_table, test_sch.test_table_repl;

SELECT * FROM ext.pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', False, pg_lsn('0/63CB0B10'), False, 'test_slot_publication');

SELECT * FROM psql_logical_slot_rewind_schema.replicated_tables;

slot_name | replication_objects
-----------+-------------------------
old_slot | test_sch.test_table
old_slot | test_sch.test_table_part
(2 rows)

Если передать список реплицируемых объектов через публикацию, то при создании слота репликации со сдвигом в прошлое старые записи, касающиеся данного слота, будут удалены, а в системную таблицу psql_logical_slot_rewind_schema.replicated_tables будут добавлены новые записи:

SELECT * FROM psql_logical_slot_rewind_schema.replicated_tables;

slot_name | replication_objects
-----------+-------------------------
old_slot | test_sch.nonrepl_table
old_slot | test_sch.nonrepl_table_part
(2 rows)
CREATE PUBLICATION test_slot_publication FOR TABLE test_sch.test_table, test_sch.test_table_repl;

SELECT * FROM ext.pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', False, pg_lsn('0/63CB0B10'), False, 'test_slot_publication');

SELECT * FROM psql_logical_slot_rewind_schema.replicated_tables;

slot_name | replication_objects
-----------+-------------------------
old_slot | test_sch.test_table
old_slot | test_sch.test_table_repl
(2 rows)

При помощи функции pg_create_logical_replication_slot_lsn можно создать временный слот логической репликации со сдвигом в прошлое, указав значение True в параметре temporary. Временный слот будет удален при завершении сессии, рестарте либо при возникновении любой ошибки в сессии:

SELECT * FROM pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', true, pg_lsn('8/B0814850'));

SELECT * FROM new_table;
ERROR: relation "new_table" does not exist

SELECT lsn, data FROM pg_logical_slot_peek_changes('old_slot', NULL, NULL, 'include-timestamp', 'on');
ERROR: replication slot "old_slot" does not exist

При помощи функции is_logical_slot_rewind_slot необходимо проверить, дошел ли слот репликации со сдвигом в прошлое до горизонта базы данных, то есть является ли он ванильным слотом (функция вернет False) или слотом со сдвигом в прошлое (функция вернет True):

SELECT is_logical_slot_rewind_slot('old_slot');

is_logical_slot_rewind_slot
------------------------------
true
(1 row)

Вычитка из слота логической репликации со сдвигом в прошлое

Вычитка из слота логической репликации, созданном в результате работы функции pg_create_logical_replication_slot_lsn, возможна, начиная с LSN, с которого он был создан (если целевой LSN находится в середине транзакции, в этом случае слот сдвигается на начало данной транзакции), если все соответствующие WAL-журналы есть в наличии, а также если все реплицируемые объекты существуют в БД, и в них не выполнялись запрещенные DDL-команды:

SELECT lsn, data FROM pg_logical_slot_peek_changes('old_slot', NULL, NULL, 'include-timestamp', 'on');

lsn | data
------------+---------------------------------------------------------------------------
8/B0814918 | BEGIN 17283
8/B0814918 | table public.test_table: INSERT: id[integer]:4 data[text]:'secret data 4'
8/B0814968 | table public.test_table: INSERT: id[integer]:5 data[text]:'secret data 5'
8/B08149B8 | table public.test_table: INSERT: id[integer]:6 data[text]:'secret data 6'
8/B0814A38 | COMMIT 17283 (at 2025-11-11 15:22:38.843267+03)
8/B0814A80 | BEGIN 17284
8/B0814A80 | table public.test_table: DELETE: (no-tuple-data)
8/B0814AC0 | table public.test_table: DELETE: (no-tuple-data)
8/B0814B30 | COMMIT 17284 (at 2025-11-11 15:23:04.538105+03)

Создать слот логической репликации при помощи функции pg_create_logical_replication_slot_lsn можно с любого LSN в прошлом, даже если системный каталог меняется в процессе создания слота (если все реплицируемые объекты существуют в БД на момент создания слота репликации, и в них не выполнялись запрещенные DDL-команды):

SELECT * FROM pg_create_logical_replication_slot_lsn('old_slot', 'test_decoding', false, pg_lsn('8/B0814AC0'));

В параллельной сессии создается и уничтожается множество новых объектов базы данных:

CREATE TABLE test_table_1 AS (SELECT * FROM test_table WHERE id%2=0);
CREATE INDEX test_table_ind1 ON test_table_1 (id);
CREATE TEMP TABLE test_table_2 AS (SELECT * FROM test_table WHERE id%5=0);
CREATE INDEX test_table_ind1 ON test_table_2 (id);
CREATE USER test_user1 WITH LOGIN PASSWORD 'tt8s$tuSer1p@ssw0rd1sc0oL!';
CREATE USER test_user2 WITH LOGIN PASSWORD 'tt8s$tuSer2p@ssw0rd1sc0oL!';
DROP TABLE test_table_1;

Данные из слота репликации вычитываются без сбоев, они корректны:

SELECT lsn, data FROM pg_logical_slot_peek_changes('old_slot', NULL, NULL, 'include-timestamp', 'on');

lsn | data
------------+---------------------------------------------------------------------------
8/B0814A80 | BEGIN 17284
8/B0814A80 | table public.test_table: DELETE: (no-tuple-data)
8/B0814AC0 | table public.test_table: DELETE: (no-tuple-data)
8/B0814B30 | COMMIT 17284 (at 2025-11-11 15:23:04.538105+03)
8/B0818FD0 | BEGIN 17285
8/B0821598 | table public.test_table: INSERT: id[integer]:7 data[text]:'very secret data 7'
8/B0821678 | table public.test_table: INSERT: id[integer]:8 data[text]:'very secret data 8'
8/B0824300 | COMMIT 17285 (at 2025-11-11 15:23:05.002133+03)
8/B08243C0 | BEGIN 17286
8/B08244A0 | COMMIT 17286 (at 2025-11-11 15:30:00.024374+03)