PostgreSQL Source Code  git master
standby.h File Reference
Include dependency graph for standby.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  RunningTransactionsData
 

Macros

#define MinSizeOfXactRunningXacts   offsetof(xl_running_xacts, xids)
 

Typedefs

typedef struct RunningTransactionsData RunningTransactionsData
 
typedef RunningTransactionsDataRunningTransactions
 

Functions

void InitRecoveryTransactionEnvironment (void)
 
void ShutdownRecoveryTransactionEnvironment (void)
 
void ResolveRecoveryConflictWithSnapshot (TransactionId latestRemovedXid, RelFileNode node)
 
void ResolveRecoveryConflictWithSnapshotFullXid (FullTransactionId latestRemovedFullXid, RelFileNode node)
 
void ResolveRecoveryConflictWithTablespace (Oid tsid)
 
void ResolveRecoveryConflictWithDatabase (Oid dbid)
 
void ResolveRecoveryConflictWithLock (LOCKTAG locktag, bool logging_conflict)
 
void ResolveRecoveryConflictWithBufferPin (void)
 
void CheckRecoveryConflictDeadlock (void)
 
void StandbyDeadLockHandler (void)
 
void StandbyTimeoutHandler (void)
 
void StandbyLockTimeoutHandler (void)
 
void LogRecoveryConflict (ProcSignalReason reason, TimestampTz wait_start, TimestampTz cur_ts, VirtualTransactionId *wait_list, bool still_waiting)
 
void StandbyAcquireAccessExclusiveLock (TransactionId xid, Oid dbOid, Oid relOid)
 
void StandbyReleaseLockTree (TransactionId xid, int nsubxids, TransactionId *subxids)
 
void StandbyReleaseAllLocks (void)
 
void StandbyReleaseOldLocks (TransactionId oldxid)
 
void LogAccessExclusiveLock (Oid dbOid, Oid relOid)
 
void LogAccessExclusiveLockPrepare (void)
 
XLogRecPtr LogStandbySnapshot (void)
 
void LogStandbyInvalidations (int nmsgs, SharedInvalidationMessage *msgs, bool relcacheInitFileInval)
 

Variables

int vacuum_defer_cleanup_age
 
int max_standby_archive_delay
 
int max_standby_streaming_delay
 
bool log_recovery_conflict_waits
 

Macro Definition Documentation

◆ MinSizeOfXactRunningXacts

#define MinSizeOfXactRunningXacts   offsetof(xl_running_xacts, xids)

Definition at line 62 of file standby.h.

Referenced by LogCurrentRunningXacts().

Typedef Documentation

◆ RunningTransactions

Definition at line 89 of file standby.h.

◆ RunningTransactionsData

Function Documentation

◆ CheckRecoveryConflictDeadlock()

void CheckRecoveryConflictDeadlock ( void  )

Definition at line 862 of file standby.c.

References Assert, ereport, errcode(), errdetail(), errmsg(), ERROR, HoldingBufferPinThatDelaysRecovery(), and InRecovery.

Referenced by ProcSleep().

863 {
864  Assert(!InRecovery); /* do not call in Startup process */
865 
867  return;
868 
869  /*
870  * Error message should match ProcessInterrupts() but we avoid calling
871  * that because we aren't handling an interrupt at this point. Note that
872  * we only cancel the current transaction here, so if we are in a
873  * subtransaction and the pin is held by a parent, then the Startup
874  * process will continue to wait even though we have avoided deadlock.
875  */
876  ereport(ERROR,
877  (errcode(ERRCODE_T_R_DEADLOCK_DETECTED),
878  errmsg("canceling statement due to conflict with recovery"),
879  errdetail("User transaction caused buffer deadlock with recovery.")));
880 }
bool InRecovery
Definition: xlog.c:207
int errcode(int sqlerrcode)
Definition: elog.c:698
#define ERROR
Definition: elog.h:46
int errdetail(const char *fmt,...)
Definition: elog.c:1042
#define ereport(elevel,...)
Definition: elog.h:157
#define Assert(condition)
Definition: c.h:804
bool HoldingBufferPinThatDelaysRecovery(void)
Definition: bufmgr.c:4231
int errmsg(const char *fmt,...)
Definition: elog.c:909

◆ InitRecoveryTransactionEnvironment()

void InitRecoveryTransactionEnvironment ( void  )

Definition at line 81 of file standby.c.

References VirtualTransactionId::backendId, HASHCTL::entrysize, GetNextLocalTransactionId(), HASH_BLOBS, hash_create(), HASH_ELEM, HASHCTL::keysize, VirtualTransactionId::localTransactionId, MyBackendId, SharedInvalBackendInit(), STANDBY_INITIALIZED, standbyState, and VirtualXactLockTableInsert().

Referenced by StartupXLOG().

