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

PGDLLIMPORT int vacuum_defer_cleanup_age
 
PGDLLIMPORT int max_standby_archive_delay
 
PGDLLIMPORT int max_standby_streaming_delay
 
PGDLLIMPORT bool log_recovery_conflict_waits
 

Macro Definition Documentation

◆ MinSizeOfXactRunningXacts

#define MinSizeOfXactRunningXacts   offsetof(xl_running_xacts, xids)

Definition at line 62 of file standby.h.

Typedef Documentation

◆ RunningTransactions

Definition at line 89 of file standby.h.

◆ RunningTransactionsData

Function Documentation

◆ CheckRecoveryConflictDeadlock()

void CheckRecoveryConflictDeadlock ( void  )

Definition at line 867 of file standby.c.

868 {
869  Assert(!InRecovery); /* do not call in Startup process */
870 
872  return;
873 
874  /*
875  * Error message should match ProcessInterrupts() but we avoid calling
876  * that because we aren't handling an interrupt at this point. Note that
877  * we only cancel the current transaction here, so if we are in a
878  * subtransaction and the pin is held by a parent, then the Startup
879  * process will continue to wait even though we have avoided deadlock.
880  */
881  ereport(ERROR,
883  errmsg("canceling statement due to conflict with recovery"),
884  errdetail("User transaction caused buffer deadlock with recovery.")));
885 }
bool HoldingBufferPinThatDelaysRecovery(void)
Definition: bufmgr.c:4364
int errdetail(const char *fmt,...)
Definition: elog.c:1037
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define ERROR
Definition: elog.h:33
#define ereport(elevel,...)
Definition: elog.h:143
Assert(fmt[strlen(fmt) - 1] !='\n')
#define ERRCODE_T_R_DEADLOCK_DETECTED
Definition: pgbench.c:80
bool InRecovery
Definition: xlogutils.c:53

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

Referenced by ProcSleep().

◆ InitRecoveryTransactionEnvironment()

void InitRecoveryTransactionEnvironment ( void  )

Definition at line 83 of file standby.c.

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

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

Referenced by StartupXLOG().

◆ LogAccessExclusiveLock()

void LogAccessExclusiveLock ( Oid  dbOid,
Oid  relOid 
)

Definition at line 1360 of file standby.c.

1361 {
1362  xl_standby_lock xlrec;
1363 
1364  xlrec.xid = GetCurrentTransactionId();
1365 
1366  xlrec.dbOid = dbOid;
1367  xlrec.relOid = relOid;
1368 
1369  LogAccessExclusiveLocks(1, &xlrec);
1371 }
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks)
Definition: standby.c:1342
TransactionId xid
Definition: lockdefs.h:51
TransactionId GetCurrentTransactionId(void)
Definition: xact.c:441
int MyXactFlags
Definition: xact.c:135
#define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK
Definition: xact.h:108

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

Referenced by LockAcquireExtended().

◆ LogAccessExclusiveLockPrepare()

void LogAccessExclusiveLockPrepare ( void  )

Definition at line 1377 of file standby.c.

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

References GetCurrentTransactionId().

Referenced by LockAcquireExtended().

◆ LogRecoveryConflict()

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

Definition at line 251 of file standby.c.

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

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

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

◆ LogStandbyInvalidations()

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

Definition at line 1399 of file standby.c.

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

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().

◆ LogStandbySnapshot()

XLogRecPtr LogStandbySnapshot ( void  )

Definition at line 1222 of file standby.c.

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

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

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

◆ ResolveRecoveryConflictWithBufferPin()

void ResolveRecoveryConflictWithBufferPin ( void  )

Definition at line 755 of file standby.c.

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

References Assert(), DeadlockTimeout, EnableTimeoutParams::delay_ms, disable_all_timeouts(), enable_timeouts(), EnableTimeoutParams::fin_time, GetCurrentTimestamp(), GetStandbyLimitTime(), got_standby_deadlock_timeout, got_standby_delay_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().

◆ ResolveRecoveryConflictWithDatabase()

void ResolveRecoveryConflictWithDatabase ( Oid  dbid)

Definition at line 531 of file standby.c.

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

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

Referenced by dbase_redo().

◆ ResolveRecoveryConflictWithLock()

void ResolveRecoveryConflictWithLock ( LOCKTAG  locktag,
bool  logging_conflict 
)

Definition at line 585 of file standby.c.

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

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().

◆ ResolveRecoveryConflictWithSnapshot()

void ResolveRecoveryConflictWithSnapshot ( TransactionId  latestRemovedXid,
RelFileNode  node 
)

Definition at line 445 of file standby.c.

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

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().

◆ ResolveRecoveryConflictWithSnapshotFullXid()

void ResolveRecoveryConflictWithSnapshotFullXid ( FullTransactionId  latestRemovedFullXid,
RelFileNode  node 
)

Definition at line 477 of file standby.c.

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

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

Referenced by btree_xlog_reuse_page(), and gistRedoPageReuse().

◆ ResolveRecoveryConflictWithTablespace()

void ResolveRecoveryConflictWithTablespace ( Oid  tsid)

Definition at line 501 of file standby.c.

