Глава 61. Унифицированные записи WAL

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

API для конструирования унифицированных записей WAL определён в access/generic_xlog.h и реализован в access/transam/generic_xlog.c.

Чтобы сформировать запись изменения данных для WAL, применяя механизм унифицированных записей WAL, выполните следующие действия:

  1. state = GenericXLogStart(relation) — начните формирование унифицированной записи WAL для заданного отношения.

  2. page = GenericXLogRegisterBuffer(state, buffer, flags) — зарегистрируйте буфер, который будет изменён текущей унифицированной записью WAL. Эта функция возвращает указатель на временную копию страницы буфера, в которой должны производиться изменения. (Модифицировать непосредственно содержимое буфера нельзя.) В третьем аргументе передаётся битовая маска флагов, применимых к этой операции. В настоящее время флаг только один — GENERIC_XLOG_FULL_IMAGE, который показывает, что в запись WAL нужно включить образ всей страницы, а не только изменения. Обычно этот флаг должен устанавливаться, когда страница новая или полностью перезаписана. Вызов GenericXLogRegisterBuffer можно повторять, если фиксируемое в WAL действие изменяет несколько страниц.

  3. Применить изменения к образам страниц, полученным на предыдущем шаге.

  4. GenericXLogFinish(state) — завершите изменения в буферах и выдайте унифицированную запись WAL.

Формирование записи WAL можно прервать на любом шаге, вызвав GenericXLogAbort(state). При этом будут отменены все изменения, внесённые в копии образов страниц.

Используя механизм унифицированных записей WAL, необходимо учитывать следующее:

  • Модифицировать буферы напрямую нельзя! Все изменения должны производиться в копиях, полученных от функции GenericXLogRegisterBuffer(). Другими словами, код, формирующий унифицированные записи WAL, никогда не должен сам вызывать BufferGetPage(). Однако, вызывающий код отвечает за закрепление/открепление и блокировку/разблокировку буферов в подходящие моменты времени. Исключительная блокировка каждого целевого буфера должна удерживаться от вызова GenericXLogRegisterBuffer() до GenericXLogFinish().

  • Регистрацию буферов (шаг 2) и модификацию образов страниц (шаг 3) можно свободно смешивать, оба этих шага можно повторять в любой последовательности. Но помните, что буферы должны регистрироваться в том же порядке, в каком для них должны получаться блокировки при воспроизведении.

  • Максимальное число буферов, которые можно зарегистрировать для унифицированной записи WAL, составляет MAX_GENERIC_XLOG_PAGES. При исчерпании этого лимита будет выдана ошибка.

  • Унифицированный тип WAL подразумевает, что страницы, подлежащие изменению, имеют стандартную структуру, в частности между pd_lower и pd_upper нет полезных данных.

  • Так как изменяются копии страниц буфера, GenericXLogStart() не начинает критическую секцию. Таким образом вы можете безопасно выделять память, выдать ошибку и т. п. между GenericXLogStart() и GenericXLogFinish(). Единственная фактическая критическая секция присутствует внутри GenericXLogFinish(). При выходе по ошибке так же не нужно заботиться о вызове GenericXLogAbort().

  • GenericXLogFinish() помечает буферы как грязные и устанавливает для них LSN. Вам делать явно это не нужно.

  • Для нежурналируемых отношений всё работает так же, за исключением того, что фактически запись в WAL не выдаётся. Таким образом, явно проверять, является ли отношение нежурналируемым, не требуется.

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

  • Если для регистрируемого буфера не задаётся GENERIC_XLOG_FULL_IMAGE, унифицированная запись WAL содержит различие между старым и новым образом страницы, которое вычисляется при побайтовом сравнении. Результат оказывается не очень компактным при перемещении данных в странице, но это может быть доработано в будущем.