82 {
84  HASHCTL hash_ctl;
85 
86  /*
87  * Initialize the hash table for tracking the list of locks held by each
88  * transaction.
89  */
90  hash_ctl.keysize = sizeof(TransactionId);
91  hash_ctl.entrysize = sizeof(RecoveryLockListsEntry);
92  RecoveryLockLists = hash_create("RecoveryLockLists",
93  64,
94  &hash_ctl,
96 
97  /*
98  * Initialize shared invalidation management for Startup process, being
99  * careful to register ourselves as a sendOnly process so we don't need to
100  * read messages, nor will we get signaled when the queue starts filling
101  * up.
102  */
104 
105  /*
106  * Lock a virtual transaction id for Startup process.
107  *
108  * We need to do GetNextLocalTransactionId() because
109  * SharedInvalBackendInit() leaves localTransactionId invalid and the lock
110  * manager doesn't like that at all.
111  *
112  * Note that we don't need to run XactLockTableInsert() because nobody
113  * needs to wait on xids. That sounds a little strange, but table locks
114  * are held by vxids and row level locks are held by xids. All queries
115  * hold AccessShareLocks so never block while we write or lock new rows.
116  */
117  vxid.backendId = MyBackendId;
120 
122 }
BackendId MyBackendId
Definition: globals.c:84
static HTAB * RecoveryLockLists
Definition: standby.c:44
#define HASH_ELEM
Definition: hsearch.h:95
uint32 TransactionId
Definition: c.h:587
void SharedInvalBackendInit(bool sendOnly)
Definition: sinvaladt.c:257
Size entrysize
Definition: hsearch.h:76
LocalTransactionId localTransactionId
Definition: lock.h:66
LocalTransactionId GetNextLocalTransactionId(void)
Definition: sinvaladt.c:766
void VirtualXactLockTableInsert(VirtualTransactionId vxid)
Definition: lock.c:4390
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:349
#define HASH_BLOBS
Definition: hsearch.h:97
Size keysize
Definition: hsearch.h:75
BackendId backendId
Definition: lock.h:65
struct RecoveryLockListsEntry RecoveryLockListsEntry
HotStandbyState standbyState
Definition: xlog.c:210

◆ LogAccessExclusiveLock()

void LogAccessExclusiveLock ( Oid  dbOid,
Oid  relOid 
)

Definition at line 1358 of file standby.c.

References xl_standby_lock::dbOid, GetCurrentTransactionId(), LogAccessExclusiveLocks(), MyXactFlags, xl_standby_lock::relOid, XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK, and xl_standby_lock::xid.

Referenced by LockAcquireExtended().

1359 {
1360  xl_standby_lock xlrec;
1361 
1362  xlrec.xid = GetCurrentTransactionId();
1363 
1364  xlrec.dbOid = dbOid;
1365  xlrec.relOid = relOid;
1366 
1367  LogAccessExclusiveLocks(1, &xlrec);
1369 }
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks)
Definition: standby.c:1340
#define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK
Definition: xact.h:108
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438
int MyXactFlags
Definition: xact.c:132
TransactionId xid
Definition: lockdefs.h:54

◆ LogAccessExclusiveLockPrepare()

void LogAccessExclusiveLockPrepare ( void  )

Definition at line 1375 of file standby.c.

References GetCurrentTransactionId().

Referenced by LockAcquireExtended().

1376 {
1377  /*
1378  * Ensure that a TransactionId has been assigned to this transaction, for
1379  * two reasons, both related to lock release on the standby. First, we
1380  * must assign an xid so that RecordTransactionCommit() and
1381  * RecordTransactionAbort() do not optimise away the transaction
1382  * completion record which recovery relies upon to release locks. It's a
1383  * hack, but for a corner case not worth adding code for into the main
1384  * commit path. Second, we must assign an xid before the lock is recorded
1385  * in shared memory, otherwise a concurrently executing
1386  * GetRunningTransactionLocks() might see a lock associated with an
1387  * InvalidTransactionId which we later assert cannot happen.
1388  */
1389  (void) GetCurrentTransactionId();
1390 }
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:438

◆ LogRecoveryConflict()

void LogRecoveryConflict ( ProcSignalReason  reason,
TimestampTz  wait_start,
TimestampTz  cur_ts,
VirtualTransactionId wait_list,
bool  still_waiting 
)

Definition at line 249 of file standby.c.

References _, appendStringInfo(), Assert, VirtualTransactionId::backendId, BackendIdGetProc(), buf, StringInfoData::data, ereport, errdetail_log_plural(), errmsg(), get_recovery_conflict_desc(), initStringInfo(), LOG, pfree(), PGPROC::pid, TimestampDifference(), and VirtualTransactionIdIsValid.

Referenced by LockBufferForCleanup(), ProcSleep(), and ResolveRecoveryConflictWithVirtualXIDs().

252 {
253  long secs;
254  int usecs;
255  long msecs;
257  int nprocs = 0;
258 
259  /*
260  * There must be no conflicting processes when the recovery conflict has
261  * already been resolved.
262  */
263  Assert(still_waiting || wait_list == NULL);
264 
265  TimestampDifference(wait_start, now, &secs, &usecs);
266  msecs = secs * 1000 + usecs / 1000;
267  usecs = usecs % 1000;
268 
269  if (wait_list)
270  {
271  VirtualTransactionId *vxids;
272 
273  /* Construct a string of list of the conflicting processes */
274  vxids = wait_list;
275  while (VirtualTransactionIdIsValid(*vxids))
276  {
277  PGPROC *proc = BackendIdGetProc(vxids->backendId);
278 
279  /* proc can be NULL if the target backend is not active */
280  if (proc)
281  {
282  if (nprocs == 0)
283  {
284  initStringInfo(&buf);
285  appendStringInfo(&buf, "%d", proc->pid);
286  }
287  else
288  appendStringInfo(&buf, ", %d", proc->pid);
289 
290  nprocs++;
291  }
292 
293  vxids++;
294  }
295  }
296 
297  /*
298  * If wait_list is specified, report the list of PIDs of active
299  * conflicting backends in a detail message. Note that if all the backends
300  * in the list are not active, no detail message is logged.
301  */
302  if (still_waiting)
303  {
304  ereport(LOG,
305  errmsg("recovery still waiting after %ld.%03d ms: %s",
306  msecs, usecs, _(get_recovery_conflict_desc(reason))),
307  nprocs > 0 ? errdetail_log_plural("Conflicting process: %s.",
308  "Conflicting processes: %s.",
309  nprocs, buf.data) : 0);
310  }
311  else
312  {
313  ereport(LOG,
314  errmsg("recovery finished waiting after %ld.%03d ms: %s",
315  msecs, usecs, _(get_recovery_conflict_desc(reason))));
316  }
317 
318  if (nprocs > 0)
319  pfree(buf.data);
320 }
#define LOG
Definition: elog.h:26
void pfree(void *pointer)
Definition: mcxt.c:1169
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:91
static char * buf
Definition: pg_test_fsync.c:68
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
#define VirtualTransactionIdIsValid(vxid)
Definition: lock.h:71
int errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: elog.c:1111
#define ereport(elevel,...)
Definition: elog.h:157
#define Assert(condition)
Definition: c.h:804
BackendId backendId
Definition: lock.h:65
int errmsg(const char *fmt,...)
Definition: elog.c:909
static const char * get_recovery_conflict_desc(ProcSignalReason reason)
Definition: standby.c:1419
void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition: timestamp.c:1656
Definition: proc.h:121
int pid
Definition: proc.h:146
#define _(x)
Definition: elog.c:89
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1544
PGPROC * BackendIdGetProc(int backendID)
Definition: sinvaladt.c:376

