mirror of
https://github.com/postgres/postgres.git
synced 2025-11-24 00:23:06 +03:00
Allow logical decoding on standbys
Unsurprisingly, this requires wal_level = logical to be set on the primary and standby. The infrastructure added in26669757b6ensures that slots are invalidated if the primary's wal_level is lowered. Creating a slot on a standby waits for a xl_running_xact record to be processed. If the primary is idle (and thus not emitting xl_running_xact records), that can take a while. To make that faster, this commit also introduces the pg_log_standby_snapshot() function. By executing it on the primary, completion of slot creation on the standby can be accelerated. Note that logical decoding on a standby does not itself enforce that required catalog rows are not removed. The user has to use physical replication slots + hot_standby_feedback or other measures to prevent that. If catalog rows required for a slot are removed, the slot is invalidated. See6af1793954for an overall design of logical decoding on a standby. Bumps catversion, for the addition of the pg_log_standby_snapshot() function. Author: "Drouvot, Bertrand" <bertranddrouvot.pg@gmail.com> Author: Andres Freund <andres@anarazel.de> (in an older version) Author: Amit Khandekar <amitdkhan.pg@gmail.com> (in an older version) Reviewed-by: Andres Freund <andres@anarazel.de> Reviewed-by: FabrÌzio de Royes Mello <fabriziomello@gmail.com> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Reviewed-By: Robert Haas <robertmhaas@gmail.com>
This commit is contained in:
@@ -152,11 +152,39 @@ xlog_decode(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
|
||||
* can restart from there.
|
||||
*/
|
||||
break;
|
||||
case XLOG_PARAMETER_CHANGE:
|
||||
{
|
||||
xl_parameter_change *xlrec =
|
||||
(xl_parameter_change *) XLogRecGetData(buf->record);
|
||||
|
||||
/*
|
||||
* If wal_level on the primary is reduced to less than
|
||||
* logical, we want to prevent existing logical slots from
|
||||
* being used. Existing logical slots on the standby get
|
||||
* invalidated when this WAL record is replayed; and further,
|
||||
* slot creation fails when wal_level is not sufficient; but
|
||||
* all these operations are not synchronized, so a logical
|
||||
* slot may creep in while the wal_level is being
|
||||
* reduced. Hence this extra check.
|
||||
*/
|
||||
if (xlrec->wal_level < WAL_LEVEL_LOGICAL)
|
||||
{
|
||||
/*
|
||||
* This can occur only on a standby, as a primary would
|
||||
* not allow to restart after changing wal_level < logical
|
||||
* if there is pre-existing logical slot.
|
||||
*/
|
||||
Assert(RecoveryInProgress());
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("logical decoding on a standby requires wal_level to be at least logical on the primary")));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XLOG_NOOP:
|
||||
case XLOG_NEXTOID:
|
||||
case XLOG_SWITCH:
|
||||
case XLOG_BACKUP_END:
|
||||
case XLOG_PARAMETER_CHANGE:
|
||||
case XLOG_RESTORE_POINT:
|
||||
case XLOG_FPW_CHANGE:
|
||||
case XLOG_FPI_FOR_HINT:
|
||||
|
||||
@@ -124,23 +124,21 @@ CheckLogicalDecodingRequirements(void)
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("logical decoding requires a database connection")));
|
||||
|
||||
/* ----
|
||||
* TODO: We got to change that someday soon...
|
||||
*
|
||||
* There's basically three things missing to allow this:
|
||||
* 1) We need to be able to correctly and quickly identify the timeline a
|
||||
* LSN belongs to
|
||||
* 2) We need to force hot_standby_feedback to be enabled at all times so
|
||||
* the primary cannot remove rows we need.
|
||||
* 3) support dropping replication slots referring to a database, in
|
||||
* dbase_redo. There can't be any active ones due to HS recovery
|
||||
* conflicts, so that should be relatively easy.
|
||||
* ----
|
||||
*/
|
||||
if (RecoveryInProgress())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("logical decoding cannot be used while in recovery")));
|
||||
{
|
||||
/*
|
||||
* This check may have race conditions, but whenever
|
||||
* XLOG_PARAMETER_CHANGE indicates that wal_level has changed, we
|
||||
* verify that there are no existing logical replication slots. And to
|
||||
* avoid races around creating a new slot,
|
||||
* CheckLogicalDecodingRequirements() is called once before creating
|
||||
* the slot, and once when logical decoding is initially starting up.
|
||||
*/
|
||||
if (GetActiveWalLevelOnStandby() < WAL_LEVEL_LOGICAL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("logical decoding on a standby requires wal_level to be at least logical on the primary")));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -342,6 +340,12 @@ CreateInitDecodingContext(const char *plugin,
|
||||
LogicalDecodingContext *ctx;
|
||||
MemoryContext old_context;
|
||||
|
||||
/*
|
||||
* On a standby, this check is also required while creating the
|
||||
* slot. Check the comments in the function.
|
||||
*/
|
||||
CheckLogicalDecodingRequirements();
|
||||
|
||||
/* shorter lines... */
|
||||
slot = MyReplicationSlot;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user