Skip to content

Commit 6d9fa52

Browse files
committed
pg_receivewal: Add --endpos option
This is primarily useful for making tests of this utility more deterministic, to avoid the complexity of starting pg_receivewal as a deamon in TAP tests. While this is less useful than the equivalent pg_recvlogical option, users can as well use it for example to enforce WAL streaming up to a end-of-backup position, to save only a minimal amount of WAL. Use this new option to stream WAL data in a deterministic way within a new set of TAP tests. Author: Michael Paquier <michael.paquier@gmail.com>
1 parent c1898c3 commit 6d9fa52

File tree

3 files changed

+91
-8
lines changed

3 files changed

+91
-8
lines changed

doc/src/sgml/ref/pg_receivewal.sgml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,22 @@ PostgreSQL documentation
9898
</listitem>
9999
</varlistentry>
100100

101+
<varlistentry>
102+
<term><option>-E <replaceable>lsn</replaceable></option></term>
103+
<term><option>--endpos=<replaceable>lsn</replaceable></option></term>
104+
<listitem>
105+
<para>
106+
Automatically stop replication and exit with normal exit status 0 when
107+
receiving reaches the specified LSN.
108+
</para>
109+
110+
<para>
111+
If there is a record with LSN exactly equal to <replaceable>lsn</>,
112+
the record will be processed.
113+
</para>
114+
</listitem>
115+
</varlistentry>
116+
101117
<varlistentry>
102118
<term><option>--if-not-exists</option></term>
103119
<listitem>

src/bin/pg_basebackup/pg_receivewal.c

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,13 @@ static int verbose = 0;
3636
static int compresslevel = 0;
3737
static int noloop = 0;
3838
static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
39-
static volatile bool time_to_abort = false;
39+
static volatile bool time_to_stop = false;
4040
static bool do_create_slot = false;
4141
static bool slot_exists_ok = false;
4242
static bool do_drop_slot = false;
4343
static bool synchronous = false;
4444
static char *replication_slot = NULL;
45+
static XLogRecPtr endpos = InvalidXLogRecPtr;
4546

4647

4748
static void usage(void);
@@ -77,6 +78,7 @@ usage(void)
7778
printf(_(" %s [OPTION]...\n"), progname);
7879
printf(_("\nOptions:\n"));
7980
printf(_(" -D, --directory=DIR receive write-ahead log files into this directory\n"));
81+
printf(_(" -E, --endpos=LSN exit after receiving the specified LSN\n"));
8082
printf(_(" --if-not-exists do not error if slot already exists when creating a slot\n"));
8183
printf(_(" -n, --no-loop do not loop on connection lost\n"));
8284
printf(_(" -s, --status-interval=SECS\n"
@@ -112,6 +114,16 @@ stop_streaming(XLogRecPtr xlogpos, uint32 timeline, bool segment_finished)
112114
progname, (uint32) (xlogpos >> 32), (uint32) xlogpos,
113115
timeline);
114116

117+
if (!XLogRecPtrIsInvalid(endpos) && endpos < xlogpos)
118+
{
119+
if (verbose)
120+
fprintf(stderr, _("%s: stopped streaming at %X/%X (timeline %u)\n"),
121+
progname, (uint32) (xlogpos >> 32), (uint32) xlogpos,
122+
timeline);
123+
time_to_stop = true;
124+
return true;
125+
}
126+
115127
/*
116128
* Note that we report the previous, not current, position here. After a
117129
* timeline switch, xlogpos points to the beginning of the segment because
@@ -128,7 +140,7 @@ stop_streaming(XLogRecPtr xlogpos, uint32 timeline, bool segment_finished)
128140
prevtimeline = timeline;
129141
prevpos = xlogpos;
130142

131-
if (time_to_abort)
143+
if (time_to_stop)
132144
{
133145
if (verbose)
134146
fprintf(stderr, _("%s: received interrupt signal, exiting\n"),
@@ -448,7 +460,7 @@ StreamLog(void)
448460
static void
449461
sigint_handler(int signum)
450462
{
451-
time_to_abort = true;
463+
time_to_stop = true;
452464
}
453465
#endif
454466

@@ -460,6 +472,7 @@ main(int argc, char **argv)
460472
{"version", no_argument, NULL, 'V'},
461473
{"directory", required_argument, NULL, 'D'},
462474
{"dbname", required_argument, NULL, 'd'},
475+
{"endpos", required_argument, NULL, 'E'},
463476
{"host", required_argument, NULL, 'h'},
464477
{"port", required_argument, NULL, 'p'},
465478
{"username", required_argument, NULL, 'U'},
@@ -481,6 +494,7 @@ main(int argc, char **argv)
481494
int c;
482495
int option_index;
483496
char *db_name;
497+
uint32 hi, lo;
484498

485499
progname = get_progname(argv[0]);
486500
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));
@@ -500,7 +514,7 @@ main(int argc, char **argv)
500514
}
501515
}
502516

503-
while ((c = getopt_long(argc, argv, "D:d:h:p:U:s:S:nwWvZ:",
517+
while ((c = getopt_long(argc, argv, "D:d:E:h:p:U:s:S:nwWvZ:",
504518
long_options, &option_index)) != -1)
505519
{
506520
switch (c)
@@ -544,6 +558,16 @@ main(int argc, char **argv)
544558
case 'S':
545559
replication_slot = pg_strdup(optarg);
546560
break;
561+
case 'E':
562+
if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
563+
{
564+
fprintf(stderr,
565+
_("%s: could not parse end position \"%s\"\n"),
566+
progname, optarg);
567+
exit(1);
568+
}
569+
endpos = ((uint64) hi) << 32 | lo;
570+
break;
547571
case 'n':
548572
noloop = 1;
549573
break;
@@ -714,11 +738,11 @@ main(int argc, char **argv)
714738
while (true)
715739
{
716740
StreamLog();
717-
if (time_to_abort)
741+
if (time_to_stop)
718742
{
719743
/*
720-
* We've been Ctrl-C'ed. That's not an error, so exit without an
721-
* errorcode.
744+
* We've been Ctrl-C'ed or end of streaming position has been
745+
* willingly reached, so exit without an error code.
722746
*/
723747
exit(0);
724748
}
Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,51 @@
11
use strict;
22
use warnings;
33
use TestLib;
4-
use Test::More tests => 8;
4+
use PostgresNode;
5+
use Test::More tests => 14;
56

