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 847 of file standby.c.

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

Referenced by ProcSleep().

848 {
849  Assert(!InRecovery); /* do not call in Startup process */
850 
852  return;
853 
854  /*
855  * Error message should match ProcessInterrupts() but we avoid calling
856  * that because we aren't handling an interrupt at this point. Note that
857  * we only cancel the current transaction here, so if we are in a
858  * subtransaction and the pin is held by a parent, then the Startup
859  * process will continue to wait even though we have avoided deadlock.
860  */
861  ereport(ERROR,
862  (errcode(ERRCODE_T_R_DEADLOCK_DETECTED),
863  errmsg("canceling statement due to conflict with recovery"),
864  errdetail("User transaction caused buffer deadlock with recovery.")));
865 }
bool InRecovery
Definition: xlog.c:206
int errcode(int sqlerrcode)
Definition: elog.c:694
#define ERROR
Definition: elog.h:45
int errdetail(const char *fmt,...)
Definition: elog.c:1038
#define ereport(elevel,...)
Definition: elog.h:155
#define Assert(condition)
Definition: c.h:804
bool HoldingBufferPinThatDelaysRecovery(void)
Definition: bufmgr.c:4147
int errmsg(const char *fmt,...)
Definition: elog.c:905

◆ 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:82
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:209

◆ LogAccessExclusiveLock()

void LogAccessExclusiveLock ( Oid  dbOid,
Oid  relOid 
)

Definition at line 1343 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().

1344 {
1345  xl_standby_lock xlrec;
1346 
1347  xlrec.xid = GetCurrentTransactionId();
1348 
1349  xlrec.dbOid = dbOid;
1350  xlrec.relOid = relOid;
1351 
1352  LogAccessExclusiveLocks(1, &xlrec);
1354 }
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks)
Definition: standby.c:1325
#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 1360 of file standby.c.

References GetCurrentTransactionId().

Referenced by LockAcquireExtended().

1361 {
1362  /*
1363  * Ensure that a TransactionId has been assigned to this transaction, for
1364  * two reasons, both related to lock release on the standby. First, we
1365  * must assign an xid so that RecordTransactionCommit() and
1366  * RecordTransactionAbort() do not optimise away the transaction
1367  * completion record which recovery relies upon to release locks. It's a
1368  * hack, but for a corner case not worth adding code for into the main
1369  * commit path. Second, we must assign an xid before the lock is recorded
1370  * in shared memory, otherwise a concurrently executing
1371  * GetRunningTransactionLocks() might see a lock associated with an
1372  * InvalidTransactionId which we later assert cannot happen.
1373  */
1374  (void) GetCurrentTransactionId();
1375 }
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 234 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().

237 {
238  long secs;
239  int usecs;
240  long msecs;
242  int nprocs = 0;
243 
244  /*
245  * There must be no conflicting processes when the recovery conflict has
246  * already been resolved.
247  */
248  Assert(still_waiting || wait_list == NULL);
249 
250  TimestampDifference(wait_start, now, &secs, &usecs);
251  msecs = secs * 1000 + usecs / 1000;
252  usecs = usecs % 1000;
253 
254  if (wait_list)
255  {
256  VirtualTransactionId *vxids;
257 
258  /* Construct a string of list of the conflicting processes */
259  vxids = wait_list;
260  while (VirtualTransactionIdIsValid(*vxids))
261  {
262  PGPROC *proc = BackendIdGetProc(vxids->backendId);
263 
264  /* proc can be NULL if the target backend is not active */
265  if (proc)
266  {
267  if (nprocs == 0)
268  {
269  initStringInfo(&buf);
270  appendStringInfo(&buf, "%d", proc->pid);
271  }
272  else
273  appendStringInfo(&buf, ", %d", proc->pid);
274 
275  nprocs++;
276  }
277 
278  vxids++;
279  }
280  }
281 
282  /*
283  * If wait_list is specified, report the list of PIDs of active
284  * conflicting backends in a detail message. Note that if all the backends
285  * in the list are not active, no detail message is logged.
286  */
287  if (still_waiting)
288  {
289  ereport(LOG,
290  errmsg("recovery still waiting after %ld.%03d ms: %s",
291  msecs, usecs, _(get_recovery_conflict_desc(reason))),
292  nprocs > 0 ? errdetail_log_plural("Conflicting process: %s.",
293  "Conflicting processes: %s.",
294  nprocs, buf.data) : 0);
295  }
296  else
297  {
298  ereport(LOG,
299  errmsg("recovery finished waiting after %ld.%03d ms: %s",
300  msecs, usecs, _(get_recovery_conflict_desc(reason))));
301  }
302 
303  if (nprocs > 0)
304  pfree(buf.data);
305 }
#define LOG
Definition: elog.h:26
void pfree(void *pointer)
Definition: mcxt.c:1057
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:1107
#define ereport(elevel,...)
Definition: elog.h:155
#define Assert(condition)
Definition: c.h:804
BackendId backendId
Definition: lock.h:65
int errmsg(const char *fmt,...)
Definition: elog.c:905
static const char * get_recovery_conflict_desc(ProcSignalReason reason)
Definition: standby.c:1404
void TimestampDifference(TimestampTz start_time, TimestampTz stop_time, long *secs, int *microsecs)
Definition: timestamp.c:1654
Definition: proc.h:121
int pid
Definition: proc.h:146
#define _(x)
Definition: elog.c:89
Datum now(PG_FUNCTION_ARGS)
Definition: timestamp.c:1542
PGPROC * BackendIdGetProc(int backendID)
Definition: sinvaladt.c:376

