Глава 63. Унифицированные записи 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 содержит различие между старым и новым образом страницы, которое вычисляется при побайтовом сравнении. Результат оказывается не очень компактным при перемещении данных в странице, но это может быть доработано в будущем.
Chapter 63. Generic WAL Records
Although all built-in WAL-logged modules have their own types of WAL records, there is also a generic WAL record type, which describes changes to pages in a generic way. This is useful for extensions that provide custom access methods, because they cannot register their own WAL redo routines.
The API for constructing generic WAL records is defined in access/generic_xlog.h and implemented in access/transam/generic_xlog.c.
To perform a WAL-logged data update using the generic WAL record facility, follow these steps:
state = GenericXLogStart(relation)— start construction of a generic WAL record for the given relation.page = GenericXLogRegisterBuffer(state, buffer, flags)— register a buffer to be modified within the current generic WAL record. This function returns a pointer to a temporary copy of the buffer's page, where modifications should be made. (Do not modify the buffer's contents directly.) The third argument is a bit mask of flags applicable to the operation. Currently the only such flag isGENERIC_XLOG_FULL_IMAGE, which indicates that a full-page image rather than a delta update should be included in the WAL record. Typically this flag would be set if the page is new or has been rewritten completely.GenericXLogRegisterBuffercan be repeated if the WAL-logged action needs to modify multiple pages.Apply modifications to the page images obtained in the previous step.
GenericXLogFinish(state)— apply the changes to the buffers and emit the generic WAL record.
WAL record construction can be canceled between any of the above steps by calling GenericXLogAbort(state). This will discard all changes to the page image copies.
Please note the following points when using the generic WAL record facility:
No direct modifications of buffers are allowed! All modifications must be done in copies acquired from
GenericXLogRegisterBuffer(). In other words, code that makes generic WAL records should never callBufferGetPage()for itself. However, it remains the caller's responsibility to pin/unpin and lock/unlock the buffers at appropriate times. Exclusive lock must be held on each target buffer from beforeGenericXLogRegisterBuffer()until afterGenericXLogFinish().Registrations of buffers (step 2) and modifications of page images (step 3) can be mixed freely, i.e., both steps may be repeated in any sequence. Keep in mind that buffers should be registered in the same order in which locks are to be obtained on them during replay.
The maximum number of buffers that can be registered for a generic WAL record is
MAX_GENERIC_XLOG_PAGES. An error will be thrown if this limit is exceeded.Generic WAL assumes that the pages to be modified have standard layout, and in particular that there is no useful data between
pd_lowerandpd_upper.Since you are modifying copies of buffer pages,
GenericXLogStart()does not start a critical section. Thus, you can safely do memory allocation, error throwing, etc. betweenGenericXLogStart()andGenericXLogFinish(). The only actual critical section is present insideGenericXLogFinish(). There is no need to worry about callingGenericXLogAbort()during an error exit, either.GenericXLogFinish()takes care of marking buffers dirty and setting their LSNs. You do not need to do this explicitly.For unlogged relations, everything works the same except that no actual WAL record is emitted. Thus, you typically do not need to do any explicit checks for unlogged relations.
The generic WAL redo function will acquire exclusive locks to buffers in the same order as they were registered. After redoing all changes, the locks will be released in the same order.
If
GENERIC_XLOG_FULL_IMAGEis not specified for a registered buffer, the generic WAL record contains a delta between the old and the new page images. This delta is based on byte-by-byte comparison. This is not very compact for the case of moving data within a page, and might be improved in the future.