Глава 66. Унифицированные записи WAL
Хотя у всех внутренних модулей, взаимодействующих с WAL, имеются собственные типы записей WAL, существует также унифицированный тип записей 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 содержит различие между старым и новым образом страницы, которое вычисляется при побайтовом сравнении. Результат оказывается не очень компактным при перемещении данных в странице, но это может быть доработано в будущем.
18.7. Preventing Server Spoofing
While the server is running, it is not possible for a malicious user to take the place of the normal database server. However, when the server is down, it is possible for a local user to spoof the normal server by starting their own server. The spoof server could read passwords and queries sent by clients, but could not return any data because the PGDATA
directory would still be secure because of directory permissions. Spoofing is possible because any user can start a database server; a client cannot identify an invalid server unless it is specially configured.
One way to prevent spoofing of local
connections is to use a Unix domain socket directory (unix_socket_directories) that has write permission only for a trusted local user. This prevents a malicious user from creating their own socket file in that directory. If you are concerned that some applications might still reference /tmp
for the socket file and hence be vulnerable to spoofing, during operating system startup create a symbolic link /tmp/.s.PGSQL.5432
that points to the relocated socket file. You also might need to modify your /tmp
cleanup script to prevent removal of the symbolic link.
Another option for local
connections is for clients to use requirepeer
to specify the required owner of the server process connected to the socket.
To prevent spoofing on TCP connections, either use SSL certificates and make sure that clients check the server's certificate, or use GSSAPI encryption (or both, if they're on separate connections).
To prevent spoofing with SSL, the server must be configured to accept only hostssl
connections (Section 20.1) and have SSL key and certificate files (Section 18.9). The TCP client must connect using sslmode=verify-ca
or verify-full
and have the appropriate root certificate file installed (Section 36.19.1).
To prevent spoofing with GSSAPI, the server must be configured to accept only hostgssenc
connections (Section 20.1) and use gss
authentication with them. The TCP client must connect using gssencmode=require
.