◆ LogStandbyInvalidations()

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

Definition at line 1382 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().

1384 {
1385  xl_invalidations xlrec;
1386 
1387  /* prepare record */
1388  memset(&xlrec, 0, sizeof(xlrec));
1389  xlrec.dbId = MyDatabaseId;
1390  xlrec.tsId = MyDatabaseTableSpace;
1391  xlrec.relcacheInitFileInval = relcacheInitFileInval;
1392  xlrec.nmsgs = nmsgs;
1393 
1394  /* perform insertion */
1395  XLogBeginInsert();
1396  XLogRegisterData((char *) (&xlrec), MinSizeOfInvalidations);
1397  XLogRegisterData((char *) msgs,
1398  nmsgs * sizeof(SharedInvalidationMessage));
1399  XLogInsert(RM_STANDBY_ID, XLOG_INVALIDATIONS);
1400 }
#define XLOG_INVALIDATIONS
Definition: standbydefs.h:36
Oid MyDatabaseTableSpace
Definition: globals.c:88
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:86
void XLogBeginInsert(void)
Definition: xloginsert.c:123

◆ LogStandbySnapshot()

XLogRecPtr LogStandbySnapshot ( void  )

Definition at line 1205 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().

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

◆ ResolveRecoveryConflictWithBufferPin()

void ResolveRecoveryConflictWithBufferPin ( void  )

Definition at line 738 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().

