Skip to content

Commit e546989

Browse files
committed
Add 'no_error' argument to pg_wal_replay_wait()
This argument allow skipping throwing an error. Instead, the result status can be obtained using pg_wal_replay_wait_status() function. Catversion is bumped. Reported-by: Michael Paquier Discussion: https://postgr.es/m/ZtUF17gF0pNpwZDI%40paquier.xyz Reviewed-by: Pavel Borisov
1 parent 73da6b8 commit e546989

File tree

7 files changed

+118
-14
lines changed

7 files changed

+118
-14
lines changed

doc/src/sgml/func.sgml

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28989,12 +28989,15 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2898928989
</para>
2899028990

2899128991
<table id="recovery-synchronization-procedure-table">
28992-
<title>Recovery Synchronization Procedure</title>
28992+
<title>Recovery Synchronization Procedure and Function</title>
2899328993
<tgroup cols="1">
2899428994
<thead>
2899528995
<row>
2899628996
<entry role="func_table_entry"><para role="func_signature">
28997-
Procedure
28997+
Procedure or Function
28998+
</para>
28999+
<para>
29000+
Type
2899829001
</para>
2899929002
<para>
2900029003
Description
@@ -29010,8 +29013,11 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2901029013
</indexterm>
2901129014
<function>pg_wal_replay_wait</function> (
2901229015
<parameter>target_lsn</parameter> <type>pg_lsn</type>,
29013-
<parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal>)
29014-
<returnvalue>void</returnvalue>
29016+
<parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal>,
29017+
<parameter>no_error</parameter> <type>bool</type> <literal>DEFAULT</literal> <literal>false</literal>)
29018+
</para>
29019+
<para>
29020+
Procedure
2901529021
</para>
2901629022
<para>
2901729023
Waits until recovery replays <literal>target_lsn</literal>.
@@ -29022,7 +29028,30 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2902229028
procedure waits until <literal>target_lsn</literal> is reached or
2902329029
the specified <parameter>timeout</parameter> has elapsed.
2902429030
On timeout, or if the server is promoted before
29025-
<literal>target_lsn</literal> is reached, an error is emitted.
29031+
<literal>target_lsn</literal> is reached, an error is emitted,
29032+
as soon as <parameter>no_error</parameter> is false.
29033+
If <parameter>no_error</parameter> is set to true, then the procedure
29034+
doesn't throw errors. The last result status could be read
29035+
with <function>pg_wal_replay_wait_status</function>.
29036+
</para></entry>
29037+
</row>
29038+
29039+
<row>
29040+
<entry role="func_table_entry"><para role="func_signature">
29041+
<indexterm>
29042+
<primary>pg_wal_replay_wait_status</primary>
29043+
</indexterm>
29044+
<function>pg_wal_replay_wait_status</function> ()
29045+
<returnvalue>text</returnvalue>
29046+
</para>
29047+
<para>
29048+
Function
29049+
</para>
29050+
<para>
29051+
Returns the last result status for
29052+
<function>pg_wal_replay_wait</function> procedure. The possible
29053+
values are <literal>success</literal>, <literal>timeout</literal>,
29054+
and <literal>not in recovery</literal>.
2902629055
</para></entry>
2902729056
</row>
2902829057
</tbody>
@@ -29044,7 +29073,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2904429073
<para>
2904529074
<function>pg_wal_replay_wait</function> should be called on standby.
2904629075
If a user calls <function>pg_wal_replay_wait</function> on primary, it
29047-
will error out. However, if <function>pg_wal_replay_wait</function> is
29076+
will error out as soon as <parameter>no_error</parameter> is false.
29077+
However, if <function>pg_wal_replay_wait</function> is
2904829078
called on primary promoted from standby and <literal>target_lsn</literal>
2904929079
was already replayed, then <function>pg_wal_replay_wait</function> just
2905029080
exits immediately.
@@ -29090,6 +29120,20 @@ postgres=# CALL pg_wal_replay_wait('0/306EE20', 100);
2909029120
ERROR: timed out while waiting for target LSN 0/306EE20 to be replayed; current replay LSN 0/306EA60
2909129121
</programlisting>
2909229122

29123+
The same example uses <function>pg_wal_replay_wait</function> with
29124+
<parameter>no_error</parameter> set to true. In this case, the result
29125+
status must be read with <function>pg_wal_replay_wait_status</function>.
29126+
29127+
<programlisting>
29128+
postgres=# CALL pg_wal_replay_wait('0/306EE20', 100, true);
29129+
CALL
29130+
postgres=# SELECT pg_wal_replay_wait_status();
29131+
pg_wal_replay_wait_status
29132+
---------------------------
29133+
timeout
29134+
(1 row)
29135+
</programlisting>
29136+
2909329137
</para>
2909429138

2909529139
<para>

src/backend/access/transam/xlogfuncs.c

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,15 +751,18 @@ pg_promote(PG_FUNCTION_ARGS)
751751
PG_RETURN_BOOL(false);
752752
}
753753