◆ LogStandbyInvalidations()

void LogStandbyInvalidations ( int  nmsgs,
SharedInvalidationMessage msgs,
bool  relcacheInitFileInval 
)

Definition at line 1397 of file standby.c.

References xl_invalidations::dbId, MinSizeOfInvalidations, MyDatabaseId, MyDatabaseTableSpace, xl_invalidations::nmsgs, xl_invalidations::relcacheInitFileInval, xl_invalidations::tsId, XLOG_INVALIDATIONS, XLogBeginInsert(), XLogInsert(), and XLogRegisterData().

Referenced by RecordTransactionCommit().

1399 {
1400  xl_invalidations xlrec;
1401 
1402  /* prepare record */
1403  memset(&xlrec, 0, sizeof(xlrec));
1404  xlrec.dbId = MyDatabaseId;
1405  xlrec.tsId = MyDatabaseTableSpace;
1406  xlrec.relcacheInitFileInval = relcacheInitFileInval;
1407  xlrec.nmsgs = nmsgs;
1408 
1409  /* perform insertion */
1410  XLogBeginInsert();
1411  XLogRegisterData((char *) (&xlrec), MinSizeOfInvalidations);
1412  XLogRegisterData((char *) msgs,
1413  nmsgs * sizeof(SharedInvalidationMessage));
1414  XLogInsert(RM_STANDBY_ID, XLOG_INVALIDATIONS);
1415 }
#define XLOG_INVALIDATIONS
Definition: standbydefs.h:36
Oid MyDatabaseTableSpace
Definition: globals.c:90
bool relcacheInitFileInval
Definition: standbydefs.h:67
#define MinSizeOfInvalidations
Definition: standbydefs.h:72
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
Oid MyDatabaseId
Definition: globals.c:88
void XLogBeginInsert(void)
Definition: xloginsert.c:123

◆ LogStandbySnapshot()

XLogRecPtr LogStandbySnapshot ( void  )

Definition at line 1220 of file standby.c.

References Assert, GetRunningTransactionData(), GetRunningTransactionLocks(), RecoveryLockListsEntry::locks, LogAccessExclusiveLocks(), LogCurrentRunningXacts(), LWLockRelease(), pfree(), wal_level, WAL_LEVEL_LOGICAL, and XLogStandbyInfoActive.

Referenced by BackgroundWriterMain(), CreateCheckPoint(), ReplicationSlotReserveWal(), and SnapBuildWaitSnapshot().

1221 {
1222  XLogRecPtr recptr;
1223  RunningTransactions running;
1224  xl_standby_lock *locks;
1225  int nlocks;
1226 
1228 
1229  /*
1230  * Get details of any AccessExclusiveLocks being held at the moment.
1231  */
1232  locks = GetRunningTransactionLocks(&nlocks);
1233  if (nlocks > 0)
1234  LogAccessExclusiveLocks(nlocks, locks);
1235  pfree(locks);
1236 
1237  /*
1238  * Log details of all in-progress transactions. This should be the last
1239  * record we write, because standby will open up when it sees this.
1240  */
1241  running = GetRunningTransactionData();
1242 
1243  /*
1244  * GetRunningTransactionData() acquired ProcArrayLock, we must release it.
1245  * For Hot Standby this can be done before inserting the WAL record
1246  * because ProcArrayApplyRecoveryInfo() rechecks the commit status using
1247  * the clog. For logical decoding, though, the lock can't be released
1248  * early because the clog might be "in the future" from the POV of the
1249  * historic snapshot. This would allow for situations where we're waiting
1250  * for the end of a transaction listed in the xl_running_xacts record
1251  * which, according to the WAL, has committed before the xl_running_xacts
1252  * record. Fortunately this routine isn't executed frequently, and it's
1253  * only a shared lock.
1254  */
1256  LWLockRelease(ProcArrayLock);
1257 
1258  recptr = LogCurrentRunningXacts(running);
1259 
1260  /* Release lock if we kept it longer ... */
1262  LWLockRelease(ProcArrayLock);
1263 
1264  /* GetRunningTransactionData() acquired XidGenLock, we must release it */
1265  LWLockRelease(XidGenLock);
1266 
1267  return recptr;
1268 }
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks)
Definition: standby.c:1340
int wal_level
Definition: xlog.c:108
xl_standby_lock * GetRunningTransactionLocks(int *nlocks)
Definition: lock.c:3932
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1816
void pfree(void *pointer)
Definition: mcxt.c:1169
static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
Definition: standby.c:1280
#define XLogStandbyInfoActive()
Definition: xlog.h:214
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
RunningTransactions GetRunningTransactionData(void)
Definition: procarray.c:2665