739 {
740  TimestampTz ltime;
741 
743 
744  ltime = GetStandbyLimitTime();
745 
746  if (GetCurrentTimestamp() >= ltime && ltime != 0)
747  {
748  /*
749  * We're already behind, so clear a path as quickly as possible.
750  */
752  }
753  else
754  {
755  /*
756  * Wake up at ltime, and check for deadlocks as well if we will be
757  * waiting longer than deadlock_timeout
758  */
759  EnableTimeoutParams timeouts[2];
760  int cnt = 0;
761 
762  if (ltime != 0)
763  {
764  timeouts[cnt].id = STANDBY_TIMEOUT;
765  timeouts[cnt].type = TMPARAM_AT;
766  timeouts[cnt].fin_time = ltime;
767  cnt++;
768  }
769 
771  timeouts[cnt].id = STANDBY_DEADLOCK_TIMEOUT;
772  timeouts[cnt].type = TMPARAM_AFTER;
773  timeouts[cnt].delay_ms = DeadlockTimeout;
774  cnt++;
775 
776  enable_timeouts(timeouts, cnt);
777  }
778 
779  /*
780  * Wait to be signaled by UnpinBuffer().
781  *
782  * We assume that only UnpinBuffer() and the timeout requests established
783  * above can wake us up here. WakeupRecovery() called by walreceiver or
784  * SIGHUP signal handler, etc cannot do that because it uses the different
785  * latch from that ProcWaitForSignal() waits on.
786  */
788 
790  {
791  /*
792  * Send out a request for hot-standby backends to check themselves for
793  * deadlocks.
794  *
795  * XXX The subsequent ResolveRecoveryConflictWithBufferPin() will wait
796  * to be signaled by UnpinBuffer() again and send a request for
797  * deadlocks check if deadlock_timeout happens. This causes the
798  * request to continue to be sent every deadlock_timeout until the
799  * buffer is unpinned or ltime is reached. This would increase the
800  * workload in the startup process and backends. In practice it may
801  * not be so harmful because the period that the buffer is kept pinned
802  * is basically no so long. But we should fix this?
803  */
806  }
807 
808  /*
809  * Clear any timeout requests established above. We assume here that the
810  * Startup process doesn't have any other timeouts than what this function
811  * uses. If that stops being true, we could cancel the timeouts
812  * individually, but that'd be slower.
813  */
814  disable_all_timeouts(false);
816 }
static volatile sig_atomic_t got_standby_deadlock_timeout
Definition: standby.c:47
static TimestampTz GetStandbyLimitTime(void)
Definition: standby.c:161
TimeoutId id
Definition: timeout.h:55
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1578
int64 TimestampTz
Definition: timestamp.h:39
TimeoutType type
Definition: timeout.h:56
#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
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
Definition: standby.c:819
void ProcWaitForSignal(uint32 wait_event_info)
Definition: proc.c:1888
#define PG_WAIT_BUFFER_PIN
Definition: pgstat.h:897
#define Assert(condition)
Definition: c.h:804
TimestampTz fin_time
Definition: timeout.h:58
int DeadlockTimeout
Definition: proc.c:60

◆ ResolveRecoveryConflictWithDatabase()

void ResolveRecoveryConflictWithDatabase ( Oid  dbid)

Definition at line 514 of file standby.c.

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

Referenced by dbase_redo().

515 {
516  /*
517  * We don't do ResolveRecoveryConflictWithVirtualXIDs() here since that
518  * only waits for transactions and completely idle sessions would block
519  * us. This is rare enough that we do this as simply as possible: no wait,
520  * just force them off immediately.
521  *
522  * No locking is required here because we already acquired
523  * AccessExclusiveLock. Anybody trying to connect while we do this will
524  * block during InitPostgres() and then disconnect when they see the
525  * database has been removed.
526  */
527  while (CountDBBackends(dbid) > 0)
528  {
530 
531  /*
532  * Wait awhile for them to die so that we avoid flooding an
533  * unresponsive backend when system is heavily loaded.
534  */
535  pg_usleep(10000);
536  }
537 }
int CountDBBackends(Oid databaseid)
Definition: procarray.c:3460
void pg_usleep(long microsec)
Definition: signal.c:53
void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
Definition: procarray.c:3521

◆ ResolveRecoveryConflictWithLock()

void ResolveRecoveryConflictWithLock ( LOCKTAG  locktag,
bool  logging_conflict 
)

Definition at line 568 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().

