mirror of
https://github.com/postgres/postgres.git
synced 2025-08-24 09:27:52 +03:00
Teach standby conflict resolution to use SIGUSR1
Conflict reason is passed through directly to the backend, so we can take decisions about the effect of the conflict based upon the local state. No specific changes, as yet, though this prepares for later work. CancelVirtualTransaction() sends signals while holding ProcArrayLock. Introduce errdetail_abort() to give message detail explaining that the abort was caused by conflict processing. Remove CONFLICT_MODE states in favour of using PROCSIG_RECOVERY_CONFLICT states directly, for clarity.
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.55 2010/01/10 15:44:28 sriggs Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.56 2010/01/16 10:05:50 sriggs Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -324,6 +324,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
|
||||
/* must be cleared with xid/xmin: */
|
||||
proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
|
||||
proc->inCommit = false; /* be sure this is cleared in abort */
|
||||
proc->recoveryConflictPending = false;
|
||||
|
||||
/* Clear the subtransaction-XID cache too while holding the lock */
|
||||
proc->subxids.nxids = 0;
|
||||
@@ -350,6 +351,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
|
||||
/* must be cleared with xid/xmin: */
|
||||
proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
|
||||
proc->inCommit = false; /* be sure this is cleared in abort */
|
||||
proc->recoveryConflictPending = false;
|
||||
|
||||
Assert(proc->subxids.nxids == 0);
|
||||
Assert(proc->subxids.overflowed == false);
|
||||
@@ -377,7 +379,7 @@ ProcArrayClearTransaction(PGPROC *proc)
|
||||
proc->xid = InvalidTransactionId;
|
||||
proc->lxid = InvalidLocalTransactionId;
|
||||
proc->xmin = InvalidTransactionId;
|
||||
proc->recoveryConflictMode = 0;
|
||||
proc->recoveryConflictPending = false;
|
||||
|
||||
/* redundant, but just in case */
|
||||
proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
|
||||
@@ -1665,7 +1667,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
|
||||
if (proc->pid == 0)
|
||||
continue;
|
||||
|
||||
if (skipExistingConflicts && proc->recoveryConflictMode > 0)
|
||||
if (skipExistingConflicts && proc->recoveryConflictPending)
|
||||
continue;
|
||||
|
||||
if (!OidIsValid(dbOid) ||
|
||||
@@ -1704,7 +1706,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
|
||||
* Returns pid of the process signaled, or 0 if not found.
|
||||
*/
|
||||
pid_t
|
||||
CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
|
||||
CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
|
||||
{
|
||||
ProcArrayStruct *arrayP = procArray;
|
||||
int index;
|
||||
@@ -1722,28 +1724,22 @@ CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
|
||||
if (procvxid.backendId == vxid.backendId &&
|
||||
procvxid.localTransactionId == vxid.localTransactionId)
|
||||
{
|
||||
/*
|
||||
* Issue orders for the proc to read next time it receives SIGINT
|
||||
*/
|
||||
if (proc->recoveryConflictMode < cancel_mode)
|
||||
proc->recoveryConflictMode = cancel_mode;
|
||||
|
||||
proc->recoveryConflictPending = true;
|
||||
pid = proc->pid;
|
||||
if (pid != 0)
|
||||
{
|
||||
/*
|
||||
* Kill the pid if it's still here. If not, that's what we wanted
|
||||
* so ignore any errors.
|
||||
*/
|
||||
(void) SendProcSignal(pid, sigmode, vxid.backendId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LWLockRelease(ProcArrayLock);
|
||||
|
||||
if (pid != 0)
|
||||
{
|
||||
/*
|
||||
* Kill the pid if it's still here. If not, that's what we wanted
|
||||
* so ignore any errors.
|
||||
*/
|
||||
kill(pid, SIGINT);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
@@ -1834,6 +1830,7 @@ CancelDBBackends(Oid databaseid)
|
||||
{
|
||||
ProcArrayStruct *arrayP = procArray;
|
||||
int index;
|
||||
pid_t pid = 0;
|
||||
|
||||
/* tell all backends to die */
|
||||
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||
@@ -1844,8 +1841,21 @@ CancelDBBackends(Oid databaseid)
|
||||
|
||||
if (proc->databaseId == databaseid)
|
||||
{
|
||||
proc->recoveryConflictMode = CONFLICT_MODE_FATAL;
|
||||
kill(proc->pid, SIGINT);
|
||||
VirtualTransactionId procvxid;
|
||||
|
||||
GET_VXID_FROM_PGPROC(procvxid, *proc);
|
||||
|
||||
proc->recoveryConflictPending = true;
|
||||
pid = proc->pid;
|
||||
if (pid != 0)
|
||||
{
|
||||
/*
|
||||
* Kill the pid if it's still here. If not, that's what we wanted
|
||||
* so ignore any errors.
|
||||
*/
|
||||
(void) SendProcSignal(pid, PROCSIG_RECOVERY_CONFLICT_DATABASE,
|
||||
procvxid.backendId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/procsignal.c,v 1.2 2010/01/02 16:57:51 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/procsignal.c,v 1.3 2010/01/16 10:05:50 sriggs Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "storage/procsignal.h"
|
||||
#include "storage/shmem.h"
|
||||
#include "storage/sinval.h"
|
||||
#include "storage/standby.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -258,5 +260,17 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
|
||||
if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
|
||||
HandleNotifyInterrupt();
|
||||
|
||||
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
|
||||
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
|
||||
|
||||
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
|
||||
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
|
||||
|
||||
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
|
||||
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
|
||||
|
||||
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
|
||||
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
|
||||
|
||||
errno = save_errno;
|
||||
}
|
||||
|
@@ -3,19 +3,15 @@
|
||||
* standby.c
|
||||
* Misc functions used in Hot Standby mode.
|
||||
*
|
||||
* InitRecoveryTransactionEnvironment()
|
||||
* ShutdownRecoveryTransactionEnvironment()
|
||||
*
|
||||
* ResolveRecoveryConflictWithVirtualXIDs()
|
||||
*
|
||||
* All functions for handling RM_STANDBY_ID, which relate to
|
||||
* AccessExclusiveLocks and starting snapshots for Hot Standby mode.
|
||||
* Plus conflict recovery processing.
|
||||
*
|
||||
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.4 2010/01/14 11:08:02 sriggs Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.5 2010/01/16 10:05:50 sriggs Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -38,7 +34,7 @@ int vacuum_defer_cleanup_age;
|
||||
static List *RecoveryLockList;
|
||||
|
||||
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
||||
char *reason, int cancel_mode);
|
||||
ProcSignalReason reason);
|
||||
static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
|
||||
static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
|
||||
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
|
||||
@@ -162,17 +158,13 @@ WaitExceedsMaxStandbyDelay(void)
|
||||
* recovery processing. Judgement has already been passed on it within
|
||||
* a specific rmgr. Here we just issue the orders to the procs. The procs
|
||||
* then throw the required error as instructed.
|
||||
*
|
||||
* We may ask for a specific cancel_mode, typically ERROR or FATAL.
|
||||
*/
|
||||
static void
|
||||
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
||||
char *reason, int cancel_mode)
|
||||
ProcSignalReason reason)
|
||||
{
|
||||
char waitactivitymsg[100];
|
||||
|
||||
Assert(cancel_mode > 0);
|
||||
|
||||
while (VirtualTransactionIdIsValid(*waitlist))
|
||||
{
|
||||
long wait_s;
|
||||
@@ -206,12 +198,6 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
||||
len = 100;
|
||||
memcpy(waitactivitymsg, oldactivitymsg, len);
|
||||
|
||||
ereport(trace_recovery(DEBUG5),
|
||||
(errmsg("virtual transaction %u/%u is blocking %s",
|
||||
waitlist->backendId,
|
||||
waitlist->localTransactionId,
|
||||
reason)));
|
||||
|
||||
pgstat_report_waiting(true);
|
||||
|
||||
logged = true;
|
||||
@@ -226,40 +212,14 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
|
||||
* Now find out who to throw out of the balloon.
|
||||
*/
|
||||
Assert(VirtualTransactionIdIsValid(*waitlist));
|
||||
pid = CancelVirtualTransaction(*waitlist, cancel_mode);
|
||||
pid = CancelVirtualTransaction(*waitlist, reason);
|
||||
|
||||
/*
|
||||
* Wait awhile for it to die so that we avoid flooding an
|
||||
* unresponsive backend when system is heavily loaded.
|
||||
*/
|
||||
if (pid != 0)
|
||||
{
|
||||
/*
|
||||
* Startup process debug messages
|
||||
*/
|
||||
switch (cancel_mode)
|
||||
{
|
||||
case CONFLICT_MODE_FATAL:
|
||||
elog(trace_recovery(DEBUG1),
|
||||
"recovery disconnects session with pid %ld because of conflict with %s",
|
||||
(long) pid,
|
||||
reason);
|
||||
break;
|
||||
case CONFLICT_MODE_ERROR:
|
||||
elog(trace_recovery(DEBUG1),
|
||||
"recovery cancels virtual transaction %u/%u pid %ld because of conflict with %s",
|
||||
waitlist->backendId,
|
||||
waitlist->localTransactionId,
|
||||
(long) pid,
|
||||
reason);
|
||||
break;
|
||||
default:
|
||||
/* No conflict pending, so fall through */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait awhile for it to die so that we avoid flooding an
|
||||
* unresponsive backend when system is heavily loaded.
|
||||
*/
|
||||
pg_usleep(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,8 +245,7 @@ ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid)
|
||||
true);
|
||||
|
||||
ResolveRecoveryConflictWithVirtualXIDs(backends,
|
||||
"snapshot conflict",
|
||||
CONFLICT_MODE_ERROR);
|
||||
PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -317,8 +276,7 @@ ResolveRecoveryConflictWithTablespace(Oid tsid)
|
||||
InvalidOid,
|
||||
false);
|
||||
ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
|
||||
"drop tablespace",
|
||||
CONFLICT_MODE_ERROR);
|
||||
PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -379,8 +337,7 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
|
||||
}
|
||||
|
||||
ResolveRecoveryConflictWithVirtualXIDs(backends,
|
||||
"exclusive lock",
|
||||
CONFLICT_MODE_ERROR);
|
||||
PROCSIG_RECOVERY_CONFLICT_LOCK);
|
||||
|
||||
if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
|
||||
!= LOCKACQUIRE_NOT_AVAIL)
|
||||
|
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.212 2010/01/15 09:19:03 heikki Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.213 2010/01/16 10:05:50 sriggs Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -324,7 +324,7 @@ InitProcess(void)
|
||||
MyProc->waitProcLock = NULL;
|
||||
for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
|
||||
SHMQueueInit(&(MyProc->myProcLocks[i]));
|
||||
MyProc->recoveryConflictMode = 0;
|
||||
MyProc->recoveryConflictPending = false;
|
||||
|
||||
/*
|
||||
* We might be reusing a semaphore that belonged to a failed process. So
|
||||
|
Reference in New Issue
Block a user