/* *Structfortheconfigurationofsynchronized_standby_slots. * *Note:thismustbeaflatrepresentationthatcanbeheldinasinglechunk *ofguc_malloc'dmemory,sothatitcanbestoredasthe"extra"dataforthe *synchronized_standby_slotsGUC.
*/ typedefstruct
{ /* Number of slot names in the slot_names[] */ int nslotnames;
/* size of version independent data */ #define ReplicationSlotOnDiskConstantSize \
offsetof(ReplicationSlotOnDisk, slotdata) /* size of the part of the slot not covered by the checksum */ #define ReplicationSlotOnDiskNotChecksummedSize \
offsetof(ReplicationSlotOnDisk, version) /* size of the part covered by the checksum */ #define ReplicationSlotOnDiskChecksummedSize \ sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskNotChecksummedSize /* size of the slot data that is version dependent */ #define ReplicationSlotOnDiskV2Size \ sizeof(ReplicationSlotOnDisk) - ReplicationSlotOnDiskConstantSize
#define SLOT_MAGIC 0x1051CA1 /* format identifier */ #define SLOT_VERSION 5/* version for new files */
/* Control array for replication slot management */
ReplicationSlotCtlData *ReplicationSlotCtl = NULL;
/* My backend's replication slot in the shared memory array */
ReplicationSlot *MyReplicationSlot = NULL;
/* GUC variables */ int max_replication_slots = 10; /* the maximum number of replication
* slots */
/* *Invalidatereplicationslotsthathaveremainedidlelongerthanthis *duration;'0'disablesit.
*/ int idle_replication_slot_timeout_secs = 0;
/* *Releaseandcleanupreplicationslots.
*/ staticvoid
ReplicationSlotShmemExit(int code, Datum arg)
{ /* Make sure active replication slots are released */ if (MyReplicationSlot != NULL)
ReplicationSlotRelease();
/* Also cleanup all the temporary slots. */
ReplicationSlotCleanup(false);
}
if (failover)
{ /* *Donotallowuserstocreatethefailoverenabledslotsonthe *standbyaswedonotsupportsynctothecascadingstandby. * *However,failoverenabledslotscanbecreatedduringslot *synchronizationbecauseweneedtoretainthesamevaluesasthe *remoteslot.
*/ if (RecoveryInProgress() && !IsSyncingReplicationSlots())
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot enable failover for a replication slot created on the standby"));
/* *Donotallowuserstocreatefailoverenabledtemporaryslots, *becausetemporaryslotswillnotbesyncedtothestandby. * *However,failoverenabledtemporaryslotscanbecreatedduring *slotsynchronization.Seethecommentsatopslotsync.cfordetails.
*/ if (persistency == RS_TEMPORARY && !IsSyncingReplicationSlots())
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot enable failover for a temporary replication slot"));
}
/* If all slots are in use, we're out of luck. */ if (slot == NULL)
ereport(ERROR,
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
errmsg("all replication slots are in use"),
errhint("Free one or increase \"max_replication_slots\".")));
/* We can now mark the slot active, and that makes it our slot. */
SpinLockAcquire(&slot->mutex);
Assert(slot->active_pid == 0);
slot->active_pid = MyProcPid;
SpinLockRelease(&slot->mutex);
MyReplicationSlot = slot;
LWLockRelease(ReplicationSlotControlLock);
/* *Createstatisticsentryforthenewlogicalslot.Wedon'tcollectany *statsforphysicalslots,sononeedtocreateanentryforthesame. *SeeReplicationSlotDropPtrforwhyweneedtodothisbeforereleasing *ReplicationSlotAllocationLock.
*/ if (SlotIsLogical(slot))
pgstat_create_replslot(slot);
/* Check if the slot exits with the given name. */
s = SearchNamedReplicationSlot(name, false); if (s == NULL || !s->in_use)
{
LWLockRelease(ReplicationSlotControlLock);
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("replication slot \"%s\" does not exist",
name)));
}
/* *Thisistheslotwewant;checkifit'sactiveundersomeother *process.Insingleusermode,wedon'tneedthischeck.
*/ if (IsUnderPostmaster)
{ /* *Getreadytosleepontheslotincaseitisactive.(Wemayend *upnotsleeping,butwedon'twanttodothiswhileholdingthe *spinlock.)
*/ if (!nowait)
ConditionVariablePrepareToSleep(&s->active_cv);
/* *Ifwefoundtheslotbutit'salreadyactiveinanotherprocess,we *waituntiltheowningprocesssignalsusthatit'sbeenreleased,or *errorout.
*/ if (active_pid != MyProcPid)
{ if (!nowait)
{ /* Wait here until we get signaled, and then restart */
ConditionVariableSleep(&s->active_cv,
WAIT_EVENT_REPLICATION_SLOT_DROP);
ConditionVariableCancelSleep(); goto retry;
}
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("replication slot \"%s\" is active for PID %d",
NameStr(s->data.name), active_pid)));
} elseif (!nowait)
ConditionVariableCancelSleep(); /* no sleep needed after all */
/* We made this slot active, so it's ours now. */
MyReplicationSlot = s;
/* *Weneedtocheckforinvalidationaftermakingtheslotourstoavoid *thepossibleraceconditionwiththecheckpointerthatcanotherwise *invalidatetheslotimmediatelyafterthecheck.
*/ if (error_if_invalid && s->data.invalidated != RS_INVAL_NONE)
ereport(ERROR,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("can no longer access replication slot \"%s\"",
NameStr(s->data.name)),
errdetail("This replication slot has been invalidated due to \"%s\".",
GetSlotInvalidationCauseName(s->data.invalidated)));
/* Let everybody know we've modified this slot */
ConditionVariableBroadcast(&s->active_cv);
/* *Thecalltopgstat_acquire_replslot()protectsagainststatsfora *differentslot,frombeforearestartorsuch,beingpresentduring *pgstat_report_replslot().
*/ if (SlotIsLogical(s))
pgstat_acquire_replslot(s);
/* might not have been set when we've been a plain slot */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
MyProc->statusFlags &= ~PROC_IN_LOGICAL_DECODING;
ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
LWLockRelease(ProcArrayLock);
/* *Donotallowuserstodroptheslotswhicharecurrentlybeingsynced *fromtheprimarytothestandby.
*/ if (RecoveryInProgress() && MyReplicationSlot->data.synced)
ereport(ERROR,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot drop replication slot \"%s\"", name),
errdetail("This replication slot is being synchronized from the primary server."));
if (SlotIsPhysical(MyReplicationSlot))
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot use %s with a physical replication slot", "ALTER_REPLICATION_SLOT"));
if (RecoveryInProgress())
{ /* *Donotallowuserstoaltertheslotswhicharecurrentlybeing *syncedfromtheprimarytothestandby.
*/ if (MyReplicationSlot->data.synced)
ereport(ERROR,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("cannot alter replication slot \"%s\"", name),
errdetail("This replication slot is being synchronized from the primary server."));
/* *Donotallowuserstoenablefailoveronthestandbyaswedonot *supportsynctothecascadingstandby.
*/ if (failover && *failover)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot enable failover for a replication slot" " on the standby"));
}
if (failover)
{ /* *Donotallowuserstoenablefailoverfortemporaryslotsaswedo *notsupportsyncingtemporaryslotstothestandby.
*/ if (*failover && MyReplicationSlot->data.persistency == RS_TEMPORARY)
ereport(ERROR,
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot enable failover for a temporary replication slot"));
if (MyReplicationSlot->data.failover != *failover)
{
SpinLockAcquire(&MyReplicationSlot->mutex);
MyReplicationSlot->data.failover = *failover;
SpinLockRelease(&MyReplicationSlot->mutex);
restart:
LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); for (i = 0; i < max_replication_slots; i++)
{
ReplicationSlot *s; char *slotname; int active_pid;
s = &ReplicationSlotCtl->replication_slots[i];
/* cannot change while ReplicationSlotCtlLock is held */ if (!s->in_use) continue;
/* only logical slots are database specific, skip */ if (!SlotIsLogical(s)) continue;
/* not our database, skip */ if (s->data.database != dboid) continue;
/* NB: intentionally including invalidated slots */
/* acquire slot, so ReplicationSlotDropAcquired can be reused */
SpinLockAcquire(&s->mutex); /* can't change while ReplicationSlotControlLock is held */
slotname = NameStr(s->data.name);
active_pid = s->active_pid; if (active_pid == 0)
{
MyReplicationSlot = s;
s->active_pid = MyProcPid;
}
SpinLockRelease(&s->mutex);
/* *Eventhoughweholdanexclusivelockonthedatabaseobjecta *logicalslotforthatDBcanstillbeactive,e.g.ifit's *concurrentlybeingdroppedbyabackendconnectedtoanotherDB. * *That'sfairlyunlikelyinpractice,sowe'lljustbailout. * *Theslotsyncworkerholdsasharedlockonthedatabasebefore *operatingonsyncedlogicalslotstoavoidconflictwiththedrop *happeninghere.Thepersistentsyncedslotsarethussafebutthere *isapossibilitythattheslotsyncworkerhascreatedatemporary *slot(whichstaysactiveevenonrelease)andwearetryingtodrop *thathere.Inpractice,thechancesofhittingthisscenarioare *lessasduringslotsynchronization,thetemporaryslotis *immediatelyconvertedtopersistentandthusissafeduetothe *sharedlocktakenonthedatabase.So,we'lljustbailoutinsuch *acase. * *XXX:Wecanconsidershuttingdowntheslotsyncworkerbefore *tryingtodropsyncedtemporaryslotshere.
*/ if (active_pid)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("replication slot \"%s\" is active for PID %d",
slotname, active_pid)));
if (max_replication_slots == 0)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("replication slots can only be used if \"max_replication_slots\" > 0")));
if (wal_level < WAL_LEVEL_REPLICA)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("replication slots can only be used if \"wal_level\" >= \"replica\"")));
}
/* *Checkwhethertheuserhasprivilegetousereplicationslots.
*/ void
CheckSlotPermissions(void)
{ if (!has_rolreplication(GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to use replication slots"),
errdetail("Only roles with the %s attribute may use replication slots.", "REPLICATION")));
}
switch (cause)
{ case RS_INVAL_WAL_REMOVED:
{
uint64 ex = oldestLSN - restart_lsn;
appendStringInfo(&err_detail,
ngettext("The slot's restart_lsn %X/%X exceeds the limit by %" PRIu64 " byte.", "The slot's restart_lsn %X/%X exceeds the limit by %" PRIu64 " bytes.",
ex),
LSN_FORMAT_ARGS(restart_lsn),
ex); /* translator: %s is a GUC variable name */
appendStringInfo(&err_hint, _("You might need to increase \"%s\"."), "max_slot_wal_keep_size"); break;
} case RS_INVAL_HORIZON:
appendStringInfo(&err_detail, _("The slot conflicted with xid horizon %u."),
snapshotConflictHorizon); break;
case RS_INVAL_WAL_LEVEL:
appendStringInfoString(&err_detail, _("Logical decoding on standby requires \"wal_level\" >= \"logical\" on the primary server.")); break;
case RS_INVAL_IDLE_TIMEOUT:
{ /* translator: %s is a GUC variable name */
appendStringInfo(&err_detail, _("The slot's idle time of %lds exceeds the configured \"%s\" duration of %ds."),
slot_idle_seconds, "idle_replication_slot_timeout",
idle_replication_slot_timeout_secs); /* translator: %s is a GUC variable name */
appendStringInfo(&err_hint, _("You might need to increase \"%s\"."), "idle_replication_slot_timeout"); break;
} case RS_INVAL_NONE:
pg_unreachable();
}
if (possible_causes & RS_INVAL_WAL_LEVEL)
{ if (SlotIsLogical(s)) return RS_INVAL_WAL_LEVEL;
}
if (possible_causes & RS_INVAL_IDLE_TIMEOUT)
{
Assert(now > 0);
if (CanInvalidateIdleSlot(s))
{ /* *Simulatetheinvalidationduetoidle_timeouttotestthe *timeoutbehaviorpromptly,withoutwaitingforittotrigger *naturally.
*/ #ifdef USE_INJECTION_POINTS if (IS_INJECTION_POINT_ATTACHED("slot-timeout-inval"))
{
*inactive_since = 0; /* since the beginning of time */ return RS_INVAL_IDLE_TIMEOUT;
} #endif
/* we do nothing if the slot is already invalid */ if (s->data.invalidated == RS_INVAL_NONE)
invalidation_cause = DetermineSlotInvalidationCause(possible_causes,
s, oldestLSN,
dboid,
snapshotConflictHorizon,
&inactive_since,
now);
/* if there's no invalidation, we're done */ if (invalidation_cause == RS_INVAL_NONE)
{
SpinLockRelease(&s->mutex); if (released_lock)
LWLockRelease(ReplicationSlotControlLock); break;
}
restart:
LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); for (int i = 0; i < max_replication_slots; i++)
{
ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i];
if (!s->in_use) continue;
/* Prevent invalidation of logical slots during binary upgrade */ if (SlotIsLogical(s) && IsBinaryUpgrade) continue;
if (InvalidatePossiblyObsoleteSlot(possible_causes, s, oldestLSN, dboid,
snapshotConflictHorizon,
&invalidated))
{ /* if the lock was released, start from scratch */ goto restart;
}
}
LWLockRelease(ReplicationSlotControlLock);
/* *Ifanyslotshavebeeninvalidated,recalculatetheresourcelimits.
*/ if (invalidated)
{
ReplicationSlotsComputeRequiredXmin(false);
ReplicationSlotsComputeRequiredLSN();
}
/* we're only creating directories here, skip if it's not our's */ if (de_type != PGFILETYPE_ERROR && de_type != PGFILETYPE_DIR) continue;
/* we crashed while a slot was being setup or deleted, clean up */ if (pg_str_endswith(replication_de->d_name, ".tmp"))
{ if (!rmtree(path, true))
{
ereport(WARNING,
(errmsg("could not remove directory \"%s\"",
path))); continue;
}
fsync_fname(PG_REPLSLOT_DIR, true); continue;
}
/* looks like a slot in a normal state, restore */
RestoreSlotFromDisk(replication_de->d_name);
}
FreeDir(replication_dir);
/* currently no slots exist, we're done. */ if (max_replication_slots <= 0) return;
/* Now that we have recovered all the data, compute replication xmin */
ReplicationSlotsComputeRequiredXmin(false);
ReplicationSlotsComputeRequiredLSN();
}
/* Create and fsync the temporary slot directory. */ if (MakePGDirectory(tmppath) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m",
tmppath)));
fsync_fname(tmppath, true);
/* Write the actual state file. */
slot->dirty = true; /* signal that we really need to write */
SaveSlotToPath(slot, tmppath, ERROR);
/* Rename the directory into place. */ if (rename(tmppath, path) != 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not rename file \"%s\" to \"%s\": %m",
tmppath, path)));
/* if write didn't set errno, assume problem is no disk space */
errno = save_errno ? save_errno : ENOSPC;
ereport(elevel,
(errcode_for_file_access(),
errmsg("could not write to file \"%s\": %m",
tmppath))); return;
}
pgstat_report_wait_end();
/* fsync the temporary file */
pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_SYNC); if (pg_fsync(fd) != 0)
{ int save_errno = errno;
/* *Loadasingleslotfromdiskintomemory.
*/ staticvoid
RestoreSlotFromDisk(constchar *name)
{
ReplicationSlotOnDisk cp; int i; char slotdir[MAXPGPATH + sizeof(PG_REPLSLOT_DIR)]; char path[MAXPGPATH + sizeof(PG_REPLSLOT_DIR) + 10]; int fd; bool restored = false; int readBytes;
pg_crc32c checksum;
TimestampTz now = 0;
/* no need to lock here, no concurrent access allowed yet */
/* delete temp file if it exists */
sprintf(slotdir, "%s/%s", PG_REPLSLOT_DIR, name);
sprintf(path, "%s/state.tmp", slotdir); if (unlink(path) < 0 && errno != ENOENT)
ereport(PANIC,
(errcode_for_file_access(),
errmsg("could not remove file \"%s\": %m", path)));
sprintf(path, "%s/state", slotdir);
elog(DEBUG1, "restoring replication slot from \"%s\"", path);
/* on some operating systems fsyncing a file requires O_RDWR */
fd = OpenTransientFile(path, O_RDWR | PG_BINARY);
/* *Wedonotneedtohandlethisaswearerename()ingthedirectoryinto *placeonlyafterwefsync()edthestatefile.
*/ if (fd < 0)
ereport(PANIC,
(errcode_for_file_access(),
errmsg("could not open file \"%s\": %m", path)));
/* *Syncstatefilebeforewe'rereadingfromit.Wemighthavecrashed *whileitwasn'tsyncedyetandweshouldn'tcontinueonthatbasis.
*/
pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_RESTORE_SYNC); if (pg_fsync(fd) != 0)
ereport(PANIC,
(errcode_for_file_access(),
errmsg("could not fsync file \"%s\": %m",
path)));
pgstat_report_wait_end();
/* Also sync the parent directory */
START_CRIT_SECTION();
fsync_fname(slotdir, true);
END_CRIT_SECTION();
/* read part of statefile that's guaranteed to be version independent */
pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_READ);
readBytes = read(fd, &cp, ReplicationSlotOnDiskConstantSize);
pgstat_report_wait_end(); if (readBytes != ReplicationSlotOnDiskConstantSize)
{ if (readBytes < 0)
ereport(PANIC,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m", path))); else
ereport(PANIC,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("could not read file \"%s\": read %d of %zu",
path, readBytes,
(Size) ReplicationSlotOnDiskConstantSize)));
}
/* verify magic */ if (cp.magic != SLOT_MAGIC)
ereport(PANIC,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("replication slot file \"%s\" has wrong magic number: %u instead of %u",
path, cp.magic, SLOT_MAGIC)));
/* verify version */ if (cp.version != SLOT_VERSION)
ereport(PANIC,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("replication slot file \"%s\" has unsupported version %u",
path, cp.version)));
/* boundary check on length */ if (cp.length != ReplicationSlotOnDiskV2Size)
ereport(PANIC,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("replication slot file \"%s\" has corrupted length %u",
path, cp.length)));
/* Now that we know the size, read the entire file */
pgstat_report_wait_start(WAIT_EVENT_REPLICATION_SLOT_READ);
readBytes = read(fd,
(char *) &cp + ReplicationSlotOnDiskConstantSize,
cp.length);
pgstat_report_wait_end(); if (readBytes != cp.length)
{ if (readBytes < 0)
ereport(PANIC,
(errcode_for_file_access(),
errmsg("could not read file \"%s\": %m", path))); else
ereport(PANIC,
(errcode(ERRCODE_DATA_CORRUPTED),
errmsg("could not read file \"%s\": read %d of %zu",
path, readBytes, (Size) cp.length)));
}
if (CloseTransientFile(fd) != 0)
ereport(PANIC,
(errcode_for_file_access(),
errmsg("could not close file \"%s\": %m", path)));
/* now verify the CRC */
INIT_CRC32C(checksum);
COMP_CRC32C(checksum,
(char *) &cp + ReplicationSlotOnDiskNotChecksummedSize,
ReplicationSlotOnDiskChecksummedSize);
FIN_CRC32C(checksum);
if (!EQ_CRC32C(checksum, cp.checksum))
ereport(PANIC,
(errmsg("checksum mismatch for replication slot file \"%s\": is %u, should be %u",
path, checksum, cp.checksum)));
/* *Ifwecrashedwithanephemeralslotactive,don'trestorebutdelete *it.
*/ if (cp.slotdata.persistency != RS_PERSISTENT)
{ if (!rmtree(slotdir, true))
{
ereport(WARNING,
(errmsg("could not remove directory \"%s\"",
slotdir)));
}
fsync_fname(PG_REPLSLOT_DIR, true); return;
}
/* *Verifythatrequirementsforthespecificslottypearemet.That's *importantbecauseifthesearen'tmetwe'renotguaranteedtoretain *allthenecessaryresourcesfortheslot. * *NB:Wehavetodoso*after*theabovechecksforephemeralslots, *becauseotherwiseaslotthatshouldn'texistanymorecouldprevent *restarts. * *NB:Changingtherequirementsherealsorequiresadapting *CheckSlotRequirements()andCheckLogicalDecodingRequirements().
*/ if (cp.slotdata.database != InvalidOid)
{ if (wal_level < WAL_LEVEL_LOGICAL)
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("logical replication slot \"%s\" exists, but \"wal_level\" < \"logical\"",
NameStr(cp.slotdata.name)),
errhint("Change \"wal_level\" to be \"logical\" or higher.")));
/* *Instandbymode,thehotstandbymustbeenabled.Thischeckis *necessarytoensurelogicalslotsareinvalidatedwhentheybecome *incompatibleduetoinsufficientwal_level.Otherwise,ifthe *primaryreduceswal_level<logicalwhilehotstandbyisdisabled, *logicalslotswouldremainvalidevenafterpromotion.
*/ if (StandbyMode && !EnableHotStandby)
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("logical replication slot \"%s\" exists on the standby, but \"hot_standby\" = \"off\"",
NameStr(cp.slotdata.name)),
errhint("Change \"hot_standby\" to be \"on\".")));
} elseif (wal_level < WAL_LEVEL_REPLICA)
ereport(FATAL,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("physical replication slot \"%s\" exists, but \"wal_level\" < \"replica\"",
NameStr(cp.slotdata.name)),
errhint("Change \"wal_level\" to be \"replica\" or higher.")));
/* nothing can be active yet, don't lock anything */ for (i = 0; i < max_replication_slots; i++)
{
ReplicationSlot *slot;
slot = &ReplicationSlotCtl->replication_slots[i];
if (slot->in_use) continue;
/* restore the entire set of persistent data */
memcpy(&slot->data, &cp.slotdata, sizeof(ReplicationSlotPersistentData));
/* initialize in memory state */
slot->effective_xmin = cp.slotdata.xmin;
slot->effective_catalog_xmin = cp.slotdata.catalog_xmin;
slot->last_saved_confirmed_flush = cp.slotdata.confirmed_flush;
slot->last_saved_restart_lsn = cp.slotdata.restart_lsn;
if (!restored)
ereport(FATAL,
(errmsg("too many replication slots active before shutdown"),
errhint("Increase \"max_replication_slots\" and try again.")));
}
/* Search lookup table for the cause having this name */ for (int i = 0; i <= RS_INVAL_MAX_CAUSES; i++)
{ if (strcmp(SlotInvalidationCauses[i].cause_name, cause_name) == 0) return SlotInvalidationCauses[i].cause;
}
Assert(false); return RS_INVAL_NONE; /* to keep compiler quiet */
}
/* *MapsanReplicationSlotInvalidationCausetotheinvalidation *reasonforareplicationslot.
*/ constchar *
GetSlotInvalidationCauseName(ReplicationSlotInvalidationCause cause)
{ /* Search lookup table for the name of this cause */ for (int i = 0; i <= RS_INVAL_MAX_CAUSES; i++)
{ if (SlotInvalidationCauses[i].cause == cause) return SlotInvalidationCauses[i].cause_name;
}
Assert(false); return"none"; /* to keep compiler quiet */
}
/* *AhelperfunctiontovalidateslotsspecifiedinGUCsynchronized_standby_slots. * *Therawnamewillbeparsed,andtheresultwillbesavedinto*elemlist.
*/ staticbool
validate_sync_standby_slots(char *rawname, List **elemlist)
{ /* Verify syntax and parse string into a list of identifiers */ if (!SplitIdentifierString(rawname, ',', elemlist))
{
GUC_check_errdetail("List syntax is invalid."); returnfalse;
}
/* Iterate the list to validate each slot name */
foreach_ptr(char, name, *elemlist)
{ int err_code; char *err_msg = NULL; char *err_hint = NULL;
if (!ReplicationSlotValidateNameInternal(name, &err_code, &err_msg,
&err_hint))
{
GUC_check_errcode(err_code);
GUC_check_errdetail("%s", err_msg); if (err_hint != NULL)
GUC_check_errhint("%s", err_hint); returnfalse;
}
}
name = synchronized_standby_slots_config->slot_names; for (int i = 0; i < synchronized_standby_slots_config->nslotnames; i++)
{
XLogRecPtr restart_lsn; bool invalidated; bool inactive;
ReplicationSlot *slot;
slot = SearchNamedReplicationSlot(name, false);
/* *Ifaslotnameprovidedinsynchronized_standby_slotsdoesnot *exist,reportamessageandexittheloop.
*/ if (!slot)
{
ereport(elevel,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("replication slot \"%s\" specified in parameter \"%s\" does not exist",
name, "synchronized_standby_slots"),
errdetail("Logical replication is waiting on the standby associated with replication slot \"%s\".",
name),
errhint("Create the replication slot \"%s\" or amend parameter \"%s\".",
name, "synchronized_standby_slots")); break;
}
/* Same as above: if a slot is not physical, exit the loop. */ if (SlotIsLogical(slot))
{
ereport(elevel,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cannot specify logical replication slot \"%s\" in parameter \"%s\"",
name, "synchronized_standby_slots"),
errdetail("Logical replication is waiting for correction on replication slot \"%s\".",
name),
errhint("Remove the logical replication slot \"%s\" from parameter \"%s\".",
name, "synchronized_standby_slots")); break;
}
if (invalidated)
{ /* Specified physical slot has been invalidated */
ereport(elevel,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("physical replication slot \"%s\" specified in parameter \"%s\" has been invalidated",
name, "synchronized_standby_slots"),
errdetail("Logical replication is waiting on the standby associated with replication slot \"%s\".",
name),
errhint("Drop and recreate the replication slot \"%s\", or amend parameter \"%s\".",
name, "synchronized_standby_slots")); break;
}
if (XLogRecPtrIsInvalid(restart_lsn) || restart_lsn < wait_for_lsn)
{ /* Log a message if no active_pid for this physical slot */ if (inactive)
ereport(elevel,
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("replication slot \"%s\" specified in parameter \"%s\" does not have active_pid",
name, "synchronized_standby_slots"),
errdetail("Logical replication is waiting on the standby associated with replication slot \"%s\".",
name),
errhint("Start the standby associated with the replication slot \"%s\", or amend parameter \"%s\".",
name, "synchronized_standby_slots"));
/* Continue if the current slot hasn't caught up. */ break;
}
Assert(restart_lsn >= wait_for_lsn);
if (XLogRecPtrIsInvalid(min_restart_lsn) ||
min_restart_lsn > restart_lsn)
min_restart_lsn = restart_lsn;
caught_up_slot_num++;
name += strlen(name) + 1;
}
LWLockRelease(ReplicationSlotControlLock);
/* *Returnfalseifnotallthestandbyshavecaughtuptothespecified *WALlocation.
*/ if (caught_up_slot_num != synchronized_standby_slots_config->nslotnames) returnfalse;
/* The ss_oldest_flush_lsn must not retreat. */
Assert(XLogRecPtrIsInvalid(ss_oldest_flush_lsn) ||
min_restart_lsn >= ss_oldest_flush_lsn);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.