569 {
570  TimestampTz ltime;
572 
574 
575  ltime = GetStandbyLimitTime();
576  now = GetCurrentTimestamp();
577 
578  /*
579  * Update waitStart if first time through after the startup process
580  * started waiting for the lock. It should not be updated every time
581  * ResolveRecoveryConflictWithLock() is called during the wait.
582  *
583  * Use the current time obtained for comparison with ltime as waitStart
584  * (i.e., the time when this process started waiting for the lock). Since
585  * getting the current time newly can cause overhead, we reuse the
586  * already-obtained time to avoid that overhead.
587  *
588  * Note that waitStart is updated without holding the lock table's
589  * partition lock, to avoid the overhead by additional lock acquisition.
590  * This can cause "waitstart" in pg_locks to become NULL for a very short
591  * period of time after the wait started even though "granted" is false.
592  * This is OK in practice because we can assume that users are likely to
593  * look at "waitstart" when waiting for the lock for a long time.
594  */
595  if (pg_atomic_read_u64(&MyProc->waitStart) == 0)
597 
598  if (now >= ltime && ltime != 0)
599  {
600  /*
601  * We're already behind, so clear a path as quickly as possible.
602  */
603  VirtualTransactionId *backends;
604 
605  backends = GetLockConflicts(&locktag, AccessExclusiveLock, NULL);
606 
607  /*
608  * Prevent ResolveRecoveryConflictWithVirtualXIDs() from reporting
609  * "waiting" in PS display by disabling its argument report_waiting
610  * because the caller, WaitOnLock(), has already reported that.
611  */
614  PG_WAIT_LOCK | locktag.locktag_type,
615  false);
616  }
617  else
618  {
619  /*
620  * Wait (or wait again) until ltime, and check for deadlocks as well
621  * if we will be waiting longer than deadlock_timeout
622  */
623  EnableTimeoutParams timeouts[2];
624  int cnt = 0;
625 
626  if (ltime != 0)
627  {
628  got_standby_lock_timeout = false;
629  timeouts[cnt].id = STANDBY_LOCK_TIMEOUT;
630  timeouts[cnt].type = TMPARAM_AT;
631  timeouts[cnt].fin_time = ltime;
632  cnt++;
633  }
634 
636  timeouts[cnt].id = STANDBY_DEADLOCK_TIMEOUT;
637  timeouts[cnt].type = TMPARAM_AFTER;
638  timeouts[cnt].delay_ms = DeadlockTimeout;
639  cnt++;
640 
641  enable_timeouts(timeouts, cnt);
642  }
643 
644  /* Wait to be signaled by the release of the Relation Lock */
646 
647  /*
648  * Exit if ltime is reached. Then all the backends holding conflicting
649  * locks will be canceled in the next ResolveRecoveryConflictWithLock()
650  * call.
651  */
653  goto cleanup;
654 
656  {
657  VirtualTransactionId *backends;
658 
659  backends = GetLockConflicts(&locktag, AccessExclusiveLock, NULL);
660 
661  /* Quick exit if there's no work to be done */
662  if (!VirtualTransactionIdIsValid(*backends))
663  goto cleanup;
664 
665  /*
666  * Send signals to all the backends holding the conflicting locks, to
667  * ask them to check themselves for deadlocks.
668  */
669  while (VirtualTransactionIdIsValid(*backends))
670  {
671  SignalVirtualTransaction(*backends,
673  false);
674  backends++;
675  }
676 
677  /*
678  * Exit if the recovery conflict has not been logged yet even though
679  * logging is enabled, so that the caller can log that. Then
680  * RecoveryConflictWithLock() is called again and we will wait again
681  * for the lock to be released.
682  */
683  if (logging_conflict)
684  goto cleanup;
685 
686  /*
687  * Wait again here to be signaled by the release of the Relation Lock,
688  * to prevent the subsequent RecoveryConflictWithLock() from causing
689  * deadlock_timeout and sending a request for deadlocks check again.
690  * Otherwise the request continues to be sent every deadlock_timeout
691  * until the relation locks are released or ltime is reached.
692  */
695  }
696 
697 cleanup:
698 
699  /*
700  * Clear any timeout requests established above. We assume here that the
701  * Startup process doesn't have any other outstanding timeouts than those
702  * used by this function. If that stops being true, we could cancel the
703  * timeouts individually, but that'd be slower.
704  */
705  disable_all_timeouts(false);
706  got_standby_lock_timeout = false;
708 }
static volatile sig_atomic_t got_standby_deadlock_timeout
Definition: standby.c:47
#define PG_WAIT_LOCK
Definition: pgstat.h:896
static TimestampTz GetStandbyLimitTime(void)
Definition: standby.c:161
TimeoutId id
Definition: timeout.h:55
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, uint32 wait_event_info, bool report_waiting)
Definition: standby.c:320
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1578
PGPROC * MyProc
Definition: proc.c:68
int64 TimestampTz
Definition: timestamp.h:39
TimeoutType type
Definition: timeout.h:56
#define InHotStandby
Definition: xlog.h:74
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:3358
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:1888
#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:862
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:58
#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:1542

◆ ResolveRecoveryConflictWithSnapshot()

void ResolveRecoveryConflictWithSnapshot ( TransactionId  latestRemovedXid,
RelFileNode  node 
)

