1
- $PostgreSQL: pgsql/src/backend/access/transam/README,v 1.8 2007/09/07 20:59:26 tgl Exp $
1
+ $PostgreSQL: pgsql/src/backend/access/transam/README,v 1.9 2007/09/08 20:31:14 tgl Exp $
2
2
3
3
The Transaction System
4
4
----------------------
@@ -238,8 +238,10 @@ reason why this would be bad is that C would see (in the row inserted by A)
238
238
earlier changes by B, and it would be inconsistent for C not to see any
239
239
of B's changes elsewhere in the database.
240
240
241
- Formally, the correctness requirement is "if A sees B as committed,
242
- and B sees C as committed, then A must see C as committed".
241
+ Formally, the correctness requirement is "if a snapshot A considers
242
+ transaction X as committed, and any of transaction X's snapshots considered
243
+ transaction Y as committed, then snapshot A must consider transaction Y as
244
+ committed".
243
245
244
246
What we actually enforce is strict serialization of commits and rollbacks
245
247
with snapshot-taking: we do not allow any transaction to exit the set of
@@ -248,42 +250,45 @@ stronger than necessary for consistency, but is relatively simple to
248
250
enforce, and it assists with some other issues as explained below.) The
249
251
implementation of this is that GetSnapshotData takes the ProcArrayLock in
250
252
shared mode (so that multiple backends can take snapshots in parallel),
251
- but xact.c must take the ProcArrayLock in exclusive mode while clearing
252
- MyProc->xid at transaction end (either commit or abort).
253
+ but ProcArrayEndTransaction must take the ProcArrayLock in exclusive mode
254
+ while clearing MyProc->xid at transaction end (either commit or abort).
253
255
254
- GetSnapshotData must in fact acquire ProcArrayLock before it calls
255
- ReadNewTransactionId. Otherwise it would be possible for a transaction A
256
- postdating the xmax to commit, and then an existing transaction B that saw
257
- A as committed to commit, before GetSnapshotData is able to acquire
258
- ProcArrayLock and finish taking its snapshot. This would violate the
259
- consistency requirement, because A would be still running and B not
260
- according to this snapshot.
256
+ ProcArrayEndTransaction also holds the lock while advancing the shared
257
+ latestCompletedXid variable. This allows GetSnapshotData to use
258
+ latestCompletedXid + 1 as xmax for its snapshot: there can be no
259
+ transaction >= this xid value that the snapshot needs to consider as
260
+ completed.
261
261
262
262
In short, then, the rule is that no transaction may exit the set of
263
- currently-running transactions between the time we fetch xmax and the time
264
- we finish building our snapshot. However, this restriction only applies
265
- to transactions that have an XID --- read-only transactions can end without
266
- acquiring ProcArrayLock, since they don't affect anyone else's snapshot.
263
+ currently-running transactions between the time we fetch latestCompletedXid
264
+ and the time we finish building our snapshot. However, this restriction
265
+ only applies to transactions that have an XID --- read-only transactions
266
+ can end without acquiring ProcArrayLock, since they don't affect anyone
267
+ else's snapshot nor latestCompletedXid.
267
268
268
269
Transaction start, per se, doesn't have any interlocking with these
269
270
considerations, since we no longer assign an XID immediately at transaction
270
- start. But when we do decide to allocate an XID, we must require
271
- GetNewTransactionId to store the new XID into the shared ProcArray before
272
- releasing XidGenLock. This ensures that when GetSnapshotData calls
273
- ReadNewTransactionId (which also takes XidGenLock), all active XIDs before
274
- the returned value of nextXid are already present in the ProcArray and
275
- can't be missed by GetSnapshotData. Unfortunately, we can't have
276
- GetNewTransactionId take ProcArrayLock to do this, else it could deadlock
277
- against GetSnapshotData. Therefore, we simply let GetNewTransactionId
278
- store into MyProc->xid without any lock. We are thereby relying on
279
- fetch/store of an XID to be atomic, else other backends might see a
280
- partially-set XID. (NOTE: for multiprocessors that need explicit memory
281
- access fence instructions, this means that acquiring/releasing XidGenLock
282
- is just as necessary as acquiring/releasing ProcArrayLock for
283
- GetSnapshotData to ensure it sees up-to-date xid fields.) This also means
284
- that readers of the ProcArray xid fields must be careful to fetch a value
285
- only once, rather than assume they can read it multiple times and get the
286
- same answer each time.
271
+ start. But when we do decide to allocate an XID, GetNewTransactionId must
272
+ store the new XID into the shared ProcArray before releasing XidGenLock.
273
+ This ensures that all top-level XIDs <= latestCompletedXid are either
274
+ present in the ProcArray, or not running anymore. (This guarantee doesn't
275
+ apply to subtransaction XIDs, because of the possibility that there's not
276
+ room for them in the subxid array; instead we guarantee that they are
277
+ present or the overflow flag is set.) If a backend released XidGenLock
278
+ before storing its XID into MyProc, then it would be possible for another
279
+ backend to allocate and commit a later XID, causing latestCompletedXid to
280
+ pass the first backend's XID, before that value became visible in the
281
+ ProcArray. That would break GetOldestXmin, as discussed below.
282
+
283
+ We allow GetNewTransactionId to store the XID into MyProc->xid (or the
284
+ subxid array) without taking ProcArrayLock. This was once necessary to
285
+ avoid deadlock; while that is no longer the case, it's still beneficial for
286
+ performance. We are thereby relying on fetch/store of an XID to be atomic,
287
+ else other backends might see a partially-set XID. This also means that
288
+ readers of the ProcArray xid fields must be careful to fetch a value only
289
+ once, rather than assume they can read it multiple times and get the same
290
+ answer each time. (Use volatile-qualified pointers when doing this, to
291
+ ensure that the C compiler does exactly what you tell it to.)
287
292
288
293
Another important activity that uses the shared ProcArray is GetOldestXmin,
289
294
which must determine a lower bound for the oldest xmin of any active MVCC
@@ -303,12 +308,10 @@ currently-active XIDs: no xact, in particular not the oldest, can exit
303
308
while we hold shared ProcArrayLock. So GetOldestXmin's view of the minimum
304
309
active XID will be the same as that of any concurrent GetSnapshotData, and
305
310
so it can't produce an overestimate. If there is no active transaction at
306
- all, GetOldestXmin returns the result of ReadNewTransactionId. Note that
307
- two concurrent executions of GetOldestXmin might not see the same result
308
- from ReadNewTransactionId --- but if there is a difference, the intervening
309
- execution(s) of GetNewTransactionId must have stored their XIDs into the
310
- ProcArray, so the later execution of GetOldestXmin will see them and
311
- compute the same global xmin anyway.
311
+ all, GetOldestXmin returns latestCompletedXid + 1, which is a lower bound
312
+ for the xmin that might be computed by concurrent or later GetSnapshotData
313
+ calls. (We know that no XID less than this could be about to appear in
314
+ the ProcArray, because of the XidGenLock interlock discussed above.)
312
315
313
316
GetSnapshotData also performs an oldest-xmin calculation (which had better
314
317
match GetOldestXmin's) and stores that into RecentGlobalXmin, which is used
0 commit comments