Skip to content

Commit bf279dd

Browse files
author
Amit Kapila
committed
Introduce a new GUC 'standby_slot_names'.
This patch provides a way to ensure that physical standbys that are potential failover candidates have received and flushed changes before the primary server making them visible to subscribers. Doing so guarantees that the promoted standby server is not lagging behind the subscribers when a failover is necessary. The logical walsender now guarantees that all local changes are sent and flushed to the standby servers corresponding to the replication slots specified in 'standby_slot_names' before sending those changes to the subscriber. Additionally, the SQL functions pg_logical_slot_get_changes, pg_logical_slot_peek_changes and pg_replication_slot_advance are modified to ensure that they process changes for failover slots only after physical slots specified in 'standby_slot_names' have confirmed WAL receipt for those. Author: Hou Zhijie and Shveta Malik Reviewed-by: Masahiko Sawada, Peter Smith, Bertrand Drouvot, Ajin Cherian, Nisha Moond, Amit Kapila Discussion: https://postgr.es/m/514f6f2f-6833-4539-39f1-96cd1e011f23@enterprisedb.com
1 parent 453c468 commit bf279dd

18 files changed

+918
-27
lines changed

doc/src/sgml/config.sgml

+39
Original file line numberDiff line numberDiff line change
@@ -4559,6 +4559,45 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
45594559
</listitem>
45604560
</varlistentry>
45614561

4562+
<varlistentry id="guc-standby-slot-names" xreflabel="standby_slot_names">
4563+
<term><varname>standby_slot_names</varname> (<type>string</type>)
4564+
<indexterm>
4565+
<primary><varname>standby_slot_names</varname> configuration parameter</primary>
4566+
</indexterm>
4567+
</term>
4568+
<listitem>
4569+
<para>
4570+
A comma-separated list of streaming replication standby server slot names
4571+
that logical WAL sender processes will wait for. Logical WAL sender processes
4572+
will send decoded changes to plugins only after the specified replication
4573+
slots confirm receiving WAL. This guarantees that logical replication
4574+
failover slots do not consume changes until those changes are received
4575+
and flushed to corresponding physical standbys. If a
4576+
logical replication connection is meant to switch to a physical standby
4577+
after the standby is promoted, the physical replication slot for the
4578+
standby should be listed here. Note that logical replication will not
4579+
proceed if the slots specified in the
4580+
<varname>standby_slot_names</varname> do not exist or are invalidated.
4581+
Additionally, the replication management functions
4582+
<link linkend="pg-replication-slot-advance">
4583+
<function>pg_replication_slot_advance</function></link>,
4584+
<link linkend="pg-logical-slot-get-changes">
4585+
<function>pg_logical_slot_get_changes</function></link>, and
4586+
<link linkend="pg-logical-slot-peek-changes">
4587+
<function>pg_logical_slot_peek_changes</function></link>,
4588+
when used with logical failover slots, will block until all
4589+
physical slots specified in <varname>standby_slot_names</varname> have
4590+
confirmed WAL receipt.
4591+
</para>
4592+
<para>
4593+
The standbys corresponding to the physical replication slots in
4594+
<varname>standby_slot_names</varname> must configure
4595+
<literal>sync_replication_slots = true</literal> so they can receive
4596+
logical failover slot changes from the primary.
4597+
</para>
4598+
</listitem>
4599+
</varlistentry>
4600+
45624601
</variablelist>
45634602
</sect2>
45644603

doc/src/sgml/func.sgml

+12-4
Original file line numberDiff line numberDiff line change
@@ -28150,7 +28150,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2815028150
</row>
2815128151

2815228152
<row>
28153-
<entry role="func_table_entry"><para role="func_signature">
28153+
<entry id="pg-logical-slot-get-changes" role="func_table_entry"><para role="func_signature">
2815428154
<indexterm>
2815528155
<primary>pg_logical_slot_get_changes</primary>
2815628156
</indexterm>
@@ -28173,11 +28173,15 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2817328173
the specified value. Note, however, that the actual number of
2817428174
rows returned may be larger, since this limit is only checked after
2817528175
adding the rows produced when decoding each new transaction commit.
28176+
If the specified slot is a logical failover slot then the function will
28177+
not return until all physical slots specified in
28178+
<link linkend="guc-standby-slot-names"><varname>standby_slot_names</varname></link>
28179+
have confirmed WAL receipt.
2817628180
</para></entry>
2817728181
</row>
2817828182

2817928183
<row>
28180-
<entry role="func_table_entry"><para role="func_signature">
28184+
<entry id="pg-logical-slot-peek-changes" role="func_table_entry"><para role="func_signature">
2818128185
<indexterm>
2818228186
<primary>pg_logical_slot_peek_changes</primary>
2818328187
</indexterm>
@@ -28232,7 +28236,7 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2823228236
</row>
2823328237

