Skip to content

Commit ae168c7

Browse files
committed
Add test module for Custom WAL Resource Manager feature.
Author: Bharath Rupireddy, Jeff Davis Discussion: https://postgr.es/m/CALj2ACVTBNA1wfVCsikfhygAbZe6kFY8Oz6PhOyhHyA4vAGouA%40mail.gmail.com
1 parent 9e54059 commit ae168c7

File tree

10 files changed

+291
-0
lines changed

10 files changed

+291
-0
lines changed

doc/src/sgml/custom-rmgr.sgml

+7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ typedef struct RmgrData
5757
} RmgrData;
5858
</programlisting>
5959
</para>
60+
61+
<para>
62+
The <filename>src/test/modules/test_custom_rmgrs</filename> module
63+
contains a working example, which demonstrates usage of custom WAL
64+
resource managers.
65+
</para>
66+
6067
<para>
6168
Then, register your new resource
6269
manager.

src/test/modules/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ SUBDIRS = \
1616
spgist_name_ops \
1717
test_bloomfilter \
1818
test_copy_callbacks \
19+
test_custom_rmgrs \
1920
test_ddl_deparse \
2021
test_extensions \
2122
test_ginpostinglist \

src/test/modules/meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ subdir('spgist_name_ops')
1010
subdir('ssl_passphrase_callback')
1111
subdir('test_bloomfilter')
1212
subdir('test_copy_callbacks')
13+
subdir('test_custom_rmgrs')
1314
subdir('test_ddl_deparse')
1415
subdir('test_extensions')
1516
subdir('test_ginpostinglist')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Generated subdirectories
2+
/log/
3+
/results/
4+
/tmp_check/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# src/test/modules/test_custom_rmgrs/Makefile
2+
3+
MODULE_big = test_custom_rmgrs
4+
OBJS = \
5+
$(WIN32RES) \
6+
test_custom_rmgrs.o
7+
PGFILEDESC = "test_custom_rmgrs - test custom WAL resource managers"
8+
9+
EXTENSION = test_custom_rmgrs
10+
DATA = test_custom_rmgrs--1.0.sql
11+
12+
EXTRA_INSTALL = contrib/pg_walinspect
13+
TAP_TESTS = 1
14+
15+
ifdef USE_PGXS
16+
PG_CONFIG = pg_config
17+
PGXS := $(shell $(PG_CONFIG) --pgxs)
18+
include $(PGXS)
19+
else
20+
subdir = src/test/modules/test_custom_rmgrs
21+
top_builddir = ../../../..
22+
include $(top_builddir)/src/Makefile.global
23+
include $(top_srcdir)/contrib/contrib-global.mk
24+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# FIXME: prevent install during main install, but not during test :/
2+
3+
test_custom_rmgrs_sources = files(
4+
'test_custom_rmgrs.c',
5+
)
6+
7+
if host_system == 'windows'
8+
test_custom_rmgrs_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
9+
'--NAME', 'test_custom_rmgrs',
10+
'--FILEDESC', 'test_custom_rmgrs - test custom WAL resource managers',])
11+
endif
12+
13+
test_custom_rmgrs = shared_module('test_custom_rmgrs',
14+
test_custom_rmgrs_sources,
15+
kwargs: pg_mod_args,
16+
)
17+
testprep_targets += test_custom_rmgrs
18+
19+
install_data(
20+
'test_custom_rmgrs.control',
21+
'test_custom_rmgrs--1.0.sql',
22+
kwargs: contrib_data_args,
23+
)
24+
25+
tests += {
26+
'name': 'test_custom_rmgrs',
27+
'sd': meson.current_source_dir(),
28+
'bd': meson.current_build_dir(),
29+
'tap': {
30+
'tests': [
31+
't/001_basic.pl',
32+
],
33+
},
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright (c) 2021-2022, PostgreSQL Global Development Group
2+
3+
use strict;
4+
use warnings;
5+
6+
use PostgreSQL::Test::Cluster;
7+
use PostgreSQL::Test::Utils;
8+
use Test::More;
9+
10+
my $node = PostgreSQL::Test::Cluster->new('main');
11+
12+
$node->init;
13+
$node->append_conf(
14+
'postgresql.conf', q{
15+
wal_level = 'replica'
16+
max_wal_senders = 4
17+
shared_preload_libraries = 'test_custom_rmgrs'
18+
});
19+
$node->start;
20+
21+
# setup
22+
$node->safe_psql('postgres', 'CREATE EXTENSION test_custom_rmgrs');
23+
24+
# pg_walinspect is required only for verifying test_custom_rmgrs output.
25+
# test_custom_rmgrs doesn't use/depend on it internally.
26+
$node->safe_psql('postgres', 'CREATE EXTENSION pg_walinspect');
27+
28+
# make sure checkpoints don't interfere with the test.
29+
my $start_lsn = $node->safe_psql('postgres',
30+
qq[SELECT lsn FROM pg_create_physical_replication_slot('regress_test_slot1', true, false);]);
31+
32+
# write and save the WAL record's returned end LSN for verifying it later
33+
my $record_end_lsn = $node->safe_psql('postgres',
34+
'SELECT * FROM test_custom_rmgrs_insert_wal_record(\'payload123\')');
35+
36+
# ensure the WAL is written and flushed to disk
37+
$node->safe_psql('postgres', 'SELECT pg_switch_wal()');
38+
39+
my $end_lsn = $node->safe_psql('postgres', 'SELECT pg_current_wal_flush_lsn()');
40+
41+
# check if our custom WAL resource manager has successfully registered with the server
42+
my $row_count =
43+
$node->safe_psql('postgres',
44+
qq[SELECT count(*) FROM pg_get_wal_resource_managers()
45+
WHERE rm_name = 'test_custom_rmgrs';]);
46+
is($row_count, '1',
47+
'custom WAL resource manager has successfully registered with the server'
48+
);
49+
50+
# check if our custom WAL resource manager has successfully written a WAL record
51+
my $expected = qq($record_end_lsn|test_custom_rmgrs|TEST_CUSTOM_RMGRS_MESSAGE|44|18|0|payload (10 bytes): payload123);
52+
my $result =
53+
$node->safe_psql('postgres',
54+
qq[SELECT end_lsn, resource_manager, record_type, record_length, main_data_length, fpi_length, description FROM pg_get_wal_records_info('$start_lsn', '$end_lsn')
55+
WHERE resource_manager = 'test_custom_rmgrs';]);
56+
is($result, $expected,
57+
'custom WAL resource manager has successfully written a WAL record'
58+
);
59+
60+
$node->stop;
61+
done_testing();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* src/test/modules/test_custom_rmgrs/test_custom_rmgrs--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION test_custom_rmgrs" to load this file. \quit
5+
6+
--
7+
-- test_custom_rmgrs_insert_wal_record()
8+
--
9+
-- Writes a simple message into WAL with the help of custom WAL
10+
-- resource manager.
11+
--
12+
CREATE FUNCTION test_custom_rmgrs_insert_wal_record(IN payload TEXT,
13+
OUT lsn pg_lsn
14+
)
15+
AS 'MODULE_PATHNAME', 'test_custom_rmgrs_insert_wal_record'
16+
LANGUAGE C STRICT PARALLEL UNSAFE;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*--------------------------------------------------------------------------
2+
*
3+
* test_custom_rmgrs.c
4+
* Code for testing custom WAL resource managers.
5+
*
6+
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
7+
* Portions Copyright (c) 1994, Regents of the University of California
8+
*
9+
* IDENTIFICATION
10+
* src/test/modules/test_custom_rmgrs/test_custom_rmgrs.c
11+
*
12+
* Custom WAL resource manager for records containing a simple textual
13+
* payload, no-op redo, and no decoding.
14+
*
15+
* -------------------------------------------------------------------------
16+
*/
17+
18+
#include "postgres.h"
19+
20+
#include "access/xlog.h"
21+
#include "access/xlog_internal.h"
22+
#include "access/xloginsert.h"
23+
#include "fmgr.h"
24+
#include "utils/pg_lsn.h"
25+
26+
PG_MODULE_MAGIC;
27+
28+
/*
29+
* test_custom_rmgrs WAL record message.
30+
*/
31+
typedef struct xl_testcustomrmgrs_message
32+
{
33+
Size message_size; /* size of the message */
34+
char message[FLEXIBLE_ARRAY_MEMBER]; /* payload */
35+
} xl_testcustomrmgrs_message;
36+
37+
#define SizeOfTestCustomRmgrsMessage (offsetof(xl_testcustomrmgrs_message, message))
38+
#define XLOG_TEST_CUSTOM_RMGRS_MESSAGE 0x00
39+
40+
/*
41+
* While developing or testing, use RM_EXPERIMENTAL_ID for rmid. For a real
42+
* extension, reserve a new resource manager ID to avoid conflicting with
43+
* other extensions; see:
44+
* https://wiki.postgresql.org/wiki/CustomWALResourceManagers
45+
*/
46+
#define RM_TESTCUSTOMRMGRS_ID RM_EXPERIMENTAL_ID
47+
#define TESTCUSTOMRMGRS_NAME "test_custom_rmgrs"
48+
49+
/* RMGR API, see xlog_internal.h */
50+
void testcustomrmgrs_redo(XLogReaderState *record);
51+
void testcustomrmgrs_desc(StringInfo buf, XLogReaderState *record);
52+
const char *testcustomrmgrs_identify(uint8 info);
53+
54+
static RmgrData testcustomrmgrs_rmgr = {
55+
.rm_name = TESTCUSTOMRMGRS_NAME,
56+
.rm_redo = testcustomrmgrs_redo,
57+
.rm_desc = testcustomrmgrs_desc,
58+
.rm_identify = testcustomrmgrs_identify
59+
};
60+
61+
/*
62+
* Module load callback
63+
*/
64+
void
65+
_PG_init(void)
66+
{
67+
/*
68+
* In order to create our own custom resource manager, we have to be
69+
* loaded via shared_preload_libraries. Otherwise, registration will fail.
70+
*/
71+
RegisterCustomRmgr(RM_TESTCUSTOMRMGRS_ID, &testcustomrmgrs_rmgr);
72+
}
73+
74+
/* RMGR API implementation */
75+
76+
/*
77+
* Redo is just a noop for this module, because we aren't testing recovery of
78+
* any real structure.
79+
*/
80+
void
81+
testcustomrmgrs_redo(XLogReaderState *record)
82+
{
83+
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
84+
85+
if (info != XLOG_TEST_CUSTOM_RMGRS_MESSAGE)
86+
elog(PANIC, "testcustomrmgrs_redo: unknown op code %u", info);
87+
}
88+
89+
void
90+
testcustomrmgrs_desc(StringInfo buf, XLogReaderState *record)
91+
{
92+
char *rec = XLogRecGetData(record);
93+
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
94+
95+
if (info == XLOG_TEST_CUSTOM_RMGRS_MESSAGE)
96+
{
97+
xl_testcustomrmgrs_message *xlrec = (xl_testcustomrmgrs_message *) rec;
98+
99+
appendStringInfo(buf, "payload (%zu bytes): ", xlrec->message_size);
100+
appendBinaryStringInfo(buf, xlrec->message, xlrec->message_size);
101+
}
102+
}
103+
104+
const char *
105+
testcustomrmgrs_identify(uint8 info)
106+
{
107+
if ((info & ~XLR_INFO_MASK) == XLOG_TEST_CUSTOM_RMGRS_MESSAGE)
108+
return "TEST_CUSTOM_RMGRS_MESSAGE";
109+
110+
return NULL;
111+
}
112+
113+
/*
114+
* SQL function for writing a simple message into WAL with the help of custom
115+
* WAL resource manager.
116+
*/
117+
PG_FUNCTION_INFO_V1(test_custom_rmgrs_insert_wal_record);
118+
Datum
119+
test_custom_rmgrs_insert_wal_record(PG_FUNCTION_ARGS)
120+
{
121+
text *arg = PG_GETARG_TEXT_PP(0);
122+
char *payload = VARDATA_ANY(arg);
123+
Size len = VARSIZE_ANY_EXHDR(arg);
124+
XLogRecPtr lsn;
125+
xl_testcustomrmgrs_message xlrec;
126+
127+
xlrec.message_size = len;
128+
129+
XLogBeginInsert();
130+
XLogRegisterData((char *) &xlrec, SizeOfTestCustomRmgrsMessage);
131+
XLogRegisterData((char *) payload, len);
132+
133+
/* Let's mark this record as unimportant, just in case. */
134+
XLogSetRecordFlags(XLOG_MARK_UNIMPORTANT);
135+
136+
lsn = XLogInsert(RM_TESTCUSTOMRMGRS_ID, XLOG_TEST_CUSTOM_RMGRS_MESSAGE);
137+
138+
PG_RETURN_LSN(lsn);
139+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
comment = 'Test code for custom WAL resource managers'
2+
default_version = '1.0'
3+
module_pathname = '$libdir/test_custom_rmgrs'
4+
relocatable = true

0 commit comments

Comments
 (0)