◆ ResolveRecoveryConflictWithBufferPin()

void ResolveRecoveryConflictWithBufferPin ( void  )

Definition at line 753 of file standby.c.

References Assert, DeadlockTimeout, EnableTimeoutParams::delay_ms, disable_all_timeouts(), enable_timeouts(), EnableTimeoutParams::fin_time, GetCurrentTimestamp(), GetStandbyLimitTime(), got_standby_deadlock_timeout, EnableTimeoutParams::id, InHotStandby, PG_WAIT_BUFFER_PIN, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, ProcWaitForSignal(), SendRecoveryConflictWithBufferPin(), STANDBY_DEADLOCK_TIMEOUT, STANDBY_TIMEOUT, TMPARAM_AFTER, TMPARAM_AT, and EnableTimeoutParams::type.

Referenced by LockBufferForCleanup().

754 {
755  TimestampTz ltime;
756 
758 
759  ltime = GetStandbyLimitTime();
760 
761  if (GetCurrentTimestamp() >= ltime && ltime != 0)
762  {
763  /*
764  * We're already behind, so clear a path as quickly as possible.
765  */
767  }
768  else
769  {
770  /*
771  * Wake up at ltime, and check for deadlocks as well if we will be
772  * waiting longer than deadlock_timeout
773  */
774  EnableTimeoutParams timeouts[2];
775  int cnt = 0;
776 
777  if (ltime != 0)
778  {
779  timeouts[cnt].id = STANDBY_TIMEOUT;
780  timeouts[cnt].type = TMPARAM_AT;
781  timeouts[cnt].fin_time = ltime;
782  cnt++;
783  }
784 
786  timeouts[cnt].id = STANDBY_DEADLOCK_TIMEOUT;
787  timeouts[cnt].type = TMPARAM_AFTER;
788  timeouts[cnt].delay_ms = DeadlockTimeout;
789  cnt++;
790 
791  enable_timeouts(timeouts, cnt);
792  }
793 
794  /*
795  * Wait to be signaled by UnpinBuffer().
796  *
797  * We assume that only UnpinBuffer() and the timeout requests established
798  * above can wake us up here. WakeupRecovery() called by walreceiver or
799  * SIGHUP signal handler, etc cannot do that because it uses the different
800  * latch from that ProcWaitForSignal() waits on.
801  */
803 
805  {
806  /*
807  * Send out a request for hot-standby backends to check themselves for
808  * deadlocks.
809  *
810  * XXX The subsequent ResolveRecoveryConflictWithBufferPin() will wait
811  * to be signaled by UnpinBuffer() again and send a request for
812  * deadlocks check if deadlock_timeout happens. This causes the
813  * request to continue to be sent every deadlock_timeout until the
814  * buffer is unpinned or ltime is reached. This would increase the
815  * workload in the startup process and backends. In practice it may
816  * not be so harmful because the period that the buffer is kept pinned
817  * is basically no so long. But we should fix this?
818  */
821  }
822 
823  /*
824  * Clear any timeout requests established above. We assume here that the
825  * Startup process doesn't have any other timeouts than what this function
826  * uses. If that stops being true, we could cancel the timeouts
827  * individually, but that'd be slower.
828  */
829  disable_all_timeouts(false);
831 }
static volatile sig_atomic_t got_standby_deadlock_timeout
Definition: standby.c:47
static TimestampTz GetStandbyLimitTime(void)
Definition: standby.c:176
TimeoutId id
Definition: timeout.h:56
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1580
int64 TimestampTz
Definition: timestamp.h:39
TimeoutType type
Definition: timeout.h:57
#define InHotStandby
Definition: xlog.h:74
void enable_timeouts(const EnableTimeoutParams *timeouts, int count)
Definition: timeout.c:572
void disable_all_timeouts(bool keep_indicators)
Definition: timeout.c:687
#define PG_WAIT_BUFFER_PIN
Definition: wait_event.h:20
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
Definition: standby.c:834
void ProcWaitForSignal(uint32 wait_event_info)
Definition: proc.c:1897
#define Assert(condition)
Definition: c.h:804
TimestampTz fin_time
Definition: timeout.h:59
int DeadlockTimeout
Definition: proc.c:60

◆ ResolveRecoveryConflictWithDatabase()

void ResolveRecoveryConflictWithDatabase ( Oid  dbid)

Definition at line 529 of file standby.c.

References CancelDBBackends(), CountDBBackends(), pg_usleep(), and PROCSIG_RECOVERY_CONFLICT_DATABASE.

Referenced by dbase_redo().

530 {
531  /*
532  * We don't do ResolveRecoveryConflictWithVirtualXIDs() here since that
533  * only waits for transactions and completely idle sessions would block
534  * us. This is rare enough that we do this as simply as possible: no wait,
535  * just force them off immediately.
536  *
537  * No locking is required here because we already acquired
538  * AccessExclusiveLock. Anybody trying to connect while we do this will
539  * block during InitPostgres() and then disconnect when they see the
540  * database has been removed.
541  */
542  while (CountDBBackends(dbid) > 0)
543  {
545 
546  /*
547  * Wait awhile for them to die so that we avoid flooding an
548  * unresponsive backend when system is heavily loaded.
549  */
550  pg_usleep(10000);
551  }
552 }
int CountDBBackends(Oid databaseid)
Definition: procarray.c:3489
void pg_usleep(long microsec)
Definition: signal.c:53
void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
Definition: procarray.c:3550

