51.9. Передача больших транзакций для логического декодирования
Базовые обработчики модуля вывода (например, begin_cb
, change_cb
, commit_cb
и message_cb
) вызываются, только когда транзакция на самом деле фиксируется. Изменения в любом случае декодируются из журнала транзакций, но передаются в модуль вывода только при фиксации (и отбрасываются, если транзакция прерывается).
Это означает, что, хотя декодирование происходит последовательно и декодируемые записи могут вытесняться на диск, чтобы уменьшить нагрузку на оперативную память, все декодированные изменения должны быть переданы, когда транзакция окончательно фиксируется (или, точнее, когда фиксация декодируется из журнала транзакций). В зависимости от размера транзакции и пропускной способности сети время передачи может значительно увеличить задержку применения.
Чтобы уменьшить задержку, связанную с большими транзакциями, модуль вывода может предоставлять дополнительный обработчик для поддержки инкрементальной передачи выполняющихся транзакций. Определены несколько обязательных обработчиков передачи (stream_start_cb
, stream_stop_cb
, stream_abort_cb
, stream_commit_cb
и stream_change_cb
) и два необязательных обработчика (stream_message_cb
) и (stream_truncate_cb
). Кроме того, если планируется поддерживать потоковую передачу двухфазных команд, нужны дополнительные обработчики. (За подробностями обратитесь к Разделу 51.10).
Когда выполняющаяся транзакция передаётся в потоке, её изменения (и сообщения) передаются блоками, границы которых обозначают вызовы stream_start_cb
и stream_stop_cb
. После того, как все декодированные изменения переданы, транзакция может быть зафиксирована обработчиком stream_commit_cb
(или, возможно, прервана обработчиком stream_abort_cb
). Если поддерживаются двухфазные фиксации, транзакция может быть подготовлена обработчиком stream_prepare_cb
, зафиксирована (COMMIT PREPARED
) обработчиком commit_prepared_cb
или прервана обработчиком rollback_prepared_cb
.
Например, последовательность вызовов обработчиков потока для одной транзакции может быть такой:
stream_start_cb(...); <-- начало первого блока изменений stream_change_cb(...); stream_change_cb(...); stream_message_cb(...); stream_change_cb(...); ... stream_change_cb(...); stream_stop_cb(...); <-- конец первого блока изменений stream_start_cb(...); <-- начало второго блока изменений stream_change_cb(...); stream_change_cb(...); stream_change_cb(...); ... stream_message_cb(...); stream_change_cb(...); stream_stop_cb(...); <-- конец второго блока изменений [a. при использовании нормальной фиксации] stream_commit_cb(...); <-- фиксация переданной транзакции [b. при использовании двухфазной фиксации] stream_prepare_cb(...); <-- подготовка переданной транзакции commit_prepared_cb(...); <-- фиксация подготовленной транзакции
На практике же последовательность вызовов обработчика может быть более сложной. В ней могут быть блоки нескольких передаваемых в потоке транзакций, какие-то транзакции могут прерываться и т. д.
Подобно поведению вытеснения на диск, передача в потоке запускается, когда общее количество изменений, декодированных из WAL (для всех текущих транзакций), превышает предел, заданный параметром logical_decoding_work_mem
. В этот момент выбирается самая большая транзакция (это определяется по объёму памяти, который уже занимают декодированные изменения) верхнего уровня, и она передаётся в потоке. Тем не менее, в некоторых случаях всё равно приходится переносить данные на диск, даже когда включена передача в потоке, поскольку порог памяти уже превышен, но полный кортеж ещё не декодирован, например декодировано добавление только в таблицу TOAST, но не в основную таблицу.
Даже при передаче в потоке больших транзакций изменения по-прежнему применяются в порядке фиксации, сохраняя те же гарантии, что и не в потоковом режиме.