Definition at line 428 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_clean(), heap_xlog_cleanup_info(), heap_xlog_freeze_page(), heap_xlog_visible(), ResolveRecoveryConflictWithSnapshotFullXid(), and spgRedoVacuumRedirect().

429 {
430  VirtualTransactionId *backends;
431 
432  /*
433  * If we get passed InvalidTransactionId then we do nothing (no conflict).
434  *
435  * This can happen when replaying already-applied WAL records after a
436  * standby crash or restart, or when replaying an XLOG_HEAP2_VISIBLE
437  * record that marks as frozen a page which was already all-visible. It's
438  * also quite common with records generated during index deletion
439  * (original execution of the deletion can reason that a recovery conflict
440  * which is sufficient for the deletion operation must take place before
441  * replay of the deletion record itself).
442  */
443  if (!TransactionIdIsValid(latestRemovedXid))
444  return;
445 
446  backends = GetConflictingVirtualXIDs(latestRemovedXid,
447  node.dbNode);
448 
452  true);
453 }
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, uint32 wait_event_info, bool report_waiting)
Definition: standby.c:320
VirtualTransactionId * GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
Definition: procarray.c:3278
#define TransactionIdIsValid(xid)
Definition: transam.h:41

◆ ResolveRecoveryConflictWithSnapshotFullXid()

void ResolveRecoveryConflictWithSnapshotFullXid ( FullTransactionId  latestRemovedFullXid,
RelFileNode  node 
)

Definition at line 460 of file standby.c.

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

Referenced by btree_xlog_reuse_page(), and gistRedoPageReuse().

462 {
463  /*
464  * ResolveRecoveryConflictWithSnapshot operates on 32-bit TransactionIds,
465  * so truncate the logged FullTransactionId. If the logged value is very
466  * old, so that XID wrap-around already happened on it, there can't be any
467  * snapshots that still see it.
468  */
470  uint64 diff;
471 
472  diff = U64FromFullTransactionId(nextXid) -
473  U64FromFullTransactionId(latestRemovedFullXid);
474  if (diff < MaxTransactionId / 2)
475  {
476  TransactionId latestRemovedXid;
477 
478  latestRemovedXid = XidFromFullTransactionId(latestRemovedFullXid);
479  ResolveRecoveryConflictWithSnapshot(latestRemovedXid, node);
480  }
481 }
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:428

◆ ResolveRecoveryConflictWithTablespace()

void ResolveRecoveryConflictWithTablespace ( Oid  tsid)

Definition at line 484 of file standby.c.

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

Referenced by tblspc_redo().

485 {
486  VirtualTransactionId *temp_file_users;
487 
488  /*
489  * Standby users may be currently using this tablespace for their
490  * temporary files. We only care about current users because
491  * temp_tablespace parameter will just ignore tablespaces that no longer
492  * exist.
493  *
494  * Ask everybody to cancel their queries immediately so we can ensure no
495  * temp files remain and we can remove the tablespace. Nuke the entire
496  * site from orbit, it's the only way to be sure.
497  *
498  * XXX: We could work out the pids of active backends using this
499  * tablespace by examining the temp filenames in the directory. We would
500  * then convert the pids into VirtualXIDs before attempting to cancel
501  * them.
502  *
503  * We don't wait for commit because drop tablespace is non-transactional.
504  */
506  InvalidOid);
510  true);
511 }
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, uint32 wait_event_info, bool report_waiting)
Definition: standby.c:320
VirtualTransactionId * GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
Definition: procarray.c:3278
#define InvalidTransactionId
Definition: transam.h:31
#define InvalidOid
Definition: postgres_ext.h:36

◆ ShutdownRecoveryTransactionEnvironment()

void ShutdownRecoveryTransactionEnvironment ( void  )

Definition at line 132 of file standby.c.

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

Referenced by StartupXLOG().

133 {
134  /* Mark all tracked in-progress transactions as finished. */
136 
137  /* Release all locks the tracked transactions were holding */
139 
140  /* Destroy the hash table of locks. */
142  RecoveryLockLists = NULL;
143 
144  /* Cleanup our VirtualTransaction */
146 }
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:4389
void StandbyReleaseAllLocks(void)
Definition: standby.c:1034

◆ StandbyAcquireAccessExclusiveLock()

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

Definition at line 933 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().