◆ ResolveRecoveryConflictWithLock()

void ResolveRecoveryConflictWithLock ( LOCKTAG  locktag,
bool  logging_conflict 
)

Definition at line 583 of file standby.c.

References AccessExclusiveLock, Assert, cleanup(), DeadlockTimeout, EnableTimeoutParams::delay_ms, disable_all_timeouts(), enable_timeouts(), EnableTimeoutParams::fin_time, GetCurrentTimestamp(), GetLockConflicts(), GetStandbyLimitTime(), got_standby_deadlock_timeout, got_standby_lock_timeout, EnableTimeoutParams::id, InHotStandby, LOCKTAG::locktag_type, MyProc, now(), pg_atomic_read_u64(), pg_atomic_write_u64(), PG_WAIT_LOCK, PROCSIG_RECOVERY_CONFLICT_LOCK, PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, ProcWaitForSignal(), ResolveRecoveryConflictWithVirtualXIDs(), SignalVirtualTransaction(), STANDBY_DEADLOCK_TIMEOUT, STANDBY_LOCK_TIMEOUT, TMPARAM_AFTER, TMPARAM_AT, EnableTimeoutParams::type, VirtualTransactionIdIsValid, and PGPROC::waitStart.

Referenced by ProcSleep().

584 {
585  TimestampTz ltime;
587 
589 
590  ltime = GetStandbyLimitTime();
591  now = GetCurrentTimestamp();
592 
593  /*
594  * Update waitStart if first time through after the startup process
595  * started waiting for the lock. It should not be updated every time
596  * ResolveRecoveryConflictWithLock() is called during the wait.
597  *
598  * Use the current time obtained for comparison with ltime as waitStart
599  * (i.e., the time when this process started waiting for the lock). Since
600  * getting the current time newly can cause overhead, we reuse the
601  * already-obtained time to avoid that overhead.
602  *
603  * Note that waitStart is updated without holding the lock table's
604  * partition lock, to avoid the overhead by additional lock acquisition.
605  * This can cause "waitstart" in pg_locks to become NULL for a very short
606  * period of time after the wait started even though "granted" is false.
607  * This is OK in practice because we can assume that users are likely to
608  * look at "waitstart" when waiting for the lock for a long time.
609  */
610  if (pg_atomic_read_u64(&MyProc->waitStart) == 0)
612 
613  if (now >= ltime && ltime != 0)
614  {
615  /*
616  * We're already behind, so clear a path as quickly as possible.
617  */
618  VirtualTransactionId *backends;
619 
620  backends = GetLockConflicts(&locktag, AccessExclusiveLock, NULL);
621 
622  /*
623  * Prevent ResolveRecoveryConflictWithVirtualXIDs() from reporting
624  * "waiting" in PS display by disabling its argument report_waiting
625  * because the caller, WaitOnLock(), has already reported that.
626  */
629  PG_WAIT_LOCK | locktag.locktag_type,
630  false);
631  }
632  else
633  {
634  /*
635  * Wait (or wait again) until ltime, and check for deadlocks as well
636  * if we will be waiting longer than deadlock_timeout
637  */
638  EnableTimeoutParams timeouts[2];
639  int cnt = 0;
640 
641  if (ltime != 0)
642  {
643  got_standby_lock_timeout = false;
644  timeouts[cnt].id = STANDBY_LOCK_TIMEOUT;
645  timeouts[cnt].type = TMPARAM_AT;
646  timeouts[cnt].fin_time = ltime;
647  cnt++;
648  }
649 
651  timeouts[cnt].id = STANDBY_DEADLOCK_TIMEOUT;
652  timeouts[cnt].type = TMPARAM_AFTER;
653  timeouts[cnt].delay_ms = DeadlockTimeout;
654  cnt++;
655 
656  enable_timeouts(timeouts, cnt);
657  }
658 
659  /* Wait to be signaled by the release of the Relation Lock */
661 
662  /*
663  * Exit if ltime is reached. Then all the backends holding conflicting
664  * locks will be canceled in the next ResolveRecoveryConflictWithLock()
665  * call.
666  */
668  goto cleanup;
669 
671  {
672  VirtualTransactionId *backends;
673 
674  backends = GetLockConflicts(&locktag, AccessExclusiveLock, NULL);
675 
676  /* Quick exit if there's no work to be done */
677  if (!VirtualTransactionIdIsValid(*backends))
678  goto cleanup;
679 
680  /*
681  * Send signals to all the backends holding the conflicting locks, to
682  * ask them to check themselves for deadlocks.
683  */
684  while (VirtualTransactionIdIsValid(*backends))
685  {
686  SignalVirtualTransaction(*backends,
688  false);
689  backends++;
690  }
691 
692  /*
693  * Exit if the recovery conflict has not been logged yet even though
694  * logging is enabled, so that the caller can log that. Then
695  * RecoveryConflictWithLock() is called again and we will wait again
696  * for the lock to be released.
697  */
698  if (logging_conflict)
699  goto cleanup;
700 
701  /*
702  * Wait again here to be signaled by the release of the Relation Lock,
703  * to prevent the subsequent RecoveryConflictWithLock() from causing
704  * deadlock_timeout and sending a request for deadlocks check again.
705  * Otherwise the request continues to be sent every deadlock_timeout
706  * until the relation locks are released or ltime is reached.
707  */
710  }
711 
712 cleanup:
713 
714  /*
715  * Clear any timeout requests established above. We assume here that the
716  * Startup process doesn't have any other outstanding timeouts than those
717  * used by this function. If that stops being true, we could cancel the
718  * timeouts individually, but that'd be slower.
719  */
720  disable_all_timeouts(false);
721  got_standby_lock_timeout = false;
723 }
static volatile sig_atomic_t got_standby_deadlock_timeout
Definition: standby.c:47
static TimestampTz GetStandbyLimitTime(void)
Definition: standby.c:176
TimeoutId id
Definition: timeout.h:56
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, uint32 wait_event_info, bool report_waiting)
Definition: standby.c:335
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1580
PGPROC * MyProc
Definition: proc.c:68
int64 TimestampTz
Definition: timestamp.h:39
TimeoutType type
Definition: timeout.h:57
#define InHotStandby
Definition: xlog.h:74
#define PG_WAIT_LOCK
Definition: wait_event.h:19
static void pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition: atomics.h:438
pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode, bool conflictPending)
Definition: procarray.c:3387
void enable_timeouts(const EnableTimeoutParams *timeouts, int count)
Definition: timeout.c:572
void disable_all_timeouts(bool keep_indicators)
Definition: timeout.c:687
void ProcWaitForSignal(uint32 wait_event_info)
Definition: proc.c:1897
#define VirtualTransactionIdIsValid(vxid)
Definition: lock.h:71
static volatile sig_atomic_t got_standby_lock_timeout
Definition: standby.c:48
static void cleanup(void)
Definition: bootstrap.c:870
VirtualTransactionId * GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode, int *countp)
Definition: lock.c:2909
uint8 locktag_type
Definition: lock.h:172
#define Assert(condition)
Definition: c.h:804
TimestampTz fin_time
Definition: timeout.h:59
#define AccessExclusiveLock
Definition: lockdefs.h:45
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition: atomics.h:429
int DeadlockTimeout
Definition: proc.c:60
pg_atomic_uint64 waitStart
Definition: proc.h:184
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1544

