2828#include "storage/procarray.h"
2929#include "storage/sinvaladt.h"
3030#include "storage/standby.h"
31+ #include "utils/hsearch.h"
32+ #include "utils/memutils.h"
3133#include "utils/ps_status.h"
3234#include "utils/timeout.h"
3335#include "utils/timestamp.h"
@@ -37,14 +39,22 @@ intvacuum_defer_cleanup_age;
3739int max_standby_archive_delay = 30 * 1000 ;
3840int max_standby_streaming_delay = 30 * 1000 ;
3941
40- static List * RecoveryLockList ;
42+ static HTAB * RecoveryLockLists ;
4143
4244static void ResolveRecoveryConflictWithVirtualXIDs (VirtualTransactionId * waitlist ,
4345ProcSignalReason reason );
4446static void SendRecoveryConflictWithBufferPin (ProcSignalReason reason );
4547static XLogRecPtr LogCurrentRunningXacts (RunningTransactions CurrRunningXacts );
4648static void LogAccessExclusiveLocks (int nlocks ,xl_standby_lock * locks );
4749
50+ /*
51+ * Keep track of all the locks owned by a given transaction.
52+ */
53+ typedef struct RecoveryLockListsEntry
54+ {
55+ TransactionId xid ;
56+ List * locks ;
57+ }RecoveryLockListsEntry ;
4858
4959/*
5060 * InitRecoveryTransactionEnvironment
6272InitRecoveryTransactionEnvironment (void )
6373{
6474VirtualTransactionId vxid ;
75+ HASHCTL hash_ctl ;
76+
77+ /*
78+ * Initialize the hash table for tracking the list of locks held by each
79+ * transaction.
80+ */
81+ memset (& hash_ctl ,0 ,sizeof (hash_ctl ));
82+ hash_ctl .keysize = sizeof (TransactionId );
83+ hash_ctl .entrysize = sizeof (RecoveryLockListsEntry );
84+ RecoveryLockLists = hash_create ("RecoveryLockLists" ,
85+ 64 ,
86+ & hash_ctl ,
87+ HASH_ELEM |HASH_BLOBS );
6588
6689/*
6790 * Initialize shared invalidation management for Startup process, being
@@ -106,6 +129,10 @@ ShutdownRecoveryTransactionEnvironment(void)
106129/* Release all locks the tracked transactions were holding */
107130StandbyReleaseAllLocks ();
108131
132+ /* Destroy the hash table of locks. */
133+ hash_destroy (RecoveryLockLists );
134+ RecoveryLockLists = NULL ;
135+
109136/* Cleanup our VirtualTransaction */
110137VirtualXactLockTableCleanup ();
111138}
@@ -585,8 +612,8 @@ StandbyLockTimeoutHandler(void)
585612 * We only keep track of AccessExclusiveLocks, which are only ever held by
586613 * one transaction on one relation.
587614 *
588- * We keep asingle dynamically expandible list of locks in local memory,
589- *RecoveryLockList , so we can keep track of the various entries made by
615+ * We keep ahash table of lists of locks in local memory keyed by xid ,
616+ *RecoveryLockLists , so we can keep track of the various entries made by
590617 * the Startup process's virtual xid in the shared lock table.
591618 *
592619 * We record the lock against the top-level xid, rather than individual
@@ -604,8 +631,10 @@ StandbyLockTimeoutHandler(void)
604631void
605632StandbyAcquireAccessExclusiveLock (TransactionId xid ,Oid dbOid ,Oid relOid )
606633{
634+ RecoveryLockListsEntry * entry ;
607635xl_standby_lock * newlock ;
608636LOCKTAG locktag ;
637+ bool found ;
609638
610639/* Already processed? */
611640if (!TransactionIdIsValid (xid )||
@@ -619,58 +648,68 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
619648/* dbOid is InvalidOid when we are locking a shared relation. */
620649Assert (OidIsValid (relOid ));
621650
651+ /* Create a new list for this xid, if we don't have one already. */
652+ entry = hash_search (RecoveryLockLists ,& xid ,HASH_ENTER ,& found );
653+ if (!found )
654+ {
655+ entry -> xid = xid ;
656+ entry -> locks = NIL ;
657+ }
658+
622659newlock = palloc (sizeof (xl_standby_lock ));
623660newlock -> xid = xid ;
624661newlock -> dbOid = dbOid ;
625662newlock -> relOid = relOid ;
626- RecoveryLockList = lappend (RecoveryLockList ,newlock );
663+ entry -> locks = lappend (entry -> locks ,newlock );
627664
628665SET_LOCKTAG_RELATION (locktag ,newlock -> dbOid ,newlock -> relOid );
629666
630667LockAcquireExtended (& locktag ,AccessExclusiveLock , true, false, false);
631668}
632669
633670static void
634- StandbyReleaseLocks ( TransactionId xid )
671+ StandbyReleaseLockList ( List * locks )
635672{
636- ListCell * cell ,
637- * prev ,
638- * next ;
639-
640- /*
641- * Release all matching locks and remove them from list
642- */
643- prev = NULL ;
644- for (cell = list_head (RecoveryLockList );cell ;cell = next )
673+ while (locks )
645674{
646- xl_standby_lock * lock = (xl_standby_lock * )lfirst (cell );
675+ xl_standby_lock * lock = (xl_standby_lock * )linitial (locks );
676+ LOCKTAG locktag ;
677+ elog (trace_recovery (DEBUG4 ),
678+ "releasing recovery lock: xid %u db %u rel %u" ,
679+ lock -> xid ,lock -> dbOid ,lock -> relOid );
680+ SET_LOCKTAG_RELATION (locktag ,lock -> dbOid ,lock -> relOid );
681+ if (!LockRelease (& locktag ,AccessExclusiveLock , true))
682+ {
683+ elog (LOG ,
684+ "RecoveryLockLists contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
685+ lock -> xid ,lock -> dbOid ,lock -> relOid );
686+ Assert (false);
687+ }
688+ pfree (lock );
689+ locks = list_delete_first (locks );
690+ }
691+ }
647692
648- next = lnext (cell );
693+ static void
694+ StandbyReleaseLocks (TransactionId xid )
695+ {
696+ RecoveryLockListsEntry * entry ;
649697
650- if (!TransactionIdIsValid (xid )|| lock -> xid == xid )
698+ if (TransactionIdIsValid (xid ))
699+ {
700+ if ((entry = hash_search (RecoveryLockLists ,& xid ,HASH_FIND ,NULL )))
651701{
652- LOCKTAG locktag ;
653-
654- elog (trace_recovery (DEBUG4 ),
655- "releasing recovery lock: xid %u db %u rel %u" ,
656- lock -> xid ,lock -> dbOid ,lock -> relOid );
657- SET_LOCKTAG_RELATION (locktag ,lock -> dbOid ,lock -> relOid );
658- if (!LockRelease (& locktag ,AccessExclusiveLock , true))
659- elog (LOG ,
660- "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
661- lock -> xid ,lock -> dbOid ,lock -> relOid );
662-
663- RecoveryLockList = list_delete_cell (RecoveryLockList ,cell ,prev );
664- pfree (lock );
702+ StandbyReleaseLockList (entry -> locks );
703+ hash_search (RecoveryLockLists ,entry ,HASH_REMOVE ,NULL );
665704}
666- else
667- prev = cell ;
668705}
706+ else
707+ StandbyReleaseAllLocks ();
669708}
670709
671710/*
672711 * Release locks for a transaction tree, starting at xid down, from
673- *RecoveryLockList .
712+ *RecoveryLockLists .
674713 *
675714 * Called during WAL replay of COMMIT/ROLLBACK when in hot standby mode,
676715 * to remove any AccessExclusiveLocks requested by a transaction.
@@ -692,30 +731,16 @@ StandbyReleaseLockTree(TransactionId xid, int nsubxids, TransactionId *subxids)
692731void
693732StandbyReleaseAllLocks (void )
694733{
695- ListCell * cell ,
696- * prev ,
697- * next ;
698- LOCKTAG locktag ;
734+ HASH_SEQ_STATUS status ;
735+ RecoveryLockListsEntry * entry ;
699736
700737elog (trace_recovery (DEBUG2 ),"release all standby locks" );
701738
702- prev = NULL ;
703- for ( cell = list_head ( RecoveryLockList ); cell ; cell = next )
739+ hash_seq_init ( & status , RecoveryLockLists ) ;
740+ while (( entry = hash_seq_search ( & status )) )
704741{
705- xl_standby_lock * lock = (xl_standby_lock * )lfirst (cell );
706-
707- next = lnext (cell );
708-
709- elog (trace_recovery (DEBUG4 ),
710- "releasing recovery lock: xid %u db %u rel %u" ,
711- lock -> xid ,lock -> dbOid ,lock -> relOid );
712- SET_LOCKTAG_RELATION (locktag ,lock -> dbOid ,lock -> relOid );
713- if (!LockRelease (& locktag ,AccessExclusiveLock , true))
714- elog (LOG ,
715- "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
716- lock -> xid ,lock -> dbOid ,lock -> relOid );
717- RecoveryLockList = list_delete_cell (RecoveryLockList ,cell ,prev );
718- pfree (lock );
742+ StandbyReleaseLockList (entry -> locks );
743+ hash_search (RecoveryLockLists ,entry ,HASH_REMOVE ,NULL );
719744}
720745}
721746
@@ -727,22 +752,17 @@ StandbyReleaseAllLocks(void)
727752void
728753StandbyReleaseOldLocks (int nxids ,TransactionId * xids )
729754{
730- ListCell * cell ,
731- * prev ,
732- * next ;
733- LOCKTAG locktag ;
755+ HASH_SEQ_STATUS status ;
756+ RecoveryLockListsEntry * entry ;
734757
735- prev = NULL ;
736- for ( cell = list_head ( RecoveryLockList ); cell ; cell = next )
758+ hash_seq_init ( & status , RecoveryLockLists ) ;
759+ while (( entry = hash_seq_search ( & status )) )
737760{
738- xl_standby_lock * lock = (xl_standby_lock * )lfirst (cell );
739761bool remove = false;
740762
741- next = lnext ( cell );
763+ Assert ( TransactionIdIsValid ( entry -> xid ) );
742764
743- Assert (TransactionIdIsValid (lock -> xid ));
744-
745- if (StandbyTransactionIdIsPrepared (lock -> xid ))
765+ if (StandbyTransactionIdIsPrepared (entry -> xid ))
746766remove = false;
747767else
748768{
@@ -751,7 +771,7 @@ StandbyReleaseOldLocks(int nxids, TransactionId *xids)
751771
752772for (i = 0 ;i < nxids ;i ++ )
753773{
754- if (lock -> xid == xids [i ])
774+ if (entry -> xid == xids [i ])
755775{
756776found = true;
757777break ;
@@ -767,19 +787,9 @@ StandbyReleaseOldLocks(int nxids, TransactionId *xids)
767787
768788if (remove )
769789{
770- elog (trace_recovery (DEBUG4 ),
771- "releasing recovery lock: xid %u db %u rel %u" ,
772- lock -> xid ,lock -> dbOid ,lock -> relOid );
773- SET_LOCKTAG_RELATION (locktag ,lock -> dbOid ,lock -> relOid );
774- if (!LockRelease (& locktag ,AccessExclusiveLock , true))
775- elog (LOG ,
776- "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
777- lock -> xid ,lock -> dbOid ,lock -> relOid );
778- RecoveryLockList = list_delete_cell (RecoveryLockList ,cell ,prev );
779- pfree (lock );
790+ StandbyReleaseLockList (entry -> locks );
791+ hash_search (RecoveryLockLists ,entry ,HASH_REMOVE ,NULL );
780792}
781- else
782- prev = cell ;
783793}
784794}
785795