Skip to content

Commit e810ac8

Browse files
ashucoekhackorum
authored andcommitted
Add FIRST N and N (...) priority syntax to synchronized_standby_slots
Extend synchronized_standby_slots to support explicit priority forms aligned with synchronous_standby_names. - FIRST N (slot1, slot2, ...) - N (slot1, slot2, ...) as shorthand for FIRST N Implementation details: - Use the SYNC_REP_DEFAULT parser distinction from the earlier refactor so plain-list syntax remains separate from priority syntax. - Extend StandbySlotsHaveCaughtup() priority handling. - Select slots in list order. - Skip missing, logical, invalidated, and inactive lagging slots. - Wait for active lagging higher-priority slots. - Clarify duplicate handling for priority syntax in the synchronized_standby_slots documentation. - Simplify caught-up comments and clarify standby confirmation wait comments to match the final control flow. Tests and docs: - Add coverage for FIRST behavior and shorthand N (...) behavior. - Add plain-list disambiguation with first-prefixed slot names. - Add FIRST duplicate-entry recovery coverage to show duplicates do not create extra priority positions. - Update docs for FIRST and shorthand priority syntax semantics. - Clarify that duplicate slot names are ignored in priority-based forms and preserve first-occurrence order. Author: Satya Narlapuram <satyanarlapuram@gmail.com> Author: Ashutosh Sharma <ashu.coek88@gmail.com> Reviewed-by: Shveta Malik <shveta.malik@gmail.com> Reviewed-by: Ajin Cherian <itsajin@gmail.com> Reviewed-by: Hou, Zhijie <houzj.fnst@fujitsu.com> Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com> Reviewed-by: Surya Poondla <suryapoondla4@gmail.com> Reviewed-by: Japin Li <japinli@hotmail.com> Reviewed-by: Shlok Kyal <shlok.kyal.oss@gmail.com>
1 parent 84b1c77 commit e810ac8

3 files changed

Lines changed: 257 additions & 47 deletions

File tree