◆ ResolveRecoveryConflictWithSnapshot()

void ResolveRecoveryConflictWithSnapshot ( TransactionId  latestRemovedXid,
RelFileNode  node 
)

Definition at line 443 of file standby.c.

References RelFileNode::dbNode, GetConflictingVirtualXIDs(), PROCSIG_RECOVERY_CONFLICT_SNAPSHOT, ResolveRecoveryConflictWithVirtualXIDs(), TransactionIdIsValid, and WAIT_EVENT_RECOVERY_CONFLICT_SNAPSHOT.

Referenced by btree_xlog_delete(), gistRedoDeleteRecord(), hash_xlog_vacuum_one_page(), heap_xlog_freeze_page(), heap_xlog_prune(), heap_xlog_visible(), ResolveRecoveryConflictWithSnapshotFullXid(), and spgRedoVacuumRedirect().

444 {
445  VirtualTransactionId *backends;
446 
447  /*
448  * If we get passed InvalidTransactionId then we do nothing (no conflict).
449  *
450  * This can happen when replaying already-applied WAL records after a
451  * standby crash or restart, or when replaying an XLOG_HEAP2_VISIBLE
452  * record that marks as frozen a page which was already all-visible. It's
453  * also quite common with records generated during index deletion
454  * (original execution of the deletion can reason that a recovery conflict
455  * which is sufficient for the deletion operation must take place before
456  * replay of the deletion record itself).
457  */
458  if (!TransactionIdIsValid(latestRemovedXid))
459  return;
460 
461  backends = GetConflictingVirtualXIDs(latestRemovedXid,
462  node.dbNode);
463 
467  true);
468 }
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, uint32 wait_event_info, bool report_waiting)
Definition: standby.c:335
VirtualTransactionId * GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
Definition: procarray.c:3307
#define TransactionIdIsValid(xid)
Definition: transam.h:41

◆ ResolveRecoveryConflictWithSnapshotFullXid()

void ResolveRecoveryConflictWithSnapshotFullXid ( FullTransactionId  latestRemovedFullXid,
RelFileNode  node 
)

Definition at line 475 of file standby.c.

References MaxTransactionId, ReadNextFullTransactionId(), ResolveRecoveryConflictWithSnapshot(), U64FromFullTransactionId, and XidFromFullTransactionId.

Referenced by btree_xlog_reuse_page(), and gistRedoPageReuse().

477 {
478  /*
479  * ResolveRecoveryConflictWithSnapshot operates on 32-bit TransactionIds,
480  * so truncate the logged FullTransactionId. If the logged value is very
481  * old, so that XID wrap-around already happened on it, there can't be any
482  * snapshots that still see it.
483  */
485  uint64 diff;
486 
487  diff = U64FromFullTransactionId(nextXid) -
488  U64FromFullTransactionId(latestRemovedFullXid);
489  if (diff < MaxTransactionId / 2)
490  {
491  TransactionId latestRemovedXid;
492 
493  latestRemovedXid = XidFromFullTransactionId(latestRemovedFullXid);
494  ResolveRecoveryConflictWithSnapshot(latestRemovedXid, node);
495  }
496 }
uint32 TransactionId
Definition: c.h:587
#define XidFromFullTransactionId(x)
Definition: transam.h:48
FullTransactionId ReadNextFullTransactionId(void)
Definition: varsup.c:261
#define MaxTransactionId
Definition: transam.h:35
#define U64FromFullTransactionId(x)
Definition: transam.h:49
void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid, RelFileNode node)
Definition: standby.c:443

