Skip to content

Commit eacce91

Browse files
MasahikoSawadaCommitfest Bot
authored andcommitted
Toggle logical decoding dynamically based on logical slot presence.
Previously logical decoding required wal_level to be set to 'logical' at server start. This commit adds functionality to automatically control logical decoding availability based on logical replication slot presence. The newly introduced module logicalctl.c allows logical decoding to be dynamically activated when needed when wal_level is set to 'replica'. When the first logical replication slot is created, the system automatically increases the effective WAL level to maintain logical-level WAL records. Conversely, after the last logical slot is dropped or invalidated, it decreases back to 'replica' WAL level. A new read-only GUC parameter effective_wal_level is introduced to monitor the actual WAL level in effect. This parameter reflects the current operational WAL level, which may differ from the configured wal_level setting. While activation occurs synchronously right after creating the first logical slot, deactivation happens asynchronously through the checkpointer process. This design choice exists because deactivation requires waiting for concurrent attempts to update logical decoding status, which can be problematic when the process is holding interrupts. This situation arises when a process cleans up temporary or ephemeral slots on error or at process exit without releasing temporary slots explicitly. This lazy approach has a drawback: it may take longer to change the effective_wal_level and disable logical decoding, especially when the checkpointer is busy with other tasks. However, since dropping the last slot should not happen frequently, we chose this approach in all deactivation cases for simpler code implementation for simplicity, even though the lazy approach is required only in error cases or at process exit time in principle. In the future, we could address this limitation either by using a dedicated worker instead of the checkpointer, or by implementing synchronous waiting during slot drops if workloads are significantly affected by the lazy deactivation of logical decoding. XXX Bump PG_CONTROL_VERSION as it adds a new field to CheckPoint struct. Reviewed-by: Shveta Malik <[email protected]> Reviewed-by: Shlok Kyal <[email protected]> Reviewed-by: Bertrand Drouvot <[email protected]> Reviewed-by: Amit Kapila <[email protected]> Reviewed-by: Hayato Kuroda <[email protected]> Reviewed-by: Ashutosh Bapat <[email protected]> Discussion: https://postgr.es/m/CAD21AoCVLeLYq09pQPaWs+Jwdni5FuJ8v2jgq-u9_uFbcp6UbA@mail.gmail.com
1 parent d4baa32 commit eacce91

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1674
-137
lines changed

doc/src/sgml/config.sgml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3034,6 +3034,17 @@ include_dir 'conf.d'
30343034
many <command>UPDATE</command> and <command>DELETE</command> statements are
30353035
executed.
30363036
</para>
3037+
<para>
3038+
It is important to note that when <varname>wal_level</varname> is set to
3039+
<literal>replica</literal>, the effective WAL level can automatically change
3040+
based on the presence of <link linkend="logicaldecoding-replication-slots">
3041+
logical replication slots</link>. The system automatically increases the
3042+
effective WAL level to <literal>logical</literal> when creating the first
3043+
logical replication slot, and decreases it back to <literal>replica</literal>
3044+
when dropping the last logical replication slot. The current effective WAL
3045+
level can be monitored through <xref linkend="guc-effective-wal-level"/>
3046+
parameter.
3047+
</para>
30373048
<para>
30383049
In releases prior to 9.6, this parameter also allowed the
30393050
values <literal>archive</literal> and <literal>hot_standby</literal>.
@@ -11817,6 +11828,38 @@ dynamic_library_path = '/usr/local/lib/postgresql:$libdir'
1181711828
</listitem>
1181811829
</varlistentry>
1181911830