2823428238
<row>
28235-
<entry role="func_table_entry"><para role="func_signature">
28239+
<entry id="pg-replication-slot-advance" role="func_table_entry"><para role="func_signature">
2823628240
<indexterm>
2823728241
<primary>pg_replication_slot_advance</primary>
2823828242
</indexterm>
@@ -28248,7 +28252,11 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset
2824828252
the name of the slot and the actual position that it was advanced to.
2824928253
The updated slot position information is written out at the next
2825028254
checkpoint if any advancing is done. So in the event of a crash, the
28251-
slot may return to an earlier position.
28255+
slot may return to an earlier position. If the specified slot is a
28256+
logical failover slot then the function will not return until all
28257+
physical slots specified in
28258+
<link linkend="guc-standby-slot-names"><varname>standby_slot_names</varname></link>
28259+
have confirmed WAL receipt.
2825228260
</para></entry>
2825328261
</row>
2825428262

doc/src/sgml/logicaldecoding.sgml

+12
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,18 @@ postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NU
384384
must be enabled on the standby. It is also necessary to specify a valid
385385
<literal>dbname</literal> in the
386386
<link linkend="guc-primary-conninfo"><varname>primary_conninfo</varname></link>.
387+
It's highly recommended that the said physical replication slot is named in
388+
<link linkend="guc-standby-slot-names"><varname>standby_slot_names</varname></link>
389+
list on the primary, to prevent the subscriber from consuming changes
390+
faster than the hot standby. Even when correctly configured, some latency
391+
is expected when sending changes to logical subscribers due to the waiting
392+
on slots named in
393+
<link linkend="guc-standby-slot-names"><varname>standby_slot_names</varname></link>.
394+
When <varname>standby_slot_names</varname> is utilized, the
395+
primary server will not completely shut down until the corresponding
396+
standbys, associated with the physical replication slots specified
397+
in <varname>standby_slot_names</varname>, have confirmed
398+
receiving the WAL up to the latest flushed position on the primary server.
387399
</para>
388400

389401
<para>

src/backend/replication/logical/logicalfuncs.c

+12
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
105105
MemoryContext per_query_ctx;
106106
MemoryContext oldcontext;
107107
XLogRecPtr end_of_wal;
108+
XLogRecPtr wait_for_wal_lsn;
108109
LogicalDecodingContext *ctx;
109110
ResourceOwner old_resowner = CurrentResourceOwner;
110111
ArrayType *arr;
@@ -224,6 +225,17 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin
224225
NameStr(MyReplicationSlot->data.plugin),
225226
format_procedure(fcinfo->flinfo->fn_oid))));
226227

228+
/*
229+
* Wait for specified streaming replication standby servers (if any)
230+
* to confirm receipt of WAL up to wait_for_wal_lsn.
231+
*/
232+
if (XLogRecPtrIsInvalid(upto_lsn))
233+
wait_for_wal_lsn = end_of_wal;
234+
else
235+
wait_for_wal_lsn = Min(upto_lsn, end_of_wal);
236+
237+
WaitForStandbyConfirmation(wait_for_wal_lsn);
238+
227239
ctx->output_writer_private = p;
228240

229241
/*

src/backend/replication/logical/slotsync.c

+11
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,10 @@ synchronize_one_slot(RemoteSlot *remote_slot, Oid remote_dbid)
488488
latestFlushPtr = GetStandbyFlushRecPtr(NULL);
489489
if (remote_slot->confirmed_lsn > latestFlushPtr)
490490
{
491+
/*
492+
* Can get here only if GUC 'standby_slot_names' on the primary server
493+
* was not configured correctly.
494+
*/
491495
ereport(AmLogicalSlotSyncWorkerProcess() ? LOG : ERROR,
492496
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
493497
errmsg("skipping slot synchronization as the received slot sync"
@@ -857,6 +861,13 @@ validate_remote_info(WalReceiverConn *wrconn)
857861
remote_in_recovery = DatumGetBool(slot_getattr(tupslot, 1, &isnull));
858862
Assert(!isnull);
859863

864+
/*
865+
* Slot sync is currently not supported on a cascading standby. This is
866+
* because if we allow it, the primary server needs to wait for all the
867+
* cascading standbys, otherwise, logical subscribers can still be ahead
868+
* of one of the cascading standbys which we plan to promote. Thus, to
869+
* avoid this additional complexity, we restrict it for the time being.
870+
*/
860871
if (remote_in_recovery)
861872
ereport(ERROR,
862873
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),

0 commit comments

Comments
 (0)