mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
Add GUC to log long wait times on recovery conflicts.
This commit adds GUC log_recovery_conflict_waits that controls whether a log message is produced when the startup process is waiting longer than deadlock_timeout for recovery conflicts. This is useful in determining if recovery conflicts prevent the recovery from applying WAL. Note that currently a log message is produced only when recovery conflict has not been resolved yet even after deadlock_timeout passes, i.e., only when the startup process is still waiting for recovery conflict even after deadlock_timeout. Author: Bertrand Drouvot, Masahiko Sawada Reviewed-by: Alvaro Herrera, Kyotaro Horiguchi, Fujii Masao Discussion: https://postgr.es/m/9a60178c-a853-1440-2cdc-c3af916cff59@amazon.com
This commit is contained in:
parent
f7a1a805cb
commit
0650ff2303
@ -6952,6 +6952,28 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry id="guc-log-recovery-conflict-waits" xreflabel="log_recovery_conflict_waits">
|
||||||
|
<term><varname>log_recovery_conflict_waits</varname> (<type>boolean</type>)
|
||||||
|
<indexterm>
|
||||||
|
<primary><varname>log_recovery_conflict_waits</varname> configuration parameter</primary>
|
||||||
|
</indexterm>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Controls whether a log message is produced when the startup process
|
||||||
|
is waiting longer than <varname>deadlock_timeout</varname>
|
||||||
|
for recovery conflicts. This is useful in determining if recovery
|
||||||
|
conflicts prevent the recovery from applying WAL.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The default is <literal>off</literal>. This parameter can only be set
|
||||||
|
in the <filename>postgresql.conf</filename> file or on the server
|
||||||
|
command line.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry id="guc-log-parameter-max-length" xreflabel="log_parameter_max_length">
|
<varlistentry id="guc-log-parameter-max-length" xreflabel="log_parameter_max_length">
|
||||||
<term><varname>log_parameter_max_length</varname> (<type>integer</type>)
|
<term><varname>log_parameter_max_length</varname> (<type>integer</type>)
|
||||||
<indexterm>
|
<indexterm>
|
||||||
|
@ -2071,6 +2071,12 @@ if (!triggered)
|
|||||||
server. The <structname>pg_stat_database</structname> system view also contains
|
server. The <structname>pg_stat_database</structname> system view also contains
|
||||||
summary information.
|
summary information.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Users can control whether a log message is produced when WAL replay is waiting
|
||||||
|
longer than <varname>deadlock_timeout</varname> for conflicts. This
|
||||||
|
is controlled by the <xref linkend="guc-log-recovery-conflict-waits"/> parameter.
|
||||||
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
<sect2 id="hot-standby-admin">
|
<sect2 id="hot-standby-admin">
|
||||||
|
@ -3809,6 +3809,8 @@ LockBufferForCleanup(Buffer buffer)
|
|||||||
{
|
{
|
||||||
BufferDesc *bufHdr;
|
BufferDesc *bufHdr;
|
||||||
char *new_status = NULL;
|
char *new_status = NULL;
|
||||||
|
TimestampTz waitStart = 0;
|
||||||
|
bool logged_recovery_conflict = false;
|
||||||
|
|
||||||
Assert(BufferIsPinned(buffer));
|
Assert(BufferIsPinned(buffer));
|
||||||
Assert(PinCountWaitBuf == NULL);
|
Assert(PinCountWaitBuf == NULL);
|
||||||
@ -3882,6 +3884,34 @@ LockBufferForCleanup(Buffer buffer)
|
|||||||
new_status[len] = '\0'; /* truncate off " waiting" */
|
new_status[len] = '\0'; /* truncate off " waiting" */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit the log message if the startup process is waiting longer
|
||||||
|
* than deadlock_timeout for recovery conflict on buffer pin.
|
||||||
|
*
|
||||||
|
* Skip this if first time through because the startup process has
|
||||||
|
* not started waiting yet in this case. So, the wait start
|
||||||
|
* timestamp is set after this logic.
|
||||||
|
*/
|
||||||
|
if (waitStart != 0 && !logged_recovery_conflict)
|
||||||
|
{
|
||||||
|
TimestampTz now = GetCurrentTimestamp();
|
||||||
|
|
||||||
|
if (TimestampDifferenceExceeds(waitStart, now,
|
||||||
|
DeadlockTimeout))
|
||||||
|
{
|
||||||
|
LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
|
||||||
|
waitStart, now, NULL);
|
||||||
|
logged_recovery_conflict = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the wait start timestamp if logging is enabled and first
|
||||||
|
* time through.
|
||||||
|
*/
|
||||||
|
if (log_recovery_conflict_waits && waitStart == 0)
|
||||||
|
waitStart = GetCurrentTimestamp();
|
||||||
|
|
||||||
/* Publish the bufid that Startup process waits on */
|
/* Publish the bufid that Startup process waits on */
|
||||||
SetStartupBufferPinWaitBufId(buffer - 1);
|
SetStartupBufferPinWaitBufId(buffer - 1);
|
||||||
/* Set alarm and then wait to be signaled by UnpinBuffer() */
|
/* Set alarm and then wait to be signaled by UnpinBuffer() */
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
int vacuum_defer_cleanup_age;
|
int vacuum_defer_cleanup_age;
|
||||||
int max_standby_archive_delay = 30 * 1000;
|
int max_standby_archive_delay = 30 * 1000;
|
||||||
int max_standby_streaming_delay = 30 * 1000;
|
int max_standby_streaming_delay = 30 * 1000;
|
||||||
|
bool log_recovery_conflict_waits = false;
|
||||||
|
|
||||||
static HTAB *RecoveryLockLists;
|
static HTAB *RecoveryLockLists;
|
||||||
|
|
||||||
@ -53,6 +54,7 @@ static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlis
|
|||||||
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
|
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
|
||||||
static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
|
static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
|
||||||
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
|
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
|
||||||
|
static const char *get_recovery_conflict_desc(ProcSignalReason reason);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keep track of all the locks owned by a given transaction.
|
* Keep track of all the locks owned by a given transaction.
|
||||||
@ -218,15 +220,83 @@ WaitExceedsMaxStandbyDelay(uint32 wait_event_info)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log the recovery conflict.
|
||||||
|
*
|
||||||
|
* wait_start is the timestamp when the caller started to wait.
|
||||||
|
* now is the timestamp when this function has been called.
|
||||||
|
* wait_list is the list of virtual transaction ids assigned to
|
||||||
|
* conflicting processes.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
|
||||||
|
TimestampTz now, VirtualTransactionId *wait_list)
|
||||||
|
{
|
||||||
|
long secs;
|
||||||
|
int usecs;
|
||||||
|
long msecs;
|
||||||
|
StringInfoData buf;
|
||||||
|
int nprocs = 0;
|
||||||
|
|
||||||
|
TimestampDifference(wait_start, now, &secs, &usecs);
|
||||||
|
msecs = secs * 1000 + usecs / 1000;
|
||||||
|
usecs = usecs % 1000;
|
||||||
|
|
||||||
|
if (wait_list)
|
||||||
|
{
|
||||||
|
VirtualTransactionId *vxids;
|
||||||
|
|
||||||
|
/* Construct a string of list of the conflicting processes */
|
||||||
|
vxids = wait_list;
|
||||||
|
while (VirtualTransactionIdIsValid(*vxids))
|
||||||
|
{
|
||||||
|
PGPROC *proc = BackendIdGetProc(vxids->backendId);
|
||||||
|
|
||||||
|
/* proc can be NULL if the target backend is not active */
|
||||||
|
if (proc)
|
||||||
|
{
|
||||||
|
if (nprocs == 0)
|
||||||
|
{
|
||||||
|
initStringInfo(&buf);
|
||||||
|
appendStringInfo(&buf, "%d", proc->pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
appendStringInfo(&buf, ", %d", proc->pid);
|
||||||
|
|
||||||
|
nprocs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
vxids++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If wait_list is specified, report the list of PIDs of active
|
||||||
|
* conflicting backends in a detail message. Note that if all the backends
|
||||||
|
* in the list are not active, no detail message is logged.
|
||||||
|
*/
|
||||||
|
ereport(LOG,
|
||||||
|
errmsg("recovery still waiting after %ld.%03d ms: %s",
|
||||||
|
msecs, usecs, _(get_recovery_conflict_desc(reason))),
|
||||||
|
nprocs > 0 ? errdetail_log_plural("Conflicting process: %s.",
|
||||||
|
"Conflicting processes: %s.",
|
||||||
|
nprocs, buf.data) : 0);
|
||||||
|
|
||||||
|
if (nprocs > 0)
|
||||||
|
pfree(buf.data);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the main executioner for any query backend that conflicts with
|
* This is the main executioner for any query backend that conflicts with
|
||||||
* recovery processing. Judgement has already been passed on it within
|
* recovery processing. Judgement has already been passed on it within
|
||||||
* a specific rmgr. Here we just issue the orders to the procs. The procs
|
* a specific rmgr. Here we just issue the orders to the procs. The procs
|
||||||
* then throw the required error as instructed.
|
* then throw the required error as instructed.
|
||||||
*
|
*
|
||||||
* If report_waiting is true, "waiting" is reported in PS display if necessary.
|
* If report_waiting is true, "waiting" is reported in PS display and the
|
||||||
* If the caller has already reported that, report_waiting should be false.
|
* wait for recovery conflict is reported in the log, if necessary. If
|
||||||
* Otherwise, "waiting" is reported twice unexpectedly.
|
* the caller is responsible for reporting them, report_waiting should be
|
||||||
|
* false. Otherwise, both the caller and this function report the same
|
||||||
|
* thing unexpectedly.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
||||||
@ -234,15 +304,16 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
|||||||
bool report_waiting)
|
bool report_waiting)
|
||||||
{
|
{
|
||||||
TimestampTz waitStart = 0;
|
TimestampTz waitStart = 0;
|
||||||
char *new_status;
|
char *new_status = NULL;
|
||||||
|
bool logged_recovery_conflict = false;
|
||||||
|
|
||||||
/* Fast exit, to avoid a kernel call if there's no work to be done. */
|
/* Fast exit, to avoid a kernel call if there's no work to be done. */
|
||||||
if (!VirtualTransactionIdIsValid(*waitlist))
|
if (!VirtualTransactionIdIsValid(*waitlist))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (report_waiting)
|
/* Set the wait start timestamp for reporting */
|
||||||
|
if (report_waiting && (log_recovery_conflict_waits || update_process_title))
|
||||||
waitStart = GetCurrentTimestamp();
|
waitStart = GetCurrentTimestamp();
|
||||||
new_status = NULL; /* we haven't changed the ps display */
|
|
||||||
|
|
||||||
while (VirtualTransactionIdIsValid(*waitlist))
|
while (VirtualTransactionIdIsValid(*waitlist))
|
||||||
{
|
{
|
||||||
@ -252,25 +323,6 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
|||||||
/* wait until the virtual xid is gone */
|
/* wait until the virtual xid is gone */
|
||||||
while (!VirtualXactLock(*waitlist, false))
|
while (!VirtualXactLock(*waitlist, false))
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Report via ps if we have been waiting for more than 500 msec
|
|
||||||
* (should that be configurable?)
|
|
||||||
*/
|
|
||||||
if (update_process_title && new_status == NULL && report_waiting &&
|
|
||||||
TimestampDifferenceExceeds(waitStart, GetCurrentTimestamp(),
|
|
||||||
500))
|
|
||||||
{
|
|
||||||
const char *old_status;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
old_status = get_ps_display(&len);
|
|
||||||
new_status = (char *) palloc(len + 8 + 1);
|
|
||||||
memcpy(new_status, old_status, len);
|
|
||||||
strcpy(new_status + len, " waiting");
|
|
||||||
set_ps_display(new_status);
|
|
||||||
new_status[len] = '\0'; /* truncate off " waiting" */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Is it time to kill it? */
|
/* Is it time to kill it? */
|
||||||
if (WaitExceedsMaxStandbyDelay(wait_event_info))
|
if (WaitExceedsMaxStandbyDelay(wait_event_info))
|
||||||
{
|
{
|
||||||
@ -289,6 +341,50 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
|||||||
if (pid != 0)
|
if (pid != 0)
|
||||||
pg_usleep(5000L);
|
pg_usleep(5000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (waitStart != 0 && (!logged_recovery_conflict || new_status == NULL))
|
||||||
|
{
|
||||||
|
TimestampTz now = 0;
|
||||||
|
bool maybe_log_conflict;
|
||||||
|
bool maybe_update_title;
|
||||||
|
|
||||||
|
maybe_log_conflict = (log_recovery_conflict_waits && !logged_recovery_conflict);
|
||||||
|
maybe_update_title = (update_process_title && new_status == NULL);
|
||||||
|
|
||||||
|
/* Get the current timestamp if not report yet */
|
||||||
|
if (maybe_log_conflict || maybe_update_title)
|
||||||
|
now = GetCurrentTimestamp();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report via ps if we have been waiting for more than 500
|
||||||
|
* msec (should that be configurable?)
|
||||||
|
*/
|
||||||
|
if (maybe_update_title &&
|
||||||
|
TimestampDifferenceExceeds(waitStart, now, 500))
|
||||||
|
{
|
||||||
|
const char *old_status;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
old_status = get_ps_display(&len);
|
||||||
|
new_status = (char *) palloc(len + 8 + 1);
|
||||||
|
memcpy(new_status, old_status, len);
|
||||||
|
strcpy(new_status + len, " waiting");
|
||||||
|
set_ps_display(new_status);
|
||||||
|
new_status[len] = '\0'; /* truncate off " waiting" */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit the log message if the startup process is waiting
|
||||||
|
* longer than deadlock_timeout for recovery conflict on
|
||||||
|
* buffer pin.
|
||||||
|
*/
|
||||||
|
if (maybe_log_conflict &&
|
||||||
|
TimestampDifferenceExceeds(waitStart, now, DeadlockTimeout))
|
||||||
|
{
|
||||||
|
LogRecoveryConflict(reason, waitStart, now, waitlist);
|
||||||
|
logged_recovery_conflict = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The virtual transaction is gone now, wait for the next one */
|
/* The virtual transaction is gone now, wait for the next one */
|
||||||
@ -405,9 +501,18 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
|
|||||||
* hot-standby backend processes. If deadlock_timeout is reached in
|
* hot-standby backend processes. If deadlock_timeout is reached in
|
||||||
* this function, all the backends holding the conflicting locks are
|
* this function, all the backends holding the conflicting locks are
|
||||||
* requested to check themselves for deadlocks.
|
* requested to check themselves for deadlocks.
|
||||||
|
*
|
||||||
|
* logging_conflict should be true if the recovery conflict has not been
|
||||||
|
* logged yet even though logging is enabled. After deadlock_timeout is
|
||||||
|
* reached and the request for deadlock check is sent, we wait again to
|
||||||
|
* be signaled by the release of the lock if logging_conflict is false.
|
||||||
|
* Otherwise we return without waiting again so that the caller can report
|
||||||
|
* the recovery conflict. In this case, then, this function is called again
|
||||||
|
* with logging_conflict=false (because the recovery conflict has already
|
||||||
|
* been logged) and we will wait again for the lock to be released.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ResolveRecoveryConflictWithLock(LOCKTAG locktag)
|
ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict)
|
||||||
{
|
{
|
||||||
TimestampTz ltime;
|
TimestampTz ltime;
|
||||||
|
|
||||||
@ -494,6 +599,15 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag)
|
|||||||
backends++;
|
backends++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exit if the recovery conflict has not been logged yet even though
|
||||||
|
* logging is enabled, so that the caller can log that. Then
|
||||||
|
* RecoveryConflictWithLock() is called again and we will wait again
|
||||||
|
* for the lock to be released.
|
||||||
|
*/
|
||||||
|
if (logging_conflict)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait again here to be signaled by the release of the Relation Lock,
|
* Wait again here to be signaled by the release of the Relation Lock,
|
||||||
* to prevent the subsequent RecoveryConflictWithLock() from causing
|
* to prevent the subsequent RecoveryConflictWithLock() from causing
|
||||||
@ -1209,3 +1323,36 @@ LogStandbyInvalidations(int nmsgs, SharedInvalidationMessage *msgs,
|
|||||||
nmsgs * sizeof(SharedInvalidationMessage));
|
nmsgs * sizeof(SharedInvalidationMessage));
|
||||||
XLogInsert(RM_STANDBY_ID, XLOG_INVALIDATIONS);
|
XLogInsert(RM_STANDBY_ID, XLOG_INVALIDATIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return the description of recovery conflict */
|
||||||
|
static const char *
|
||||||
|
get_recovery_conflict_desc(ProcSignalReason reason)
|
||||||
|
{
|
||||||
|
const char *reasonDesc = gettext_noop("unknown reason");
|
||||||
|
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
|
||||||
|
reasonDesc = gettext_noop("recovery conflict on buffer pin");
|
||||||
|
break;
|
||||||
|
case PROCSIG_RECOVERY_CONFLICT_LOCK:
|
||||||
|
reasonDesc = gettext_noop("recovery conflict on lock");
|
||||||
|
break;
|
||||||
|
case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
|
||||||
|
reasonDesc = gettext_noop("recovery conflict on tablespace");
|
||||||
|
break;
|
||||||
|
case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
|
||||||
|
reasonDesc = gettext_noop("recovery conflict on snapshot");
|
||||||
|
break;
|
||||||
|
case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
|
||||||
|
reasonDesc = gettext_noop("recovery conflict on buffer deadlock");
|
||||||
|
break;
|
||||||
|
case PROCSIG_RECOVERY_CONFLICT_DATABASE:
|
||||||
|
reasonDesc = gettext_noop("recovery conflict on database");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reasonDesc;
|
||||||
|
}
|
||||||
|
@ -1064,8 +1064,10 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
LWLock *partitionLock = LockHashPartitionLock(hashcode);
|
LWLock *partitionLock = LockHashPartitionLock(hashcode);
|
||||||
PROC_QUEUE *waitQueue = &(lock->waitProcs);
|
PROC_QUEUE *waitQueue = &(lock->waitProcs);
|
||||||
LOCKMASK myHeldLocks = MyProc->heldLocks;
|
LOCKMASK myHeldLocks = MyProc->heldLocks;
|
||||||
|
TimestampTz standbyWaitStart = 0;
|
||||||
bool early_deadlock = false;
|
bool early_deadlock = false;
|
||||||
bool allow_autovacuum_cancel = true;
|
bool allow_autovacuum_cancel = true;
|
||||||
|
bool logged_recovery_conflict = false;
|
||||||
ProcWaitStatus myWaitStatus;
|
ProcWaitStatus myWaitStatus;
|
||||||
PGPROC *proc;
|
PGPROC *proc;
|
||||||
PGPROC *leader = MyProc->lockGroupLeader;
|
PGPROC *leader = MyProc->lockGroupLeader;
|
||||||
@ -1261,6 +1263,14 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
else
|
else
|
||||||
enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
|
enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
|
||||||
}
|
}
|
||||||
|
else if (log_recovery_conflict_waits)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Set the wait start timestamp if logging is enabled and in hot
|
||||||
|
* standby.
|
||||||
|
*/
|
||||||
|
standbyWaitStart = GetCurrentTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If somebody wakes us between LWLockRelease and WaitLatch, the latch
|
* If somebody wakes us between LWLockRelease and WaitLatch, the latch
|
||||||
@ -1280,8 +1290,42 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
|
|||||||
{
|
{
|
||||||
if (InHotStandby)
|
if (InHotStandby)
|
||||||
{
|
{
|
||||||
/* Set a timer and wait for that or for the Lock to be granted */
|
bool maybe_log_conflict =
|
||||||
ResolveRecoveryConflictWithLock(locallock->tag.lock);
|
(standbyWaitStart != 0 && !logged_recovery_conflict);
|
||||||
|
|
||||||
|
/* Set a timer and wait for that or for the lock to be granted */
|
||||||
|
ResolveRecoveryConflictWithLock(locallock->tag.lock,
|
||||||
|
maybe_log_conflict);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit the log message if the startup process is waiting longer
|
||||||
|
* than deadlock_timeout for recovery conflict on lock.
|
||||||
|
*/
|
||||||
|
if (maybe_log_conflict)
|
||||||
|
{
|
||||||
|
TimestampTz now = GetCurrentTimestamp();
|
||||||
|
|
||||||
|
if (TimestampDifferenceExceeds(standbyWaitStart, now,
|
||||||
|
DeadlockTimeout))
|
||||||
|
{
|
||||||
|
VirtualTransactionId *vxids;
|
||||||
|
int cnt;
|
||||||
|
|
||||||
|
vxids = GetLockConflicts(&locallock->tag.lock,
|
||||||
|
AccessExclusiveLock, &cnt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Log the recovery conflict and the list of PIDs of
|
||||||
|
* backends holding the conflicting lock. Note that we do
|
||||||
|
* logging even if there are no such backends right now
|
||||||
|
* because the startup process here has already waited
|
||||||
|
* longer than deadlock_timeout.
|
||||||
|
*/
|
||||||
|
LogRecoveryConflict(PROCSIG_RECOVERY_CONFLICT_LOCK,
|
||||||
|
standbyWaitStart, now, cnt > 0 ? vxids : NULL);
|
||||||
|
logged_recovery_conflict = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1573,7 +1573,15 @@ static struct config_bool ConfigureNamesBool[] =
|
|||||||
false,
|
false,
|
||||||
NULL, NULL, NULL
|
NULL, NULL, NULL
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
{"log_recovery_conflict_waits", PGC_SIGHUP, LOGGING_WHAT,
|
||||||
|
gettext_noop("Logs standby recovery conflict waits."),
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
&log_recovery_conflict_waits,
|
||||||
|
false,
|
||||||
|
NULL, NULL, NULL
|
||||||
|
},
|
||||||
{
|
{
|
||||||
{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
|
{"log_hostname", PGC_SIGHUP, LOGGING_WHAT,
|
||||||
gettext_noop("Logs the host name in the connection logs."),
|
gettext_noop("Logs the host name in the connection logs."),
|
||||||
|
@ -552,6 +552,8 @@
|
|||||||
# %% = '%'
|
# %% = '%'
|
||||||
# e.g. '<%u%%%d> '
|
# e.g. '<%u%%%d> '
|
||||||
#log_lock_waits = off # log lock waits >= deadlock_timeout
|
#log_lock_waits = off # log lock waits >= deadlock_timeout
|
||||||
|
#log_recovery_conflict_waits = off # log standby recovery conflict waits
|
||||||
|
# >= deadlock_timeout
|
||||||
#log_parameter_max_length = -1 # when logging statements, limit logged
|
#log_parameter_max_length = -1 # when logging statements, limit logged
|
||||||
# bind-parameter values to N bytes;
|
# bind-parameter values to N bytes;
|
||||||
# -1 means print in full, 0 disables
|
# -1 means print in full, 0 disables
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
extern int vacuum_defer_cleanup_age;
|
extern int vacuum_defer_cleanup_age;
|
||||||
extern int max_standby_archive_delay;
|
extern int max_standby_archive_delay;
|
||||||
extern int max_standby_streaming_delay;
|
extern int max_standby_streaming_delay;
|
||||||
|
extern bool log_recovery_conflict_waits;
|
||||||
|
|
||||||
extern void InitRecoveryTransactionEnvironment(void);
|
extern void InitRecoveryTransactionEnvironment(void);
|
||||||
extern void ShutdownRecoveryTransactionEnvironment(void);
|
extern void ShutdownRecoveryTransactionEnvironment(void);
|
||||||
@ -32,12 +33,14 @@ extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid,
|
|||||||
extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
|
extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
|
||||||
extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
|
extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
|
||||||
|
|
||||||
extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag);
|
extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag, bool logging_conflict);
|
||||||
extern void ResolveRecoveryConflictWithBufferPin(void);
|
extern void ResolveRecoveryConflictWithBufferPin(void);
|
||||||
extern void CheckRecoveryConflictDeadlock(void);
|
extern void CheckRecoveryConflictDeadlock(void);
|
||||||
extern void StandbyDeadLockHandler(void);
|
extern void StandbyDeadLockHandler(void);
|
||||||
extern void StandbyTimeoutHandler(void);
|
extern void StandbyTimeoutHandler(void);
|
||||||
extern void StandbyLockTimeoutHandler(void);
|
extern void StandbyLockTimeoutHandler(void);
|
||||||
|
extern void LogRecoveryConflict(ProcSignalReason reason, TimestampTz wait_start,
|
||||||
|
TimestampTz cur_ts, VirtualTransactionId *wait_list);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Standby Rmgr (RM_STANDBY_ID)
|
* Standby Rmgr (RM_STANDBY_ID)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user