Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 9402869

Browse files
committed
Advance backend's advertised xmin more aggressively.
Currently, a backend will reset it's PGXACT->xmin value when it doesn't have any registered snapshots left. That covered the common case that a transaction in read committed mode runs several queries, one after each other, as there would be no snapshots active between those queries. However, if you hold cursors across each of the query, we didn't get a chance to reset xmin. To make that better, keep all the registered snapshots in a pairing heap, ordered by xmin so that it's always quick to find the snapshot with the smallest xmin. That allows us to advance PGXACT->xmin whenever the oldest snapshot is deregistered, even if there are others still active. Per discussion originally started by Jeff Davis back in 2009 and more recently by Robert Haas.
1 parent 779fdcd commit 9402869

File tree

3 files changed

+99
-34
lines changed

3 files changed

+99
-34
lines changed

src/backend/utils/time/snapmgr.c

+76-33
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
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
123124
static 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? */
135137
bool FirstSnapshotSet = false;
@@ -150,7 +152,7 @@ static Snapshot FirstXactSnapshot = NULL;
150152
/* Current xact's exported snapshots (a list of Snapshot structs) */
151153
static List *exportedSnapshots = NIL;
152154

153-
155+
/* Prototypes for local functions */
154156
static Snapshot CopySnapshot(Snapshot snapshot);
155157
static void FreeSnapshot(Snapshot snapshot);
156158
static 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
*/
692722
static void
693723
SnapshotResetXmin(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)
13031345
bool
13041346
ThereAreNoPriorRegisteredSnapshots(void)
13051347
{
1306-
if (RegisteredSnapshots <= 1)
1348+
if (pairingheap_is_empty(&RegisteredSnapshots) ||
1349+
pairingheap_is_singular(&RegisteredSnapshots))
13071350
return true;
13081351

13091352
return false;

src/include/lib/pairingheap.h

+19
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ typedef struct pairingheap_node
2929
struct pairingheap_node *prev_or_parent;
3030
} pairingheap_node;
3131

32+
/*
33+
* Return the containing struct of 'type' where 'membername' is the
34+
* pairingheap_node pointed at by 'ptr'.
35+
*
36+
* This is used to convert a pairingheap_node * back to its containing struct.
37+
*/
38+
#define pairingheap_container(type, membername, ptr) \
39+
(AssertVariableIsOfTypeMacro(ptr, pairingheap_node *), \
40+
AssertVariableIsOfTypeMacro(((type *) NULL)->membername, pairingheap_node), \
41+
((type *) ((char *) (ptr) - offsetof(type, membername))))
42+
43+
/*
44+
* Like pairingheap_container, but used when the pointer is 'const ptr'
45+
*/
46+
#define pairingheap_const_container(type, membername, ptr) \
47+
(AssertVariableIsOfTypeMacro(ptr, const pairingheap_node *), \
48+
AssertVariableIsOfTypeMacro(((type *) NULL)->membername, pairingheap_node), \
49+
((const type *) ((const char *) (ptr) - offsetof(type, membername))))
50+
3251
/*
3352
* For a max-heap, the comparator must return <0 iff a < b, 0 iff a == b,
3453
* and >0 iff a > b. For a min-heap, the conditions are reversed.

src/include/utils/snapshot.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SNAPSHOT_H
1515

1616
#include "access/htup.h"
17+
#include "lib/pairingheap.h"
1718
#include "storage/buf.h"
1819

1920

@@ -91,7 +92,9 @@ typedef struct SnapshotData
9192
*/
9293
CommandId curcid; /* in my xact, CID < curcid are visible */
9394
uint32 active_count; /* refcount on ActiveSnapshot stack */
94-
uint32 regd_count; /* refcount on RegisteredSnapshotList */
95+
uint32 regd_count; /* refcount on RegisteredSnapshots */
96+
97+
pairingheap_node ph_node; /* link in the RegisteredSnapshots heap */
9598
} SnapshotData;
9699

97100
/*

0 commit comments

Comments
 (0)