Глава 58. Унифицированные записи WAL
Хотя у всех внутренних модулей, взаимодействующих с WAL, имеются собственные типы записей WAL, существует также унифицированный тип записей WAL, описывающий изменения в страницах унифицированным образом. Это полезно для расширений, реализующих собственные методы доступа, так как они не могут зарегистрировать свои подпрограммы воспроизведения изменений WAL.
API для конструирования унифицированных записей WAL определён в access/generic_xlog.h и реализован в access/transam/generic_xlog.c.
Чтобы сформировать запись изменения данных для WAL, применяя механизм унифицированных записей WAL, выполните следующие действия:
- state = GenericXLogStart(relation)— начните формирование унифицированной записи WAL для заданного отношения.
- page = GenericXLogRegisterBuffer(state, buffer, flags)— зарегистрируйте буфер, который будет изменён текущей унифицированной записью WAL. Эта функция возвращает указатель на временную копию страницы буфера, в которой должны производиться изменения. (Модифицировать непосредственно содержимое буфера нельзя.) В третьем аргументе передаётся битовая маска флагов, применимых к этой операции. В настоящее время флаг только один —- GENERIC_XLOG_FULL_IMAGE, который показывает, что в запись WAL нужно включить образ всей страницы, а не только изменения. Обычно этот флаг должен устанавливаться, когда страница новая или полностью перезаписана. Вызов- GenericXLogRegisterBufferможно повторять, если фиксируемое в WAL действие изменяет несколько страниц.
- Примените изменения к образам страниц, полученным на предыдущем шаге. 
- 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 содержит различие между старым и новым образом страницы, которое вычисляется при побайтовом сравнении. Результат оказывается не очень компактным при перемещении данных в странице, но это может быть доработано в будущем.