Skip to content

Commit 58454d0

Browse files
committed
Propagate xactStartTimestamp and stmtStartTimestamp to parallel workers.
Previously, a worker process would establish values for these based on its own start time. In v10 and up, this can trivially be shown to cause misbehavior of transaction_timestamp(), timestamp_in(), and related functions which are (perhaps unwisely?) marked parallel-safe. It seems likely that other behaviors might diverge from what happens in the parent as well. It's not as trivial to demonstrate problems in 9.6 or 9.5, but I'm sure it's still possible, so back-patch to all branches containing parallel worker infrastructure. In HEAD only, mark now() and statement_timestamp() as parallel-safe (other affected functions already were). While in theory we could still squeeze that change into v11, it doesn't seem important enough to force a last-minute catversion bump. Konstantin Knizhnik, whacked around a bit by me Discussion: https://postgr.es/m/6406dbd2-5d37-4cb6-6eb2-9c44172c7e7c@postgrespro.ru
1 parent 142cfd3 commit 58454d0

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

src/backend/access/transam/parallel.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ typedef struct FixedParallelState
7979
PGPROC *parallel_master_pgproc;
8080
pid_t parallel_master_pid;
8181
BackendId parallel_master_backend_id;
82+
TimestampTz xact_ts;
83+
TimestampTz stmt_ts;
8284

8385
/* Mutex protects remaining fields. */
8486
slock_t mutex;
@@ -289,6 +291,8 @@ InitializeParallelDSM(ParallelContext *pcxt)
289291
fps->parallel_master_pgproc = MyProc;
290292
fps->parallel_master_pid = MyProcPid;
291293
fps->parallel_master_backend_id = MyBackendId;
294+
fps->xact_ts = GetCurrentTransactionStartTimestamp();
295+
fps->stmt_ts = GetCurrentStatementStartTimestamp();
292296
SpinLockInit(&fps->mutex);
293297
fps->last_xlog_end = 0;
294298
shm_toc_insert(pcxt->toc, PARALLEL_KEY_FIXED, fps);
@@ -1118,6 +1122,13 @@ ParallelWorkerMain(Datum main_arg)
11181122
fps->parallel_master_pid))
11191123
return;
11201124

1125+
/*
1126+
* Restore transaction and statement start-time timestamps. This must
1127+
* happen before anything that would start a transaction, else asserts in
1128+
* xact.c will fire.
1129+
*/
1130+
SetParallelStartTimestamps(fps->xact_ts, fps->stmt_ts);
1131+
11211132
/*
11221133
* Identify the entry point to be called. In theory this could result in
11231134
* loading an additional library, though most likely the entry point is in

src/backend/access/transam/xact.c

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,22 @@ GetCurrentCommandId(bool used)
701701
return currentCommandId;
702702
}
703703

704+
/*
705+
* SetParallelStartTimestamps
706+
*
707+
* In a parallel worker, we should inherit the parent transaction's
708+
* timestamps rather than setting our own. The parallel worker
709+
* infrastructure must call this to provide those values before
710+
* calling StartTransaction() or SetCurrentStatementStartTimestamp().
711+
*/
712+
void
713+
SetParallelStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts)
714+
{
715+
Assert(IsParallelWorker());
716+
xactStartTimestamp = xact_ts;
717+
stmtStartTimestamp = stmt_ts;
718+
}
719+
704720
/*
705721
* GetCurrentTransactionStartTimestamp
706722
*/
@@ -735,11 +751,17 @@ GetCurrentTransactionStopTimestamp(void)
735751

736752
/*
737753
* SetCurrentStatementStartTimestamp
754+
*
755+
* In a parallel worker, this should already have been provided by a call
756+
* to SetParallelStartTimestamps().
738757
*/
739758
void
740759
SetCurrentStatementStartTimestamp(void)
741760
{
742-
stmtStartTimestamp = GetCurrentTimestamp();
761+
if (!IsParallelWorker())
762+
stmtStartTimestamp = GetCurrentTimestamp();
763+
else
764+
Assert(stmtStartTimestamp != 0);
743765
}
744766

745767
/*
@@ -1893,10 +1915,16 @@ StartTransaction(void)
18931915
/*
18941916
* set transaction_timestamp() (a/k/a now()). We want this to be the same
18951917
* as the first command's statement_timestamp(), so don't do a fresh
1896-
* GetCurrentTimestamp() call (which'd be expensive anyway). Also, mark
1897-
* xactStopTimestamp as unset.
1918+
* GetCurrentTimestamp() call (which'd be expensive anyway). In a
1919+
* parallel worker, this should already have been provided by a call to
1920+
* SetParallelStartTimestamps().
1921+
*
1922+
* Also, mark xactStopTimestamp as unset.
18981923
*/
1899-
xactStartTimestamp = stmtStartTimestamp;
1924+
if (!IsParallelWorker())
1925+
xactStartTimestamp = stmtStartTimestamp;
1926+
else
1927+
Assert(xactStartTimestamp != 0);
19001928
xactStopTimestamp = 0;
19011929
pgstat_report_xact_timestamp(xactStartTimestamp);
19021930

src/include/access/xact.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ extern SubTransactionId GetCurrentSubTransactionId(void);
337337
extern void MarkCurrentTransactionIdLoggedIfAny(void);
338338
extern bool SubTransactionIsActive(SubTransactionId subxid);
339339
extern CommandId GetCurrentCommandId(bool used);
340+
extern void SetParallelStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts);
340341
extern TimestampTz GetCurrentTransactionStartTimestamp(void);
341342
extern TimestampTz GetCurrentStatementStartTimestamp(void);
342343
extern TimestampTz GetCurrentTransactionStopTimestamp(void);

0 commit comments

Comments
 (0)