Skip to content

Commit a780b2f

Browse files
committed
Fix broken snapshot handling in parallel workers.
Pengchengliu reported an assertion failure in a parallel woker while performing a parallel scan using an overflowed snapshot. The proximate cause is that TransactionXmin was set to an incorrect value. The underlying cause is incorrect snapshot handling in parallel.c. In particular, InitializeParallelDSM() was unconditionally calling GetTransactionSnapshot(), because I (rhaas) mistakenly thought that was always retrieving an existing snapshot whereas, at isolation levels less than REPEATABLE READ, it's actually taking a new one. So instead do this only at higher isolation levels where there actually is a single snapshot for the whole transaction. By itself, this is not a sufficient fix, because we still need to guarantee that TransactionXmin gets set properly in the workers. The easiest way to do that seems to be to install the leader's active snapshot as the transaction snapshot if the leader did not serialize a transaction snapshot. This doesn't affect the results of future GetTrasnactionSnapshot() calls since those have to take a new snapshot anyway; what we care about is the side effect of setting TransactionXmin. Report by Pengchengliu. Patch by Greg Nancarrow, except for some comment text which I supplied. Discussion: https://postgr.es/m/[email protected]
1 parent 43d4dd8 commit a780b2f

File tree

1 file changed

+38
-14
lines changed

1 file changed

+38
-14
lines changed

src/backend/access/transam/parallel.c

+38-14
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,11 @@ InitializeParallelDSM(ParallelContext *pcxt)
254254
shm_toc_estimate_chunk(&pcxt->estimator, guc_len);
255255
combocidlen = EstimateComboCIDStateSpace();
256256
shm_toc_estimate_chunk(&pcxt->estimator, combocidlen);
257-
tsnaplen = EstimateSnapshotSpace(transaction_snapshot);
258-
shm_toc_estimate_chunk(&pcxt->estimator, tsnaplen);
257+
if (IsolationUsesXactSnapshot())
258+
{
259+
tsnaplen = EstimateSnapshotSpace(transaction_snapshot);
260+
shm_toc_estimate_chunk(&pcxt->estimator, tsnaplen);
261+
}
259262
asnaplen = EstimateSnapshotSpace(active_snapshot);
260263
shm_toc_estimate_chunk(&pcxt->estimator, asnaplen);
261264
tstatelen = EstimateTransactionStateSpace();
@@ -366,11 +369,19 @@ InitializeParallelDSM(ParallelContext *pcxt)
366369
SerializeComboCIDState(combocidlen, combocidspace);
367370
shm_toc_insert(pcxt->toc, PARALLEL_KEY_COMBO_CID, combocidspace);
368371

369-
/* Serialize transaction snapshot and active snapshot. */
370-
tsnapspace = shm_toc_allocate(pcxt->toc, tsnaplen);
371-
SerializeSnapshot(transaction_snapshot, tsnapspace);
372-
shm_toc_insert(pcxt->toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT,
373-
tsnapspace);
372+
/*
373+
* Serialize the transaction snapshot if the transaction
374+
* isolation-level uses a transaction snapshot.
375+
*/
376+
if (IsolationUsesXactSnapshot())
377+
{
378+
tsnapspace = shm_toc_allocate(pcxt->toc, tsnaplen);
379+
SerializeSnapshot(transaction_snapshot, tsnapspace);
380+
shm_toc_insert(pcxt->toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT,
381+
tsnapspace);
382+
}
383+
384+
/* Serialize the active snapshot. */
374385
asnapspace = shm_toc_allocate(pcxt->toc, asnaplen);
375386
SerializeSnapshot(active_snapshot, asnapspace);
376387
shm_toc_insert(pcxt->toc, PARALLEL_KEY_ACTIVE_SNAPSHOT, asnapspace);
@@ -1261,6 +1272,8 @@ ParallelWorkerMain(Datum main_arg)
12611272
char *uncommittedenumsspace;
12621273
StringInfoData msgbuf;
12631274
char *session_dsm_handle_space;
1275+
Snapshot tsnapshot;
1276+
Snapshot asnapshot;
12641277

12651278
/* Set flag to indicate that we're initializing a parallel worker. */
12661279
InitializingParallelWorker = true;
@@ -1408,14 +1421,25 @@ ParallelWorkerMain(Datum main_arg)
14081421
shm_toc_lookup(toc, PARALLEL_KEY_SESSION_DSM, false);
14091422
AttachSession(*(dsm_handle *) session_dsm_handle_space);
14101423

1411-
/* Restore transaction snapshot. */
1412-
tsnapspace = shm_toc_lookup(toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT, false);
1413-
RestoreTransactionSnapshot(RestoreSnapshot(tsnapspace),
1414-
fps->parallel_leader_pgproc);
1415-
1416-
/* Restore active snapshot. */
1424+
/*
1425+
* If the transaction isolation level is REPEATABLE READ or SERIALIZABLE,
1426+
* the leader has serialized the transaction snapshot and we must restore
1427+
* it. At lower isolation levels, there is no transaction-lifetime
1428+
* snapshot, but we need TransactionXmin to get set to a value which is
1429+
* less than or equal to the xmin of every snapshot that will be used by
1430+
* this worker. The easiest way to accomplish that is to install the
1431+
* active snapshot as the transaction snapshot. Code running in this
1432+
* parallel worker might take new snapshots via GetTransactionSnapshot()
1433+
* or GetLatestSnapshot(), but it shouldn't have any way of acquiring a
1434+
* snapshot older than the active snapshot.
1435+
*/
14171436
asnapspace = shm_toc_lookup(toc, PARALLEL_KEY_ACTIVE_SNAPSHOT, false);
1418-
PushActiveSnapshot(RestoreSnapshot(asnapspace));
1437+
tsnapspace = shm_toc_lookup(toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT, true);
1438+
asnapshot = RestoreSnapshot(asnapspace);
1439+
tsnapshot = tsnapspace ? RestoreSnapshot(tsnapspace) : asnapshot;
1440+
RestoreTransactionSnapshot(tsnapshot,
1441+
fps->parallel_leader_pgproc);
1442+
PushActiveSnapshot(asnapshot);
14191443

14201444
/*
14211445
* We've changed which tuples we can see, and must therefore invalidate

0 commit comments

Comments
 (0)