46
46
47
47
#include "access/transam.h"
48
48
#include "access/xact.h"
49
+ #include "lib/pairingheap.h"
49
50
#include "miscadmin.h"
50
51
#include "storage/predicate.h"
51
52
#include "storage/proc.h"
@@ -123,13 +124,14 @@ typedef struct ActiveSnapshotElt
123
124
static ActiveSnapshotElt * ActiveSnapshot = NULL ;
124
125
125
126
/*
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.
131
130
*/
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 };
133
135
134
136
/* first GetTransactionSnapshot call in a transaction? */
135
137
bool FirstSnapshotSet = false;
@@ -150,7 +152,7 @@ static Snapshot FirstXactSnapshot = NULL;
150
152
/* Current xact's exported snapshots (a list of Snapshot structs) */
151
153
static List * exportedSnapshots = NIL ;
152
154
153
-
155
+ /* Prototypes for local functions */
154
156
static Snapshot CopySnapshot (Snapshot snapshot );
155
157
static void FreeSnapshot (Snapshot snapshot );
156
158
static void SnapshotResetXmin (void );
@@ -183,7 +185,7 @@ GetTransactionSnapshot(void)
183
185
/* First call in transaction? */
184
186
if (!FirstSnapshotSet )
185
187
{
186
- Assert (RegisteredSnapshots == 0 );
188
+ Assert (pairingheap_is_empty ( & RegisteredSnapshots ) );
187
189
Assert (FirstXactSnapshot == NULL );
188
190
189
191
/*
@@ -205,7 +207,7 @@ GetTransactionSnapshot(void)
205
207
FirstXactSnapshot = CurrentSnapshot ;
206
208
/* Mark it as "registered" in FirstXactSnapshot */
207
209
FirstXactSnapshot -> regd_count ++ ;
208
- RegisteredSnapshots ++ ;
210
+ pairingheap_add ( & RegisteredSnapshots , & FirstXactSnapshot -> ph_node ) ;
209
211
}
210
212
else
211
213
CurrentSnapshot = GetSnapshotData (& CurrentSnapshotData );
@@ -350,7 +352,7 @@ SetTransactionSnapshot(Snapshot sourcesnap, TransactionId sourcexid)
350
352
/* Caller should have checked this already */
351
353
Assert (!FirstSnapshotSet );
352
354
353
- Assert (RegisteredSnapshots == 0 );
355
+ Assert (pairingheap_is_empty ( & RegisteredSnapshots ) );
354
356
Assert (FirstXactSnapshot == NULL );
355
357
Assert (!HistoricSnapshotActive ());
356
358
@@ -413,7 +415,7 @@ SetTransactionSnapshot(Snapshot sourcesnap, TransactionId sourcexid)
413
415
FirstXactSnapshot = CurrentSnapshot ;
414
416
/* Mark it as "registered" in FirstXactSnapshot */
415
417
FirstXactSnapshot -> regd_count ++ ;
416
- RegisteredSnapshots ++ ;
418
+ pairingheap_add ( & RegisteredSnapshots , & FirstXactSnapshot -> ph_node ) ;
417
419
}
418
420
419
421
FirstSnapshotSet = true;
@@ -639,7 +641,8 @@ RegisterSnapshotOnOwner(Snapshot snapshot, ResourceOwner owner)
639
641
snap -> regd_count ++ ;
640
642
ResourceOwnerRememberSnapshot (owner , snap );
641
643
642
- RegisteredSnapshots ++ ;
644
+ if (snap -> regd_count == 1 )
645
+ pairingheap_add (& RegisteredSnapshots , & snap -> ph_node );
643
646
644
647
return snap ;
645
648
}
@@ -671,29 +674,70 @@ UnregisterSnapshotFromOwner(Snapshot snapshot, ResourceOwner owner)
671
674
return ;
672
675
673
676
Assert (snapshot -> regd_count > 0 );
674
- Assert (RegisteredSnapshots > 0 );
677
+ Assert (! pairingheap_is_empty ( & RegisteredSnapshots ) );
675
678
676
679
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 )
679
686
{
680
687
FreeSnapshot (snapshot );
681
688
SnapshotResetXmin ();
682
689
}
683
690
}
684
691
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
+
685
710
/*
686
711
* SnapshotResetXmin
687
712
*
688
713
* If there are no more snapshots, we can reset our PGXACT->xmin to InvalidXid.
689
714
* Note we can do this without locking because we assume that storing an Xid
690
715
* 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.
691
721
*/
692
722
static void
693
723
SnapshotResetXmin (void )
694
724
{
695
- if (RegisteredSnapshots == 0 && ActiveSnapshot == NULL )
725
+ Snapshot minSnapshot ;
726
+
727
+ if (ActiveSnapshot != NULL )
728
+ return ;
729
+
730
+ if (pairingheap_is_empty (& RegisteredSnapshots ))
731
+ {
696
732
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 ;
697
741
}
698
742
699
743
/*
@@ -769,8 +813,8 @@ AtEOXact_Snapshot(bool isCommit)
769
813
if (FirstXactSnapshot != NULL )
770
814
{
771
815
Assert (FirstXactSnapshot -> regd_count > 0 );
772
- Assert (RegisteredSnapshots > 0 );
773
- RegisteredSnapshots -- ;
816
+ Assert (! pairingheap_is_empty ( & RegisteredSnapshots ) );
817
+ pairingheap_remove ( & RegisteredSnapshots , & FirstXactSnapshot -> ph_node ) ;
774
818
}
775
819
FirstXactSnapshot = NULL ;
776
820
@@ -782,6 +826,7 @@ AtEOXact_Snapshot(bool isCommit)
782
826
TransactionId myxid = GetTopTransactionId ();
783
827
int i ;
784
828
char buf [MAXPGPATH ];
829
+ ListCell * lc ;
785
830
786
831
/*
787
832
* Get rid of the files. Unlink failure is only a WARNING because (1)
@@ -798,14 +843,13 @@ AtEOXact_Snapshot(bool isCommit)
798
843
/*
799
844
* As with the FirstXactSnapshot, we needn't spend any effort on
800
845
* 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.
806
847
*/
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
+ }
809
853
810
854
exportedSnapshots = NIL ;
811
855
}
@@ -815,9 +859,8 @@ AtEOXact_Snapshot(bool isCommit)
815
859
{
816
860
ActiveSnapshotElt * active ;
817
861
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" );
821
864
822
865
/* complain about unpopped active snapshots */
823
866
for (active = ActiveSnapshot ; active != NULL ; active = active -> as_next )
@@ -829,7 +872,7 @@ AtEOXact_Snapshot(bool isCommit)
829
872
* it'll go away with TopTransactionContext.
830
873
*/
831
874
ActiveSnapshot = NULL ;
832
- RegisteredSnapshots = 0 ;
875
+ pairingheap_reset ( & RegisteredSnapshots ) ;
833
876
834
877
CurrentSnapshot = NULL ;
835
878
SecondarySnapshot = NULL ;
@@ -900,8 +943,7 @@ ExportSnapshot(Snapshot snapshot)
900
943
* Copy the snapshot into TopTransactionContext, add it to the
901
944
* exportedSnapshots list, and mark it pseudo-registered. We do this to
902
945
* 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.
905
947
*/
906
948
snapshot = CopySnapshot (snapshot );
907
949
@@ -910,7 +952,7 @@ ExportSnapshot(Snapshot snapshot)
910
952
MemoryContextSwitchTo (oldcxt );
911
953
912
954
snapshot -> regd_count ++ ;
913
- RegisteredSnapshots ++ ;
955
+ pairingheap_add ( & RegisteredSnapshots , & snapshot -> ph_node ) ;
914
956
915
957
/*
916
958
* Fill buf with a text serialization of the snapshot, plus identification
@@ -1303,7 +1345,8 @@ DeleteAllExportedSnapshotFiles(void)
1303
1345
bool
1304
1346
ThereAreNoPriorRegisteredSnapshots (void )
1305
1347
{
1306
- if (RegisteredSnapshots <= 1 )
1348
+ if (pairingheap_is_empty (& RegisteredSnapshots ) ||
1349
+ pairingheap_is_singular (& RegisteredSnapshots ))
1307
1350
return true;
1308
1351
1309
1352
return false;
0 commit comments