67
program_help_ok('pg_receivewal');
78
program_version_ok('pg_receivewal');
89
program_options_handling_ok('pg_receivewal');
10+
11+
my $primary = get_new_node('primary');
12+
$primary->init(allows_streaming => 1);
13+
$primary->start;
14+
15+
my $stream_dir = $primary->basedir . '/archive_wal';
16+
mkdir($stream_dir);
17+
18+
# Sanity checks for command line options.
19+
$primary->command_fails(['pg_receivewal'],
20+
'pg_receivewal needs target directory specified');
21+
$primary->command_fails(
22+
[ 'pg_receivewal', '-D', $stream_dir, '--create-slot', '--drop-slot' ],
23+
'failure if both --create-slot and --drop-slot specified');
24+
$primary->command_fails(
25+
[ 'pg_receivewal', '-D', $stream_dir, '--create-slot' ],
26+
'failure if --create-slot specified without --slot');
27+
28+
# Slot creation and drop
29+
my $slot_name = 'test';
30+
$primary->command_ok(
31+
[ 'pg_receivewal', '--slot', $slot_name, '--create-slot' ],
32+
'creating a replication slot');
33+
$primary->command_ok([ 'pg_receivewal', '--slot', $slot_name, '--drop-slot' ],
34+
'dropping a replication slot');
35+
36+
# Generate some WAL. Use --synchronous at the same time to add more
37+
# code coverage. Switch to the next segment first so that subsequent
38+
# restarts of pg_receivewal will see this segment as full..
39+
$primary->psql('postgres', 'CREATE TABLE test_table(x integer);');
40+
$primary->psql('postgres', 'SELECT pg_switch_wal();');
41+
my $nextlsn =
42+
$primary->safe_psql('postgres', 'SELECT pg_current_wal_insert_lsn();');
43+
chomp($nextlsn);
44+
$primary->psql('postgres',
45+
'INSERT INTO test_table VALUES (generate_series(1,100));');
46+
47+
# Stream up to the given position.
48+
$primary->command_ok(
49+
[ 'pg_receivewal', '-D', $stream_dir, '--verbose',
50+
'--endpos', $nextlsn, '--synchronous', '--no-loop' ],
51+
'streaming some WAL with --synchronous');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy