Re: CREATE INDEX CONCURRENTLY does not index prepared xact's data

From: Noah Misch <noah(at)leadboat(dot)com>
To: Andrey Borodin <x4mmm(at)yandex-team(dot)ru>
Cc: PostgreSQL mailing lists <pgsql-bugs(at)lists(dot)postgresql(dot)org>, Michael Paquier <michael(at)paquier(dot)xyz>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, Peter Geoghegan <pg(at)bowt(dot)ie>, Andres Freund <andres(at)anarazel(dot)de>
Subject: Re: CREATE INDEX CONCURRENTLY does not index prepared xact's data
Date: 2021-10-17 15:12:05
Message-ID: 20211017151205.GA2384251@rfd.leadboat.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

On Sat, Sep 25, 2021 at 01:10:12PM -0700, Noah Misch wrote:
> On Sat, Sep 25, 2021 at 10:25:05PM +0500, Andrey Borodin wrote:
> > > 20 сент. 2021 г., в 09:41, Noah Misch <noah(at)leadboat(dot)com> написал(а):

> I'll queue a task to review the rest of the patch.

I think the attached version is ready for commit. Notable differences
vs. v14:

- Made TwoPhaseGetXidByVXid() stop leaking TwoPhaseStateLock when it found a
second match.

- Instead of moving the ProcArrayClearTransaction() call within
PrepareTransaction(), move the PostPrepare_Locks() call. I didn't find any
bug in the other way, but it's a good principle to maximize similarity with
CommitTransaction(). PostPrepare_Locks() has no counterpart in
CommitTransaction(), so that principle is indifferent to moving it.

- inval-build-race-v0.1.patch was incompatible with debug_discard_caches. The
being-built relation would always get invalidated, making
RelationBuildDesc() an infinite loop. I've fixed this by making
RelationCacheInvalidate() invalidate in-progress rels only when called for
sinval overflow, not when called for debug_discard_caches. This adds some
function arguments that only matter in assert builds. That's not great, but
InvalidateSystemCaches() is expensive anyway. I considered instead adding
functions HoldDebugInval() and ResumeDebugInval(), which RelationBuildDesc()
would use to suppress debug_discard_caches during any iteration after the
first. That didn't seem better.

- Discard the in-progress array after an ERROR during RelationBuildDesc().

- Made the relcache.c changes repalloc the list of in-progress rels as needed.

- Changed the background_pgbench args from ($dbname, $stdin, \$stdout, $timer,
$files, $opts) to ($opts, $files, \$stdout, $timer). $dbname was unused.
pgbench doesn't make interesting use of its stdin, so I dropped that arg
until we have a use case. $opts and $files seem akin to the $dbname arg of
background_psql, so I moved them to the front. I'm not sure that last
change was an improvement.

- Made 001_pgbench_with_server.pl use PostgresNode::pgbench(), rather than
duplicate code. Factored out a subroutine of PostgresNode::pgbench() and
PostgresNode::background_pgbench().

- Lots of comment and naming changes.

One thing not done here is to change the tests to use CREATE INDEX
CONCURRENTLY instead of REINDEX CONCURRENTLY, so they're back-patchable to v11
and earlier. I may do that before pushing, or I may just omit the tests from
older branches.

> > > - v13 WaitPreparedXact() experiences starvation when a steady stream of
> > > prepared transactions have the same VXID. Since VXID reuse entails
> > > reconnecting, starvation will be unnoticeable in systems that follow best
> > > practices around connection lifespan. The 2021-08-23 patch version didn't
> > > have that hazard.
> > I think the probability of such a stream is abysmal. You not only need a stream of 2PC with the same vxid, but a stream of overlapping 2PC with the same vxid. And the most critical thing that can happen - CIC waiting for the stream to became one-2PC-at-a-time for a moment.
>
> You're probably right about that.

I didn't know of the "stateP->nextLXID = nextLocalTransactionId;" in
CleanupInvalidationState(), which indeed makes this all but impossible.

On Sat, Aug 07, 2021 at 03:19:57PM -0700, Noah Misch wrote:
> On Sun, Aug 08, 2021 at 12:00:55AM +0500, Andrey Borodin wrote:
> > Changes:
> > 1. Added assert in step 2 (fix for missed invalidation message). I wonder how deep possibly could be RelationBuildDesc() inside RelationBuildDesc() inside RelationBuildDesc() ... ? If the depth is unlimited we, probably, need a better data structure.
>
> I don't know either, hence that quick data structure to delay the question.
> debug_discard_caches=3 may help answer the question. RelationBuildDesc()
> reads pg_constraint, which is !rd_isnailed. Hence, I expect one can at least
> get RelationBuildDesc("pg_constraint") inside RelationBuildDesc("user_table").

debug_discard_caches=5 yields a depth of eight when opening a relation having
a CHECK constraint:

my_rel_having_check_constraint
pg_constraint_conrelid_contypid_conname_index
pg_index
pg_constraint
pg_constraint
pg_constraint
pg_constraint
pg_constraint

While debug_discard_caches doesn't permit higher values, I think one could
reach depths greater than eight by, for example, having a number of sessions
invalidate pg_constraint as often as possible. Hence, I'm glad the code no
longer relies on a depth limit.

Attachment Content-Type Size
inval-build-race-v1.patch text/plain 25.4 KB
prepared-transactions-cic-series202107-v15nm.patch text/plain 19.1 KB

In response to

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Александр Королев 2021-10-18 04:19:30 Re: BUG #17233: Incorrect behavior of DELETE command with bad subquery in WHERE clause
Previous Message Tom Lane 2021-10-17 14:57:16 Re: BUG #17233: Incorrect behavior of DELETE command with bad subquery in WHERE clause