Пользовательские менеджеры ресурсов 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, из-за чего сервер может не запуститься.