@@ -2122,6 +2122,154 @@ sub lsn
2122
2122
2123
2123
=pod
2124
2124
2125
+ =item $node->write_wal($tli, $lsn, $segment_size, $data)
2126
+
2127
+ Write some arbitrary data in WAL for the given segment at $lsn (in bytes).
2128
+ This should be called while the cluster is not running.
2129
+
2130
+ Returns the path of the WAL segment written to.
2131
+
2132
+ =cut
2133
+
2134
+ sub write_wal
2135
+ {
2136
+ my ($self , $tli , $lsn , $segment_size , $data ) = @_ ;
2137
+
2138
+ # Calculate segment number and offset position in segment based on the
2139
+ # input LSN.
2140
+ my $segment = $lsn / $segment_size ;
2141
+ my $offset = $lsn % $segment_size ;
2142
+ my $path =
2143
+ sprintf (" %s /pg_wal/%08X%08X%08X" , $self -> data_dir, $tli , 0, $segment );
2144
+
2145
+ open my $fh , " +<:raw" , $path or die " could not open WAL segment $path " ;
2146
+ seek ($fh , $offset , SEEK_SET) or die " could not seek WAL segment $path " ;
2147
+ print $fh $data ;
2148
+ close $fh ;
2149
+
2150
+ return $path ;
2151
+ }
2152
+
2153
+ =pod
2154
+
2155
+ =item $node->emit_wal($size)
2156
+
2157
+ Emit a WAL record of arbitrary size, using pg_logical_emit_message().
2158
+
2159
+ Returns the end LSN of the record inserted, in bytes.
2160
+
2161
+ =cut
2162
+
2163
+ sub emit_wal
2164
+ {
2165
+ my ($self , $size ) = @_ ;
2166
+
2167
+ return int (
2168
+ $self -> safe_psql(
2169
+ ' postgres' ,
2170
+ " SELECT pg_logical_emit_message(true, '', repeat('a', $size )) - '0/0'"
2171
+ ));
2172
+ }
2173
+
2174
+
2175
+ # Private routine returning the current insert LSN of a node, in bytes.
2176
+ # Used by the routines below in charge of advancing WAL to arbitrary
2177
+ # positions. The insert LSN is returned in bytes.
2178
+ sub _get_insert_lsn
2179
+ {
2180
+ my ($self ) = @_ ;
2181
+ return int (
2182
+ $self -> safe_psql(
2183
+ ' postgres' , " SELECT pg_current_wal_insert_lsn() - '0/0'" ));
2184
+ }
2185
+
2186
+ =pod
2187
+
2188
+ =item $node->advance_wal_out_of_record_splitting_zone($wal_block_size)
2189
+
2190
+ Advance WAL at the end of a page, making sure that we are far away enough
2191
+ from the end of a page that we could insert a couple of small records.
2192
+
2193
+ This inserts a few records of a fixed size, until the threshold gets close
2194
+ enough to the end of the WAL page inserting records to.
2195
+
2196
+ Returns the end LSN up to which WAL has advanced, in bytes.
2197
+
2198
+ =cut
2199
+
2200
+ sub advance_wal_out_of_record_splitting_zone
2201
+ {
2202
+ my ($self , $wal_block_size ) = @_ ;
2203
+
2204
+ my $page_threshold = $wal_block_size / 4;
2205
+ my $end_lsn = $self -> _get_insert_lsn();
2206
+ my $page_offset = $end_lsn % $wal_block_size ;
2207
+ while ($page_offset >= $wal_block_size - $page_threshold )
2208
+ {
2209
+ $self -> emit_wal($page_threshold );
2210
+ $end_lsn = $self -> _get_insert_lsn();
2211
+ $page_offset = $end_lsn % $wal_block_size ;
2212
+ }
2213
+ return $end_lsn ;
2214
+ }
2215
+
2216
+ =pod
2217
+
2218
+ =item $node->advance_wal_to_record_splitting_zone($wal_block_size)
2219
+
2220
+ Advance WAL so close to the end of a page that an XLogRecordHeader would not
2221
+ fit on it.
2222
+
2223
+ Returns the end LSN up to which WAL has advanced, in bytes.
2224
+
2225
+ =cut
2226
+
2227
+ sub advance_wal_to_record_splitting_zone
2228
+ {
2229
+ my ($self , $wal_block_size ) = @_ ;
2230
+
2231
+ # Size of record header.
2232
+ my $RECORD_HEADER_SIZE = 24;
2233
+
2234
+ my $end_lsn = $self -> _get_insert_lsn();
2235
+ my $page_offset = $end_lsn % $wal_block_size ;
2236
+
2237
+ # Get fairly close to the end of a page in big steps
2238
+ while ($page_offset <= $wal_block_size - 512)
2239
+ {
2240
+ $self -> emit_wal($wal_block_size - $page_offset - 256);
2241
+ $end_lsn = $self -> _get_insert_lsn();
2242
+ $page_offset = $end_lsn % $wal_block_size ;
2243
+ }
2244
+
2245
+ # Calibrate our message size so that we can get closer 8 bytes at
2246
+ # a time.
2247
+ my $message_size = $wal_block_size - 80;
2248
+ while ($page_offset <= $wal_block_size - $RECORD_HEADER_SIZE )
2249
+ {
2250
+ $self -> emit_wal($message_size );
2251
+ $end_lsn = $self -> _get_insert_lsn();
2252
+
2253
+ my $old_offset = $page_offset ;
2254
+ $page_offset = $end_lsn % $wal_block_size ;
2255
+
2256
+ # Adjust the message size until it causes 8 bytes changes in
2257
+ # offset, enough to be able to split a record header.
2258
+ my $delta = $page_offset - $old_offset ;
2259
+ if ($delta > 8)
2260
+ {
2261
+ $message_size -= 8;
2262
+ }
2263
+ elsif ($delta <= 0)
2264
+ {
2265
+ $message_size += 8;
2266
+ }
2267
+ }
2268
+ return $end_lsn ;
2269
+ }
2270
+
2271
+ =pod
2272
+
2125
2273
=item $node->wait_for_catchup(standby_name, mode, target_lsn)
2126
2274
2127
2275
Wait for the node with application_name standby_name (usually from node->name,
0 commit comments