1 /*-------------------------------------------------------------------------
3 * PostgreSQL snapshot manager
5 * We keep track of snapshots in two ways: the "registered snapshots" list,
6 * and the "active snapshot" stack. All snapshots in either of them live in
7 * persistent memory. When a snapshot is no longer in any of these lists
8 * (tracked by separate refcounts on each snapshot), its memory can be freed.
10 * These arrangements let us reset MyProc->xmin when there are no snapshots
11 * referenced by this transaction. (One possible improvement would be to be
12 * able to advance Xmin when the snapshot with the earliest Xmin is no longer
13 * referenced. That's a bit harder though, it requires more locking, and
14 * anyway it should be rather uncommon to keep snapshots referenced for too
17 * Note: parts of this code could probably be replaced by appropriate use
21 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
22 * Portions Copyright (c) 1994, Regents of the University of California
27 *-------------------------------------------------------------------------
31 #include "access/transam.h"
32 #include "access/xact.h"
33 #include "storage/proc.h"
34 #include "storage/procarray.h"
35 #include "utils/memutils.h"
36 #include "utils/memutils.h"
37 #include "utils/snapmgr.h"
38 #include "utils/tqual.h"
42 * CurrentSnapshot points to the only snapshot taken in a serializable
43 * transaction, and to the latest one taken in a read-committed transaction.
44 * SecondarySnapshot is a snapshot that's always up-to-date as of the current
45 * instant, even on a serializable transaction. It should only be used for
46 * special-purpose code (say, RI checking.)
48 * These SnapshotData structs are static to simplify memory allocation
49 * (see the hack in GetSnapshotData to avoid repeated malloc/free).
51 static SnapshotData CurrentSnapshotData
= {HeapTupleSatisfiesMVCC
};
52 static SnapshotData SecondarySnapshotData
= {HeapTupleSatisfiesMVCC
};
54 /* Pointers to valid snapshots */
55 static Snapshot CurrentSnapshot
= NULL
;
56 static Snapshot SecondarySnapshot
= NULL
;
59 * These are updated by GetSnapshotData. We initialize them this way
60 * for the convenience of TransactionIdIsInProgress: even in bootstrap
61 * mode, we don't want it to say that BootstrapTransactionId is in progress.
63 * RecentGlobalXmin is initialized to InvalidTransactionId, to ensure that no
64 * one tries to use a stale value. Readers should ensure that it has been set
65 * to something else before using it.
67 TransactionId TransactionXmin
= FirstNormalTransactionId
;
68 TransactionId RecentXmin
= FirstNormalTransactionId
;
69 TransactionId RecentGlobalXmin
= InvalidTransactionId
;
72 * Elements of the list of registered snapshots.
74 * Note that we keep refcounts both here and in SnapshotData. This is because
75 * the same snapshot may be registered more than once in a subtransaction, and
76 * if a subxact aborts we want to be able to subtract the correct amount of
77 * counts from SnapshotData. (Another approach would be keeping one
78 * RegdSnapshotElt each time a snapshot is registered, but that seems
79 * unnecessary wastage.)
81 * NB: the code assumes that elements in this list are in non-increasing
82 * order of s_level; also, the list must be NULL-terminated.
84 typedef struct RegdSnapshotElt
89 struct RegdSnapshotElt
*s_next
;
93 * Elements of the active snapshot stack.
95 * It's not necessary to keep a refcount like we do for the registered list;
96 * each element here accounts for exactly one active_count on SnapshotData.
97 * We cannot condense them like we do for RegdSnapshotElt because it would mess
98 * up the order of entries in the stack.
100 * NB: the code assumes that elements in this list are in non-increasing
101 * order of as_level; also, the list must be NULL-terminated.
103 typedef struct ActiveSnapshotElt
107 struct ActiveSnapshotElt
*as_next
;
110 /* Head of the list of registered snapshots */
111 static RegdSnapshotElt
*RegisteredSnapshotList
= NULL
;
113 /* Top of the stack of active snapshots */
114 static ActiveSnapshotElt
*ActiveSnapshot
= NULL
;
116 /* first GetTransactionSnapshot call in a transaction? */
117 bool FirstSnapshotSet
= false;
120 * Remembers whether this transaction registered a serializable snapshot at
121 * start. We cannot trust FirstSnapshotSet in combination with
122 * IsXactIsoLevelSerializable, because GUC may be reset before us.
124 static bool registered_serializable
= false;
127 static Snapshot
CopySnapshot(Snapshot snapshot
);
128 static void FreeSnapshot(Snapshot snapshot
);
129 static void SnapshotResetXmin(void);
133 * GetTransactionSnapshot
134 * Get the appropriate snapshot for a new query in a transaction.
137 * Note that the return value may point at static storage that will be modified
138 * by future calls and by CommandCounterIncrement(). Callers should call
139 * RegisterSnapshot or PushActiveSnapshot on the returned snap if it is to be
143 GetTransactionSnapshot(void)
145 /* First call in transaction? */
146 if (!FirstSnapshotSet
)
148 CurrentSnapshot
= GetSnapshotData(&CurrentSnapshotData
);
149 FirstSnapshotSet
= true;
152 * In serializable mode, the first snapshot must live until end of xact
153 * regardless of what the caller does with it, so we must register it
154 * internally here and unregister it at end of xact.
156 if (IsXactIsoLevelSerializable
)
158 CurrentSnapshot
= RegisterSnapshot(CurrentSnapshot
);
159 registered_serializable
= true;
162 return CurrentSnapshot
;
165 if (IsXactIsoLevelSerializable
)
166 return CurrentSnapshot
;
168 CurrentSnapshot
= GetSnapshotData(&CurrentSnapshotData
);
170 return CurrentSnapshot
;
175 * Get a snapshot that is up-to-date as of the current instant,
176 * even if we are executing in SERIALIZABLE mode.
179 GetLatestSnapshot(void)
181 /* Should not be first call in transaction */
182 if (!FirstSnapshotSet
)
183 elog(ERROR
, "no snapshot has been set");
185 SecondarySnapshot
= GetSnapshotData(&SecondarySnapshotData
);
187 return SecondarySnapshot
;
191 * SnapshotSetCommandId
192 * Propagate CommandCounterIncrement into the static snapshots, if set
195 SnapshotSetCommandId(CommandId curcid
)
197 if (!FirstSnapshotSet
)
201 CurrentSnapshot
->curcid
= curcid
;
202 if (SecondarySnapshot
)
203 SecondarySnapshot
->curcid
= curcid
;
208 * Copy the given snapshot.
210 * The copy is palloc'd in TopTransactionContext and has initial refcounts set
211 * to 0. The returned snapshot has the copied flag set.
214 CopySnapshot(Snapshot snapshot
)
220 Assert(snapshot
!= InvalidSnapshot
);
222 /* We allocate any XID arrays needed in the same palloc block. */
223 size
= subxipoff
= sizeof(SnapshotData
) +
224 snapshot
->xcnt
* sizeof(TransactionId
);
225 if (snapshot
->subxcnt
> 0)
226 size
+= snapshot
->subxcnt
* sizeof(TransactionId
);
228 newsnap
= (Snapshot
) MemoryContextAlloc(TopTransactionContext
, size
);
229 memcpy(newsnap
, snapshot
, sizeof(SnapshotData
));
231 newsnap
->regd_count
= 0;
232 newsnap
->active_count
= 0;
233 newsnap
->copied
= true;
235 /* setup XID array */
236 if (snapshot
->xcnt
> 0)
238 newsnap
->xip
= (TransactionId
*) (newsnap
+ 1);
239 memcpy(newsnap
->xip
, snapshot
->xip
,
240 snapshot
->xcnt
* sizeof(TransactionId
));
245 /* setup subXID array */
246 if (snapshot
->subxcnt
> 0)
248 newsnap
->subxip
= (TransactionId
*) ((char *) newsnap
+ subxipoff
);
249 memcpy(newsnap
->subxip
, snapshot
->subxip
,
250 snapshot
->subxcnt
* sizeof(TransactionId
));
253 newsnap
->subxip
= NULL
;
260 * Free the memory associated with a snapshot.
263 FreeSnapshot(Snapshot snapshot
)
265 Assert(snapshot
->regd_count
== 0);
266 Assert(snapshot
->active_count
== 0);
267 Assert(snapshot
->copied
);
274 * Set the given snapshot as the current active snapshot
276 * If this is the first use of this snapshot, create a new long-lived copy with
277 * active refcount=1. Otherwise, only increment the refcount.
280 PushActiveSnapshot(Snapshot snap
)
282 ActiveSnapshotElt
*newactive
;
284 Assert(snap
!= InvalidSnapshot
);
286 newactive
= MemoryContextAlloc(TopTransactionContext
, sizeof(ActiveSnapshotElt
));
287 /* Static snapshot? Create a persistent copy */
288 newactive
->as_snap
= snap
->copied
? snap
: CopySnapshot(snap
);
289 newactive
->as_next
= ActiveSnapshot
;
290 newactive
->as_level
= GetCurrentTransactionNestLevel();
292 newactive
->as_snap
->active_count
++;
294 ActiveSnapshot
= newactive
;
298 * PushUpdatedSnapshot
299 * As above, except we set the snapshot's CID to the current CID.
302 PushUpdatedSnapshot(Snapshot snapshot
)
307 * We cannot risk modifying a snapshot that's possibly already used
308 * elsewhere, so make a new copy to scribble on.
310 newsnap
= CopySnapshot(snapshot
);
311 newsnap
->curcid
= GetCurrentCommandId(false);
313 PushActiveSnapshot(newsnap
);
319 * Remove the topmost snapshot from the active snapshot stack, decrementing the
320 * reference count, and free it if this was the last reference.
323 PopActiveSnapshot(void)
325 ActiveSnapshotElt
*newstack
;
327 newstack
= ActiveSnapshot
->as_next
;
329 Assert(ActiveSnapshot
->as_snap
->active_count
> 0);
331 ActiveSnapshot
->as_snap
->active_count
--;
333 if (ActiveSnapshot
->as_snap
->active_count
== 0 &&
334 ActiveSnapshot
->as_snap
->regd_count
== 0)
335 FreeSnapshot(ActiveSnapshot
->as_snap
);
337 pfree(ActiveSnapshot
);
338 ActiveSnapshot
= newstack
;
345 * Return the topmost snapshot in the Active stack.
348 GetActiveSnapshot(void)
350 Assert(ActiveSnapshot
!= NULL
);
352 return ActiveSnapshot
->as_snap
;
357 * Return whether there is at least one snapshot in the Active stack
360 ActiveSnapshotSet(void)
362 return ActiveSnapshot
!= NULL
;
367 * Register a snapshot as being in use
369 * If InvalidSnapshot is passed, it is not registered.
372 RegisterSnapshot(Snapshot snapshot
)
374 RegdSnapshotElt
*elt
;
375 RegdSnapshotElt
*newhead
;
378 if (snapshot
== InvalidSnapshot
)
379 return InvalidSnapshot
;
381 level
= GetCurrentTransactionNestLevel();
384 * If there's already an item in the list for the same snapshot and the
385 * same subxact nest level, increment its refcounts. Otherwise create a
388 for (elt
= RegisteredSnapshotList
; elt
!= NULL
; elt
= elt
->s_next
)
390 if (elt
->s_level
< level
)
393 if (elt
->s_snap
== snapshot
&& elt
->s_level
== level
)
395 elt
->s_snap
->regd_count
++;
403 * Create the new list element. If it's not been copied into persistent
404 * memory already, we must do so; otherwise we can just increment the
407 newhead
= MemoryContextAlloc(TopTransactionContext
, sizeof(RegdSnapshotElt
));
408 newhead
->s_next
= RegisteredSnapshotList
;
409 /* Static snapshot? Create a persistent copy */
410 newhead
->s_snap
= snapshot
->copied
? snapshot
: CopySnapshot(snapshot
);
411 newhead
->s_level
= level
;
412 newhead
->s_count
= 1;
414 newhead
->s_snap
->regd_count
++;
416 RegisteredSnapshotList
= newhead
;
418 return RegisteredSnapshotList
->s_snap
;
423 * Signals that a snapshot is no longer necessary
425 * If both reference counts fall to zero, the snapshot memory is released.
426 * If only the registered list refcount falls to zero, just the list element is
430 UnregisterSnapshot(Snapshot snapshot
)
432 RegdSnapshotElt
*prev
= NULL
;
433 RegdSnapshotElt
*elt
;
436 if (snapshot
== InvalidSnapshot
)
439 for (elt
= RegisteredSnapshotList
; elt
!= NULL
; elt
= elt
->s_next
)
441 if (elt
->s_snap
== snapshot
)
443 Assert(elt
->s_snap
->regd_count
> 0);
444 Assert(elt
->s_count
> 0);
446 elt
->s_snap
->regd_count
--;
450 if (elt
->s_count
== 0)
452 /* delink it from the registered snapshot list */
454 prev
->s_next
= elt
->s_next
;
456 RegisteredSnapshotList
= elt
->s_next
;
458 /* free the snapshot itself if it's no longer relevant */
459 if (elt
->s_snap
->regd_count
== 0 && elt
->s_snap
->active_count
== 0)
460 FreeSnapshot(elt
->s_snap
);
462 /* and free the list element */
473 elog(WARNING
, "unregistering failed for snapshot %p", snapshot
);
481 * If there are no more snapshots, we can reset our PGPROC->xmin to InvalidXid.
482 * Note we can do this without locking because we assume that storing an Xid
486 SnapshotResetXmin(void)
488 if (RegisteredSnapshotList
== NULL
&& ActiveSnapshot
== NULL
)
489 MyProc
->xmin
= InvalidTransactionId
;
493 * AtSubCommit_Snapshot
496 AtSubCommit_Snapshot(int level
)
498 ActiveSnapshotElt
*active
;
499 RegdSnapshotElt
*regd
;
502 * Relabel the active snapshots set in this subtransaction as though they
503 * are owned by the parent subxact.
505 for (active
= ActiveSnapshot
; active
!= NULL
; active
= active
->as_next
)
507 if (active
->as_level
< level
)
509 active
->as_level
= level
- 1;
513 * Reassign all registered snapshots to the parent subxact.
515 * Note: this code is somewhat bogus in that we could end up with multiple
516 * entries for the same snapshot and the same subxact level (my parent's
517 * level). Cleaning that up is more trouble than it's currently worth,
520 for (regd
= RegisteredSnapshotList
; regd
!= NULL
; regd
= regd
->s_next
)
522 if (regd
->s_level
== level
)
528 * AtSubAbort_Snapshot
529 * Clean up snapshots after a subtransaction abort
532 AtSubAbort_Snapshot(int level
)
534 RegdSnapshotElt
*prev
;
535 RegdSnapshotElt
*regd
;
537 /* Forget the active snapshots set by this subtransaction */
538 while (ActiveSnapshot
&& ActiveSnapshot
->as_level
>= level
)
540 ActiveSnapshotElt
*next
;
542 next
= ActiveSnapshot
->as_next
;
545 * Decrement the snapshot's active count. If it's still registered or
546 * marked as active by an outer subtransaction, we can't free it yet.
548 Assert(ActiveSnapshot
->as_snap
->active_count
>= 1);
549 ActiveSnapshot
->as_snap
->active_count
-= 1;
551 if (ActiveSnapshot
->as_snap
->active_count
== 0 &&
552 ActiveSnapshot
->as_snap
->regd_count
== 0)
553 FreeSnapshot(ActiveSnapshot
->as_snap
);
555 /* and free the stack element */
556 pfree(ActiveSnapshot
);
558 ActiveSnapshot
= next
;
561 /* Unregister all snapshots registered during this subtransaction */
563 for (regd
= RegisteredSnapshotList
; regd
!= NULL
; )
565 if (regd
->s_level
>= level
)
567 RegdSnapshotElt
*tofree
;
570 prev
->s_next
= regd
->s_next
;
572 RegisteredSnapshotList
= regd
->s_next
;
577 tofree
->s_snap
->regd_count
-= tofree
->s_count
;
579 /* free the snapshot if possible */
580 if (tofree
->s_snap
->regd_count
== 0 &&
581 tofree
->s_snap
->active_count
== 0)
582 FreeSnapshot(tofree
->s_snap
);
584 /* and free the list element */
599 * Snapshot manager's cleanup function for end of transaction
602 AtEOXact_Snapshot(bool isCommit
)
604 /* On commit, complain about leftover snapshots */
607 ActiveSnapshotElt
*active
;
608 RegdSnapshotElt
*regd
;
611 * On a serializable snapshot we must first unregister our private
612 * refcount to the serializable snapshot.
614 if (registered_serializable
)
615 UnregisterSnapshot(CurrentSnapshot
);
617 /* complain about unpopped active snapshots */
618 for (active
= ActiveSnapshot
; active
!= NULL
; active
= active
->as_next
)
619 elog(WARNING
, "snapshot %p still active", active
);
621 /* complain about any unregistered snapshot */
622 for (regd
= RegisteredSnapshotList
; regd
!= NULL
; regd
= regd
->s_next
)
624 "snapshot %p not destroyed at commit (%d regd refs, %d active refs)",
625 regd
->s_snap
, regd
->s_snap
->regd_count
,
626 regd
->s_snap
->active_count
);
630 * And reset our state. We don't need to free the memory explicitly --
631 * it'll go away with TopTransactionContext.
633 ActiveSnapshot
= NULL
;
634 RegisteredSnapshotList
= NULL
;
636 CurrentSnapshot
= NULL
;
637 SecondarySnapshot
= NULL
;
639 FirstSnapshotSet
= false;
640 registered_serializable
= false;