754+
static WaitLSNResult lastWaitLSNResult = WAIT_LSN_RESULT_SUCCESS;
755+
754756
/*
755-
* Waits until recovery replays the target LSN with optional timeout.
757+
* Waits until recovery replays the target LSN with optional timeout. Unless
758+
* 'no_error' provided throws an error on failure
756759
*/
757760
Datum
758761
pg_wal_replay_wait(PG_FUNCTION_ARGS)
759762
{
760763
XLogRecPtr target_lsn = PG_GETARG_LSN(0);
761764
int64 timeout = PG_GETARG_INT64(1);
762-
WaitLSNResult result;
765+
bool no_error = PG_GETARG_BOOL(2);
763766

764767
if (timeout < 0)
765768
ereport(ERROR,
@@ -800,13 +803,16 @@ pg_wal_replay_wait(PG_FUNCTION_ARGS)
800803
*/
801804
Assert(MyProc->xmin == InvalidTransactionId);
802805

803-
result = WaitForLSNReplay(target_lsn, timeout);
806+
lastWaitLSNResult = WaitForLSNReplay(target_lsn, timeout);
807+
808+
if (no_error)
809+
PG_RETURN_VOID();
804810

805811
/*
806812
* Process the result of WaitForLSNReplay(). Throw appropriate error if
807813
* needed.
808814
*/
809-
switch (result)
815+
switch (lastWaitLSNResult)
810816
{
811817
case WAIT_LSN_RESULT_SUCCESS:
812818
/* Nothing to do on success */
@@ -832,3 +838,27 @@ pg_wal_replay_wait(PG_FUNCTION_ARGS)
832838

833839
PG_RETURN_VOID();
834840
}
841+
842+
Datum
843+
pg_wal_replay_wait_status(PG_FUNCTION_ARGS)
844+
{
845+
const char *result_string = "";
846+
847+
/* Process the result of WaitForLSNReplay(). */
848+
switch (lastWaitLSNResult)
849+
{
850+
case WAIT_LSN_RESULT_SUCCESS:
851+
result_string = "success";
852+
break;
853+
854+
case WAIT_LSN_RESULT_TIMEOUT:
855+
result_string = "timeout";
856+
break;
857+
858+
case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
859+
result_string = "not in recovery";
860+
break;
861+
}
862+
863+
PG_RETURN_TEXT_P(cstring_to_text(result_string));
864+
}

src/backend/access/transam/xlogwait.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
*
33
* xlogwait.c
44
* Implements waiting for the given replay LSN, which is used in
5-
* CALL pg_wal_replay_wait(target_lsn pg_lsn, timeout float8).
5+
* CALL pg_wal_replay_wait(target_lsn pg_lsn,
6+
* timeout float8, no_error bool).
67
*
78
* Copyright (c) 2024, PostgreSQL Global Development Group
89
*

src/backend/catalog/system_functions.sql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,9 @@ CREATE OR REPLACE FUNCTION
414414
json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
415415
RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100 AS 'json_populate_recordset' PARALLEL SAFE;
416416

417-
CREATE OR REPLACE PROCEDURE pg_wal_replay_wait(target_lsn pg_lsn, timeout int8 DEFAULT 0)
417+
CREATE OR REPLACE PROCEDURE pg_wal_replay_wait(target_lsn pg_lsn,
418+
timeout int8 DEFAULT 0,
419+
no_error bool DEFAULT false)
418420
LANGUAGE internal AS 'pg_wal_replay_wait';
419421

420422
CREATE OR REPLACE FUNCTION pg_logical_slot_get_changes(

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202410222
60+
#define CATALOG_VERSION_NO 202410241
6161

6262
#endif

src/include/catalog/pg_proc.dat

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6665,8 +6665,13 @@
66656665
{ oid => '8593',
66666666
descr => 'wait for the target LSN to be replayed on standby with an optional timeout',
66676667
proname => 'pg_wal_replay_wait', prokind => 'p', prorettype => 'void',
6668-
proargtypes => 'pg_lsn int8', proargnames => '{target_lsn,timeout}',
6668+
proargtypes => 'pg_lsn int8 bool', proargnames => '{target_lsn,timeout,no_error}',
66696669
prosrc => 'pg_wal_replay_wait' },
6670+
{ oid => '8594',
6671+
descr => 'the last result for pg_wal_replay_wait()',
6672+
proname => 'pg_wal_replay_wait_status', prorettype => 'text',
6673+
proargtypes => '',
6674+
prosrc => 'pg_wal_replay_wait_status' },
66706675

66716676
{ oid => '6224', descr => 'get resource managers loaded in system',
66726677
proname => 'pg_get_wal_resource_managers', prorows => '50', proretset => 't',

src/test/recovery/t/043_wal_replay_wait.pl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@
7777
ok( $stderr =~ /timed out while waiting for target LSN/,
7878
"get timeout on waiting for unreachable LSN");
7979

80+
$output = $node_standby->safe_psql(
81+
'postgres', qq[
82+
CALL pg_wal_replay_wait('${lsn2}', 10, true);
83+
SELECT pg_wal_replay_wait_status();]);
84+
ok( $output eq "success",
85+
"pg_wal_replay_wait_status() returns correct status after successful waiting"
86+
);
87+
$output = $node_standby->safe_psql(
88+
'postgres', qq[
89+
CALL pg_wal_replay_wait('${lsn3}', 10, true);
90+
SELECT pg_wal_replay_wait_status();]);
91+
ok($output eq "timeout",
92+
"pg_wal_replay_wait_status() returns correct status after timeout");
93+
8094
# 4. Check that pg_wal_replay_wait() triggers an error if called on primary,
8195
# within another function, or inside a transaction with an isolation level
8296
# higher than READ COMMITTED.
@@ -193,6 +207,14 @@
193207

194208
ok(1, 'wait for already replayed LSN exits immediately even after promotion');
195209

210+
$output = $node_standby->safe_psql(
211+
'postgres', qq[
212+
CALL pg_wal_replay_wait('${lsn4}', 10, true);
213+
SELECT pg_wal_replay_wait_status();]);
214+
ok( $output eq "not in recovery",
215+
"pg_wal_replay_wait_status() returns correct status after standby promotion"
216+
);
217+
196218
$node_standby->stop;
197219
$node_primary->stop;
198220

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