doc/src/sgml/config.sgml

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5274,6 +5274,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
52745274
sender processes must wait on before delivering decoded changes. This
52755275
parameter uses the following syntax:
52765276
<synopsis>
5277+
[FIRST] <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="parameter">slot_name</replaceable> [, ...] )
52775278
ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="parameter">slot_name</replaceable> [, ...] )
52785279
<replaceable class="parameter">slot_name</replaceable> [, ...]
52795280
</synopsis>
@@ -5285,9 +5286,24 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
52855286
<replaceable class="parameter">num_sync</replaceable>
52865287
must be an integer value greater than zero and must not exceed the
52875288
number of listed slots.
5288-
Other forms supported by
5289-
<xref linkend="guc-synchronous-standby-names"/>, such as priority
5290-
syntax, are not supported.
5289+
</para>
5290+
<para>
5291+
The keyword <literal>FIRST</literal>, coupled with
5292+
<replaceable class="parameter">num_sync</replaceable>, specifies
5293+
priority-based semantics. Logical decoding will wait for the first
5294+
<replaceable class="parameter">num_sync</replaceable> available
5295+
physical slots in priority order (the order they appear in the list).
5296+
Missing, logical, or invalidated slots are skipped. Inactive slots are
5297+
skipped only while they are lagging. However, if a slot exists and is
5298+
valid and active but has not yet caught up, the system will wait for it
5299+
rather than skipping to lower-priority slots. If, after skipping
5300+
unusable slots, fewer than
5301+
<replaceable class="parameter">num_sync</replaceable> usable slots
5302+
remain, logical decoding waits until enough slots become usable and
5303+
caught up, or until the configuration is changed. The keyword
5304+
<literal>FIRST</literal> is optional in this form, so
5305+
<literal>2 (slot1, slot2, slot3)</literal> and
5306+
<literal>FIRST 2 (slot1, slot2, slot3)</literal> are equivalent.
52915307
</para>
52925308
<para>
52935309
A plain comma-separated list without a keyword specifies that
@@ -5324,19 +5340,26 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
53245340
duplicate entries are ignored and only the first occurrence is used.
53255341
The semantics of <varname>synchronized_standby_slots</varname> are
53265342
therefore based on the unique set of listed slot names, preserving the
5327-
original order of first occurrence. This means that
5328-
<literal>ANY 2 (slot1, slot1, slot2, slot3)</literal> is treated the
5329-
same as <literal>ANY 2 (slot1, slot2, slot3)</literal>, and a plain
5330-
list such as <literal>(slot1, slot1, slot2)</literal> is treated the
5331-
same as <literal>(slot1, slot2)</literal>. In particular,
5343+
original order of first occurrence. This means that, in
5344+
priority-based forms, duplicates do not create additional priority
5345+
positions: for example,
5346+
<literal>FIRST 2 (slot1, slot1, slot2, slot3)</literal> is treated the
5347+
same as <literal>FIRST 2 (slot1, slot2, slot3)</literal>.
5348+
Likewise, <literal>ANY 2 (slot1, slot1, slot2, slot3)</literal> is
5349+
treated the same as <literal>ANY 2 (slot1, slot2, slot3)</literal>,
5350+
and a plain list such as <literal>(slot1, slot1, slot2)</literal>
5351+
is treated the same as <literal>(slot1, slot2)</literal>. In particular,
53325352
<replaceable class="parameter">num_sync</replaceable> must not exceed
53335353
the number of unique listed slots. Such a configuration results in an
53345354
error to prevent indefinite waits in WAL sender processes due to a
53355355
misconfigured <varname>synchronized_standby_slots</varname> setting.
53365356
</para>
5337-
<para>
5338-
<literal>ANY</literal> is case-insensitive.
5339-
</para>
5357+
<para>
5358+
<literal>FIRST</literal> and <literal>ANY</literal> are case-insensitive.
5359+
If these keywords are used as the name of a replication slot,
5360+
the <replaceable class="parameter">slot_name</replaceable> must
5361+
be double-quoted.
5362+
</para>
53405363
<para>
53415364
The use of <varname>synchronized_standby_slots</varname> guarantees
53425365
that logical replication

src/backend/replication/slot.c

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3070,6 +3070,8 @@ CompactSyncRepConfigMemberNames(SyncRepConfigData *config)
30703070
*
30713071
* slot1, slot2 -- wait for ALL listed slots
30723072
* ANY N (slot1, slot2, ...) -- wait for any N-of-M (quorum)
3073+
* FIRST N (slot1, slot2, ...) -- wait for first N in priority order
3074+
* N (slot1, slot2, ...) -- shorthand for FIRST N
30733075
*
30743076
* Note: Simple list syntax is interpreted as "wait for ALL" for this GUC,
30753077
* unlike synchronous_standby_names where it means "FIRST 1".
@@ -3110,14 +3112,6 @@ check_synchronized_standby_slots(char **newval, void **extra, GucSource source)
31103112
return false;
31113113
}
31123114