11831+
<varlistentry id="guc-effective-wal-level" xreflabel="effective_wal_level">
11832+
<term><varname>effective_wal_level</varname> (<type>enum</type>)
11833+
<indexterm>
11834+
<primary><varname>effective_wal_level</varname> configuration parameter</primary>
11835+
</indexterm>
11836+
</term>
11837+
<listitem>
11838+
<para>
11839+
Reports the actual WAL logging level currently in effect in the
11840+
system. This parameter shares the same set of values as
11841+
<xref linkend="guc-wal-level"/>, but reflects the operational WAL
11842+
level rather than the configured setting. For descriptions of
11843+
possible values, refer to the <varname>wal_level</varname>
11844+
parameter documentation.
11845+
</para>
11846+
<para>
11847+
The effective WAL level can differ from the configured
11848+
<varname>wal_level</varname> in certain situations. For example,
11849+
when <varname>wal_level</varname> is set to <literal>replica</literal>
11850+
and the system has one or more logical replication slots,
11851+
<varname>effective_wal_level</varname> will show <literal>logical</literal>
11852+
to indicate that the system is maintaining WAL records at
11853+
<literal>logical</literal> level equivalent.
11854+
</para>
11855+
<para>
11856+
On standby servers, <varname>effective_wal_level</varname> matches
11857+
the value of <varname>effective_wal_level</varname> from the most
11858+
upstream server in the replication chain.
11859+
</para>
11860+
</listitem>
11861+
</varlistentry>
11862+
1182011863
<varlistentry id="guc-huge-pages-status" xreflabel="huge_pages_status">
1182111864
<term><varname>huge_pages_status</varname> (<type>enum</type>)
1182211865
<indexterm>

doc/src/sgml/logical-replication.sgml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,7 +2377,7 @@ CONTEXT: processing remote data for replication origin "pg_16395" during "INSER
23772377

23782378
<para>
23792379
<link linkend="guc-wal-level"><varname>wal_level</varname></link> must be
2380-
set to <literal>logical</literal>.
2380+
set to <literal>replica</literal> or <literal>logical</literal>.
23812381
</para>
23822382

23832383
<para>
@@ -2498,7 +2498,7 @@ CONTEXT: processing remote data for replication origin "pg_16395" during "INSER
24982498
<para>
24992499
The new cluster must have
25002500
<link linkend="guc-wal-level"><varname>wal_level</varname></link> as
2501-
<literal>logical</literal>.
2501+
<literal>replica</literal> or <literal>logical</literal>.
25022502
</para>
25032503
</listitem>
25042504
<listitem>

doc/src/sgml/logicaldecoding.sgml

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848
<para>
4949
Before you can use logical decoding, you must set
50-
<xref linkend="guc-wal-level"/> to <literal>logical</literal> and
50+
<xref linkend="guc-wal-level"/> to <literal>replica</literal> or higher and
5151
<xref linkend="guc-max-replication-slots"/> to at least 1. Then, you
5252
should connect to the target database (in the example
5353
below, <literal>postgres</literal>) as a superuser.
@@ -257,6 +257,47 @@ postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NU
257257
log</link>, which describe changes on a storage level, into an
258258
application-specific form such as a stream of tuples or SQL statements.
259259
</para>
260+
261+
<para>
262+
Logical decoding becomes available in two conditions:
263+
</para>
264+
<itemizedlist>
265+
<listitem>
266+
<para>
267+
When <xref linkend="guc-wal-level"/> is set to <literal>logical</literal>.
268+
</para>
269+
</listitem>
270+
<listitem>
271+
<para>
272+
When <xref linkend="guc-wal-level"/> is set to <literal>replica</literal>
273+
and at least one valid logical replication slot exists on the system.
274+
</para>
275+
</listitem>
276+
</itemizedlist>
277+
<para>
278+
If either condition is met, the operational WAL level becomes equivalent
279+
to <literal>logical</literal>, which can be monitored through the
280+
<xref linkend="guc-effective-wal-level"/> parameter.
281+
</para>
282+
<para>
283+
When <varname>wal_level</varname> is set to <literal>replica</literal>,
284+
logical decoding is automatically activated upon creation of the first
285+
logical replication slot. This activation process involves several steps
286+
and requires synchronization among processes, ensuring system-wide
287+
consistency. Conversely, if <varname>wal_level</varname> set to
288+
<literal>replica</literal> and the last logical replication slot is dropped
289+
or invalidated, logical decoding is automatically disabled. Note that the
290+
deactivation of logical decoding might take some time as it is performed
291+
asynchronously by the checkpointer process.
292+
</para>
293+
294+
<caution>
295+
<para>
296+
When <varname>wal_level</varname> is set to <literal>replica</literal>,
297+
dropping or invalidating the last logical slot disables logical decoding
298+
on the primary, resulting in slots on standbys being invalidated.
299+
</para>
300+
</caution>
260301
</sect2>
261302

