Re: now() vs transaction_timestamp() - Mailing list pgsql-hackers

From Tom Lane
Subject Re: now() vs transaction_timestamp()
Date
Msg-id 4911.1538774725@sss.pgh.pa.us
Whole thread Raw
In response to Re: now() vs transaction_timestamp()  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: now() vs transaction_timestamp()  (Tom Lane <tgl@sss.pgh.pa.us>)
Re: now() vs transaction_timestamp()  (Amit Kapila <amit.kapila16@gmail.com>)
Re: now() vs transaction_timestamp()  (Konstantin Knizhnik <k.knizhnik@postgrespro.ru>)
List pgsql-hackers
I wrote:
> Konstantin Knizhnik <k.knizhnik@postgrespro.ru> 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


pgsql-hackers by date:

Previous
From: "Jonathan S. Katz"
Date:
Subject: Re: Postgres 11 release notes
Next
From: Tom Lane
Date:
Subject: Re: now() vs transaction_timestamp()