934 {
935  RecoveryLockListsEntry *entry;
936  xl_standby_lock *newlock;
937  LOCKTAG locktag;
938  bool found;
939 
940  /* Already processed? */
941  if (!TransactionIdIsValid(xid) ||
942  TransactionIdDidCommit(xid) ||
944  return;
945 
947  "adding recovery lock: db %u rel %u", dbOid, relOid);
948 
949  /* dbOid is InvalidOid when we are locking a shared relation. */
950  Assert(OidIsValid(relOid));
951 
952  /* Create a new list for this xid, if we don't have one already. */
953  entry = hash_search(RecoveryLockLists, &xid, HASH_ENTER, &found);
954  if (!found)
955  {
956  entry->xid = xid;
957  entry->locks = NIL;
958  }
959 
960  newlock = palloc(sizeof(xl_standby_lock));
961  newlock->xid = xid;
962  newlock->dbOid = dbOid;
963  newlock->relOid = relOid;
964  entry->locks = lappend(entry->locks, newlock);
965 
966  SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid);
967 
968  (void) LockAcquire(&locktag, AccessExclusiveLock, true, false);
969 }
#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:3568
#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:950
#define elog(elevel,...)
Definition: elog.h:227
#define TransactionIdIsValid(xid)
Definition: transam.h:41

◆ StandbyDeadLockHandler()

void StandbyDeadLockHandler ( void  )

Definition at line 878 of file standby.c.

References got_standby_deadlock_timeout.

Referenced by StartupProcessMain().

879 {
881 }
static volatile sig_atomic_t got_standby_deadlock_timeout
Definition: standby.c:47

◆ StandbyLockTimeoutHandler()

void StandbyLockTimeoutHandler ( void  )

Definition at line 901 of file standby.c.

References got_standby_lock_timeout.

Referenced by StartupProcessMain().

902 {
904 }
static volatile sig_atomic_t got_standby_lock_timeout
Definition: standby.c:48

◆ StandbyReleaseAllLocks()

void StandbyReleaseAllLocks ( void  )

Definition at line 1034 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().

1035 {
1037  RecoveryLockListsEntry *entry;
1038 
1039  elog(trace_recovery(DEBUG2), "release all standby locks");
1040 
1041  hash_seq_init(&status, RecoveryLockLists);
1042  while ((entry = hash_seq_search(&status)))
1043  {
1044  StandbyReleaseLockList(entry->locks);
1045  hash_search(RecoveryLockLists, entry, HASH_REMOVE, NULL);
1046  }
1047 }
static void StandbyReleaseLockList(List *locks)
Definition: standby.c:972
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:3568
#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:227
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 1020 of file standby.c.

References i, and StandbyReleaseLocks().

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

1021 {
1022  int i;
1023 
1024  StandbyReleaseLocks(xid);
1025 
1026  for (i = 0; i < nsubxids; i++)
1027  StandbyReleaseLocks(subxids[i]);
1028 }
static void StandbyReleaseLocks(TransactionId xid)
Definition: standby.c:996
int i

◆ StandbyReleaseOldLocks()

void StandbyReleaseOldLocks ( TransactionId  oldxid)

Definition at line 1055 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().

1056 {
1058  RecoveryLockListsEntry *entry;
1059 
1060  hash_seq_init(&status, RecoveryLockLists);
1061  while ((entry = hash_seq_search(&status)))
1062  {
1063  Assert(TransactionIdIsValid(entry->xid));
1064 
1065  /* Skip if prepared transaction. */
1066  if (StandbyTransactionIdIsPrepared(entry->xid))
1067  continue;
1068 
1069  /* Skip if >= oldxid. */
1070  if (!TransactionIdPrecedes(entry->xid, oldxid))
1071  continue;
1072 
1073  /* Remove all locks and hash table entry. */
1074  StandbyReleaseLockList(entry->locks);
1075  hash_search(RecoveryLockLists, entry, HASH_REMOVE, NULL);
1076  }
1077 }
static void StandbyReleaseLockList(List *locks)
Definition: standby.c:972
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:1368
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 889 of file standby.c.

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

Referenced by StartupProcessMain().

890 {
891  /* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
893 
895 }
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
Definition: standby.c:819
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().