@@ -525,7 +525,9 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
525525 LOCKTAG locktag ;
526526
527527 /* Already processed? */
528- if (TransactionIdDidCommit (xid ) || TransactionIdDidAbort (xid ))
528+ if (!TransactionIdIsValid (xid ) ||
529+ TransactionIdDidCommit (xid ) ||
530+ TransactionIdDidAbort (xid ))
529531 return ;
530532
531533 elog (trace_recovery (DEBUG4 ),
@@ -607,34 +609,86 @@ StandbyReleaseLockTree(TransactionId xid, int nsubxids, TransactionId *subxids)
607609}
608610
609611/*
610- * StandbyReleaseLocksMany
611- * Release standby locks held by XIDs < removeXid
612- *
613- * If keepPreparedXacts is true, keep prepared transactions even if
614- * they're older than removeXid
612+ * Called at end of recovery and when we see a shutdown checkpoint.
615613 */
616- static void
617- StandbyReleaseLocksMany (TransactionId removeXid , bool keepPreparedXacts )
614+ void
615+ StandbyReleaseAllLocks (void )
616+ {
617+ ListCell * cell ,
618+ * prev ,
619+ * next ;
620+ LOCKTAG locktag ;
621+
622+ elog (trace_recovery (DEBUG2 ), "release all standby locks" );
623+
624+ prev = NULL ;
625+ for (cell = list_head (RecoveryLockList ); cell ; cell = next )
626+ {
627+ xl_standby_lock * lock = (xl_standby_lock * ) lfirst (cell );
628+
629+ next = lnext (cell );
630+
631+ elog (trace_recovery (DEBUG4 ),
632+ "releasing recovery lock: xid %u db %u rel %u" ,
633+ lock -> xid , lock -> dbOid , lock -> relOid );
634+ SET_LOCKTAG_RELATION (locktag , lock -> dbOid , lock -> relOid );
635+ if (!LockRelease (& locktag , AccessExclusiveLock , true))
636+ elog (LOG ,
637+ "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
638+ lock -> xid , lock -> dbOid , lock -> relOid );
639+ RecoveryLockList = list_delete_cell (RecoveryLockList , cell , prev );
640+ pfree (lock );
641+ }
642+ }
643+
644+ /*
645+ * StandbyReleaseOldLocks
646+ * Release standby locks held by XIDs that aren't running, as long
647+ * as they're not prepared transactions.
648+ */
649+ void
650+ StandbyReleaseOldLocks (int nxids , TransactionId * xids )
618651{
619652 ListCell * cell ,
620653 * prev ,
621654 * next ;
622655 LOCKTAG locktag ;
623656
624- /*
625- * Release all matching locks.
626- */
627657 prev = NULL ;
628658 for (cell = list_head (RecoveryLockList ); cell ; cell = next )
629659 {
630660 xl_standby_lock * lock = (xl_standby_lock * ) lfirst (cell );
661+ bool remove = false;
631662
632663 next = lnext (cell );
633664
634- if (!TransactionIdIsValid (removeXid ) || TransactionIdPrecedes (lock -> xid , removeXid ))
665+ Assert (TransactionIdIsValid (lock -> xid ));
666+
667+ if (StandbyTransactionIdIsPrepared (lock -> xid ))
668+ remove = false;
669+ else
670+ {
671+ int i ;
672+ bool found = false;
673+
674+ for (i = 0 ; i < nxids ; i ++ )
675+ {
676+ if (lock -> xid == xids [i ])
677+ {
678+ found = true;
679+ break ;
680+ }
681+ }
682+
683+ /*
684+ * If its not a running transaction, remove it.
685+ */
686+ if (!found )
687+ remove = true;
688+ }
689+
690+ if (remove )
635691 {
636- if (keepPreparedXacts && StandbyTransactionIdIsPrepared (lock -> xid ))
637- continue ;
638692 elog (trace_recovery (DEBUG4 ),
639693 "releasing recovery lock: xid %u db %u rel %u" ,
640694 lock -> xid , lock -> dbOid , lock -> relOid );
@@ -651,27 +705,6 @@ StandbyReleaseLocksMany(TransactionId removeXid, bool keepPreparedXacts)
651705 }
652706}
653707
654- /*
655- * Called at end of recovery and when we see a shutdown checkpoint.
656- */
657- void
658- StandbyReleaseAllLocks (void )
659- {
660- elog (trace_recovery (DEBUG2 ), "release all standby locks" );
661- StandbyReleaseLocksMany (InvalidTransactionId , false);
662- }
663-
664- /*
665- * StandbyReleaseOldLocks
666- * Release standby locks held by XIDs < removeXid, as long
667- * as they're not prepared transactions.
668- */
669- void
670- StandbyReleaseOldLocks (TransactionId removeXid )
671- {
672- StandbyReleaseLocksMany (removeXid , true);
673- }
674-
675708/*
676709 * --------------------------------------------------------------------
677710 * Recovery handling for Rmgr RM_STANDBY_ID
@@ -813,6 +846,13 @@ standby_desc(StringInfo buf, uint8 xl_info, char *rec)
813846 * Later, when we apply the running xact data we must be careful to ignore
814847 * transactions already committed, since those commits raced ahead when
815848 * making WAL entries.
849+ *
850+ * The loose timing also means that locks may be recorded that have a
851+ * zero xid, since xids are removed from procs before locks are removed.
852+ * So we must prune the lock list down to ensure we hold locks only for
853+ * currently running xids, performed by StandbyReleaseOldLocks().
854+ * Zero xids should no longer be possible, but we may be replaying WAL
855+ * from a time when they were possible.
816856 */
817857void
818858LogStandbySnapshot (TransactionId * nextXid )
0 commit comments