262303
<sect2 id="logicaldecoding-replication-slots">
@@ -328,8 +369,7 @@ postgres=# select * from pg_logical_slot_get_changes('regression_slot', NULL, NU
328369
that could be needed by the logical decoding on the standby (as it does
329370
not know about the <literal>catalog_xmin</literal> on the standby).
330371
Existing logical slots on standby also get invalidated if
331-
<varname>wal_level</varname> on the primary is reduced to less than
332-
<literal>logical</literal>.
372+
logical decoding is disabled on the primary.
333373
This is done as soon as the standby detects such a change in the WAL stream.
334374
It means that, for walsenders that are lagging (if any), some WAL records up
335375
to the <varname>wal_level</varname> parameter change on the primary won't be

doc/src/sgml/ref/pg_createsubscriber.sgml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,12 +379,12 @@ PostgreSQL documentation
379379
<para>
380380
The source server must accept connections from the target server. The
381381
source server must not be in recovery. The source server must have <xref
382-
linkend="guc-wal-level"/> as <literal>logical</literal>. The source server
383-
must have <xref linkend="guc-max-replication-slots"/> configured to a value
384-
greater than or equal to the number of specified databases plus existing
385-
replication slots. The source server must have <xref
386-
linkend="guc-max-wal-senders"/> configured to a value greater than or equal
387-
to the number of specified databases and existing WAL sender processes.
382+
linkend="guc-wal-level"/> as <literal>replica</literal> or <literal>logical</literal>.
383+
The source server must have <xref linkend="guc-max-replication-slots"/>
384+
configured to a value greater than or equal to the number of specified
385+
databases plus existing replication slots. The source server must have
386+
<xref linkend="guc-max-wal-senders"/> configured to a value greater than or
387+
equal to the number of specified databases and existing WAL sender processes.
388388
</para>
389389
</refsect2>
390390

doc/src/sgml/system-views.sgml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,8 +3062,9 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx
30623062
<listitem>
30633063
<para>
30643064
<literal>wal_level_insufficient</literal> means that the
3065-
primary doesn't have a <xref linkend="guc-wal-level"/> sufficient to
3066-
perform logical decoding. It is set only for logical slots.
3065+
logical decoding is disabled on the primary (See
3066+
<xref linkend="logicaldecoding-explanation-log-dec"/>). It is set
3067+
only for logical slots.
30673068
</para>
30683069
</listitem>
30693070
<listitem>

