Skip to content

Commit f4b77e8

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 e35705f commit f4b77e8

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
@@ -247,8 +247,11 @@ InitializeParallelDSM(ParallelContext *pcxt)
247247
shm_toc_estimate_chunk(&pcxt->estimator, guc_len);
248248
combocidlen = EstimateComboCIDStateSpace();
249249
shm_toc_estimate_chunk(&pcxt->estimator, combocidlen);
250-
tsnaplen = EstimateSnapshotSpace(transaction_snapshot);
251-
shm_toc_estimate_chunk(&pcxt->estimator, tsnaplen);
250+
if (IsolationUsesXactSnapshot())
251+
{
252+
tsnaplen = EstimateSnapshotSpace(transaction_snapshot);
253+
shm_toc_estimate_chunk(&pcxt->estimator, tsnaplen);
254+
}
252255
asnaplen = EstimateSnapshotSpace(active_snapshot);
253256
shm_toc_estimate_chunk(&pcxt->estimator, asnaplen);
254257
tstatelen = EstimateTransactionStateSpace();
@@ -356,11 +359,19 @@ InitializeParallelDSM(ParallelContext *pcxt)
356359
SerializeComboCIDState(combocidlen, combocidspace);
357360
shm_toc_insert(pcxt->toc, PARALLEL_KEY_COMBO_CID, combocidspace);
358361

359-
/* Serialize transaction snapshot and active snapshot. */
360-
tsnapspace = shm_toc_allocate(pcxt->toc, tsnaplen);
361-
SerializeSnapshot(transaction_snapshot, tsnapspace);
362-
shm_toc_insert(pcxt->toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT,
363-
tsnapspace);
362+
/*
363+
* Serialize the transaction snapshot if the transaction
364+
* isolation-level uses a transaction snapshot.
365+
*/
366+
if (IsolationUsesXactSnapshot())
367+
{
368+
tsnapspace = shm_toc_allocate(pcxt->toc, tsnaplen);
369+
SerializeSnapshot(transaction_snapshot, tsnapspace);
370+
shm_toc_insert(pcxt->toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT,
371+
tsnapspace);
372+
}
373+
374+
/* Serialize the active snapshot. */
364375
asnapspace = shm_toc_allocate(pcxt->toc, asnaplen);
365376
SerializeSnapshot(active_snapshot, asnapspace);
366377
shm_toc_insert(pcxt->toc, PARALLEL_KEY_ACTIVE_SNAPSHOT, asnapspace);
@@ -1226,6 +1237,8 @@ ParallelWorkerMain(Datum main_arg)
12261237
char *enumblacklistspace;
12271238
StringInfoData msgbuf;
12281239
char *session_dsm_handle_space;
1240+
Snapshot tsnapshot;
1241+
Snapshot asnapshot;
12291242

12301243
/* Set flag to indicate that we're initializing a parallel worker. */
12311244
InitializingParallelWorker = true;
@@ -1373,14 +1386,25 @@ ParallelWorkerMain(Datum main_arg)
13731386
shm_toc_lookup(toc, PARALLEL_KEY_SESSION_DSM, false);
13741387
AttachSession(*(dsm_handle *) session_dsm_handle_space);
13751388

1376-
/* Restore transaction snapshot. */
1377-
tsnapspace = shm_toc_lookup(toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT, false);
1378-
RestoreTransactionSnapshot(RestoreSnapshot(tsnapspace),
1379-
fps->parallel_master_pgproc);
1380-
1381-
/* Restore active snapshot. */
1389+
/*
1390+
* If the transaction isolation level is REPEATABLE READ or SERIALIZABLE,
1391+
* the leader has serialized the transaction snapshot and we must restore
1392+
* it. At lower isolation levels, there is no transaction-lifetime
1393+
* snapshot, but we need TransactionXmin to get set to a value which is
1394+
* less than or equal to the xmin of every snapshot that will be used by
1395+
* this worker. The easiest way to accomplish that is to install the
1396+
* active snapshot as the transaction snapshot. Code running in this
1397+
* parallel worker might take new snapshots via GetTransactionSnapshot()
1398+
* or GetLatestSnapshot(), but it shouldn't have any way of acquiring a
1399+
* snapshot older than the active snapshot.
1400+
*/
13821401
asnapspace = shm_toc_lookup(toc, PARALLEL_KEY_ACTIVE_SNAPSHOT, false);
1383-
PushActiveSnapshot(RestoreSnapshot(asnapspace));
1402+
tsnapspace = shm_toc_lookup(toc, PARALLEL_KEY_TRANSACTION_SNAPSHOT, true);
1403+
asnapshot = RestoreSnapshot(asnapspace);
1404+
tsnapshot = tsnapspace ? RestoreSnapshot(tsnapspace) : asnapshot;
1405+
RestoreTransactionSnapshot(tsnapshot,
1406+
fps->parallel_master_pgproc);
1407+
PushActiveSnapshot(asnapshot);
13841408

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

0 commit comments

Comments
 (0)