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
This commit is contained in:
parent
73da6b8d1b
commit
e546989a26
@ -28989,12 +28989,15 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<table id="recovery-synchronization-procedure-table">
|
<table id="recovery-synchronization-procedure-table">
|
||||||
<title>Recovery Synchronization Procedure</title>
|
<title>Recovery Synchronization Procedure and Function</title>
|
||||||
<tgroup cols="1">
|
<tgroup cols="1">
|
||||||
<thead>
|
<thead>
|
||||||
<row>
|
<row>
|
||||||
<entry role="func_table_entry"><para role="func_signature">
|
<entry role="func_table_entry"><para role="func_signature">
|
||||||
Procedure
|
Procedure or Function
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Type
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Description
|
Description
|
||||||
@ -29010,8 +29013,11 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
|||||||
</indexterm>
|
</indexterm>
|
||||||
<function>pg_wal_replay_wait</function> (
|
<function>pg_wal_replay_wait</function> (
|
||||||
<parameter>target_lsn</parameter> <type>pg_lsn</type>,
|
<parameter>target_lsn</parameter> <type>pg_lsn</type>,
|
||||||
<parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal>)
|
<parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal>,
|
||||||
<returnvalue>void</returnvalue>
|
<parameter>no_error</parameter> <type>bool</type> <literal>DEFAULT</literal> <literal>false</literal>)
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Procedure
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Waits until recovery replays <literal>target_lsn</literal>.
|
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
|
|||||||
procedure waits until <literal>target_lsn</literal> is reached or
|
procedure waits until <literal>target_lsn</literal> is reached or
|
||||||
the specified <parameter>timeout</parameter> has elapsed.
|
the specified <parameter>timeout</parameter> has elapsed.
|
||||||
On timeout, or if the server is promoted before
|
On timeout, or if the server is promoted before
|
||||||
<literal>target_lsn</literal> is reached, an error is emitted.
|
<literal>target_lsn</literal> is reached, an error is emitted,
|
||||||
|
as soon as <parameter>no_error</parameter> is false.
|
||||||
|
If <parameter>no_error</parameter> is set to true, then the procedure
|
||||||
|
doesn't throw errors. The last result status could be read
|
||||||
|
with <function>pg_wal_replay_wait_status</function>.
|
||||||
|
</para></entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry role="func_table_entry"><para role="func_signature">
|
||||||
|
<indexterm>
|
||||||
|
<primary>pg_wal_replay_wait_status</primary>
|
||||||
|
</indexterm>
|
||||||
|
<function>pg_wal_replay_wait_status</function> ()
|
||||||
|
<returnvalue>text</returnvalue>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Function
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Returns the last result status for
|
||||||
|
<function>pg_wal_replay_wait</function> procedure. The possible
|
||||||
|
values are <literal>success</literal>, <literal>timeout</literal>,
|
||||||
|
and <literal>not in recovery</literal>.
|
||||||
</para></entry>
|
</para></entry>
|
||||||
</row>
|
</row>
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -29044,7 +29073,8 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
|||||||
<para>
|
<para>
|
||||||
<function>pg_wal_replay_wait</function> should be called on standby.
|
<function>pg_wal_replay_wait</function> should be called on standby.
|
||||||
If a user calls <function>pg_wal_replay_wait</function> on primary, it
|
If a user calls <function>pg_wal_replay_wait</function> on primary, it
|
||||||
will error out. However, if <function>pg_wal_replay_wait</function> is
|
will error out as soon as <parameter>no_error</parameter> is false.
|
||||||
|
However, if <function>pg_wal_replay_wait</function> is
|
||||||
called on primary promoted from standby and <literal>target_lsn</literal>
|
called on primary promoted from standby and <literal>target_lsn</literal>
|
||||||
was already replayed, then <function>pg_wal_replay_wait</function> just
|
was already replayed, then <function>pg_wal_replay_wait</function> just
|
||||||
exits immediately.
|
exits immediately.
|
||||||
@ -29090,6 +29120,20 @@ postgres=# CALL pg_wal_replay_wait('0/306EE20', 100);
|
|||||||
ERROR: timed out while waiting for target LSN 0/306EE20 to be replayed; current replay LSN 0/306EA60
|
ERROR: timed out while waiting for target LSN 0/306EE20 to be replayed; current replay LSN 0/306EA60
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
|
The same example uses <function>pg_wal_replay_wait</function> with
|
||||||
|
<parameter>no_error</parameter> set to true. In this case, the result
|
||||||
|
status must be read with <function>pg_wal_replay_wait_status</function>.
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
postgres=# CALL pg_wal_replay_wait('0/306EE20', 100, true);
|
||||||
|
CALL
|
||||||
|
postgres=# SELECT pg_wal_replay_wait_status();
|
||||||
|
pg_wal_replay_wait_status
|
||||||
|
---------------------------
|
||||||
|
timeout
|
||||||
|
(1 row)
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -751,15 +751,18 @@ pg_promote(PG_FUNCTION_ARGS)
|
|||||||
PG_RETURN_BOOL(false);
|
PG_RETURN_BOOL(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static WaitLSNResult lastWaitLSNResult = WAIT_LSN_RESULT_SUCCESS;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Waits until recovery replays the target LSN with optional timeout.
|
* Waits until recovery replays the target LSN with optional timeout. Unless
|
||||||
|
* 'no_error' provided throws an error on failure
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
pg_wal_replay_wait(PG_FUNCTION_ARGS)
|
pg_wal_replay_wait(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
XLogRecPtr target_lsn = PG_GETARG_LSN(0);
|
XLogRecPtr target_lsn = PG_GETARG_LSN(0);
|
||||||
int64 timeout = PG_GETARG_INT64(1);
|
int64 timeout = PG_GETARG_INT64(1);
|
||||||
WaitLSNResult result;
|
bool no_error = PG_GETARG_BOOL(2);
|
||||||
|
|
||||||
if (timeout < 0)
|
if (timeout < 0)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
@ -800,13 +803,16 @@ pg_wal_replay_wait(PG_FUNCTION_ARGS)
|
|||||||
*/
|
*/
|
||||||
Assert(MyProc->xmin == InvalidTransactionId);
|
Assert(MyProc->xmin == InvalidTransactionId);
|
||||||
|
|
||||||
result = WaitForLSNReplay(target_lsn, timeout);
|
lastWaitLSNResult = WaitForLSNReplay(target_lsn, timeout);
|
||||||
|
|
||||||
|
if (no_error)
|
||||||
|
PG_RETURN_VOID();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process the result of WaitForLSNReplay(). Throw appropriate error if
|
* Process the result of WaitForLSNReplay(). Throw appropriate error if
|
||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
switch (result)
|
switch (lastWaitLSNResult)
|
||||||
{
|
{
|
||||||
case WAIT_LSN_RESULT_SUCCESS:
|
case WAIT_LSN_RESULT_SUCCESS:
|
||||||
/* Nothing to do on success */
|
/* Nothing to do on success */
|
||||||
@ -832,3 +838,27 @@ pg_wal_replay_wait(PG_FUNCTION_ARGS)
|
|||||||
|
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
pg_wal_replay_wait_status(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
const char *result_string = "";
|
||||||
|
|
||||||
|
/* Process the result of WaitForLSNReplay(). */
|
||||||
|
switch (lastWaitLSNResult)
|
||||||
|
{
|
||||||
|
case WAIT_LSN_RESULT_SUCCESS:
|
||||||
|
result_string = "success";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAIT_LSN_RESULT_TIMEOUT:
|
||||||
|
result_string = "timeout";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
|
||||||
|
result_string = "not in recovery";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_TEXT_P(cstring_to_text(result_string));
|
||||||
|
}
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
*
|
*
|
||||||
* xlogwait.c
|
* xlogwait.c
|
||||||
* Implements waiting for the given replay LSN, which is used in
|
* Implements waiting for the given replay LSN, which is used in
|
||||||
* CALL pg_wal_replay_wait(target_lsn pg_lsn, timeout float8).
|
* CALL pg_wal_replay_wait(target_lsn pg_lsn,
|
||||||
|
* timeout float8, no_error bool).
|
||||||
*
|
*
|
||||||
* Copyright (c) 2024, PostgreSQL Global Development Group
|
* Copyright (c) 2024, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
|
@ -414,7 +414,9 @@ CREATE OR REPLACE FUNCTION
|
|||||||
json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
|
json_populate_recordset(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)
|
||||||
RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100 AS 'json_populate_recordset' PARALLEL SAFE;
|
RETURNS SETOF anyelement LANGUAGE internal STABLE ROWS 100 AS 'json_populate_recordset' PARALLEL SAFE;
|
||||||
|
|
||||||
CREATE OR REPLACE PROCEDURE pg_wal_replay_wait(target_lsn pg_lsn, timeout int8 DEFAULT 0)
|
CREATE OR REPLACE PROCEDURE pg_wal_replay_wait(target_lsn pg_lsn,
|
||||||
|
timeout int8 DEFAULT 0,
|
||||||
|
no_error bool DEFAULT false)
|
||||||
LANGUAGE internal AS 'pg_wal_replay_wait';
|
LANGUAGE internal AS 'pg_wal_replay_wait';
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION pg_logical_slot_get_changes(
|
CREATE OR REPLACE FUNCTION pg_logical_slot_get_changes(
|
||||||
|
@ -57,6 +57,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 202410222
|
#define CATALOG_VERSION_NO 202410241
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -6665,8 +6665,13 @@
|
|||||||
{ oid => '8593',
|
{ oid => '8593',
|
||||||
descr => 'wait for the target LSN to be replayed on standby with an optional timeout',
|
descr => 'wait for the target LSN to be replayed on standby with an optional timeout',
|
||||||
proname => 'pg_wal_replay_wait', prokind => 'p', prorettype => 'void',
|
proname => 'pg_wal_replay_wait', prokind => 'p', prorettype => 'void',
|
||||||
proargtypes => 'pg_lsn int8', proargnames => '{target_lsn,timeout}',
|
proargtypes => 'pg_lsn int8 bool', proargnames => '{target_lsn,timeout,no_error}',
|
||||||
prosrc => 'pg_wal_replay_wait' },
|
prosrc => 'pg_wal_replay_wait' },
|
||||||
|
{ oid => '8594',
|
||||||
|
descr => 'the last result for pg_wal_replay_wait()',
|
||||||
|
proname => 'pg_wal_replay_wait_status', prorettype => 'text',
|
||||||
|
proargtypes => '',
|
||||||
|
prosrc => 'pg_wal_replay_wait_status' },
|
||||||
|
|
||||||
{ oid => '6224', descr => 'get resource managers loaded in system',
|
{ oid => '6224', descr => 'get resource managers loaded in system',
|
||||||
proname => 'pg_get_wal_resource_managers', prorows => '50', proretset => 't',
|
proname => 'pg_get_wal_resource_managers', prorows => '50', proretset => 't',
|
||||||
|
@ -77,6 +77,20 @@ $node_standby->psql(
|
|||||||
ok( $stderr =~ /timed out while waiting for target LSN/,
|
ok( $stderr =~ /timed out while waiting for target LSN/,
|
||||||
"get timeout on waiting for unreachable LSN");
|
"get timeout on waiting for unreachable LSN");
|
||||||
|
|
||||||
|
$output = $node_standby->safe_psql(
|
||||||
|
'postgres', qq[
|
||||||
|
CALL pg_wal_replay_wait('${lsn2}', 10, true);
|
||||||
|
SELECT pg_wal_replay_wait_status();]);
|
||||||
|
ok( $output eq "success",
|
||||||
|
"pg_wal_replay_wait_status() returns correct status after successful waiting"
|
||||||
|
);
|
||||||
|
$output = $node_standby->safe_psql(
|
||||||
|
'postgres', qq[
|
||||||
|
CALL pg_wal_replay_wait('${lsn3}', 10, true);
|
||||||
|
SELECT pg_wal_replay_wait_status();]);
|
||||||
|
ok($output eq "timeout",
|
||||||
|
"pg_wal_replay_wait_status() returns correct status after timeout");
|
||||||
|
|
||||||
# 4. Check that pg_wal_replay_wait() triggers an error if called on primary,
|
# 4. Check that pg_wal_replay_wait() triggers an error if called on primary,
|
||||||
# within another function, or inside a transaction with an isolation level
|
# within another function, or inside a transaction with an isolation level
|
||||||
# higher than READ COMMITTED.
|
# higher than READ COMMITTED.
|
||||||
@ -193,6 +207,14 @@ $node_standby->safe_psql('postgres', "CALL pg_wal_replay_wait('${lsn5}');");
|
|||||||
|
|
||||||
ok(1, 'wait for already replayed LSN exits immediately even after promotion');
|
ok(1, 'wait for already replayed LSN exits immediately even after promotion');
|
||||||
|
|
||||||
|
$output = $node_standby->safe_psql(
|
||||||
|
'postgres', qq[
|
||||||
|
CALL pg_wal_replay_wait('${lsn4}', 10, true);
|
||||||
|
SELECT pg_wal_replay_wait_status();]);
|
||||||
|
ok( $output eq "not in recovery",
|
||||||
|
"pg_wal_replay_wait_status() returns correct status after standby promotion"
|
||||||
|
);
|
||||||
|
|
||||||
$node_standby->stop;
|
$node_standby->stop;
|
||||||
$node_primary->stop;
|
$node_primary->stop;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user