]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Propagate xactStartTimestamp and stmtStartTimestamp to parallel workers.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Oct 2018 16:00:10 +0000 (12:00 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Oct 2018 16:00:10 +0000 (12:00 -0400)
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

src/backend/access/transam/parallel.c
src/backend/access/transam/xact.c
src/include/access/xact.h

index bf2fbd69982110e1facb74187f850a6a494eddf4..1c5b4d067fce2a83878f9143365cb9e47957394d 100644 (file)
@@ -71,6 +71,8 @@ typedef struct FixedParallelState
        PGPROC     *parallel_master_pgproc;
        pid_t           parallel_master_pid;
        BackendId       parallel_master_backend_id;
+       TimestampTz xact_ts;
+       TimestampTz stmt_ts;
 
        /* Entrypoint for parallel workers. */
        parallel_worker_main_type entrypoint;
@@ -282,6 +284,8 @@ InitializeParallelDSM(ParallelContext *pcxt)
        fps->parallel_master_pgproc = MyProc;
        fps->parallel_master_pid = MyProcPid;
        fps->parallel_master_backend_id = MyBackendId;
+       fps->xact_ts = GetCurrentTransactionStartTimestamp();
+       fps->stmt_ts = GetCurrentStatementStartTimestamp();
        fps->entrypoint = pcxt->entrypoint;
        SpinLockInit(&fps->mutex);
        fps->last_xlog_end = 0;
@@ -924,6 +928,13 @@ ParallelWorkerMain(Datum main_arg)
         * backend-local state to match the original backend.
         */
 
+       /*
+        * Restore transaction and statement start-time timestamps.  This must
+        * happen before anything that would start a transaction, else asserts in
+        * xact.c will fire.
+        */
+       SetParallelStartTimestamps(fps->xact_ts, fps->stmt_ts);
+
        /*
         * Load libraries that were loaded by original backend.  We want to do
         * this before restoring GUCs, because the libraries might define custom
index d3632c25cae2c1d72ecf2f78b8998205b00b397b..bda98e7924a6019acac56e07cee1959f621fb569 100644 (file)
@@ -698,6 +698,22 @@ GetCurrentCommandId(bool used)
        return currentCommandId;
 }
 
+/*
+ *     SetParallelStartTimestamps
+ *
+ * In a parallel worker, we should inherit the parent transaction's
+ * timestamps rather than setting our own.  The parallel worker
+ * infrastructure must call this to provide those values before
+ * calling StartTransaction() or SetCurrentStatementStartTimestamp().
+ */
+void
+SetParallelStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts)
+{
+       Assert(IsParallelWorker());
+       xactStartTimestamp = xact_ts;
+       stmtStartTimestamp = stmt_ts;
+}
+
 /*
  *     GetCurrentTransactionStartTimestamp
  */
@@ -732,11 +748,17 @@ GetCurrentTransactionStopTimestamp(void)
 
 /*
  *     SetCurrentStatementStartTimestamp
+ *
+ * In a parallel worker, this should already have been provided by a call
+ * to SetParallelStartTimestamps().
  */
 void
 SetCurrentStatementStartTimestamp(void)
 {
-       stmtStartTimestamp = GetCurrentTimestamp();
+       if (!IsParallelWorker())
+               stmtStartTimestamp = GetCurrentTimestamp();
+       else
+               Assert(stmtStartTimestamp != 0);
 }
 
 /*
@@ -1875,10 +1897,16 @@ StartTransaction(void)
        /*
         * set transaction_timestamp() (a/k/a now()).  We want this to be the same
         * as the first command's statement_timestamp(), so don't do a fresh
-        * GetCurrentTimestamp() call (which'd be expensive anyway).  Also, mark
-        * xactStopTimestamp as unset.
+        * GetCurrentTimestamp() call (which'd be expensive anyway).  In a
+        * parallel worker, this should already have been provided by a call to
+        * SetParallelStartTimestamps().
+        *
+        * Also, mark xactStopTimestamp as unset.
         */
-       xactStartTimestamp = stmtStartTimestamp;
+       if (!IsParallelWorker())
+               xactStartTimestamp = stmtStartTimestamp;
+       else
+               Assert(xactStartTimestamp != 0);
        xactStopTimestamp = 0;
        pgstat_report_xact_timestamp(xactStartTimestamp);
 
index b688ad952adb28a8786dafde8d1b9e30cc70d6e6..e0010a4d965f3297d41b768ea62013c986eb2f93 100644 (file)
@@ -313,6 +313,7 @@ extern SubTransactionId GetCurrentSubTransactionId(void);
 extern void MarkCurrentTransactionIdLoggedIfAny(void);
 extern bool SubTransactionIsActive(SubTransactionId subxid);
 extern CommandId GetCurrentCommandId(bool used);
+extern void SetParallelStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts);
 extern TimestampTz GetCurrentTransactionStartTimestamp(void);
 extern TimestampTz GetCurrentStatementStartTimestamp(void);
 extern TimestampTz GetCurrentTransactionStopTimestamp(void);