27
27
#include "storage/procarray.h"
28
28
#include "storage/sinvaladt.h"
29
29
#include "storage/standby.h"
30
+ #include "utils/hsearch.h"
31
+ #include "utils/memutils.h"
30
32
#include "utils/ps_status.h"
31
33
#include "utils/timeout.h"
32
34
#include "utils/timestamp.h"
@@ -36,7 +38,7 @@ int vacuum_defer_cleanup_age;
36
38
int max_standby_archive_delay = 30 * 1000 ;
37
39
int max_standby_streaming_delay = 30 * 1000 ;
38
40
39
- static List * RecoveryLockList ;
41
+ static HTAB * RecoveryLockLists ;
40
42
41
43
static void ResolveRecoveryConflictWithVirtualXIDs (VirtualTransactionId * waitlist ,
42
44
ProcSignalReason reason );
@@ -45,6 +47,14 @@ static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
45
47
static XLogRecPtr LogCurrentRunningXacts (RunningTransactions CurrRunningXacts );
46
48
static void LogAccessExclusiveLocks (int nlocks , xl_standby_lock * locks );
47
49
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 ;
48
58
49
59
/*
50
60
* InitRecoveryTransactionEnvironment
62
72
InitRecoveryTransactionEnvironment (void )
63
73
{
64
74
VirtualTransactionId 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
+ hash_ctl .hash = tag_hash ;
85
+ RecoveryLockLists = hash_create ("RecoveryLockLists" ,
86
+ 64 ,
87
+ & hash_ctl ,
88
+ HASH_ELEM | HASH_FUNCTION );
65
89
66
90
/*
67
91
* Initialize shared invalidation management for Startup process, being
@@ -106,6 +130,10 @@ ShutdownRecoveryTransactionEnvironment(void)
106
130
/* Release all locks the tracked transactions were holding */
107
131
StandbyReleaseAllLocks ();
108
132
133
+ /* Destroy the hash table of locks. */
134
+ hash_destroy (RecoveryLockLists );
135
+ RecoveryLockLists = NULL ;
136
+
109
137
/* Cleanup our VirtualTransaction */
110
138
VirtualXactLockTableCleanup ();
111
139
}
@@ -548,8 +576,8 @@ StandbyTimeoutHandler(void)
548
576
* We only keep track of AccessExclusiveLocks, which are only ever held by
549
577
* one transaction on one relation, and don't worry about lock queuing.
550
578
*
551
- * We keep a single dynamically expandible list of locks in local memory,
552
- * RecoveryLockList , so we can keep track of the various entries made by
579
+ * We keep a hash table of lists of locks in local memory keyed by xid ,
580
+ * RecoveryLockLists , so we can keep track of the various entries made by
553
581
* the Startup process's virtual xid in the shared lock table.
554
582
*
555
583
* We record the lock against the top-level xid, rather than individual
@@ -567,8 +595,10 @@ StandbyTimeoutHandler(void)
567
595
void
568
596
StandbyAcquireAccessExclusiveLock (TransactionId xid , Oid dbOid , Oid relOid )
569
597
{
598
+ RecoveryLockListsEntry * entry ;
570
599
xl_standby_lock * newlock ;
571
600
LOCKTAG locktag ;
601
+ bool found ;
572
602
573
603
/* Already processed? */
574
604
if (!TransactionIdIsValid (xid ) ||
@@ -582,11 +612,19 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
582
612
/* dbOid is InvalidOid when we are locking a shared relation. */
583
613
Assert (OidIsValid (relOid ));
584
614
615
+ /* Create a new list for this xid, if we don't have one already. */
616
+ entry = hash_search (RecoveryLockLists , & xid , HASH_ENTER , & found );
617
+ if (!found )
618
+ {
619
+ entry -> xid = xid ;
620
+ entry -> locks = NIL ;
621
+ }
622
+
585
623
newlock = palloc (sizeof (xl_standby_lock ));
586
624
newlock -> xid = xid ;
587
625
newlock -> dbOid = dbOid ;
588
626
newlock -> relOid = relOid ;
589
- RecoveryLockList = lappend (RecoveryLockList , newlock );
627
+ entry -> locks = lappend (entry -> locks , newlock );
590
628
591
629
/*
592
630
* Attempt to acquire the lock as requested, if not resolve conflict
@@ -599,46 +637,48 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
599
637
}
600
638
601
639
static void
602
- StandbyReleaseLocks ( TransactionId xid )
640
+ StandbyReleaseLockList ( List * locks )
603
641
{
604
- ListCell * cell ,
605
- * prev ,
606
- * next ;
607
-
608
- /*
609
- * Release all matching locks and remove them from list
610
- */
611
- prev = NULL ;
612
- for (cell = list_head (RecoveryLockList ); cell ; cell = next )
642
+ while (locks )
613
643
{
614
- xl_standby_lock * lock = (xl_standby_lock * ) lfirst (cell );
644
+ xl_standby_lock * lock = (xl_standby_lock * ) linitial (locks );
645
+ LOCKTAG locktag ;
646
+ elog (trace_recovery (DEBUG4 ),
647
+ "releasing recovery lock: xid %u db %u rel %u" ,
648
+ lock -> xid , lock -> dbOid , lock -> relOid );
649
+ SET_LOCKTAG_RELATION (locktag , lock -> dbOid , lock -> relOid );
650
+ if (!LockRelease (& locktag , AccessExclusiveLock , true))
651
+ {
652
+ elog (LOG ,
653
+ "RecoveryLockLists contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
654
+ lock -> xid , lock -> dbOid , lock -> relOid );
655
+ Assert (false);
656
+ }
657
+ pfree (lock );
658
+ locks = list_delete_first (locks );
659
+ }
660
+ }
615
661
616
- next = lnext (cell );
662
+ static void
663
+ StandbyReleaseLocks (TransactionId xid )
664
+ {
665
+ RecoveryLockListsEntry * entry ;
617
666
618
- if (!TransactionIdIsValid (xid ) || lock -> xid == xid )
667
+ if (TransactionIdIsValid (xid ))
668
+ {
669
+ if ((entry = hash_search (RecoveryLockLists , & xid , HASH_FIND , NULL )))
619
670
{
620
- LOCKTAG locktag ;
621
-
622
- elog (trace_recovery (DEBUG4 ),
623
- "releasing recovery lock: xid %u db %u rel %u" ,
624
- lock -> xid , lock -> dbOid , lock -> relOid );
625
- SET_LOCKTAG_RELATION (locktag , lock -> dbOid , lock -> relOid );
626
- if (!LockRelease (& locktag , AccessExclusiveLock , true))
627
- elog (LOG ,
628
- "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
629
- lock -> xid , lock -> dbOid , lock -> relOid );
630
-
631
- RecoveryLockList = list_delete_cell (RecoveryLockList , cell , prev );
632
- pfree (lock );
671
+ StandbyReleaseLockList (entry -> locks );
672
+ hash_search (RecoveryLockLists , entry , HASH_REMOVE , NULL );
633
673
}
634
- else
635
- prev = cell ;
636
674
}
675
+ else
676
+ StandbyReleaseAllLocks ();
637
677
}
638
678
639
679
/*
640
680
* Release locks for a transaction tree, starting at xid down, from
641
- * RecoveryLockList .
681
+ * RecoveryLockLists .
642
682
*
643
683
* Called during WAL replay of COMMIT/ROLLBACK when in hot standby mode,
644
684
* to remove any AccessExclusiveLocks requested by a transaction.
@@ -660,30 +700,16 @@ StandbyReleaseLockTree(TransactionId xid, int nsubxids, TransactionId *subxids)
660
700
void
661
701
StandbyReleaseAllLocks (void )
662
702
{
663
- ListCell * cell ,
664
- * prev ,
665
- * next ;
666
- LOCKTAG locktag ;
703
+ HASH_SEQ_STATUS status ;
704
+ RecoveryLockListsEntry * entry ;
667
705
668
706
elog (trace_recovery (DEBUG2 ), "release all standby locks" );
669
707
670
- prev = NULL ;
671
- for ( cell = list_head ( RecoveryLockList ); cell ; cell = next )
708
+ hash_seq_init ( & status , RecoveryLockLists ) ;
709
+ while (( entry = hash_seq_search ( & status )) )
672
710
{
673
- xl_standby_lock * lock = (xl_standby_lock * ) lfirst (cell );
674
-
675
- next = lnext (cell );
676
-
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
- elog (LOG ,
683
- "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
684
- lock -> xid , lock -> dbOid , lock -> relOid );
685
- RecoveryLockList = list_delete_cell (RecoveryLockList , cell , prev );
686
- pfree (lock );
711
+ StandbyReleaseLockList (entry -> locks );
712
+ hash_search (RecoveryLockLists , entry , HASH_REMOVE , NULL );
687
713
}
688
714
}
689
715
@@ -695,22 +721,17 @@ StandbyReleaseAllLocks(void)
695
721
void
696
722
StandbyReleaseOldLocks (int nxids , TransactionId * xids )
697
723
{
698
- ListCell * cell ,
699
- * prev ,
700
- * next ;
701
- LOCKTAG locktag ;
724
+ HASH_SEQ_STATUS status ;
725
+ RecoveryLockListsEntry * entry ;
702
726
703
- prev = NULL ;
704
- for ( cell = list_head ( RecoveryLockList ); cell ; cell = next )
727
+ hash_seq_init ( & status , RecoveryLockLists ) ;
728
+ while (( entry = hash_seq_search ( & status )) )
705
729
{
706
- xl_standby_lock * lock = (xl_standby_lock * ) lfirst (cell );
707
730
bool remove = false;
708
731
709
- next = lnext ( cell );
732
+ Assert ( TransactionIdIsValid ( entry -> xid ) );
710
733
711
- Assert (TransactionIdIsValid (lock -> xid ));
712
-
713
- if (StandbyTransactionIdIsPrepared (lock -> xid ))
734
+ if (StandbyTransactionIdIsPrepared (entry -> xid ))
714
735
remove = false;
715
736
else
716
737
{
@@ -719,7 +740,7 @@ StandbyReleaseOldLocks(int nxids, TransactionId *xids)
719
740
720
741
for (i = 0 ; i < nxids ; i ++ )
721
742
{
722
- if (lock -> xid == xids [i ])
743
+ if (entry -> xid == xids [i ])
723
744
{
724
745
found = true;
725
746
break ;
@@ -735,19 +756,9 @@ StandbyReleaseOldLocks(int nxids, TransactionId *xids)
735
756
736
757
if (remove )
737
758
{
738
- elog (trace_recovery (DEBUG4 ),
739
- "releasing recovery lock: xid %u db %u rel %u" ,
740
- lock -> xid , lock -> dbOid , lock -> relOid );
741
- SET_LOCKTAG_RELATION (locktag , lock -> dbOid , lock -> relOid );
742
- if (!LockRelease (& locktag , AccessExclusiveLock , true))
743
- elog (LOG ,
744
- "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u" ,
745
- lock -> xid , lock -> dbOid , lock -> relOid );
746
- RecoveryLockList = list_delete_cell (RecoveryLockList , cell , prev );
747
- pfree (lock );
759
+ StandbyReleaseLockList (entry -> locks );
760
+ hash_search (RecoveryLockLists , entry , HASH_REMOVE , NULL );
748
761
}
749
- else
750
- prev = cell ;
751
762
}
752
763
}
753
764
0 commit comments