3113-
if (syncrep_parse_result->syncrep_method == SYNC_REP_PRIORITY)
3114-
{
3115-
GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
3116-
GUC_check_errmsg("priority syntax is not supported for parameter \"%s\"",
3117-
"synchronized_standby_slots");
3118-
return false;
3119-
}
3120-
31213115
if (syncrep_parse_result->num_sync <= 0)
31223116
{
31233117
GUC_check_errcode(ERRCODE_INVALID_PARAMETER_VALUE);
@@ -3339,6 +3333,12 @@ ReportUnavailableSyncStandbySlots(SyncStandbySlotsStateInfo *slot_states,
33393333
* Simple list (e.g., "slot1, slot2"):
33403334
* ALL slots must have caught up. Returns false otherwise.
33413335
*
3336+
* FIRST N (e.g., "FIRST 2 (slot1, slot2, slot3)"):
3337+
* Wait for the first N eligible slots in priority order. Skips missing,
3338+
* invalid, logical, and inactive-lagging slots to find N eligible slots.
3339+
* If an active slot is lagging, waits for it (does not skip to lower
3340+
* priority slots).
3341+
*
33423342
* ANY N (e.g., "ANY 2 (slot1, slot2, slot3)"):
33433343
* Wait for any N eligible slots. Skips missing, invalid, logical, and
33443344
* lagging slots (inactive or active) to find N slots that have caught up.
@@ -3389,11 +3389,14 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
33893389
* first slot that is missing/invalid/logical, or the first slot that is
33903390
* lagging (inactive or active).
33913391
*
3392-
* wait_for_all = false means we select N from M candidates (ANY N syntax).
3393-
* In this mode, slots already caught up are counted even if inactive, and
3394-
* lagging slots are skipped until enough slots have caught up.
3395-
* Duplicate configured slot names do not appear here because the check hook
3396-
* compacts them out of the parsed configuration.
3392+
* wait_for_all = false means we select N from M candidates (FIRST N or
3393+
* ANY N syntax). In this mode, slots already caught up are counted even if
3394+
* inactive. In FIRST N mode, we skip missing/invalid/logical slots and
3395+
* lagging inactive slots, but wait for an active lagging slot with higher
3396+
* priority. In ANY N mode, we skip lagging slots (inactive or active) to
3397+
* find any N that have caught up. Duplicate configured slot names do not
3398+
* appear here because the check hook compacts them out of the parsed
3399+
* configuration.
33973400
*/
33983401
required = synchronized_standby_slots_config->num_sync;
33993402
wait_for_all = (synchronized_standby_slots_config->syncrep_method == SYNC_REP_DEFAULT);
@@ -3476,16 +3479,19 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
34763479
* If a slot is inactive and lagging, report it as inactive. If it
34773480
* is active and lagging, report it as lagging.
34783481
*
3479-
* In ALL mode: must wait for it. In ANY N (quorum) mode: skip and
3480-
* use another slot.
3482+
* In ALL mode: must wait for it. In FIRST N (priority) mode:
3483+
* lagging active slots block, while inactive slots can be
3484+
* skipped. In ANY N (quorum) mode: skip and use another slot.
34813485
*/
34823486
slot_states[num_slot_states].slot_name = name;
34833487
slot_states[num_slot_states].state =
34843488
inactive ? SS_SLOT_INACTIVE_LAGGING : SS_SLOT_ACTIVE_LAGGING;
34853489
slot_states[num_slot_states].restart_lsn = restart_lsn;
34863490
num_slot_states++;
34873491

3488-
if (wait_for_all)
3492+
if (wait_for_all ||
3493+
(!inactive &&
3494+
synchronized_standby_slots_config->syncrep_method == SYNC_REP_PRIORITY))
34893495
break;
34903496
goto next_slot;
34913497
}
@@ -3498,7 +3504,7 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
34983504

34993505
caught_up_slot_num++;
35003506

3501-
/* Stop processing if the required number of slots have caught up. */
3507+
/* Stop once the required number of slots have caught up. */
35023508
if (caught_up_slot_num >= required)
35033509
break;
35043510

@@ -3513,8 +3519,8 @@ StandbySlotsHaveCaughtup(XLogRecPtr wait_for_lsn, int elevel)
35133519
* problem states and return false.
35143520
*
35153521
* We only emit messages when the requirement is not met to avoid
3516-
* misleading messages in quorum mode where other slots may have satisfied
3517-
* the condition despite some slots having issues.
3522+
* misleading messages in quorum/priority mode where other slots may have
3523+
* satisfied the condition despite some slots having issues.
35183524
*/
35193525
if (caught_up_slot_num < required)
35203526
{

0 commit comments

Comments
 (0)