mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Implement pg_wal_replay_wait() stored procedure
pg_wal_replay_wait() is to be used on standby and specifies waiting for the specific WAL location to be replayed before starting the transaction. This option is useful when the user makes some data changes on primary and needs a guarantee to see these changes on standby. The queue of waiters is stored in the shared memory array sorted by LSN. During replay of WAL waiters whose LSNs are already replayed are deleted from the shared memory array and woken up by setting of their latches. pg_wal_replay_wait() needs to wait without any snapshot held. Otherwise, the snapshot could prevent the replay of WAL records implying a kind of self-deadlock. This is why it is only possible to implement pg_wal_replay_wait() as a procedure working in a non-atomic context, not a function. Catversion is bumped. Discussion: https://postgr.es/m/eb12f9b03851bb2583adab5df9579b4b%40postgrespro.ru Author: Kartyshov Ivan, Alexander Korotkov Reviewed-by: Michael Paquier, Peter Eisentraut, Dilip Kumar, Amit Kapila Reviewed-by: Alexander Lakhin, Bharath Rupireddy, Euler Taveira
This commit is contained in:
@ -28284,6 +28284,119 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
|
||||
the pause, the rate of WAL generation and available disk space.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
There are also procedures to control the progress of recovery.
|
||||
They are shown in <xref linkend="procedures-recovery-control-table"/>.
|
||||
These procedures may be executed only during recovery.
|
||||
</para>
|
||||
|
||||
<table id="procedures-recovery-control-table">
|
||||
<title>Recovery Control Procedures</title>
|
||||
<tgroup cols="1">
|
||||
<thead>
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
Procedure
|
||||
</para>
|
||||
<para>
|
||||
Description
|
||||
</para></entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<indexterm>
|
||||
<primary>pg_wal_replay_wait</primary>
|
||||
</indexterm>
|
||||
<function>pg_wal_replay_wait</function> (
|
||||
<parameter>target_lsn</parameter> <type>pg_lsn</type>,
|
||||
<parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal>)
|
||||
<returnvalue>void</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
If <parameter>timeout</parameter> is not specified or zero, this
|
||||
procedure returns once WAL is replayed upto
|
||||
<literal>target_lsn</literal>.
|
||||
If the <parameter>timeout</parameter> is specified (in
|
||||
milliseconds) and greater than zero, the procedure waits until the
|
||||
server actually replays the WAL upto <literal>target_lsn</literal> or
|
||||
until the given time has passed. On timeout, an error is emitted.
|
||||
</para></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
||||
<para>
|
||||
<function>pg_wal_replay_wait</function> waits till
|
||||
<parameter>target_lsn</parameter> to be replayed on standby.
|
||||
That is, after this function execution, the value returned by
|
||||
<function>pg_last_wal_replay_lsn</function> should be greater or equal
|
||||
to the <parameter>target_lsn</parameter> value. This is useful to achieve
|
||||
read-your-writes-consistency, while using async replica for reads and
|
||||
primary for writes. In that case <acronym>lsn</acronym> of the last
|
||||
modification should be stored on the client application side or the
|
||||
connection pooler side.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You can use <function>pg_wal_replay_wait</function> to wait for
|
||||
the <type>pg_lsn</type> value. For example, an application could update
|
||||
the <literal>movie</literal> table and get the <acronym>lsn</acronym> after
|
||||
changes just made. This example uses <function>pg_current_wal_insert_lsn</function>
|
||||
on primary server to get the <acronym>lsn</acronym> given that
|
||||
<varname>synchronous_commit</varname> could be set to
|
||||
<literal>off</literal>.
|
||||
|
||||
<programlisting>
|
||||
postgres=# UPDATE movie SET genre = 'Dramatic' WHERE genre = 'Drama';
|
||||
UPDATE 100
|
||||
postgres=# SELECT pg_current_wal_insert_lsn();
|
||||
pg_current_wal_insert_lsn
|
||||
--------------------
|
||||
0/306EE20
|
||||
(1 row)
|
||||
</programlisting>
|
||||
|
||||
Then an application could run <function>pg_wal_replay_wait</function>
|
||||
with the <acronym>lsn</acronym> obtained from primary. After that the
|
||||
changes made of primary should be guaranteed to be visible on replica.
|
||||
|
||||
<programlisting>
|
||||
postgres=# CALL pg_wal_replay_wait('0/306EE20');
|
||||
CALL
|
||||
postgres=# SELECT * FROM movie WHERE genre = 'Drama';
|
||||
genre
|
||||
-------
|
||||
(0 rows)
|
||||
</programlisting>
|
||||
|
||||
It may also happen that target <acronym>lsn</acronym> is not achieved
|
||||
within the timeout. In that case the error is thrown.
|
||||
|
||||
<programlisting>
|
||||
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
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<function>pg_wal_replay_wait</function> can't be used within
|
||||
the transaction, another procedure or function. Any of them holds a
|
||||
snapshot, which could prevent the replay of WAL records.
|
||||
|
||||
<programlisting>
|
||||
postgres=# BEGIN;
|
||||
BEGIN
|
||||
postgres=*# CALL pg_wal_replay_wait('0/306EE20');
|
||||
ERROR: pg_wal_replay_wait() must be only called in non-atomic context
|
||||
DETAIL: Make sure pg_wal_replay_wait() isn't called within a transaction, another procedure, or a function.
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="functions-snapshot-synchronization">
|
||||
|
Reference in New Issue
Block a user