Пользовательские менеджеры ресурсов WAL
В этой главе рассматривается интерфейс между ядром системы QHB и пользовательскими менеджерами ресурсов WAL, позволяющий расширениям напрямую интегрировать свои объекты в WAL.
Расширению, особенно реализующему табличные методы доступа или индексные методы доступа, может понадобиться использовать WAL для восстановления, репликации и/или логического декодирования. Пользовательские менеджеры ресурсов представляют собой более гибкую альтернативу типовым записям WAL (которые не поддерживают логическое декодирование), но реализовать их в расширении сложнее.
Чтобы создать новый пользовательский менеджер ресурсов WAL, сначала определите структуру RmgrData с реализациями методов менеджера ресурсов.
/*
* Таблица методов для менеджеров ресурсов.
*
* Эта структура должна быть постоянно синхронизирована с определением PG_RMGR в
* файле rmgr.c.
*
* Метод rm_identify должен вернуть имя записи, исходя из xl_info (без ссылки на
* rmid). Например, XLOG_BTREE_VACUUM может быть назван "VACUUM". Затем можно
* вызвать метод rm_desc для получения дополнительной информации о записи, если
* таковая имеется (например, последний блок).
*
* Метод rm_mask принимает на вход страницу, модифицированную менеджером ресурсов, и
* маскирует биты, которые не должны помечаться при проверке wal_consistency_checking.
*
* RmgrTable[] индексируется значениями RmgrId (см. rmgrlist.h). Если rm_name имеет
* значение NULL, соответствующая запись RmgrTable считается недопустимой.
*/
typedef struct RmgrData
{
const char *rm_name;
void (*rm_redo) (XLogReaderState *record);
void (*rm_desc) (StringInfo buf, XLogReaderState *record);
const char *(*rm_identify) (uint8 info);
void (*rm_startup) (void);
void (*rm_cleanup) (void);
void (*rm_mask) (char *pagedata, BlockNumber blkno);
void (*rm_decode) (struct LogicalDecodingContext *ctx,
struct XLogRecordBuffer *buf);
} RmgrData;
Приведенный ниже рабочий пример демонстрирует использование пользовательского менеджера ресурсов WAL.
#include "qhb.h"
#include "access/xlog.h"
#include "access/xlog_internal.h"
#include "access/xloginsert.h"
#include "fmgr.h"
#include "utils/pg_lsn.h"
#include "varatt.h"
PG_MODULE_MAGIC;
/*
* Сообщение записи WAL test_custom_rmgrs.
*/
typedef struct xl_testcustomrmgrs_message
{
Size message_size; /* size of the message */
char message[FLEXIBLE_ARRAY_MEMBER]; /* payload */
} xl_testcustomrmgrs_message;
#define SizeOfTestCustomRmgrsMessage (offsetof(xl_testcustomrmgrs_message, message))
#define XLOG_TEST_CUSTOM_RMGRS_MESSAGE 0x00
/*
* При разработке или тестировании используйте для rmid идентификатор RM_EXPERIMENTAL_ID.
* Для настоящего приложения резервируйте новый идентификатор менеджера ресурсов
* во избежание конфликтов с другими расширениями; см.:
* https://wiki.postgresql.org/wiki/CustomWALResourceManagers
*/
#define RM_TESTCUSTOMRMGRS_ID RM_EXPERIMENTAL_ID
#define TESTCUSTOMRMGRS_NAME "test_custom_rmgrs"
/* RMGR API, см. xlog_internal.h */
void testcustomrmgrs_redo(XLogReaderState *record);
void testcustomrmgrs_desc(StringInfo buf, XLogReaderState *record);
const char *testcustomrmgrs_identify(uint8 info);
static const RmgrData testcustomrmgrs_rmgr = {
.rm_name = TESTCUSTOMRMGRS_NAME,
.rm_redo = testcustomrmgrs_redo,
.rm_desc = testcustomrmgrs_desc,
.rm_identify = testcustomrmgrs_identify
};
/*
* Функция обратного вызова для загрузки модуля
*/
void
_PG_init(void)
{
/*
* Чтобы создать собственный пользовательский менеджер ресурсов, следует загрузить
* его посредством shared_preload_libraries. Иначе регистрация завершится ошибкой.
*/
RegisterCustomRmgr(RM_TESTCUSTOMRMGRS_ID, &testcustomrmgrs_rmgr);
}
/* Реализация RMGR API */
/*
* Redo — это просто пустая команда для этого модуля, поскольку мы не тестируем
* восстановление реальной структуры.
*/
void
testcustomrmgrs_redo(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info != XLOG_TEST_CUSTOM_RMGRS_MESSAGE)
elog(PANIC, "testcustomrmgrs_redo: unknown op code %u", info);
}
void
testcustomrmgrs_desc(StringInfo buf, XLogReaderState *record)
{
char *rec = XLogRecGetData(record);
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == XLOG_TEST_CUSTOM_RMGRS_MESSAGE)
{
xl_testcustomrmgrs_message *xlrec = (xl_testcustomrmgrs_message *) rec;
appendStringInfo(buf, "payload (%zu bytes): ", xlrec->message_size);
appendBinaryStringInfo(buf, xlrec->message, xlrec->message_size);
}
}
const char *
testcustomrmgrs_identify(uint8 info)
{
if ((info & ~XLR_INFO_MASK) == XLOG_TEST_CUSTOM_RMGRS_MESSAGE)
return "TEST_CUSTOM_RMGRS_MESSAGE";
return NULL;
}
/*
* Функция SQL для записи в WAL простого сообщения с помощью пользовательского
* менеджера ресурсов WAL.
*/
PG_FUNCTION_INFO_V1(test_custom_rmgrs_insert_wal_record);
Datum
test_custom_rmgrs_insert_wal_record(PG_FUNCTION_ARGS)
{
text *arg = PG_GETARG_TEXT_PP(0);
char *payload = VARDATA_ANY(arg);
Size len = VARSIZE_ANY_EXHDR(arg);
XLogRecPtr lsn;
xl_testcustomrmgrs_message xlrec;
xlrec.message_size = len;
XLogBeginInsert();
XLogRegisterData(&xlrec, SizeOfTestCustomRmgrsMessage);
XLogRegisterData(payload, len);
/* Давайте на всякий случай пометим эту запись неважной. */
XLogSetRecordFlags(XLOG_MARK_UNIMPORTANT);
lsn = XLogInsert(RM_TESTCUSTOMRMGRS_ID, XLOG_TEST_CUSTOM_RMGRS_MESSAGE);
PG_RETURN_LSN(lsn);
}
Затем зарегистрируйте свой новый менеджер ресурсов.
/*
* Регистрация нового пользовательского менеджера ресурсов WAL.
*
* Идентификаторы менеджеров ресурсов должны быть глобально уникальными среди всех
* расширений. Зарезервируйте уникальный RmgrId для вашего расширения на странице
* https://wiki.postgresql.org/wiki/CustomWALResourceManagers, чтобы избежать
* конфликтов с другими разработчиками расширений. Во время разработки пользуйтесь
* RM_EXPERIMENTAL_ID, чтобы не резервировать новые идентификаторы без необходимости.
*/
extern void RegisterCustomRmgr(RmgrId rmid, const RmgrData *rmgr);
Функцию RegisterCustomRmgr следует вызывать из функции _PG_init модуля расширения. Во время разработки нового расширения в качестве rmid используйте RM_EXPERIMENTAL_ID. Когда будете готовы представить расширение пользователям, зарезервируйте новый идентификатор менеджера ресурсов на странице Custom WAL Resource Manager (Пользовательский менеджер ресурсов WAL).
Добавьте модуль расширения, реализующий пользовательский менеджер ресурсов, в список параметра shared_preload_libraries чтобы он загружался сразу при запуске QHB.
Примечание
Расширение должно оставаться в shared_preload_libraries до тех пор, пока в системе могут существовать какие-либо пользовательские записи WAL. В противном случае QHB не сможет применять или декодировать пользовательские записи WAL, из-за чего сервер может не запуститься.