Skip to content

Commit 3a7ae6b

Browse files
committed
Revert pg_wal_replay_wait() stored procedure
This commit reverts 3c5db1d, and subsequent improvements and fixes including 8036d73, 867d396, 3ac3ec5, 0868d7a, 85b98b8, 2520226, 014f9f3, e658038, e155564, 5035172, 6cfebfe, 73da6b8, and e546989. The reason for reverting is a set of remaining issues. Most notably, the stored procedure appears to need more effort than the utility statement to turn the backend into a "snapshot-less" state. This makes an approach to use stored procedures questionable. Catversion is bumped. Discussion: https://postgr.es/m/Zyhj2anOPRKtb0xW%40paquier.xyz
1 parent 3293b71 commit 3a7ae6b

File tree

22 files changed

+9
-1020
lines changed

22 files changed

+9
-1020
lines changed

doc/src/sgml/func.sgml

Lines changed: 0 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -29000,176 +29000,6 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2900029000
the pause, the rate of WAL generation and available disk space.
2900129001
</para>
2900229002

29003-
<para>
29004-
The procedure shown in <xref linkend="recovery-synchronization-procedure-table"/>
29005-
can be executed only during recovery.
29006-
</para>
29007-
29008-
<table id="recovery-synchronization-procedure-table">
29009-
<title>Recovery Synchronization Procedure and Function</title>
29010-
<tgroup cols="1">
29011-
<thead>
29012-
<row>
29013-
<entry role="func_table_entry"><para role="func_signature">
29014-
Procedure or Function
29015-
</para>
29016-
<para>
29017-
Type
29018-
</para>
29019-
<para>
29020-
Description
29021-
</para></entry>
29022-
</row>
29023-
</thead>
29024-
29025-
<tbody>
29026-
<row>
29027-
<entry role="func_table_entry"><para role="func_signature">
29028-
<indexterm>
29029-
<primary>pg_wal_replay_wait</primary>
29030-
</indexterm>
29031-
<function>pg_wal_replay_wait</function> (
29032-
<parameter>target_lsn</parameter> <type>pg_lsn</type>,
29033-
<parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal>,
29034-
<parameter>no_error</parameter> <type>bool</type> <literal>DEFAULT</literal> <literal>false</literal>)
29035-
</para>
29036-
<para>
29037-
Procedure
29038-
</para>
29039-
<para>
29040-
Waits until recovery replays <literal>target_lsn</literal>.
29041-
If no <parameter>timeout</parameter> is specified or it is set to
29042-
zero, this procedure waits indefinitely for the
29043-
<literal>target_lsn</literal>. If the <parameter>timeout</parameter>
29044-
is specified (in milliseconds) and is greater than zero, the
29045-
procedure waits until <literal>target_lsn</literal> is reached or
29046-
the specified <parameter>timeout</parameter> has elapsed.
29047-
On timeout, or if the server is promoted before
29048-
<literal>target_lsn</literal> is reached, an error is emitted,
29049-
as soon as <parameter>no_error</parameter> is false.
29050-
If <parameter>no_error</parameter> is set to true, then the procedure
29051-
doesn't throw errors. The last result status could be read
29052-
with <function>pg_wal_replay_wait_status</function>.
29053-
</para></entry>
29054-
</row>
29055-
29056-
<row>
29057-
<entry role="func_table_entry"><para role="func_signature">
29058-
<indexterm>
29059-
<primary>pg_wal_replay_wait_status</primary>
29060-
</indexterm>
29061-
<function>pg_wal_replay_wait_status</function> ()
29062-
<returnvalue>text</returnvalue>
29063-
</para>
29064-
<para>
29065-
Function
29066-
</para>
29067-
<para>
29068-
Returns the last result status for
29069-
<function>pg_wal_replay_wait</function> procedure. The possible
29070-
values are <literal>success</literal>, <literal>timeout</literal>,
29071-
and <literal>not in recovery</literal>.
29072-
</para></entry>
29073-
</row>
29074-
</tbody>
29075-
</tgroup>
29076-
</table>
29077-
29078-
<para>
29079-
<function>pg_wal_replay_wait</function> waits till
29080-
<parameter>target_lsn</parameter> to be replayed on standby.
29081-
That is, after this function execution, the value returned by
29082-
<function>pg_last_wal_replay_lsn</function> should be greater or equal
29083-
to the <parameter>target_lsn</parameter> value. This is useful to achieve
29084-
read-your-writes-consistency, while using async replica for reads and
29085-
primary for writes. In that case <acronym>lsn</acronym> of the last
29086-
modification should be stored on the client application side or the
29087-
connection pooler side.
29088-
</para>
29089-
29090-
<para>
29091-
<function>pg_wal_replay_wait</function> should be called on standby.
29092-
If a user calls <function>pg_wal_replay_wait</function> on primary, it
29093-
will error out as soon as <parameter>no_error</parameter> is false.
29094-
However, if <function>pg_wal_replay_wait</function> is
29095-
called on primary promoted from standby and <literal>target_lsn</literal>
29096-
was already replayed, then <function>pg_wal_replay_wait</function> just
29097-
exits immediately.
29098-
</para>
29099-
29100-
<para>
29101-
You can use <function>pg_wal_replay_wait</function> to wait for
29102-
the <type>pg_lsn</type> value. For example, an application could update
29103-
the <literal>movie</literal> table and get the <acronym>lsn</acronym> after
29104-
changes just made. This example uses <function>pg_current_wal_insert_lsn</function>
29105-
on primary server to get the <acronym>lsn</acronym> given that
29106-
<varname>synchronous_commit</varname> could be set to
29107-
<literal>off</literal>.
29108-
29109-
<programlisting>
29110-
postgres=# UPDATE movie SET genre = 'Dramatic' WHERE genre = 'Drama';
29111-
UPDATE 100
29112-
postgres=# SELECT pg_current_wal_insert_lsn();
29113-
pg_current_wal_insert_lsn
29114-
--------------------
29115-
0/306EE20
29116-
(1 row)
29117-
</programlisting>
29118-
29119-
Then an application could run <function>pg_wal_replay_wait</function>
29120-
with the <acronym>lsn</acronym> obtained from primary. After that the
29121-
changes made on primary should be guaranteed to be visible on replica.
29122-
29123-
<programlisting>
29124-
postgres=# CALL pg_wal_replay_wait('0/306EE20');
29125-
CALL
29126-
postgres=# SELECT * FROM movie WHERE genre = 'Drama';
29127-
genre
29128-
-------
29129-
(0 rows)
29130-
</programlisting>
29131-
29132-
It may also happen that target <acronym>lsn</acronym> is not reached
29133-
within the timeout. In that case the error is thrown.
29134-
29135-
<programlisting>
29136-
postgres=# CALL pg_wal_replay_wait('0/306EE20', 100);
29137-
ERROR: timed out while waiting for target LSN 0/306EE20 to be replayed; current replay LSN 0/306EA60
29138-
</programlisting>
29139-
29140-
The same example uses <function>pg_wal_replay_wait</function> with
29141-
<parameter>no_error</parameter> set to true. In this case, the result
29142-
status must be read with <function>pg_wal_replay_wait_status</function>.
29143-
29144-
<programlisting>
29145-
postgres=# CALL pg_wal_replay_wait('0/306EE20', 100, true);
29146-
CALL
29147-
postgres=# SELECT pg_wal_replay_wait_status();
29148-
pg_wal_replay_wait_status
29149-
---------------------------
29150-
timeout
29151-
(1 row)
29152-
</programlisting>
29153-
29154-
</para>
29155-
29156-
<para>
29157-
<function>pg_wal_replay_wait</function> can't be used within
29158-
a transaction with an isolation level higher than
29159-
<literal>READ COMMITTED</literal>, another procedure, or a function.
29160-
All the cases above imply holding a snapshot, which could prevent
29161-
WAL records from replaying (see <xref linkend="hot-standby-conflict"/>)
29162-
and cause an indirect deadlock.
29163-
29164-
<programlisting>
29165-
postgres=# BEGIN;
29166-
BEGIN
29167-
postgres=*# CALL pg_wal_replay_wait('0/306EE20');
29168-
ERROR: pg_wal_replay_wait() must be only called without an active or registered snapshot
29169-
DETAIL: Make sure pg_wal_replay_wait() isn't called within a transaction with an isolation level higher than READ COMMITTED, another procedure, or a function.
29170-
</programlisting>
29171-
29172-
</para>
2917329003
</sect2>
2917429004

2917529005
<sect2 id="functions-snapshot-synchronization">

src/backend/access/transam/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ OBJS = \
3636
xlogreader.o \
3737
xlogrecovery.o \
3838
xlogstats.o \
39-
xlogutils.o \
40-
xlogwait.o
39+
xlogutils.o
4140

4241
include $(top_srcdir)/src/backend/common.mk
4342

src/backend/access/transam/meson.build

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ backend_sources += files(
2424
'xlogrecovery.c',
2525
'xlogstats.c',
2626
'xlogutils.c',
27-
'xlogwait.c',
2827
)
2928

3029
# used by frontend programs to build a frontend xlogreader

src/backend/access/transam/xact.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#include "access/xloginsert.h"
3232
#include "access/xlogrecovery.h"
3333
#include "access/xlogutils.h"
34-
#include "access/xlogwait.h"
3534
#include "catalog/index.h"
3635
#include "catalog/namespace.h"
3736
#include "catalog/pg_enum.h"
@@ -2827,11 +2826,6 @@ AbortTransaction(void)
28272826
*/
28282827
LWLockReleaseAll();
28292828

2830-
/*
2831-
* Cleanup waiting for LSN if any.
2832-
*/
2833-
WaitLSNCleanup();
2834-
28352829
/* Clear wait information and command progress indicator */
28362830
pgstat_report_wait_end();
28372831
pgstat_progress_end_command();

