]> granicus.if.org Git - postgresql/commitdiff
Move call to GetTopTransactionId() earlier in LockAcquire(),
authorSimon Riggs <simon@2ndQuadrant.com>
Mon, 29 Nov 2010 01:10:56 +0000 (01:10 +0000)
committerSimon Riggs <simon@2ndQuadrant.com>
Mon, 29 Nov 2010 01:10:56 +0000 (01:10 +0000)
removing an infrequently occurring race condition in Hot Standby.
An xid must be assigned before a lock appears in shared memory,
rather than immediately after, else GetRunningTransactionLocks()
may see InvalidTransactionId, causing assertion failures during
lock processing on standby.

Bug report and diagnosis by Fujii Masao, fix by me.

src/backend/storage/ipc/standby.c
src/backend/storage/lmgr/lock.c
src/include/storage/standby.h

index 154147e44c2b6b33aae554cbd7c1f1883e3122d9..ca094a01b40dbea205459f8cf764b49153553777 100644 (file)
@@ -953,14 +953,6 @@ LogAccessExclusiveLock(Oid dbOid, Oid relOid)
 {
        xl_standby_lock xlrec;
 
-       /*
-        * Ensure that a TransactionId has been assigned to this transaction. We
-        * don't actually need the xid yet but if we don't do this then
-        * RecordTransactionCommit() and RecordTransactionAbort() will optimise
-        * away the transaction completion record which recovery relies upon to
-        * release locks. It's a hack, but for a corner case not worth adding code
-        * for into the main commit path.
-        */
        xlrec.xid = GetTopTransactionId();
 
        /*
@@ -973,3 +965,24 @@ LogAccessExclusiveLock(Oid dbOid, Oid relOid)
 
        LogAccessExclusiveLocks(1, &xlrec);
 }
+
+/*
+ * Prepare to log an AccessExclusiveLock, for use during LockAcquire()
+ */
+void
+LogAccessExclusiveLockPrepare(void)
+{
+       /*
+        * Ensure that a TransactionId has been assigned to this transaction,
+        * for two reasons, both related to lock release on the standby.
+        * First, we must assign an xid so that RecordTransactionCommit() and
+        * RecordTransactionAbort() do not optimise away the transaction
+        * completion record which recovery relies upon to release locks. It's
+        * a hack, but for a corner case not worth adding code for into the
+        * main commit path. Second, must must assign an xid before the lock
+        * is recorded in shared memory, otherwise a concurrently executing
+        * GetRunningTransactionLocks() might see a lock associated with an
+        * InvalidTransactionId which we later assert cannot happen.
+        */
+       (void) GetTopTransactionId();
+}
index b196174f6e1df4b53f2b4bc158f5cab382d66294..f48193c8e732129dae5f8a325309b944d57e891d 100644 (file)
@@ -499,6 +499,7 @@ LockAcquireExtended(const LOCKTAG *locktag,
        int                     partition;
        LWLockId        partitionLock;
        int                     status;
+       bool            log_lock = false;
 
        if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
                elog(ERROR, "unrecognized lock method: %d", lockmethodid);
@@ -579,6 +580,24 @@ LockAcquireExtended(const LOCKTAG *locktag,
                return LOCKACQUIRE_ALREADY_HELD;
        }
 
+       /*
+        * Emit a WAL record if acquisition of this lock needs to be replayed in a
+        * standby server. Only AccessExclusiveLocks can conflict with lock types
+        * that read-only transactions can acquire in a standby server.
+        *
+        * Make sure this definition matches the one in GetRunningTransactionLocks().
+        *
+        * First we prepare to log, then after lock acquired we issue log record.
+        */
+       if (lockmode >= AccessExclusiveLock &&
+               locktag->locktag_type == LOCKTAG_RELATION &&
+               !RecoveryInProgress() &&
+               XLogStandbyInfoActive())
+       {
+               LogAccessExclusiveLockPrepare();
+               log_lock = true;
+       }
+
        /*
         * Otherwise we've got to mess with the shared lock table.
         */
@@ -868,15 +887,9 @@ LockAcquireExtended(const LOCKTAG *locktag,
 
        /*
         * Emit a WAL record if acquisition of this lock need to be replayed in a
-        * standby server. Only AccessExclusiveLocks can conflict with lock types
-        * that read-only transactions can acquire in a standby server.
-        *
-        * Make sure this definition matches the one GetRunningTransactionLocks().
+        * standby server.
         */
-       if (lockmode >= AccessExclusiveLock &&
-               locktag->locktag_type == LOCKTAG_RELATION &&
-               !RecoveryInProgress() &&
-               XLogStandbyInfoActive())
+       if (log_lock)
        {
                /*
                 * Decode the locktag back to the original values, to avoid sending
index 0654c5bcccfa9564e01ff7aa14568f8ed76feb10..42fa0c04a1788d4fe182c66309fc34e555bbdfa8 100644 (file)
@@ -109,6 +109,7 @@ typedef struct RunningTransactionsData
 typedef RunningTransactionsData *RunningTransactions;
 
 extern void LogAccessExclusiveLock(Oid dbOid, Oid relOid);
+extern void LogAccessExclusiveLockPrepare(void);
 
 extern void LogStandbySnapshot(TransactionId *oldestActiveXid, TransactionId *nextXid);