◆ ResolveRecoveryConflictWithTablespace()

void ResolveRecoveryConflictWithTablespace ( Oid  tsid)

Definition at line 499 of file standby.c.

References GetConflictingVirtualXIDs(), InvalidOid, InvalidTransactionId, PROCSIG_RECOVERY_CONFLICT_TABLESPACE, ResolveRecoveryConflictWithVirtualXIDs(), and WAIT_EVENT_RECOVERY_CONFLICT_TABLESPACE.

Referenced by tblspc_redo().

500 {
501  VirtualTransactionId *temp_file_users;
502 
503  /*
504  * Standby users may be currently using this tablespace for their
505  * temporary files. We only care about current users because
506  * temp_tablespace parameter will just ignore tablespaces that no longer
507  * exist.
508  *
509  * Ask everybody to cancel their queries immediately so we can ensure no
510  * temp files remain and we can remove the tablespace. Nuke the entire
511  * site from orbit, it's the only way to be sure.
512  *
513  * XXX: We could work out the pids of active backends using this
514  * tablespace by examining the temp filenames in the directory. We would
515  * then convert the pids into VirtualXIDs before attempting to cancel
516  * them.
517  *
518  * We don't wait for commit because drop tablespace is non-transactional.
519  */
521  InvalidOid);
525  true);
526 }
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, uint32 wait_event_info, bool report_waiting)
Definition: standby.c:335
VirtualTransactionId * GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
Definition: procarray.c:3307
#define InvalidTransactionId
Definition: transam.h:31
#define InvalidOid
Definition: postgres_ext.h:36

◆ ShutdownRecoveryTransactionEnvironment()

void ShutdownRecoveryTransactionEnvironment ( void  )

Definition at line 138 of file standby.c.

References ExpireAllKnownAssignedTransactionIds(), hash_destroy(), StandbyReleaseAllLocks(), and VirtualXactLockTableCleanup().

Referenced by StartupProcExit(), and StartupXLOG().

139 {
140  /*
141  * Do nothing if RecoveryLockLists is NULL because which means that
142  * transaction tracking has not been yet initialized or has been already
143  * shutdowned. This prevents transaction tracking from being shutdowned
144  * unexpectedly more than once.
145  */
146  if (RecoveryLockLists == NULL)
147  return;
148 
149  /* Mark all tracked in-progress transactions as finished. */
151 
152  /* Release all locks the tracked transactions were holding */
154 
155  /* Destroy the hash table of locks. */
157  RecoveryLockLists = NULL;
158 
159  /* Cleanup our VirtualTransaction */
161 }
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:862
void VirtualXactLockTableCleanup(void)
Definition: lock.c:4413
static HTAB * RecoveryLockLists
Definition: standby.c:44
void ExpireAllKnownAssignedTransactionIds(void)
Definition: procarray.c:4421
void StandbyReleaseAllLocks(void)
Definition: standby.c:1049

◆ StandbyAcquireAccessExclusiveLock()

void StandbyAcquireAccessExclusiveLock ( TransactionId  xid,
Oid  dbOid,
Oid  relOid 
)

Definition at line 948 of file standby.c.

References AccessExclusiveLock, Assert, xl_standby_lock::dbOid, DEBUG4, elog, HASH_ENTER, hash_search(), lappend(), LockAcquire(), RecoveryLockListsEntry::locks, NIL, OidIsValid, palloc(), xl_standby_lock::relOid, SET_LOCKTAG_RELATION, trace_recovery(), TransactionIdDidAbort(), TransactionIdDidCommit(), TransactionIdIsValid, xl_standby_lock::xid, and RecoveryLockListsEntry::xid.

Referenced by lock_twophase_standby_recover(), and standby_redo().

949 {
950  RecoveryLockListsEntry *entry;
951  xl_standby_lock *newlock;
952  LOCKTAG locktag;
953  bool found;
954 
955  /* Already processed? */
956  if (!TransactionIdIsValid(xid) ||
957  TransactionIdDidCommit(xid) ||
959  return;
960 
962  "adding recovery lock: db %u rel %u", dbOid, relOid);
963 
964  /* dbOid is InvalidOid when we are locking a shared relation. */
965  Assert(OidIsValid(relOid));
966 
967  /* Create a new list for this xid, if we don't have one already. */
968  entry = hash_search(RecoveryLockLists, &xid, HASH_ENTER, &found);
969  if (!found)
970  {
971  entry->xid = xid;
972  entry->locks = NIL;
973  }
974 
975  newlock = palloc(sizeof(xl_standby_lock));
976  newlock->xid = xid;
977  newlock->dbOid = dbOid;
978  newlock->relOid = relOid;
979  entry->locks = lappend(entry->locks, newlock);
980 
981  SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid);
982 
983  (void) LockAcquire(&locktag, AccessExclusiveLock, true, false);
984 }
#define NIL
Definition: pg_list.h:65
LockAcquireResult LockAcquire(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock, bool dontWait)
Definition: lock.c:746
static HTAB * RecoveryLockLists
Definition: standby.c:44
Definition: lock.h:166
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:125
#define DEBUG4
Definition: elog.h:22
#define OidIsValid(objectId)
Definition: c.h:710
TransactionId xid
Definition: standby.c:64
int trace_recovery(int trace_level)
Definition: elog.c:3609
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:183
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:181
List * lappend(List *list, void *datum)
Definition: list.c:336
TransactionId xid
Definition: lockdefs.h:54
#define Assert(condition)
Definition: c.h:804
#define AccessExclusiveLock
Definition: lockdefs.h:45
void * palloc(Size size)
Definition: mcxt.c:1062
#define elog(elevel,...)
Definition: elog.h:232
#define TransactionIdIsValid(xid)
Definition: transam.h:41