src/backend/access/transam/xlog.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
#include "access/xlogreader.h"
6363
#include "access/xlogrecovery.h"
6464
#include "access/xlogutils.h"
65-
#include "access/xlogwait.h"
6665
#include "backup/basebackup.h"
6766
#include "catalog/catversion.h"
6867
#include "catalog/pg_control.h"
@@ -6174,12 +6173,6 @@ StartupXLOG(void)
61746173
UpdateControlFile();
61756174
LWLockRelease(ControlFileLock);
61766175

6177-
/*
6178-
* Wake up all waiters for replay LSN. They need to report an error that
6179-
* recovery was ended before reaching the target LSN.
6180-
*/
6181-
WaitLSNWakeup(InvalidXLogRecPtr);
6182-
61836176
/*
61846177
* Shutdown the recovery environment. This must occur after
61856178
* RecoverPreparedTransactions() (see notes in lock_twophase_recover())

src/backend/access/transam/xlogfuncs.c

Lines changed: 1 addition & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,17 @@
2222
#include "access/xlog_internal.h"
2323
#include "access/xlogbackup.h"
2424
#include "access/xlogrecovery.h"
25-
#include "access/xlogwait.h"
2625
#include "catalog/pg_type.h"
2726
#include "funcapi.h"
2827
#include "miscadmin.h"
2928
#include "pgstat.h"
3029
#include "replication/walreceiver.h"
3130
#include "storage/fd.h"
32-
#include "storage/proc.h"
31+
#include "storage/latch.h"
3332
#include "storage/standby.h"
3433
#include "utils/builtins.h"
3534
#include "utils/memutils.h"
3635
#include "utils/pg_lsn.h"
37-
#include "utils/snapmgr.h"
3836
#include "utils/timestamp.h"
3937

4038
/*
@@ -750,115 +748,3 @@ pg_promote(PG_FUNCTION_ARGS)
750748
wait_seconds)));
751749
PG_RETURN_BOOL(false);
752750
}
753-
754-
static WaitLSNResult lastWaitLSNResult = WAIT_LSN_RESULT_SUCCESS;
755-
756-
/*
757-
* Waits until recovery replays the target LSN with optional timeout. Unless
758-
* 'no_error' provided throws an error on failure
759-
*/
760-
Datum
761-
pg_wal_replay_wait(PG_FUNCTION_ARGS)
762-
{
763-
XLogRecPtr target_lsn = PG_GETARG_LSN(0);
764-
int64 timeout = PG_GETARG_INT64(1);
765-
bool no_error = PG_GETARG_BOOL(2);
766-
767-
if (timeout < 0)
768-
ereport(ERROR,
769-
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
770-
errmsg("\"timeout\" must not be negative")));
771-
772-
/*
773-
* We are going to wait for the LSN replay. We should first care that we
774-
* don't hold a snapshot and correspondingly our MyProc->xmin is invalid.
775-
* Otherwise, our snapshot could prevent the replay of WAL records
776-
* implying a kind of self-deadlock. This is the reason why
777-
* pg_wal_replay_wait() is a procedure, not a function.
778-
*
779-
* At first, we should check there is no active snapshot. According to
780-
* PlannedStmtRequiresSnapshot(), even in an atomic context, CallStmt is
781-
* processed with a snapshot. Thankfully, we can pop this snapshot,
782-
* because PortalRunUtility() can tolerate this.
783-
*/
784-
if (ActiveSnapshotSet())
785-
PopActiveSnapshot();
786-
787-
/*
788-
* At second, invalidate a catalog snapshot if any. And we should be done
789-
* with the preparation.
790-
*/
791-
InvalidateCatalogSnapshot();
792-
793-
/* Give up if there is still an active or registered snapshot. */
794-
if (GetOldestSnapshot())
795-
ereport(ERROR,
796-
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
797-
errmsg("pg_wal_replay_wait() must be only called without an active or registered snapshot"),
798-
errdetail("Make sure pg_wal_replay_wait() isn't called within a transaction with an isolation level higher than READ COMMITTED, another procedure, or a function.")));
799-
800-
/*
801-
* As the result we should hold no snapshot, and correspondingly our xmin
802-
* should be unset.
803-
*/
804-
Assert(MyProc->xmin == InvalidTransactionId);
805-
806-
lastWaitLSNResult = WaitForLSNReplay(target_lsn, timeout);
807-
808-
if (no_error)
809-
PG_RETURN_VOID();
810-
811-
/*
812-
* Process the result of WaitForLSNReplay(). Throw appropriate error if
813-
* needed.
814-
*/
815-
switch (lastWaitLSNResult)
816-
{
817-
case WAIT_LSN_RESULT_SUCCESS:
818-
/* Nothing to do on success */
819-
break;
820-
821-
case WAIT_LSN_RESULT_TIMEOUT:
822-
ereport(ERROR,
823-
(errcode(ERRCODE_QUERY_CANCELED),
824-
errmsg("timed out while waiting for target LSN %X/%X to be replayed; current replay LSN %X/%X",
825-
LSN_FORMAT_ARGS(target_lsn),
826-
LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL)))));
827-
break;
828-
829-
case WAIT_LSN_RESULT_NOT_IN_RECOVERY:
830-
ereport(ERROR,
831-
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
832-
errmsg("recovery is not in progress"),
833-
errdetail("Recovery ended before replaying target LSN %X/%X; last replay LSN %X/%X.",
834-
LSN_FORMAT_ARGS(target_lsn),
835-
LSN_FORMAT_ARGS(GetXLogReplayRecPtr(NULL)))));
836-
break;
837-
}
838-
839-
PG_RETURN_VOID();
840-
}
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/xlogrecovery.c

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
#include "access/xlogreader.h"
4141
#include "access/xlogrecovery.h"
4242
#include "access/xlogutils.h"
43-
#include "access/xlogwait.h"
4443
#include "backup/basebackup.h"
4544
#include "catalog/pg_control.h"
4645
#include "commands/tablespace.h"
@@ -1829,16 +1828,6 @@ PerformWalRecovery(void)
18291828
break;
18301829
}
18311830

1832-
/*
1833-
* If we replayed an LSN that someone was waiting for then walk
1834-
* over the shared memory array and set latches to notify the
1835-
* waiters.
1836-
*/
1837-
if (waitLSNState &&
1838-
(XLogRecoveryCtl->lastReplayedEndRecPtr >=
1839-
pg_atomic_read_u64(&waitLSNState->minWaitedLSN)))
1840-
WaitLSNWakeup(XLogRecoveryCtl->lastReplayedEndRecPtr);
1841-
18421831
/* Else, try to fetch the next WAL record */
18431832
record = ReadRecord(xlogprefetcher, LOG, false, replayTLI);
18441833
} while (record != NULL);

0 commit comments

Comments
 (0)