Received: from malur.postgresql.org ([2a02:16a8:dc51::56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1g8m3O-0002Bj-U4 for pgsql-hackers@arkaria.postgresql.org; Sat, 06 Oct 2018 12:51:31 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1g8m3M-00044s-3G for pgsql-hackers@arkaria.postgresql.org; Sat, 06 Oct 2018 12:51:28 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.89) (envelope-from ) id 1g8m3L-00044k-OG for pgsql-hackers@lists.postgresql.org; Sat, 06 Oct 2018 12:51:27 +0000 Received: from mail.postgrespro.ru ([93.174.131.138]) by magus.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1g8m3E-0002Eq-Hm for pgsql-hackers@postgresql.org; Sat, 06 Oct 2018 12:51:26 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.postgrespro.ru (Postfix) with ESMTP id 2397321C240C; Sat, 6 Oct 2018 15:51:19 +0300 (MSK) X-Virus-Scanned: Debian amavisd-new at postgrespro.ru X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=x tagged_above=-99 required=4 WHITELISTED tests=[] autolearn=unavailable Received: from [192.168.0.37] (unknown [92.243.182.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (Client did not present a certificate) by mail.postgrespro.ru (Postfix) with ESMTPSA id AB51321C2409; Sat, 6 Oct 2018 15:51:18 +0300 (MSK) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mail; t=1538830278; bh=JOEGxhZ3fOhWmB4n1oUqz18LNL4wiCBmJ2XypoQjyIU=; h=Subject:To:Cc:References:From:Date:In-Reply-To; b=GcrPQ/fe98CBDi1BUrnvnX7lagln/zYRC9WFuoG5NbsP+RKWueYEWeIpILMq+9Oht AyYE5aD4wviRSZvqCzm+rUmOyci8K0ge+zG46ySmdSK2gB5coaCVB47Mw4r1qmCD1C XG0ZHaLzTLCQVK3D/bqbw1AI6fkqiUBT3IAnLW2k= Subject: Re: now() vs transaction_timestamp() To: Tom Lane Cc: PostgreSQL Hackers , Robert Haas References: <6406dbd2-5d37-4cb6-6eb2-9c44172c7e7c@postgrespro.ru> <4268.1538747795@sss.pgh.pa.us> <4911.1538774725@sss.pgh.pa.us> From: Konstantin Knizhnik Message-ID: <9c78b24f-4ec9-e1dd-bdf6-e09e9b5f0e31@postgrespro.ru> Date: Sat, 6 Oct 2018 15:51:17 +0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.9.1 MIME-Version: 1.0 In-Reply-To: <4911.1538774725@sss.pgh.pa.us> Content-Type: multipart/mixed; boundary="------------AB3AB136731E7C86C2181B77" Content-Language: en-US List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk This is a multi-part message in MIME format. --------------AB3AB136731E7C86C2181B77 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: 7bit On 06.10.2018 00:25, Tom Lane wrote: > I wrote: >> Konstantin Knizhnik writes: >>> Postgres documentation says that |"now()| is a traditional PostgreSQL >>> equivalent to |transaction_timestamp()|". >>> Also both use the same implementation. >> Right. >>> But them have different parallel safety property: >> That seems like a bug/thinko. I am not sure which property setting is >> correct though. It'd only be safe if the parallel-query infrastructure >> transfers the relevant timestamp to workers, and I don't know if it does. > The answer is that it doesn't; indeed, xactStartTimestamp is local inside > xact.c, and it's easy to see by inspection that there is nothing at all > that sets it except StartTransaction(). What is happening, if you do > > set force_parallel_mode to 1; > select transaction_timestamp(); > > is that the value you get back is the time that the parallel worker did > StartTransaction(). It is easy to show that this is utterly broken: > transaction_timestamp() holds still within a transaction until you set > force_parallel_mode, and then it does not. > > regression=# begin; > BEGIN > regression=# select transaction_timestamp(); > transaction_timestamp > ------------------------------- > 2018-10-05 17:00:11.764019-04 > (1 row) > > regression=# select transaction_timestamp(); > transaction_timestamp > ------------------------------- > 2018-10-05 17:00:11.764019-04 > (1 row) > > regression=# select transaction_timestamp(); > transaction_timestamp > ------------------------------- > 2018-10-05 17:00:11.764019-04 > (1 row) > > regression=# set force_parallel_mode to 1; > SET > regression=# select transaction_timestamp(); > transaction_timestamp > ------------------------------- > 2018-10-05 17:00:21.983122-04 > (1 row) > > Looking at the related functions, I see that now() and > statement_timestamp() are marked stable/restricted, which is OK, while > clock_timestamp() and timeofday() are marked volatile/safe which seems odd > but on reflection I think it's OK. Their values wouldn't hold still in > the parent process either, so there's no reason to disallow workers from > running them. > > So transaction_timestamp() is definitely buggy, but we're not out of the > woods yet: SQLValueFunction is treated as parallel-safe, but it also has > some instances that are equivalent to transaction_timestamp and so do not > work correctly. > > regression=# begin; > BEGIN > regression=# select current_time; > current_time > -------------------- > 17:12:35.942968-04 > (1 row) > > regression=# select current_time; > current_time > -------------------- > 17:12:35.942968-04 > (1 row) > > regression=# set force_parallel_mode to 1; > SET > regression=# select current_time; > current_time > -------------------- > 17:12:55.462141-04 > (1 row) > > regression=# set force_parallel_mode to 0; > SET > regression=# select current_time; > current_time > -------------------- > 17:12:35.942968-04 > (1 row) > > Ain't that fun? > > My initial thought was that we should just re-mark transaction_timestamp() > as parallel-restricted and call it a day, but we'd then have to do the > same for SQLValueFunction, which is not much fun because it does have > variants that are parallel safe (and teaching max_parallel_hazard_walker > which is which seems like a recipe for bugs). > > Also, while it might not be quite too late to force a catversion bump > in v11, this is demonstrably also broken in v10, and we can't do that > there. > > So maybe the right answer is to change the parallel mode infrastructure > so it transmits xactStartTimestamp, making transaction_timestamp() > retroactively safe, and then in HEAD only we could re-mark now() as > safe. We might as well do the same for statement_timestamp as well. > > Thoughts? > > regards, tom lane Attached please find very small patch fixing the problem (propagating transaction and statement timestamps to workers). --------------AB3AB136731E7C86C2181B77 Content-Type: text/x-patch; name="parallel_timestamp.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="parallel_timestamp.patch" diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index cdaa32e..abc41e7 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -87,6 +87,8 @@ typedef struct FixedParallelState PGPROC *parallel_master_pgproc; pid_t parallel_master_pid; BackendId parallel_master_backend_id; + TimestampTz xact_ts; + TimestampTz stmt_ts; /* Mutex protects remaining fields. */ slock_t mutex; @@ -318,6 +320,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(); SpinLockInit(&fps->mutex); fps->last_xlog_end = 0; shm_toc_insert(pcxt->toc, PARALLEL_KEY_FIXED, fps); @@ -1351,6 +1355,9 @@ ParallelWorkerMain(Datum main_arg) tstatespace = shm_toc_lookup(toc, PARALLEL_KEY_TRANSACTION_STATE, false); StartParallelWorkerTransaction(tstatespace); + /* Restore current transaction and statement timestamps */ + SetCurrentStartTimestamps(fps->xact_ts, fps->stmt_ts); + /* Restore combo CID state. */ combocidspace = shm_toc_lookup(toc, PARALLEL_KEY_COMBO_CID, false); RestoreComboCIDState(combocidspace); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 875be18..c8fc140 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -675,6 +675,16 @@ GetCurrentCommandId(bool used) } /* + * SetCurrentStartTimestamps + */ +void +SetCurrentStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts) +{ + xactStartTimestamp = xact_ts; + stmtStartTimestamp = stmt_ts; +} + +/* * GetCurrentTransactionStartTimestamp */ TimestampTz diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 083e879..05afa36 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -356,6 +356,7 @@ extern TransactionId GetCurrentTransactionId(void); extern TransactionId GetCurrentTransactionIdIfAny(void); extern TransactionId GetStableLatestTransactionId(void); extern SubTransactionId GetCurrentSubTransactionId(void); +extern void SetCurrentStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts); extern void MarkCurrentTransactionIdLoggedIfAny(void); extern bool SubTransactionIsActive(SubTransactionId subxid); extern CommandId GetCurrentCommandId(bool used); diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 8e4145f..dfa8395 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -2595,13 +2595,13 @@ proname => 'timetzdate_pl', prolang => '14', prorettype => 'timestamptz', proargtypes => 'timetz date', prosrc => 'select ($2 + $1)' }, { oid => '1299', descr => 'current transaction time', - proname => 'now', provolatile => 's', proparallel => 'r', + proname => 'now', provolatile => 's', proparallel => 's', prorettype => 'timestamptz', proargtypes => '', prosrc => 'now' }, { oid => '2647', descr => 'current transaction time', proname => 'transaction_timestamp', provolatile => 's', prorettype => 'timestamptz', proargtypes => '', prosrc => 'now' }, { oid => '2648', descr => 'current statement time', - proname => 'statement_timestamp', provolatile => 's', proparallel => 'r', + proname => 'statement_timestamp', provolatile => 's', proparallel => 's', prorettype => 'timestamptz', proargtypes => '', prosrc => 'statement_timestamp' }, { oid => '2649', descr => 'current clock time', --------------AB3AB136731E7C86C2181B77--