On Wed, Dec 23, 2015 at 9:40 PM, Jeff Janes <[email protected]> wrote:
> On Wed, Sep 23, 2015 at 11:33 PM, Jeff Janes <[email protected]> wrote:
>>
>> On further thought, neither do I. The attached patch inverts
>> ResolveRecoveryConflictWithLock to be called back from the lmgr code so that
>> is it like ResolveRecoveryConflictWithBufferPin code. It does not try to
>> cancel the conflicting lock holders from the signal handler, rather it just
>> loops an extra time and cancels the transactions on the next call.
>>
>> It looks like the deadlock detection is adequately handled within normal
>> lmgr code within the back-ends of the other parties to the deadlock, so I
>> didn't do a timeout for deadlock detection purposes.
>
> I was testing that this still applies to git HEAD. And it doesn't due
> to the re-factoring of lock.h into lockdef.h. (And apparently it
> never actually did, because that refactoring happened long before I
> wrote this patch. I guess I must have done this work against
> 9_5_STABLE.)
>
> standby.h cannot include lock.h because standby.h is included
> indirectly by pg_xlogdump. But it has to get LOCKTAG which is only in
> lock.h.
>
> Does this mean that standby.h also needs to get parts spun off into a
> new standbydef.h that can be included from front-end code?
That is how I've done it.
The lock cancel patch applies over the header split patch.
Cheers,
Jeff
diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c
new file mode 100644
index 3d35f38..acd590b
*** a/src/backend/access/rmgrdesc/standbydesc.c
--- b/src/backend/access/rmgrdesc/standbydesc.c
***************
*** 14,20 ****
*/
#include "postgres.h"
! #include "storage/standby.h"
static void
standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec)
--- 14,20 ----
*/
#include "postgres.h"
! #include "storage/standbydefs.h"
static void
standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec)
diff --git a/src/bin/pg_xlogdump/rmgrdesc.c b/src/bin/pg_xlogdump/rmgrdesc.c
new file mode 100644
index 5b88a8d..f9cd395
*** a/src/bin/pg_xlogdump/rmgrdesc.c
--- b/src/bin/pg_xlogdump/rmgrdesc.c
***************
*** 27,33 ****
#include "commands/tablespace.h"
#include "replication/origin.h"
#include "rmgrdesc.h"
! #include "storage/standby.h"
#include "utils/relmapper.h"
#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
--- 27,33 ----
#include "commands/tablespace.h"
#include "replication/origin.h"
#include "rmgrdesc.h"
! #include "storage/standbydefs.h"
#include "utils/relmapper.h"
#define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup) \
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
new file mode 100644
index 40b329b..f27a7ba
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
***************
*** 14,22 ****
#ifndef STANDBY_H
#define STANDBY_H
! #include "access/xlogreader.h"
! #include "lib/stringinfo.h"
! #include "storage/lockdefs.h"
#include "storage/procsignal.h"
#include "storage/relfilenode.h"
--- 14,20 ----
#ifndef STANDBY_H
#define STANDBY_H
! #include "storage/standbydefs.h"
#include "storage/procsignal.h"
#include "storage/relfilenode.h"
*************** extern void StandbyReleaseLockTree(Trans
*** 51,91 ****
extern void StandbyReleaseAllLocks(void);
extern void StandbyReleaseOldLocks(int nxids, TransactionId *xids);
- /*
- * XLOG message types
- */
- #define XLOG_STANDBY_LOCK 0x00
- #define XLOG_RUNNING_XACTS 0x10
-
- typedef struct xl_standby_locks
- {
- int nlocks; /* number of entries in locks array */
- xl_standby_lock locks[FLEXIBLE_ARRAY_MEMBER];
- } xl_standby_locks;
-
- /*
- * When we write running xact data to WAL, we use this structure.
- */
- typedef struct xl_running_xacts
- {
- int xcnt; /* # of xact ids in xids[] */
- int subxcnt; /* # of subxact ids in xids[] */
- bool subxid_overflow; /* snapshot overflowed, subxids missing */
- TransactionId nextXid; /* copy of ShmemVariableCache->nextXid */
- TransactionId oldestRunningXid; /* *not* oldestXmin */
- TransactionId latestCompletedXid; /* so we can set xmax */
-
- TransactionId xids[FLEXIBLE_ARRAY_MEMBER];
- } xl_running_xacts;
-
#define MinSizeOfXactRunningXacts offsetof(xl_running_xacts, xids)
- /* Recovery handlers for the Standby Rmgr (RM_STANDBY_ID) */
- extern void standby_redo(XLogReaderState *record);
- extern void standby_desc(StringInfo buf, XLogReaderState *record);
- extern const char *standby_identify(uint8 info);
-
/*
* Declarations for GetRunningTransactionData(). Similar to Snapshots, but
* not quite. This has nothing at all to do with visibility on this server,
--- 49,57 ----
diff --git a/src/include/storage/standbydefs.h b/src/include/storage/standbydefs.h
new file mode 100644
index ...a2c9db8
*** a/src/include/storage/standbydefs.h
--- b/src/include/storage/standbydefs.h
***************
*** 0 ****
--- 1,53 ----
+ /*-------------------------------------------------------------------------
+ *
+ * standbydef.h
+ * Frontend exposed definitions for hot standby mode.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/storage/standbydefs.h
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef STANDBYDEFS_H
+ #define STANDBYDEFS_H
+
+ #include "access/xlogreader.h"
+ #include "lib/stringinfo.h"
+ #include "storage/lockdefs.h"
+
+ /* Recovery handlers for the Standby Rmgr (RM_STANDBY_ID) */
+ extern void standby_redo(XLogReaderState *record);
+ extern void standby_desc(StringInfo buf, XLogReaderState *record);
+ extern const char *standby_identify(uint8 info);
+
+ /*
+ * XLOG message types
+ */
+ #define XLOG_STANDBY_LOCK 0x00
+ #define XLOG_RUNNING_XACTS 0x10
+
+ typedef struct xl_standby_locks
+ {
+ int nlocks; /* number of entries in locks array */
+ xl_standby_lock locks[FLEXIBLE_ARRAY_MEMBER];
+ } xl_standby_locks;
+
+ /*
+ * When we write running xact data to WAL, we use this structure.
+ */
+ typedef struct xl_running_xacts
+ {
+ int xcnt; /* # of xact ids in xids[] */
+ int subxcnt; /* # of subxact ids in xids[] */
+ bool subxid_overflow; /* snapshot overflowed, subxids missing */
+ TransactionId nextXid; /* copy of ShmemVariableCache->nextXid */
+ TransactionId oldestRunningXid; /* *not* oldestXmin */
+ TransactionId latestCompletedXid; /* so we can set xmax */
+
+ TransactionId xids[FLEXIBLE_ARRAY_MEMBER];
+ } xl_running_xacts;
+
+ #endif /* STANDBYDEFS_H */
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
new file mode 100644
index 581837a..b993ca8
*** a/src/backend/postmaster/startup.c
--- b/src/backend/postmaster/startup.c
*************** StartupProcessMain(void)
*** 203,208 ****
--- 203,209 ----
*/
RegisterTimeout(STANDBY_DEADLOCK_TIMEOUT, StandbyDeadLockHandler);
RegisterTimeout(STANDBY_TIMEOUT, StandbyTimeoutHandler);
+ RegisterTimeout(STANDBY_LOCK_TIMEOUT, StandbyLockTimeoutHandler);
/*
* Unblock signals (they were blocked when the postmaster forked us)
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
new file mode 100644
index 292bed5..4b428de
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
*************** static List *RecoveryLockList;
*** 41,47 ****
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
ProcSignalReason reason);
- static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
--- 41,46 ----
*************** ResolveRecoveryConflictWithDatabase(Oid
*** 337,375 ****
}
}
! static void
! ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
{
! VirtualTransactionId *backends;
! bool lock_acquired = false;
! int num_attempts = 0;
! LOCKTAG locktag;
! SET_LOCKTAG_RELATION(locktag, dbOid, relOid);
! /*
! * If blowing away everybody with conflicting locks doesn't work, after
! * the first two attempts then we just start blowing everybody away until
! * it does work. We do this because its likely that we either have too
! * many locks and we just can't get one at all, or that there are many
! * people crowding for the same table. Recovery must win; the end
! * justifies the means.
! */
! while (!lock_acquired)
! {
! if (++num_attempts < 3)
! backends = GetLockConflicts(&locktag, AccessExclusiveLock);
! else
! backends = GetConflictingVirtualXIDs(InvalidTransactionId,
! InvalidOid);
ResolveRecoveryConflictWithVirtualXIDs(backends,
PROCSIG_RECOVERY_CONFLICT_LOCK);
! if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
! != LOCKACQUIRE_NOT_AVAIL)
! lock_acquired = true;
}
}
/*
--- 336,402 ----
}
}
! /*
! * ResolveRecoveryConflictWithLock is called from ProcSleep()
! * to resolve conflicts with other backends holding relation locks.
! *
! * The WaitLatch sleep normally done in ProcSleep()
! * (when not InHotStandby) is performed here, for code clarity.
! *
! * We either resolve conflicts immediately or set a timeout to wake us at
! * the limit of our patience.
! *
! * Resolve conflicts by cancelling to all backends holding a conflicting
! * lock. As we are already queued to be granted the lock, no new lock
! * requests conflicting with ours will be granted in the meantime.
! *
! * Deadlocks involving the Startup process and an ordinary backend process
! * will be detected by the deadlock detector within the ordinary backend.
! */
! void
! ResolveRecoveryConflictWithLock(LOCKTAG locktag)
{
! TimestampTz ltime;
! Assert(InHotStandby);
! ltime = GetStandbyLimitTime();
+ if (GetCurrentTimestamp() >= ltime)
+ {
+ /*
+ * We're already behind, so clear a path as quickly as possible.
+ */
+ VirtualTransactionId *backends;
+ backends = GetLockConflicts(&locktag, AccessExclusiveLock);
ResolveRecoveryConflictWithVirtualXIDs(backends,
PROCSIG_RECOVERY_CONFLICT_LOCK);
+ }
+ else
+ {
+ /*
+ * Wake up at ltime
+ */
+ EnableTimeoutParams timeouts[1];
! timeouts[0].id = STANDBY_LOCK_TIMEOUT;
! timeouts[0].type = TMPARAM_AT;
! timeouts[0].fin_time = ltime;
! enable_timeouts(timeouts, 1);
}
+
+ /* Wait to be signaled by the release of the Relation Lock */
+ ProcWaitForSignal();
+
+ /*
+ * Clear any timeout requests established above. We assume here that the
+ * Startup process doesn't have any other outstanding timeouts than what
+ * this function
+ * uses. If that stops being true, we could cancel the timeouts
+ * individually, but that'd be slower.
+ */
+ disable_all_timeouts(false);
+
}
/*
*************** StandbyTimeoutHandler(void)
*** 532,537 ****
--- 559,574 ----
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
}
+ /*
+ * StandbyLockTimeoutHandler() will be called if STANDBY_LOCK_TIMEOUT is exceeded.
+ * This doesn't need to do anything, simply waking up is enough.
+ */
+ void
+ StandbyLockTimeoutHandler(void)
+ {
+ /* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
+ disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
+ }
/*
* -----------------------------------------------------
*************** StandbyTimeoutHandler(void)
*** 545,551 ****
* process is the proxy by which the original locks are implemented.
*
* We only keep track of AccessExclusiveLocks, which are only ever held by
! * one transaction on one relation, and don't worry about lock queuing.
*
* We keep a single dynamically expandible list of locks in local memory,
* RelationLockList, so we can keep track of the various entries made by
--- 582,588 ----
* process is the proxy by which the original locks are implemented.
*
* We only keep track of AccessExclusiveLocks, which are only ever held by
! * one transaction on one relation.
*
* We keep a single dynamically expandible list of locks in local memory,
* RelationLockList, so we can keep track of the various entries made by
*************** StandbyAcquireAccessExclusiveLock(Transa
*** 587,600 ****
newlock->relOid = relOid;
RecoveryLockList = lappend(RecoveryLockList, newlock);
- /*
- * Attempt to acquire the lock as requested, if not resolve conflict
- */
SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid);
! if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
! == LOCKACQUIRE_NOT_AVAIL)
! ResolveRecoveryConflictWithLock(newlock->dbOid, newlock->relOid);
}
static void
--- 624,632 ----
newlock->relOid = relOid;
RecoveryLockList = lappend(RecoveryLockList, newlock);
SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid);
! LockAcquireExtended(&locktag, AccessExclusiveLock, true, false, false);
}
static void
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
new file mode 100644
index bb10c1b..4f13f9d
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
***************
*** 42,47 ****
--- 42,48 ----
#include "postmaster/autovacuum.h"
#include "replication/slot.h"
#include "replication/syncrep.h"
+ #include "storage/standby.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/pmsignal.h"
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1080,1099 ****
* If LockTimeout is set, also enable the timeout for that. We can save a
* few cycles by enabling both timeout sources in one call.
*/
! if (LockTimeout > 0)
{
! EnableTimeoutParams timeouts[2];
!
! timeouts[0].id = DEADLOCK_TIMEOUT;
! timeouts[0].type = TMPARAM_AFTER;
! timeouts[0].delay_ms = DeadlockTimeout;
! timeouts[1].id = LOCK_TIMEOUT;
! timeouts[1].type = TMPARAM_AFTER;
! timeouts[1].delay_ms = LockTimeout;
! enable_timeouts(timeouts, 2);
}
- else
- enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
/*
* If somebody wakes us between LWLockRelease and WaitLatch, the latch
--- 1081,1103 ----
* If LockTimeout is set, also enable the timeout for that. We can save a
* few cycles by enabling both timeout sources in one call.
*/
! if (!InHotStandby)
{
! if (LockTimeout > 0)
! {
! EnableTimeoutParams timeouts[2];
!
! timeouts[0].id = DEADLOCK_TIMEOUT;
! timeouts[0].type = TMPARAM_AFTER;
! timeouts[0].delay_ms = DeadlockTimeout;
! timeouts[1].id = LOCK_TIMEOUT;
! timeouts[1].type = TMPARAM_AFTER;
! timeouts[1].delay_ms = LockTimeout;
! enable_timeouts(timeouts, 2);
! }
! else
! enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
}
/*
* If somebody wakes us between LWLockRelease and WaitLatch, the latch
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 1111,1125 ****
*/
do
{
! WaitLatch(MyLatch, WL_LATCH_SET, 0);
! ResetLatch(MyLatch);
! /* check for deadlocks first, as that's probably log-worthy */
! if (got_deadlock_timeout)
{
! CheckDeadLock();
! got_deadlock_timeout = false;
}
- CHECK_FOR_INTERRUPTS();
/*
* waitStatus could change from STATUS_WAITING to something else
--- 1115,1137 ----
*/
do
{
! if (InHotStandby)
{
! /* Set a timer and wait for that or for the Lock to be granted */
! ResolveRecoveryConflictWithLock(locallock->tag.lock);
! }
! else
! {
! WaitLatch(MyLatch, WL_LATCH_SET, 0);
! ResetLatch(MyLatch);
! /* check for deadlocks first, as that's probably log-worthy */
! if (got_deadlock_timeout)
! {
! CheckDeadLock();
! got_deadlock_timeout = false;
! }
! CHECK_FOR_INTERRUPTS();
}
/*
* waitStatus could change from STATUS_WAITING to something else
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
new file mode 100644
index f27a7ba..957ba19
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
***************
*** 15,20 ****
--- 15,21 ----
#define STANDBY_H
#include "storage/standbydefs.h"
+ #include "storage/lock.h"
#include "storage/procsignal.h"
#include "storage/relfilenode.h"
*************** extern void ResolveRecoveryConflictWithS
*** 31,40 ****
--- 32,43 ----
extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
+ extern void ResolveRecoveryConflictWithLock(LOCKTAG locktag);
extern void ResolveRecoveryConflictWithBufferPin(void);
extern void CheckRecoveryConflictDeadlock(void);
extern void StandbyDeadLockHandler(void);
extern void StandbyTimeoutHandler(void);
+ extern void StandbyLockTimeoutHandler(void);
/*
* Standby Rmgr (RM_STANDBY_ID)
diff --git a/src/include/utils/timeout.h b/src/include/utils/timeout.h
new file mode 100644
index 30581a1..33b5016
*** a/src/include/utils/timeout.h
--- b/src/include/utils/timeout.h
*************** typedef enum TimeoutId
*** 29,34 ****
--- 29,35 ----
STATEMENT_TIMEOUT,
STANDBY_DEADLOCK_TIMEOUT,
STANDBY_TIMEOUT,
+ STANDBY_LOCK_TIMEOUT,
/* First user-definable timeout reason */
USER_TIMEOUT,
/* Maximum number of timeout reasons */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers