Skip to content

Commit bc062cb

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/002f01d748ac$eaa781a0$bff684e0$@tju.edu.cn
1 parent 794025e commit bc062cb

File tree

1 file changed

+38
-14
lines changed

1 file changed

+38
-14
lines changed

src/backend/access/transam/parallel.c

Lines changed: 38 additions & 14 deletions
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);
@@ -1260,6 +1271,8 @@ ParallelWorkerMain(Datum main_arg)
12601271
char *enumblacklistspace;
12611272
StringInfoData msgbuf;
12621273
char *session_dsm_handle_space;
1274+
Snapshot tsnapshot;
1275+
Snapshot asnapshot;
12631276

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

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

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

0 commit comments

Comments
 (0)