src/backend/access/heap/heapam.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8891,8 +8891,9 @@ log_heap_update(Relation reln, Buffer oldbuf,
88918891
*
88928892
* Skip this if we're taking a full-page image of the new page, as we
88938893
* don't include the new tuple in the WAL record in that case. Also
8894-
* disable if wal_level='logical', as logical decoding needs to be able to
8895-
* read the new tuple in whole from the WAL record alone.
8894+
* disable if logical decoding is enabled and the relation requires WAL to
8895+
* be logged for logical decoding, as it needs to be able to read the new
8896+
* tuple in whole from the WAL record alone.
88968897
*/
88978898
if (oldbuf == newbuf && !need_tuple_data &&
88988899
!XLogCheckBufferNeedsBackup(newbuf))
@@ -9064,8 +9065,8 @@ log_heap_update(Relation reln, Buffer oldbuf,
90649065
/*
90659066
* Perform XLogInsert of an XLOG_HEAP2_NEW_CID record
90669067
*
9067-
* This is only used in wal_level >= WAL_LEVEL_LOGICAL, and only for catalog
9068-
* tuples.
9068+
* This is only used when effective_wal_level is logical, and only for
9069+
* catalog tuples.
90699070
*/
90709071
static XLogRecPtr
90719072
log_heap_new_cid(Relation relation, HeapTuple tup)

src/backend/access/rmgrdesc/xlogdesc.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
6666
CheckPoint *checkpoint = (CheckPoint *) rec;
6767

6868
appendStringInfo(buf, "redo %X/%08X; "
69-
"tli %u; prev tli %u; fpw %s; wal_level %s; xid %u:%u; oid %u; multi %u; offset %u; "
69+
"tli %u; prev tli %u; fpw %s; wal_level %s; logical decoding %s; xid %u:%u; oid %u; multi %u; offset %u; "
7070
"oldest xid %u in DB %u; oldest multi %u in DB %u; "
7171
"oldest/newest commit timestamp xid: %u/%u; "
7272
"oldest running xid %u; %s",
@@ -75,6 +75,7 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
7575
checkpoint->PrevTimeLineID,
7676
checkpoint->fullPageWrites ? "true" : "false",
7777
get_wal_level_string(checkpoint->wal_level),
78+
checkpoint->logicalDecodingEnabled ? "true" : "false",
7879
EpochFromFullTransactionId(checkpoint->nextXid),
7980
XidFromFullTransactionId(checkpoint->nextXid),
8081
checkpoint->nextOid,
@@ -167,6 +168,13 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
167168
memcpy(&wal_level, rec, sizeof(int));
168169
appendStringInfo(buf, "wal_level %s", get_wal_level_string(wal_level));
169170
}
171+
else if (info == XLOG_LOGICAL_DECODING_STATUS_CHANGE)
172+
{
173+
bool enabled;
174+
175+
memcpy(&enabled, rec, sizeof(bool));
176+
appendStringInfoString(buf, enabled ? "true" : "false");
177+
}
170178
}
171179

172180
const char *
@@ -218,6 +226,9 @@ xlog_identify(uint8 info)
218226
case XLOG_CHECKPOINT_REDO:
219227
id = "CHECKPOINT_REDO";
220228
break;
229+
case XLOG_LOGICAL_DECODING_STATUS_CHANGE:
230+
id = "LOGICAL_DECODING_STATUS_CHANGE";
231+
break;
221232
}
222233

223234
return id;

src/backend/access/transam/xact.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,9 @@ MarkCurrentTransactionIdLoggedIfAny(void)
552552
* operation in a subtransaction. We require that for logical decoding, see
553553
* LogicalDecodingProcessRecord.
554554
*
555-
* This returns true if wal_level >= logical and we are inside a valid
556-
* subtransaction, for which the assignment was not yet written to any WAL
557-
* record.
555+
* This returns true if effective_wal_level is logical and we are inside
556+
* a valid subtransaction, for which the assignment was not yet written to
557+
* any WAL record.
558558
*/
559559
bool
560560
IsSubxactTopXidLogPending(void)
@@ -563,7 +563,7 @@ IsSubxactTopXidLogPending(void)
563563
if (CurrentTransactionState->topXidLogged)
564564
return false;
565565

566-
/* wal_level has to be logical */
566+
/* effective_wal_level has to be logical */
567567
if (!XLogLogicalInfoActive())
568568
return false;
569569

@@ -682,14 +682,14 @@ AssignTransactionId(TransactionState s)
682682
}
683683

684684
/*
685-
* When wal_level=logical, guarantee that a subtransaction's xid can only
686-
* be seen in the WAL stream if its toplevel xid has been logged before.
687-
* If necessary we log an xact_assignment record with fewer than
688-
* PGPROC_MAX_CACHED_SUBXIDS. Note that it is fine if didLogXid isn't set
689-
* for a transaction even though it appears in a WAL record, we just might
690-
* superfluously log something. That can happen when an xid is included
691-
* somewhere inside a wal record, but not in XLogRecord->xl_xid, like in
692-
* xl_standby_locks.
685+
* When effective_wal_level is logical, guarantee that a subtransaction's
686+
* xid can only be seen in the WAL stream if its toplevel xid has been
687+
* logged before. If necessary we log an xact_assignment record with fewer
688+
* than PGPROC_MAX_CACHED_SUBXIDS. Note that it is fine if didLogXid isn't
689+
* set for a transaction even though it appears in a WAL record, we just
690+
* might superfluously log something. That can happen when an xid is
691+
* included somewhere inside a wal record, but not in XLogRecord->xl_xid,
692+
* like in xl_standby_locks.
693693
*/
694694
if (isSubXact && XLogLogicalInfoActive() &&
695695
!TopTransactionStateData.didLogXid)

0 commit comments

Comments
 (0)