◆ StandbyDeadLockHandler()

void StandbyDeadLockHandler ( void  )

Definition at line 893 of file standby.c.

References got_standby_deadlock_timeout.

Referenced by StartupProcessMain().

894 {
896 }
static volatile sig_atomic_t got_standby_deadlock_timeout
Definition: standby.c:47

◆ StandbyLockTimeoutHandler()

void StandbyLockTimeoutHandler ( void  )

Definition at line 916 of file standby.c.

References got_standby_lock_timeout.

Referenced by StartupProcessMain().

917 {
919 }
static volatile sig_atomic_t got_standby_lock_timeout
Definition: standby.c:48

◆ StandbyReleaseAllLocks()

void StandbyReleaseAllLocks ( void  )

Definition at line 1049 of file standby.c.

References DEBUG2, elog, HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), RecoveryLockListsEntry::locks, StandbyReleaseLockList(), status(), and trace_recovery().

Referenced by ShutdownRecoveryTransactionEnvironment(), and StandbyReleaseLocks().

1050 {
1052  RecoveryLockListsEntry *entry;
1053 
1054  elog(trace_recovery(DEBUG2), "release all standby locks");
1055 
1056  hash_seq_init(&status, RecoveryLockLists);
1057  while ((entry = hash_seq_search(&status)))
1058  {
1059  StandbyReleaseLockList(entry->locks);
1060  hash_search(RecoveryLockLists, entry, HASH_REMOVE, NULL);
1061  }
1062 }
static void StandbyReleaseLockList(List *locks)
Definition: standby.c:987
static HTAB * RecoveryLockLists
Definition: standby.c:44
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
int trace_recovery(int trace_level)
Definition: elog.c:3609
#define DEBUG2
Definition: elog.h:24
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
#define elog(elevel,...)
Definition: elog.h:232
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227

◆ StandbyReleaseLockTree()

void StandbyReleaseLockTree ( TransactionId  xid,
int  nsubxids,
TransactionId subxids 
)

Definition at line 1035 of file standby.c.

References i, and StandbyReleaseLocks().

Referenced by RecoverPreparedTransactions(), xact_redo_abort(), and xact_redo_commit().

1036 {
1037  int i;
1038 
1039  StandbyReleaseLocks(xid);
1040 
1041  for (i = 0; i < nsubxids; i++)
1042  StandbyReleaseLocks(subxids[i]);
1043 }
static void StandbyReleaseLocks(TransactionId xid)
Definition: standby.c:1011
int i

◆ StandbyReleaseOldLocks()

void StandbyReleaseOldLocks ( TransactionId  oldxid)

Definition at line 1070 of file standby.c.

References Assert, HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), RecoveryLockListsEntry::locks, StandbyReleaseLockList(), StandbyTransactionIdIsPrepared(), status(), TransactionIdIsValid, TransactionIdPrecedes(), and RecoveryLockListsEntry::xid.

Referenced by ProcArrayApplyRecoveryInfo().

1071 {
1073  RecoveryLockListsEntry *entry;
1074 
1075  hash_seq_init(&status, RecoveryLockLists);
1076  while ((entry = hash_seq_search(&status)))
1077  {
1078  Assert(TransactionIdIsValid(entry->xid));
1079 
1080  /* Skip if prepared transaction. */
1081  if (StandbyTransactionIdIsPrepared(entry->xid))
1082  continue;
1083 
1084  /* Skip if >= oldxid. */
1085  if (!TransactionIdPrecedes(entry->xid, oldxid))
1086  continue;
1087 
1088  /* Remove all locks and hash table entry. */
1089  StandbyReleaseLockList(entry->locks);
1090  hash_search(RecoveryLockLists, entry, HASH_REMOVE, NULL);
1091  }
1092 }
static void StandbyReleaseLockList(List *locks)
Definition: standby.c:987
static HTAB * RecoveryLockLists
Definition: standby.c:44
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:954
TransactionId xid
Definition: standby.c:64
bool StandbyTransactionIdIsPrepared(TransactionId xid)
Definition: twophase.c:1381
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define Assert(condition)
Definition: c.h:804
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1436
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1426
#define TransactionIdIsValid(xid)
Definition: transam.h:41
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227

◆ StandbyTimeoutHandler()

void StandbyTimeoutHandler ( void  )

Definition at line 904 of file standby.c.

References disable_timeout(), PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, SendRecoveryConflictWithBufferPin(), and STANDBY_DEADLOCK_TIMEOUT.

Referenced by StartupProcessMain().

905 {
906  /* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
908 
910 }
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
Definition: standby.c:834
void disable_timeout(TimeoutId id, bool keep_indicator)
Definition: timeout.c:621

Variable Documentation

◆ log_recovery_conflict_waits

bool log_recovery_conflict_waits

◆ max_standby_archive_delay

int max_standby_archive_delay

Definition at line 40 of file standby.c.

Referenced by GetStandbyLimitTime().

◆ max_standby_streaming_delay

int max_standby_streaming_delay

Definition at line 41 of file standby.c.

Referenced by GetStandbyLimitTime().

◆ vacuum_defer_cleanup_age

int vacuum_defer_cleanup_age

Definition at line 39 of file standby.c.

Referenced by ComputeXidHorizons(), and GetSnapshotData().