1
1
/*-------------------------------------------------------------------------
2
+ *
2
3
* snapmgr.c
3
4
* PostgreSQL snapshot manager
4
5
*
8
9
* (tracked by separate refcounts on each snapshot), its memory can be freed.
9
10
*
10
11
* The FirstXactSnapshot, if any, is treated a bit specially: we increment its
11
- * regd_count and count it in RegisteredSnapshots, but this reference is not
12
+ * regd_count and list it in RegisteredSnapshots, but this reference is not
12
13
* tracked by a resource owner. We used to use the TopTransactionResourceOwner
13
14
* to track this snapshot reference, but that introduces logical circularity
14
15
* and thus makes it impossible to clean up in a sane fashion. It's better to
15
16
* handle this reference as an internally-tracked registration, so that this
16
17
* module is entirely lower-level than ResourceOwners.
17
18
*
18
19
* Likewise, any snapshots that have been exported by pg_export_snapshot
19
- * have regd_count = 1 and are counted in RegisteredSnapshots, but are not
20
+ * have regd_count = 1 and are listed in RegisteredSnapshots, but are not
20
21
* tracked by any resource owner.
21
22
*
23
+ * Likewise, the CatalogSnapshot is listed in RegisteredSnapshots when it
24
+ * is valid, but is not tracked by any resource owner.
25
+ *
22
26
* The same is true for historic snapshots used during logical decoding,
23
- * their lifetime is managed separately (as they life longer as one xact.c
27
+ * their lifetime is managed separately (as they live longer than one xact.c
24
28
* transaction).
25
29
*
26
30
* These arrangements let us reset MyPgXact->xmin when there are no snapshots
27
- * referenced by this transaction. (One possible improvement would be to be
28
- * able to advance Xmin when the snapshot with the earliest Xmin is no longer
29
- * referenced. That's a bit harder though, it requires more locking, and
30
- * anyway it should be rather uncommon to keep temporary snapshots referenced
31
- * for too long.)
31
+ * referenced by this transaction, and advance it when the one with oldest
32
+ * Xmin is no longer referenced. For simplicity however, only registered
33
+ * snapshots not active snapshots participate in tracking which one is oldest;
34
+ * we don't try to change MyPgXact->xmin except when the active-snapshot
35
+ * stack is empty.
32
36
*
33
37
*
34
38
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
66
70
* SecondarySnapshot is a snapshot that's always up-to-date as of the current
67
71
* instant, even in transaction-snapshot mode. It should only be used for
68
72
* special-purpose code (say, RI checking.) CatalogSnapshot points to an
69
- * MVCC snapshot intended to be used for catalog scans; we must refresh it
73
+ * MVCC snapshot intended to be used for catalog scans; we must invalidate it
70
74
* whenever a system catalog change occurs.
71
75
*
72
76
* These SnapshotData structs are static to simplify memory allocation
@@ -82,11 +86,6 @@ static Snapshot SecondarySnapshot = NULL;
82
86
static Snapshot CatalogSnapshot = NULL ;
83
87
static Snapshot HistoricSnapshot = NULL ;
84
88
85
- /*
86
- * Staleness detection for CatalogSnapshot.
87
- */
88
- static bool CatalogSnapshotStale = true;
89
-
90
89
/*
91
90
* These are updated by GetSnapshotData. We initialize them this way
92
91
* for the convenience of TransactionIdIsInProgress: even in bootstrap
@@ -125,8 +124,7 @@ static ActiveSnapshotElt *ActiveSnapshot = NULL;
125
124
126
125
/*
127
126
* 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.
127
+ * quickly find the one with lowest xmin, to advance our MyPgXact->xmin.
130
128
*/
131
129
static int xmin_cmp (const pairingheap_node * a , const pairingheap_node * b ,
132
130
void * arg );
@@ -201,6 +199,12 @@ GetTransactionSnapshot(void)
201
199
/* First call in transaction? */
202
200
if (!FirstSnapshotSet )
203
201
{
202
+ /*
203
+ * Don't allow catalog snapshot to be older than xact snapshot. Must
204
+ * do this first to allow the empty-heap Assert to succeed.
205
+ */
206
+ InvalidateCatalogSnapshot ();
207
+
204
208
Assert (pairingheap_is_empty (& RegisteredSnapshots ));
205
209
Assert (FirstXactSnapshot == NULL );
206
210
@@ -232,9 +236,6 @@ GetTransactionSnapshot(void)
232
236
else
233
237
CurrentSnapshot = GetSnapshotData (& CurrentSnapshotData );
234
238
235
- /* Don't allow catalog snapshot to be older than xact snapshot. */
236
- CatalogSnapshotStale = true;
237
-
238
239
FirstSnapshotSet = true;
239
240
return CurrentSnapshot ;
240
241
}
@@ -243,7 +244,7 @@ GetTransactionSnapshot(void)
243
244
return CurrentSnapshot ;
244
245
245
246
/* Don't allow catalog snapshot to be older than xact snapshot. */
246
- CatalogSnapshotStale = true ;
247
+ InvalidateCatalogSnapshot () ;
247
248
248
249
CurrentSnapshot = GetSnapshotData (& CurrentSnapshotData );
249
250
@@ -318,36 +319,72 @@ GetNonHistoricCatalogSnapshot(Oid relid)
318
319
* scan a relation for which neither catcache nor snapshot invalidations
319
320
* are sent, we must refresh the snapshot every time.
320
321
*/
321
- if (!CatalogSnapshotStale && !RelationInvalidatesSnapshotsOnly (relid ) &&
322
+ if (CatalogSnapshot &&
323
+ !RelationInvalidatesSnapshotsOnly (relid ) &&
322
324
!RelationHasSysCache (relid ))
323
- CatalogSnapshotStale = true ;
325
+ InvalidateCatalogSnapshot () ;
324
326
325
- if (CatalogSnapshotStale )
327
+ if (CatalogSnapshot == NULL )
326
328
{
327
329
/* Get new snapshot. */
328
330
CatalogSnapshot = GetSnapshotData (& CatalogSnapshotData );
329
331
330
332
/*
331
- * Mark new snapshost as valid. We must do this last, in case an
332
- * ERROR occurs inside GetSnapshotData().
333
+ * Make sure the catalog snapshot will be accounted for in decisions
334
+ * about advancing PGXACT->xmin. We could apply RegisterSnapshot, but
335
+ * that would result in making a physical copy, which is overkill; and
336
+ * it would also create a dependency on some resource owner, which we
337
+ * do not want for reasons explained at the head of this file. Instead
338
+ * just shove the CatalogSnapshot into the pairing heap manually. This
339
+ * has to be reversed in InvalidateCatalogSnapshot, of course.
340
+ *
341
+ * NB: it had better be impossible for this to throw error, since the
342
+ * CatalogSnapshot pointer is already valid.
333
343
*/
334
- CatalogSnapshotStale = false ;
344
+ pairingheap_add ( & RegisteredSnapshots , & CatalogSnapshot -> ph_node ) ;
335
345
}
336
346
337
347
return CatalogSnapshot ;
338
348
}
339
349
340
350
/*
341
- * Mark the current catalog snapshot as invalid. We could change this API
342
- * to allow the caller to provide more fine-grained invalidation details, so
343
- * that a change to relation A wouldn't prevent us from using our cached
344
- * snapshot to scan relation B, but so far there's no evidence that the CPU
345
- * cycles we spent tracking such fine details would be well-spent.
351
+ * InvalidateCatalogSnapshot
352
+ * Mark the current catalog snapshot, if any, as invalid
353
+ *
354
+ * We could change this API to allow the caller to provide more fine-grained
355
+ * invalidation details, so that a change to relation A wouldn't prevent us
356
+ * from using our cached snapshot to scan relation B, but so far there's no
357
+ * evidence that the CPU cycles we spent tracking such fine details would be
358
+ * well-spent.
359
+ */
360
+ void
361
+ InvalidateCatalogSnapshot (void )
362
+ {
363
+ if (CatalogSnapshot )
364
+ {
365
+ pairingheap_remove (& RegisteredSnapshots , & CatalogSnapshot -> ph_node );
366
+ CatalogSnapshot = NULL ;
367
+ SnapshotResetXmin ();
368
+ }
369
+ }
370
+
371
+ /*
372
+ * InvalidateCatalogSnapshotConditionally
373
+ * Drop catalog snapshot if it's the only one we have
374
+ *
375
+ * This is called when we are about to wait for client input, so we don't
376
+ * want to continue holding the catalog snapshot if it might mean that the
377
+ * global xmin horizon can't advance. However, if there are other snapshots
378
+ * still active or registered, the catalog snapshot isn't likely to be the
379
+ * oldest one, so we might as well keep it.
346
380
*/
347
381
void
348
- InvalidateCatalogSnapshot ( )
382
+ InvalidateCatalogSnapshotConditionally ( void )
349
383
{
350
- CatalogSnapshotStale = true;
384
+ if (CatalogSnapshot &&
385
+ ActiveSnapshot == NULL &&
386
+ pairingheap_is_singular (& RegisteredSnapshots ))
387
+ InvalidateCatalogSnapshot ();
351
388
}
352
389
353
390
/*
@@ -364,6 +401,7 @@ SnapshotSetCommandId(CommandId curcid)
364
401
CurrentSnapshot -> curcid = curcid ;
365
402
if (SecondarySnapshot )
366
403
SecondarySnapshot -> curcid = curcid ;
404
+ /* Should we do the same with CatalogSnapshot? */
367
405
}
368
406
369
407
/*
@@ -381,6 +419,9 @@ SetTransactionSnapshot(Snapshot sourcesnap, TransactionId sourcexid,
381
419
/* Caller should have checked this already */
382
420
Assert (!FirstSnapshotSet );
383
421
422
+ /* Better do this to ensure following Assert succeeds. */
423
+ InvalidateCatalogSnapshot ();
424
+
384
425
Assert (pairingheap_is_empty (& RegisteredSnapshots ));
385
426
Assert (FirstXactSnapshot == NULL );
386
427
Assert (!HistoricSnapshotActive ());
@@ -769,7 +810,15 @@ xmin_cmp(const pairingheap_node *a, const pairingheap_node *b, void *arg)
769
810
* Even if there are some remaining snapshots, we may be able to advance our
770
811
* PGXACT->xmin to some degree. This typically happens when a portal is
771
812
* dropped. For efficiency, we only consider recomputing PGXACT->xmin when
772
- * the active snapshot stack is empty.
813
+ * the active snapshot stack is empty; this allows us not to need to track
814
+ * which active snapshot is oldest.
815
+ *
816
+ * Note: it's tempting to use GetOldestSnapshot() here so that we can include
817
+ * active snapshots in the calculation. However, that compares by LSN not
818
+ * xmin so it's not entirely clear that it's the same thing. Also, we'd be
819
+ * critically dependent on the assumption that the bottommost active snapshot
820
+ * stack entry has the oldest xmin. (Current uses of GetOldestSnapshot() are
821
+ * not actually critical, but this would be.)
773
822
*/
774
823
static void
775
824
SnapshotResetXmin (void )
@@ -855,7 +904,7 @@ AtEOXact_Snapshot(bool isCommit)
855
904
{
856
905
/*
857
906
* In transaction-snapshot mode we must release our privately-managed
858
- * reference to the transaction snapshot. We must decrement
907
+ * reference to the transaction snapshot. We must remove it from
859
908
* RegisteredSnapshots to keep the check below happy. But we don't bother
860
909
* to do FreeSnapshot, for two reasons: the memory will go away with
861
910
* TopTransactionContext anyway, and if someone has left the snapshot
@@ -895,7 +944,7 @@ AtEOXact_Snapshot(bool isCommit)
895
944
/*
896
945
* As with the FirstXactSnapshot, we needn't spend any effort on
897
946
* cleaning up the per-snapshot data structures, but we do need to
898
- * unlink them from RegisteredSnapshots to prevent a warning below.
947
+ * remove them from RegisteredSnapshots to prevent a warning below.
899
948
*/
900
949
foreach (lc , exportedSnapshots )
901
950
{
@@ -907,6 +956,9 @@ AtEOXact_Snapshot(bool isCommit)
907
956
exportedSnapshots = NIL ;
908
957
}
909
958
959
+ /* Drop catalog snapshot if any */
960
+ InvalidateCatalogSnapshot ();
961
+
910
962
/* On commit, complain about leftover snapshots */
911
963
if (isCommit )
912
964
{
0 commit comments