4646
4747#include "access/transam.h"
4848#include "access/xact.h"
49+ #include "lib/pairingheap.h"
4950#include "miscadmin.h"
5051#include "storage/predicate.h"
5152#include "storage/proc.h"
@@ -123,13 +124,14 @@ typedef struct ActiveSnapshotElt
123124static ActiveSnapshotElt * ActiveSnapshot = NULL ;
124125
125126/*
126- * How many snapshots is resowner.c tracking for us?
127- *
128- * Note: for now, a simple counter is enough. However, if we ever want to be
129- * smarter about advancing our MyPgXact->xmin we will need to be more
130- * sophisticated about this, perhaps keeping our own list of snapshots.
127+ * Currently registered Snapshots. Ordered in a heap by xmin, so that we can
128+ * quickly find the one with lowest xmin, to advance our MyPgXat->xmin.
129+ * resowner.c also tracks these.
131130 */
132- static int RegisteredSnapshots = 0 ;
131+ static int xmin_cmp (const pairingheap_node * a , const pairingheap_node * b ,
132+ void * arg );
133+
134+ static pairingheap RegisteredSnapshots = { & xmin_cmp , NULL , NULL };
133135
134136/* first GetTransactionSnapshot call in a transaction? */
135137bool FirstSnapshotSet = false;
@@ -150,7 +152,7 @@ static Snapshot FirstXactSnapshot = NULL;
150152/* Current xact's exported snapshots (a list of Snapshot structs) */
151153static List * exportedSnapshots = NIL ;
152154
153-
155+ /* Prototypes for local functions */
154156static Snapshot CopySnapshot (Snapshot snapshot );
155157static void FreeSnapshot (Snapshot snapshot );
156158static void SnapshotResetXmin (void );
@@ -183,7 +185,7 @@ GetTransactionSnapshot(void)
183185 /* First call in transaction? */
184186 if (!FirstSnapshotSet )
185187 {
186- Assert (RegisteredSnapshots == 0 );
188+ Assert (pairingheap_is_empty ( & RegisteredSnapshots ) );
187189 Assert (FirstXactSnapshot == NULL );
188190
189191 /*
@@ -205,7 +207,7 @@ GetTransactionSnapshot(void)
205207 FirstXactSnapshot = CurrentSnapshot ;
206208 /* Mark it as "registered" in FirstXactSnapshot */
207209 FirstXactSnapshot -> regd_count ++ ;
208- RegisteredSnapshots ++ ;
210+ pairingheap_add ( & RegisteredSnapshots , & FirstXactSnapshot -> ph_node ) ;
209211 }
210212 else
211213 CurrentSnapshot = GetSnapshotData (& CurrentSnapshotData );
@@ -350,7 +352,7 @@ SetTransactionSnapshot(Snapshot sourcesnap, TransactionId sourcexid)
350352 /* Caller should have checked this already */
351353 Assert (!FirstSnapshotSet );
352354
353- Assert (RegisteredSnapshots == 0 );
355+ Assert (pairingheap_is_empty ( & RegisteredSnapshots ) );
354356 Assert (FirstXactSnapshot == NULL );
355357 Assert (!HistoricSnapshotActive ());
356358
@@ -413,7 +415,7 @@ SetTransactionSnapshot(Snapshot sourcesnap, TransactionId sourcexid)
413415 FirstXactSnapshot = CurrentSnapshot ;
414416 /* Mark it as "registered" in FirstXactSnapshot */
415417 FirstXactSnapshot -> regd_count ++ ;
416- RegisteredSnapshots ++ ;
418+ pairingheap_add ( & RegisteredSnapshots , & FirstXactSnapshot -> ph_node ) ;
417419 }
418420
419421 FirstSnapshotSet = true;
@@ -639,7 +641,8 @@ RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
639641 snap -> regd_count ++ ;
640642 ResourceOwnerRememberSnapshot (owner , snap );
641643
642- RegisteredSnapshots ++ ;
644+ if (snap -> regd_count == 1 )
645+ pairingheap_add (& RegisteredSnapshots , & snap -> ph_node );
643646
644647 return snap ;
645648}
@@ -671,29 +674,70 @@ UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
671674 return ;
672675
673676 Assert (snapshot -> regd_count > 0 );
674- Assert (RegisteredSnapshots > 0 );
677+ Assert (! pairingheap_is_empty ( & RegisteredSnapshots ) );
675678
676679 ResourceOwnerForgetSnapshot (owner , snapshot );
677- RegisteredSnapshots -- ;
678- if (-- snapshot -> regd_count == 0 && snapshot -> active_count == 0 )
680+
681+ snapshot -> regd_count -- ;
682+ if (snapshot -> regd_count == 0 )
683+ pairingheap_remove (& RegisteredSnapshots , & snapshot -> ph_node );
684+
685+ if (snapshot -> regd_count == 0 && snapshot -> active_count == 0 )
679686 {
680687 FreeSnapshot (snapshot );
681688 SnapshotResetXmin ();
682689 }
683690}
684691
692+ /*
693+ * Comparison function for RegisteredSnapshots heap. Snapshots are ordered
694+ * by xmin, so that the snapshot with smallest xmin is at the top.
695+ */
696+ static int
697+ xmin_cmp (const pairingheap_node * a , const pairingheap_node * b , void * arg )
698+ {
699+ const SnapshotData * asnap = pairingheap_const_container (SnapshotData , ph_node , a );
700+ const SnapshotData * bsnap = pairingheap_const_container (SnapshotData , ph_node , b );
701+
702+ if (TransactionIdPrecedes (asnap -> xmin , bsnap -> xmin ))
703+ return 1 ;
704+ else if (TransactionIdFollows (asnap -> xmin , bsnap -> xmin ))
705+ return -1 ;
706+ else
707+ return 0 ;
708+ }
709+
685710/*
686711 * SnapshotResetXmin
687712 *
688713 * If there are no more snapshots, we can reset our PGXACT->xmin to InvalidXid.
689714 * Note we can do this without locking because we assume that storing an Xid
690715 * is atomic.
716+ *
717+ * Even if there are some remaining snapshots, we may be able to advance our
718+ * PGXACT->xmin to some degree. This typically happens when a portal is
719+ * dropped. For efficiency, we only consider recomputing PGXACT->xmin when
720+ * the active snapshot stack is empty.
691721 */
692722static void
693723SnapshotResetXmin (void )
694724{
695- if (RegisteredSnapshots == 0 && ActiveSnapshot == NULL )
725+ Snapshot minSnapshot ;
726+
727+ if (ActiveSnapshot != NULL )
728+ return ;
729+
730+ if (pairingheap_is_empty (& RegisteredSnapshots ))
731+ {
696732 MyPgXact -> xmin = InvalidTransactionId ;
733+ return ;
734+ }
735+
736+ minSnapshot = pairingheap_container (SnapshotData , ph_node ,
737+ pairingheap_first (& RegisteredSnapshots ));
738+
739+ if (TransactionIdPrecedes (MyPgXact -> xmin , minSnapshot -> xmin ))
740+ MyPgXact -> xmin = minSnapshot -> xmin ;
697741}
698742
699743/*
@@ -769,8 +813,8 @@ AtEOXact_Snapshot(bool isCommit)
769813 if (FirstXactSnapshot != NULL )
770814 {
771815 Assert (FirstXactSnapshot -> regd_count > 0 );
772- Assert (RegisteredSnapshots > 0 );
773- RegisteredSnapshots -- ;
816+ Assert (! pairingheap_is_empty ( & RegisteredSnapshots ) );
817+ pairingheap_remove ( & RegisteredSnapshots , & FirstXactSnapshot -> ph_node ) ;
774818 }
775819 FirstXactSnapshot = NULL ;
776820
@@ -782,6 +826,7 @@ AtEOXact_Snapshot(bool isCommit)
782826 TransactionId myxid = GetTopTransactionId ();
783827 int i ;
784828 char buf [MAXPGPATH ];
829+ ListCell * lc ;
785830
786831 /*
787832 * Get rid of the files. Unlink failure is only a WARNING because (1)
@@ -798,14 +843,13 @@ AtEOXact_Snapshot(bool isCommit)
798843 /*
799844 * As with the FirstXactSnapshot, we needn't spend any effort on
800845 * cleaning up the per-snapshot data structures, but we do need to
801- * adjust the RegisteredSnapshots count to prevent a warning below.
802- *
803- * Note: you might be thinking "why do we have the exportedSnapshots
804- * list at all? All we need is a counter!". You're right, but we do
805- * it this way in case we ever feel like improving xmin management.
846+ * unlink them from RegisteredSnapshots to prevent a warning below.
806847 */
807- Assert (RegisteredSnapshots >= list_length (exportedSnapshots ));
808- RegisteredSnapshots -= list_length (exportedSnapshots );
848+ foreach (lc , exportedSnapshots )
849+ {
850+ Snapshot snap = (Snapshot ) lfirst (lc );
851+ pairingheap_remove (& RegisteredSnapshots , & snap -> ph_node );
852+ }
809853
810854 exportedSnapshots = NIL ;
811855 }
@@ -815,9 +859,8 @@ AtEOXact_Snapshot(bool isCommit)
815859 {
816860 ActiveSnapshotElt * active ;
817861
818- if (RegisteredSnapshots != 0 )
819- elog (WARNING , "%d registered snapshots seem to remain after cleanup" ,
820- RegisteredSnapshots );
862+ if (!pairingheap_is_empty (& RegisteredSnapshots ))
863+ elog (WARNING , "registered snapshots seem to remain after cleanup" );
821864
822865 /* complain about unpopped active snapshots */
823866 for (active = ActiveSnapshot ; active != NULL ; active = active -> as_next )
@@ -829,7 +872,7 @@ AtEOXact_Snapshot(bool isCommit)
829872 * it'll go away with TopTransactionContext.
830873 */
831874 ActiveSnapshot = NULL ;
832- RegisteredSnapshots = 0 ;
875+ pairingheap_reset ( & RegisteredSnapshots ) ;
833876
834877 CurrentSnapshot = NULL ;
835878 SecondarySnapshot = NULL ;
@@ -900,8 +943,7 @@ ExportSnapshot(Snapshot snapshot)
900943 * Copy the snapshot into TopTransactionContext, add it to the
901944 * exportedSnapshots list, and mark it pseudo-registered. We do this to
902945 * ensure that the snapshot's xmin is honored for the rest of the
903- * transaction. (Right now, because SnapshotResetXmin is so stupid, this
904- * is overkill; but later we might make that routine smarter.)
946+ * transaction.
905947 */
906948 snapshot = CopySnapshot (snapshot );
907949
@@ -910,7 +952,7 @@ ExportSnapshot(Snapshot snapshot)
910952 MemoryContextSwitchTo (oldcxt );
911953
912954 snapshot -> regd_count ++ ;
913- RegisteredSnapshots ++ ;
955+ pairingheap_add ( & RegisteredSnapshots , & snapshot -> ph_node ) ;
914956
915957 /*
916958 * Fill buf with a text serialization of the snapshot, plus identification
@@ -1303,7 +1345,8 @@ DeleteAllExportedSnapshotFiles(void)
13031345bool
13041346ThereAreNoPriorRegisteredSnapshots (void )
13051347{
1306- if (RegisteredSnapshots <= 1 )
1348+ if (pairingheap_is_empty (& RegisteredSnapshots ) ||
1349+ pairingheap_is_singular (& RegisteredSnapshots ))
13071350 return true;
13081351
13091352 return false;
0 commit comments