502 {
503  VirtualTransactionId *temp_file_users;
504 
505  /*
506  * Standby users may be currently using this tablespace for their
507  * temporary files. We only care about current users because
508  * temp_tablespace parameter will just ignore tablespaces that no longer
509  * exist.
510  *
511  * Ask everybody to cancel their queries immediately so we can ensure no
512  * temp files remain and we can remove the tablespace. Nuke the entire
513  * site from orbit, it's the only way to be sure.
514  *
515  * XXX: We could work out the pids of active backends using this
516  * tablespace by examining the temp filenames in the directory. We would
517  * then convert the pids into VirtualXIDs before attempting to cancel
518  * them.
519  *
520  * We don't wait for commit because drop tablespace is non-transactional.
521  */
523  InvalidOid);
527  true);
528 }
#define InvalidOid
Definition: postgres_ext.h:36
@ PROCSIG_RECOVERY_CONFLICT_TABLESPACE
Definition: procsignal.h:41
#define InvalidTransactionId
Definition: transam.h:31
@ WAIT_EVENT_RECOVERY_CONFLICT_TABLESPACE
Definition: wait_event.h:121

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

Referenced by tblspc_redo().

◆ ShutdownRecoveryTransactionEnvironment()

void ShutdownRecoveryTransactionEnvironment ( void  )

Definition at line 140 of file standby.c.

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

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

Referenced by StartupProcExit(), and StartupXLOG().

◆ StandbyAcquireAccessExclusiveLock()

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

Definition at line 948 of file standby.c.

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 OidIsValid(objectId)
Definition: c.h:710
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:3423
#define elog(elevel,...)
Definition: elog.h:218
#define DEBUG4
Definition: elog.h:21
@ HASH_ENTER
Definition: hsearch.h:114
List * lappend(List *list, void *datum)
Definition: list.c:336
LockAcquireResult LockAcquire(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock, bool dontWait)
Definition: lock.c:747
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:184
void * palloc(Size size)
Definition: mcxt.c:1068
#define NIL
Definition: pg_list.h:65
Definition: lock.h:168
TransactionId xid
Definition: standby.c:66
bool TransactionIdDidCommit(TransactionId transactionId)
Definition: transam.c:125
bool TransactionIdDidAbort(TransactionId transactionId)
Definition: transam.c:181

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

Referenced by lock_twophase_standby_recover(), and standby_redo().

◆ StandbyDeadLockHandler()

void StandbyDeadLockHandler ( void  )

Definition at line 898 of file standby.c.

899 {
901 }

References got_standby_deadlock_timeout.

Referenced by StartupProcessMain().

◆ StandbyLockTimeoutHandler()

void StandbyLockTimeoutHandler ( void  )

Definition at line 916 of file standby.c.

917 {
919 }

References got_standby_lock_timeout.

Referenced by StartupProcessMain().

◆ StandbyReleaseAllLocks()

void StandbyReleaseAllLocks ( void  )

Definition at line 1051 of file standby.c.

1052 {
1054  RecoveryLockListsEntry *entry;
1055 
1056  elog(trace_recovery(DEBUG2), "release all standby locks");
1057 
1059  while ((entry = hash_seq_search(&status)))
1060  {
1061  StandbyReleaseLockList(entry->locks);
1062  hash_search(RecoveryLockLists, entry, HASH_REMOVE, NULL);
1063  }
1064 }
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 DEBUG2
Definition: elog.h:23
@ HASH_REMOVE
Definition: hsearch.h:115
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:229
static void StandbyReleaseLockList(List *locks)
Definition: standby.c:987

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

Referenced by ShutdownRecoveryTransactionEnvironment(), and StandbyReleaseLocks().

◆ StandbyReleaseLockTree()

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

Definition at line 1037 of file standby.c.

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

References i, and StandbyReleaseLocks().

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

◆ StandbyReleaseOldLocks()

void StandbyReleaseOldLocks ( TransactionId  oldxid)

Definition at line 1072 of file standby.c.

1073 {
1075  RecoveryLockListsEntry *entry;
1076 
1078  while ((entry = hash_seq_search(&status)))
1079  {
1080  Assert(TransactionIdIsValid(entry->xid));
1081 
1082  /* Skip if prepared transaction. */
1083  if (StandbyTransactionIdIsPrepared(entry->xid))
1084  continue;
1085 
1086  /* Skip if >= oldxid. */
1087  if (!TransactionIdPrecedes(entry->xid, oldxid))
1088  continue;
1089 
1090  /* Remove all locks and hash table entry. */
1091  StandbyReleaseLockList(entry->locks);
1092  hash_search(RecoveryLockLists, entry, HASH_REMOVE, NULL);
1093  }
1094 }
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
bool StandbyTransactionIdIsPrepared(TransactionId xid)
Definition: twophase.c:1454

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

Referenced by ProcArrayApplyRecoveryInfo().

◆ StandbyTimeoutHandler()

void StandbyTimeoutHandler ( void  )

Definition at line 907 of file standby.c.

908 {
910 }

References got_standby_delay_timeout.

Referenced by StartupProcessMain().

Variable Documentation

◆ log_recovery_conflict_waits

PGDLLIMPORT bool log_recovery_conflict_waits
extern

◆ max_standby_archive_delay

PGDLLIMPORT int max_standby_archive_delay
extern

Definition at line 41 of file standby.c.

Referenced by GetStandbyLimitTime().

◆ max_standby_streaming_delay

PGDLLIMPORT int max_standby_streaming_delay
extern

Definition at line 42 of file standby.c.

Referenced by GetStandbyLimitTime().

◆ vacuum_defer_cleanup_age

PGDLLIMPORT int vacuum_defer_cleanup_age
extern

Definition at line 40 of file standby.c.

Referenced by ComputeXidHorizons(), and GetSnapshotData().