1# Copyright (c) 2021, PostgreSQL Global Development Group 2 3# Tests for already-propagated WAL segments ending in incomplete WAL records. 4 5use strict; 6use warnings; 7 8use FindBin; 9use PostgresNode; 10use TestLib; 11use Test::More; 12 13plan tests => 3; 14 15# Test: Create a physical replica that's missing the last WAL file, 16# then restart the primary to create a divergent WAL file and observe 17# that the replica replays the "overwrite contrecord" from that new 18# file. 19 20my $node = PostgresNode->get_new_node('primary'); 21$node->init(allows_streaming => 1); 22$node->append_conf('postgresql.conf', 'wal_keep_segments=16'); 23$node->start; 24 25$node->safe_psql('postgres', 'create table filler (a int, b text)'); 26 27# Now consume all remaining room in the current WAL segment, leaving 28# space enough only for the start of a largish record. 29$node->safe_psql( 30 'postgres', q{ 31DO $$ 32DECLARE 33 wal_segsize int := setting::int FROM pg_settings WHERE name = 'wal_segment_size'; 34 remain int; 35 iters int := 0; 36BEGIN 37 LOOP 38 INSERT into filler 39 select g, repeat(md5(g::text), (random() * 60 + 1)::int) 40 from generate_series(1, 10) g; 41 42 remain := wal_segsize - (pg_current_wal_insert_lsn() - '0/0') % wal_segsize; 43 IF remain < 2 * setting::int from pg_settings where name = 'block_size' THEN 44 RAISE log 'exiting after % iterations, % bytes to end of WAL segment', iters, remain; 45 EXIT; 46 END IF; 47 iters := iters + 1; 48 END LOOP; 49END 50$$; 51}); 52 53my $initfile = $node->safe_psql('postgres', 54 'SELECT pg_walfile_name(pg_current_wal_insert_lsn())'); 55$node->safe_psql('postgres', 56 qq{SELECT pg_logical_emit_message(true, 'test 026', repeat('xyzxz', 123456))} 57); 58#$node->safe_psql('postgres', qq{create table foo ()}); 59my $endfile = $node->safe_psql('postgres', 60 'SELECT pg_walfile_name(pg_current_wal_insert_lsn())'); 61ok($initfile != $endfile, "$initfile differs from $endfile"); 62 63# Now stop abruptly, to avoid a stop checkpoint. We can remove the tail file 64# afterwards, and on startup the large message should be overwritten with new 65# contents 66$node->stop('immediate'); 67 68unlink $node->basedir . "/pgdata/pg_wal/$endfile" 69 or die "could not unlink " . $node->basedir . "/pgdata/pg_wal/$endfile: $!"; 70 71# OK, create a standby at this spot. 72$node->backup_fs_cold('backup'); 73my $node_standby = PostgresNode->get_new_node('standby'); 74$node_standby->init_from_backup($node, 'backup', has_streaming => 1); 75 76$node_standby->start; 77$node->start; 78 79$node->safe_psql('postgres', 80 qq{create table foo (a text); insert into foo values ('hello')}); 81$node->safe_psql('postgres', 82 qq{SELECT pg_logical_emit_message(true, 'test 026', 'AABBCC')}); 83 84my $until_lsn = $node->safe_psql('postgres', "SELECT pg_current_wal_lsn()"); 85my $caughtup_query = 86 "SELECT '$until_lsn'::pg_lsn <= pg_last_wal_replay_lsn()"; 87$node_standby->poll_query_until('postgres', $caughtup_query) 88 or die "Timed out while waiting for standby to catch up"; 89 90ok($node_standby->safe_psql('postgres', 'select * from foo') eq 'hello', 91 'standby replays past overwritten contrecord'); 92 93# Verify message appears in standby's log 94my $log = slurp_file($node_standby->logfile); 95like( 96 $log, 97 qr[successfully skipped missing contrecord at], 98 "found log line in standby"); 99 100$node->stop; 101$node_standby->stop; 102