bool InRecovery = false;
/* Are we recovering using offline XLOG archives? */
-static bool InArchiveRecovery = false;
+bool InArchiveRecovery = false;
+
+static XLogRecPtr LastRec;
/*
- * Local copy of shared RecoveryProcessingMode variable. True actually
- * means "not known, need to check the shared state"
+ * Local copy of SharedRecoveryInProgress variable. True actually means "not
+ * known, need to check the shared state"
*/
- static bool LocalRecoveryProcessingMode = true;
+ static bool LocalRecoveryInProgress = true;
+/* is the database proven consistent yet? */
+bool reachedSafeStartPoint = false;
+
/* Was the last xlog file restored from archive, or local? */
static bool restoredFromArchive = false;
static bool
recoveryStopsHere(XLogRecord *record, bool *includeThis)
{
- bool stopsHere;
- uint8 record_info;
- TimestampTz recordXtime;
-
+ bool stopsHere = false;
+ bool pauseHere = false;
+ static bool paused = false;
+ uint8 record_info = 0; /* valid iff (is_xact_completion_record) */
+ TimestampTz recordXtime = 0;
+ bool is_xact_completion_record = false;
+
/* We only consider stopping at COMMIT or ABORT records */
- if (record->xl_rmid != RM_XACT_ID)
- return false;
- record_info = record->xl_info & ~XLR_INFO_MASK;
- if (record_info == XLOG_XACT_COMMIT)
+ if (record->xl_rmid == RM_XACT_ID)
{
- xl_xact_commit *recordXactCommitData;
+ record_info = record->xl_info & ~XLR_INFO_MASK;
+ if (record_info == XLOG_XACT_COMMIT)
+ {
+ xl_xact_commit *recordXactCommitData;
- recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record);
- recordXtime = recordXactCommitData->xact_time;
- }
- else if (record_info == XLOG_XACT_ABORT)
- {
- xl_xact_abort *recordXactAbortData;
+ recordXactCommitData = (xl_xact_commit *) XLogRecGetData(record);
+ recordXtime = recordXactCommitData->xact_time;
+ is_xact_completion_record = true;
+ }
+ else if (record_info == XLOG_XACT_ABORT)
+ {
+ xl_xact_abort *recordXactAbortData;
- recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
- recordXtime = recordXactAbortData->xact_time;
- }
- else
- return false;
+ recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record);
+ recordXtime = recordXactAbortData->xact_time;
+ is_xact_completion_record = true;
+ }
- /* Do we have a PITR target at all? */
- if (!recoveryTarget)
- {
- recoveryLastXTime = recordXtime;
- return false;
+ /* Remember the most recent COMMIT/ABORT time for logging purposes */
+ if (is_xact_completion_record)
+ {
+ recoveryLastXTime = recordXtime;
+ recoveryLastXid = record->xl_xid;
+ }
}
- if (recoveryTargetExact)
+ do
{
+ int prevRecoveryTargetMode = recoveryTargetMode;
+
+ CHECK_FOR_INTERRUPTS();
+
/*
- * there can be only one transaction end record with this exact
- * transactionid
- *
- * when testing for an xid, we MUST test for equality only, since
- * transactions are numbered in the order they start, not the order
- * they complete. A higher numbered xid will complete before you about
- * 50% of the time...
+ * Check if we were requested to exit without finishing
+ * recovery.
*/
- stopsHere = (record->xl_xid == recoveryTargetXid);
- if (stopsHere)
- *includeThis = recoveryTargetInclusive;
- }
- else
- {
+ if (shutdown_requested)
- proc_exit(0);
++ proc_exit(1);
+
/*
- * there can be many transactions that share the same commit time, so
- * we stop after the last one, if we are inclusive, or stop at the
- * first one if we are exclusive
+ * Let's see if user has updated our recoveryTargetMode.
*/
- if (recoveryTargetInclusive)
- stopsHere = (recordXtime > recoveryTargetTime);
- else
- stopsHere = (recordXtime >= recoveryTargetTime);
- if (stopsHere)
- *includeThis = false;
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+ recoveryTargetMode = xlogctl->recoveryTargetMode;
+ if (recoveryTargetMode != RECOVERY_TARGET_NONE)
+ {
+ recoveryTargetXid = xlogctl->recoveryTargetXid;
+ recoveryTargetTime = xlogctl->recoveryTargetTime;
+
+ /* Don't reset counter while we're advancing */
+ if (recoveryTargetAdvance <= 0)
+ {
+ recoveryTargetAdvance = xlogctl->recoveryTargetAdvance;
+ xlogctl->recoveryTargetAdvance = 0;
+ }
+ }
+ if (is_xact_completion_record)
+ {
+ xlogctl->recoveryLastXTime = recordXtime;
+ xlogctl->recoveryLastXid = record->xl_xid;
+ }
+ xlogctl->recoveryLastRecPtr = LastRec;
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ /* Decide how to act on any pause target */
+ switch (recoveryTargetMode)
+ {
+ case RECOVERY_TARGET_PAUSE_LSN:
+ return false;
+
+ case RECOVERY_TARGET_NONE:
+ /*
+ * If we aren't paused and we're not looking to stop,
+ * just exit out quickly and get on with recovery.
+ */
+ if (paused)
+ {
+ ereport(LOG,
+ (errmsg("recovery restarting after pause")));
+ set_ps_display("recovery continues", false);
+ paused = false;
+ }
+ return false;
+
+ case RECOVERY_TARGET_PAUSE_ALL:
+ pauseHere = true;
+ break;
+
+ case RECOVERY_TARGET_ADVANCE:
+ if (paused)
+ {
+ if (recoveryTargetAdvance-- > 0)
+ {
+ elog(LOG, "recovery advancing 1 record");
+ return false;
+ }
+ else
+ break;
+ }
+
+ if (recoveryTargetAdvance-- <= 0)
+ pauseHere = true;
+ break;
+
+ case RECOVERY_TARGET_STOP_IMMEDIATE:
+ case RECOVERY_TARGET_STOP_XID:
+ case RECOVERY_TARGET_STOP_TIME:
+ paused = false;
+ break;
+
+ /*
+ * If we're paused, and mode has changed reset to allow new settings
+ * to apply and maybe allow us to continue.
+ */
+ if (paused && prevRecoveryTargetMode != recoveryTargetMode)
+ paused = false;
+
+ case RECOVERY_TARGET_PAUSE_XID:
+ /*
+ * there can be only one transaction end record with this exact
+ * transactionid
+ *
+ * when testing for an xid, we MUST test for equality only, since
+ * transactions are numbered in the order they start, not the order
+ * they complete. A higher numbered xid will complete before you about
+ * 50% of the time...
+ */
+ if (is_xact_completion_record)
+ pauseHere = (record->xl_xid == recoveryTargetXid);
+ break;
+
+ case RECOVERY_TARGET_PAUSE_TIME:
+ /*
+ * there can be many transactions that share the same commit time, so
+ * we pause after the last one, if we are inclusive, or pause at the
+ * first one if we are exclusive
+ */
+ if (is_xact_completion_record)
+ {
+ if (recoveryTargetInclusive)
+ pauseHere = (recoveryLastXTime > recoveryTargetTime);
+ else
+ pauseHere = (recoveryLastXTime >= recoveryTargetTime);
+ }
+ break;
+
+ default:
+ ereport(WARNING,
+ (errmsg("unknown recovery mode %d, continuing recovery",
+ recoveryTargetMode)));
+ return false;
+ }
+
+ /*
+ * If we just entered pause, issue log messages
+ */
+ if (pauseHere && !paused)
+ {
+ if (is_xact_completion_record)
+ {
+ if (record_info == XLOG_XACT_COMMIT)
+ ereport(LOG,
+ (errmsg("recovery pausing before commit of transaction %u, log time %s",
+ record->xl_xid,
+ timestamptz_to_str(recoveryLastXTime))));
+ else
+ ereport(LOG,
+ (errmsg("recovery pausing before abort of transaction %u, log time %s",
+ record->xl_xid,
+ timestamptz_to_str(recoveryLastXTime))));
+ }
+ else
+ ereport(LOG,
+ (errmsg("recovery pausing; last recovered transaction %u, "
+ "last recovered xact timestamp %s",
+ recoveryLastXid,
+ timestamptz_to_str(recoveryLastXTime))));
+
+ set_ps_display("recovery paused", false);
+
+ paused = true;
+ }
+
+ /*
+ * Pause for a while before rechecking mode at top of loop.
+ */
+ if (paused)
+ {
+ recoveryTargetAdvance = 0;
+
+ /*
+ * Update the recoveryTargetMode
+ */
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+ xlogctl->recoveryTargetMode = RECOVERY_TARGET_PAUSE_ALL;
+ xlogctl->recoveryTargetAdvance = 0;
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ pg_usleep(200000L);
+ }
+
+ /*
+ * We leave the loop at the bottom only if our recovery mode is
+ * set (or has been recently reset) to one of the stop options.
+ */
+ } while (paused);
+
+ /*
+ * Decide how to act if stop target mode set. We run this separately from
+ * pause to allow user to reset their stop target while paused.
+ */
+ switch (recoveryTargetMode)
+ {
+ case RECOVERY_TARGET_STOP_IMMEDIATE:
+ ereport(LOG,
+ (errmsg("recovery stopping immediately due to user request")));
+ return true;
+
+ case RECOVERY_TARGET_STOP_XID:
+ /*
+ * there can be only one transaction end record with this exact
+ * transactionid
+ *
+ * when testing for an xid, we MUST test for equality only, since
+ * transactions are numbered in the order they start, not the order
+ * they complete. A higher numbered xid will complete before you about
+ * 50% of the time...
+ */
+ if (is_xact_completion_record)
+ {
+ stopsHere = (record->xl_xid == recoveryTargetXid);
+ if (stopsHere)
+ *includeThis = recoveryTargetInclusive;
+ }
+ break;
+
+ case RECOVERY_TARGET_STOP_TIME:
+ /*
+ * there can be many transactions that share the same commit time, so
+ * we stop after the last one, if we are inclusive, or stop at the
+ * first one if we are exclusive
+ */
+ if (is_xact_completion_record)
+ {
+ if (recoveryTargetInclusive)
+ stopsHere = (recoveryLastXTime > recoveryTargetTime);
+ else
+ stopsHere = (recoveryLastXTime >= recoveryTargetTime);
+ if (stopsHere)
+ *includeThis = false;
+ }
+ break;
}
if (stopsHere)
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
+ }
- if (recoveryStopAfter)
- recoveryLastXTime = recordXtime;
+ return stopsHere;
+}
+
+static void
+recoveryPausesAfterLSN(void)
+{
+ while (recoveryTargetMode == RECOVERY_TARGET_PAUSE_LSN &&
+ XLByteLE(recoveryTargetLSN, LastRec))
+ {
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+ recoveryTargetMode = xlogctl->recoveryTargetMode;
+ recoveryTargetLSN = xlogctl->recoveryTargetLSN;
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ pg_usleep(25000L);
+ }
+}
+
+/*
+ * Utility function used by various user functions to set the recovery
+ * target mode. This allows user control over the progress of recovery.
+ */
+static void
+SetRecoveryTargetMode(int mode, TransactionId xid, TimestampTz ts,
+ XLogRecPtr lsn, int advance)
+{
- if (!IsRecoveryProcessingMode())
++ if (!RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is not in progress"),
+ errhint("WAL control functions can only be executed during recovery.")));
+
+ if (!InRecovery && !superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to control recovery")));
+
+
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+ xlogctl->recoveryTargetMode = mode;
+
+ if (mode == RECOVERY_TARGET_STOP_XID ||
+ mode == RECOVERY_TARGET_PAUSE_XID)
+ xlogctl->recoveryTargetXid = xid;
+ else if (mode == RECOVERY_TARGET_STOP_TIME ||
+ mode == RECOVERY_TARGET_PAUSE_TIME)
+ xlogctl->recoveryTargetTime = ts;
+ else if (mode == RECOVERY_TARGET_ADVANCE)
+ xlogctl->recoveryTargetAdvance = advance;
+ else if (mode == RECOVERY_TARGET_PAUSE_LSN)
+ xlogctl->recoveryTargetLSN = lsn;
+
+ SpinLockRelease(&xlogctl->info_lck);
}
- PG_RETURN_BOOL(IsRecoveryProcessingMode());
+}
+
+/*
+ * Forces recovery mode to reset to unfrozen.
+ * Returns void.
+ */
+Datum
+pg_recovery_continue(PG_FUNCTION_ARGS)
+{
+ SetRecoveryTargetMode(RECOVERY_TARGET_NONE,
+ InvalidTransactionId, 0, InvalidXLogRecPtr, 0);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Pause recovery immediately. Stays paused until asked to play again.
+ * Returns void.
+ */
+Datum
+pg_recovery_pause(PG_FUNCTION_ARGS)
+{
+ SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_ALL,
+ InvalidTransactionId, 0, InvalidXLogRecPtr, 0);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Pause recovery at stated xid, if ever seen. Once paused, stays paused
+ * until asked to play again.
+ */
+Datum
+pg_recovery_pause_xid(PG_FUNCTION_ARGS)
+{
+ int xidi = PG_GETARG_INT32(0);
+ TransactionId xid = (TransactionId) xidi;
+
+ if (xid < 3)
+ elog(ERROR, "cannot specify special values for transaction id");
+
+ SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_XID,
+ xid, 0, InvalidXLogRecPtr, 0);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Pause recovery at stated timestamp, if ever reached. Once paused, stays paused
+ * until asked to play again.
+ */
+Datum
+pg_recovery_pause_time(PG_FUNCTION_ARGS)
+{
+ TimestampTz ts = PG_GETARG_TIMESTAMPTZ(0);
+
+ SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_TIME,
+ InvalidTransactionId, ts, InvalidXLogRecPtr, 0);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Pause recovery after stated LSN, if ever reached. Once paused, stays paused
+ * until asked to play again.
+ */
+Datum
+pg_recovery_pause_lsn(PG_FUNCTION_ARGS)
+{
+ XLogRecPtr lsn;
+
+ lsn.xlogid = PG_GETARG_INT32(0);
+ lsn.xrecoff = PG_GETARG_INT32(1);
+
+ SetRecoveryTargetMode(RECOVERY_TARGET_PAUSE_LSN,
+ InvalidTransactionId, 0, lsn, 0);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * If paused, advance N records.
+ */
+Datum
+pg_recovery_advance(PG_FUNCTION_ARGS)
+{
+ int adv = PG_GETARG_INT32(0);
+
+ if (adv < 1)
+ elog(ERROR, "recovery advance must be greater than or equal to 1");
+
+ SetRecoveryTargetMode(RECOVERY_TARGET_ADVANCE,
+ InvalidTransactionId, 0, InvalidXLogRecPtr, adv);
+
+ PG_RETURN_VOID();
+}
+
+/*
+ * Forces recovery to stop now if paused, or at end of next record if playing.
+ */
+Datum
+pg_recovery_stop(PG_FUNCTION_ARGS)
+{
+ SetRecoveryTargetMode(RECOVERY_TARGET_STOP_IMMEDIATE,
+ InvalidTransactionId, 0, InvalidXLogRecPtr, 0);
+
+ PG_RETURN_VOID();
+}
+
+Datum
+pg_current_recovery_target(PG_FUNCTION_ARGS)
+{
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+
+ recoveryTargetMode = xlogctl->recoveryTargetMode;
+ if (recoveryTargetMode != RECOVERY_TARGET_NONE)
+ {
+ recoveryTargetXid = xlogctl->recoveryTargetXid;
+ recoveryTargetTime = xlogctl->recoveryTargetTime;
+ recoveryTargetAdvance = xlogctl->recoveryTargetAdvance;
+ }
+
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ switch (recoveryTargetMode)
+ {
+ case RECOVERY_TARGET_NONE:
+ appendStringInfo(&buf, "No recovery target has been set");
+ break;
+ case RECOVERY_TARGET_PAUSE_ALL:
+ appendStringInfo(&buf, "Recovery paused");
+ break;
+ case RECOVERY_TARGET_PAUSE_XID:
+ appendStringInfo(&buf, "Recovery will pause after commit of transaction %u", recoveryTargetXid);
+ break;
+ case RECOVERY_TARGET_PAUSE_TIME:
+ appendStringInfo(&buf, "Recovery will pause after transaction completion timestamp %s",
+ timestamptz_to_str(recoveryTargetTime));
+ break;
+ case RECOVERY_TARGET_PAUSE_LSN:
+ appendStringInfo(&buf, "Recovery will pause after applying record at xlog location %X/%X",
+ recoveryTargetLSN.xlogid,
+ recoveryTargetLSN.xrecoff);
+ break;
+ case RECOVERY_TARGET_ADVANCE:
+ appendStringInfo(&buf, "Recovery will advance");
+ break;
+ case RECOVERY_TARGET_STOP_IMMEDIATE:
+ appendStringInfo(&buf, "No recovery target has been set");
+ break;
+ case RECOVERY_TARGET_STOP_XID:
+ appendStringInfo(&buf, "Recovery will stop after commit of transaction %u", recoveryTargetXid);
+ break;
+ case RECOVERY_TARGET_STOP_TIME:
+ appendStringInfo(&buf, "Recovery will stop after transaction completion timestamp %s",
+ timestamptz_to_str(recoveryTargetTime));
+ break;
+ }
+
+ PG_RETURN_TEXT_P(cstring_to_text(buf.data));
+}
+
+/*
+ * Returns bool with current recovery mode, a global state.
+ */
+Datum
+pg_is_in_recovery(PG_FUNCTION_ARGS)
+{
++ PG_RETURN_BOOL(RecoveryInProgress());
+}
+
+/*
+ * Returns timestamp of last completed transaction
+ */
+Datum
+pg_last_recovered_xact_timestamp(PG_FUNCTION_ARGS)
+{
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+
+ recoveryLastXTime = xlogctl->recoveryLastXTime;
+
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ PG_RETURN_TIMESTAMPTZ(recoveryLastXTime);
+}
+
+/*
+ * Returns xid of last completed transaction
+ */
+Datum
+pg_last_recovered_xid(PG_FUNCTION_ARGS)
+{
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+
+ recoveryLastXid = xlogctl->recoveryLastXid;
+
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ PG_RETURN_INT32(recoveryLastXid);
+}
+
+/*
+ * Returns xlog location of last recovered WAL record.
+ */
+Datum
+pg_last_recovered_xlog_location(PG_FUNCTION_ARGS)
+{
+ char location[MAXFNAMELEN];
+
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile XLogCtlData *xlogctl = XLogCtl;
+
+ SpinLockAcquire(&xlogctl->info_lck);
+
+ LastRec = xlogctl->recoveryLastRecPtr;
+
+ SpinLockRelease(&xlogctl->info_lck);
+ }
+
+ snprintf(location, sizeof(location), "%X/%X",
+ LastRec.xlogid, LastRec.xrecoff);
+ PG_RETURN_TEXT_P(cstring_to_text(location));
+}
+
+/*
+ * Returns delay in milliseconds, or -1 if delay too large
+ */
+int
+GetLatestReplicationDelay(void)
+{
+ long delay_secs;
+ int delay_usecs;
+ int delay;
+ TimestampTz currTz = GetCurrentTimestamp();
+
+ TimestampDifference(recoveryLastXTime, currTz,
+ &delay_secs, &delay_usecs);
+
+ /*
+ * If delay is very large we probably aren't looking at
+ * a replication situation at all, just a recover from backup.
+ * So return a special value instead.
+ */
+ if (delay_secs > (long)(INT_MAX / 1000))
+ delay = -1;
else
- recoveryLastXTime = recordXtime;
+ delay = (int)(delay_secs * 1000) + (delay_usecs / 1000);
- return stopsHere;
+ return delay;
}
/*
pfree(buf.data);
}
#endif
-
- /*
- * Check if we were requested to exit without finishing
- * recovery.
- */
- if (shutdown_requested)
- proc_exit(1);
--
/*
* Have we reached our safe starting point? If so, we can
* tell postmaster that the database is consistent now.
/*
* Initialize TimeLineID and RedoRecPtr the first time we see that
- * recovery is finished.
+ * recovery is finished. InitPostgres() relies upon this behaviour
+ * to ensure that InitXLOGAccess() is called at backend startup.
*/
- if (!LocalRecoveryProcessingMode)
+ if (!LocalRecoveryInProgress)
InitXLOGAccess();
- return LocalRecoveryProcessingMode;
+ return LocalRecoveryInProgress;
}
}
CheckpointStats.ckpt_segs_recycled);
LWLockRelease(CheckpointLock);
-}
- if (!shutdown && !IsRecoveryProcessingMode())
+ /*
+ * Take a snapshot of running transactions and write this to WAL.
+ * This allows us to reconstruct the state of running transactions
+ * during archive recovery, if required.
+ *
+ * If we are shutting down, or Startup process is completing crash
+ * recovery we don't need to write running xact data.
+ */
++ if (!shutdown && !RecoveryInProgress())
+ LogCurrentRunningXacts();
+}
+
/*
* Flush all data in shared memory to disk, and fsync
*
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to run a backup")));
- if (IsRecoveryProcessingMode())
++ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("WAL control functions cannot be executed during recovery.")));
+
if (!XLogArchivingActive())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to run a backup"))));
- if (IsRecoveryProcessingMode())
++ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("WAL control functions cannot be executed during recovery.")));
+
if (!XLogArchivingActive())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to switch transaction log files"))));
- if (IsRecoveryProcessingMode())
++ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("WAL control functions cannot be executed during recovery.")));
+
switchpoint = RequestXLogSwitch();
/*
{
char location[MAXFNAMELEN];
- if (IsRecoveryProcessingMode())
++ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("WAL control functions cannot be executed during recovery.")));
+
/* Make sure we have an up-to-date local LogwrtResult */
{
/* use volatile pointer to prevent code rearrangement */
XLogRecPtr current_recptr;
char location[MAXFNAMELEN];
- if (IsRecoveryProcessingMode())
++ if (RecoveryInProgress())
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("recovery is in progress"),
+ errhint("WAL control functions cannot be executed during recovery.")));
+
/*
* Get the current end-of-WAL position ... shared lock is sufficient
*/
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
- if (IsRecoveryProcessingMode())
+ /*
+ * Remove any UnobservedXids remaining
+ */
++ if (RecoveryInProgress())
+ {
+ for (index = 0; index < nsubxids; index++)
+ UnobservedTransactionsRemoveXid(subxids[index], false);
+ }
+
if (TransactionIdIsValid(latestXid))
{
Assert(TransactionIdIsValid(proc->xid));
else
{
/* Shouldn't be trying to remove a live transaction here */
- Assert(IsRecoveryProcessingMode() || !TransactionIdIsValid(proc->xid));
- Assert(!TransactionIdIsValid(proc->xid));
++ Assert(RecoveryInProgress() || !TransactionIdIsValid(proc->xid));
}
for (index = 0; index < arrayP->numProcs; index++)
/* Ooops */
LWLockRelease(ProcArrayLock);
- elog(IsRecoveryProcessingMode() ? ERROR : LOG,
- elog(LOG, "failed to find proc %p in ProcArray", proc);
++ elog(RecoveryInProgress() ? ERROR : LOG,
+ "failed to find proc %p in ProcArray", proc);
}
+/*
+ * Initialisation when we switch into PM_RECOVERY mode.
+ * Expected caller is InitRecoveryTransactionEnvironment()
+ */
+void
+ProcArrayInitRecoveryEnvironment(void)
+{
+ PublishStartupProcessInformation();
+}
/*
* ProcArrayEndTransaction -- mark a transaction as no longer running
TransactionId result;
int index;
- Assert(allDbs || !IsRecoveryProcessingMode());
+ /* Cannot look for individual databases during recovery */
++ Assert(allDbs || !RecoveryInProgress());
+
LWLockAcquire(ProcArrayLock, LW_SHARED);
/*
if (snapshot->xip == NULL)
{
/*
- * First call for this snapshot
+ * First call for this snapshot. In recovery we need an additional
+ * space allocation to allow for UnobservedXids, which never occur
+ * in normal running.
*/
- if (IsRecoveryProcessingMode())
- snapshot->xip = (TransactionId *)
- malloc(arrayP->maxProcs * sizeof(TransactionId));
++ if (RecoveryInProgress())
+ snapshot->xip = (TransactionId *)
+ malloc(3 * arrayP->maxProcs * sizeof(TransactionId));
+ else
+ snapshot->xip = (TransactionId *)
+ malloc(arrayP->maxProcs * sizeof(TransactionId));
if (snapshot->xip == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
*/
LWLockAcquire(ProcArrayLock, LW_SHARED);
- if (IsRecoveryProcessingMode() && !arrayP->allowStandbySnapshots)
++ if (RecoveryInProgress() && !arrayP->allowStandbySnapshots)
+ {
+ LWLockRelease(ProcArrayLock);
+ ereport(ERROR,
+ (errcode(ERRCODE_QUERY_CANCELED),
+ errmsg("canceling statement because standby snapshots are currently disabled"),
+ errdetail("Valid MVCC snapshot cannot be taken at this time."),
+ errhint("Contact your administrator if this error recurs frequently")));
+ }
+
/* xmax is always latestCompletedXid + 1 */
xmax = ShmemVariableCache->latestCompletedXid;
Assert(TransactionIdIsNormal(xmax));
if (proc->subxids.overflowed)
subcount = -1; /* overflowed */
else
- {
- int nxids = proc->subxids.nxids;
+ {
+ int nxids = proc->subxids.nxids;
+
+ if (nxids > 0)
+ {
+ memcpy(snapshot->subxip + subcount,
+ (void *) proc->subxids.xids,
+ nxids * sizeof(TransactionId));
+ subcount += nxids;
+ }
+ }
+ }
+ }
- if (nxids > 0)
- {
- memcpy(snapshot->subxip + subcount,
- (void *) proc->subxids.xids,
- nxids * sizeof(TransactionId));
- subcount += nxids;
- }
- }
- }
+ /*
+ * Also check for unobserved xids. There is no need for us to specify
- * that this is only if IsRecoveryProcessingMode(), since the list will
++ * that this is only if RecoveryInProgress(), since the list will
+ * always be empty when normal processing begins and the test will be
+ * optimised to nearly nothing very quickly.
+ */
+ for (index = 0; index < arrayP->numUnobservedXids; index++)
+ {
+ volatile TransactionId *UnobservedXids;
+ TransactionId xid;
+
+ UnobservedXids = (TransactionId *) &(arrayP->procs[arrayP->maxProcs]);
+
+ /* Fetch xid just once - see GetNewTransactionId */
+ xid = UnobservedXids[index];
+
+ /*
+ * If there are no more visible xids, we're done. This works
+ * because UnobservedXids is maintained in strict ascending order.
+ */
+ if (!TransactionIdIsNormal(xid) || TransactionIdPrecedes(xid, xmax))
+ break;
+
+ /*
+ * Add unobserved xids onto the main xip array.
+ */
+ snapshot->xip[count++] = xid;
+
+ /*
+ * Check to see if this changes xmin. It is possible that an unobserved
+ * xid could be xmin if there is contention between long-lived
+ * transactions.
+ */
+ if (TransactionIdPrecedes(xid, xmin))
+ xmin = xid;
}
if (!TransactionIdIsValid(MyProc->xmin))
int autovac_pids[MAXAUTOVACPIDS];
int tries;
- Assert(!IsRecoveryProcessingMode());
+ /* Gives wrong answer in recovery, so make sure we don't use it */
++ Assert(!RecoveryInProgress());
+
/* 50 tries with 100ms sleep between tries makes 5 sec total wait */
for (tries = 0; tries < 50; tries++)
{