Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m9IDw-00087z-UO for pgsql-bugs@arkaria.postgresql.org; Fri, 30 Jul 2021 02:26:09 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.92) (envelope-from ) id 1m9IDv-00053N-L0 for pgsql-bugs@arkaria.postgresql.org; Fri, 30 Jul 2021 02:26:07 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1m9IDv-00053E-7R for pgsql-bugs@lists.postgresql.org; Fri, 30 Jul 2021 02:26:07 +0000 Received: from mail-io1-xd2c.google.com ([2607:f8b0:4864:20::d2c]) by magus.postgresql.org with esmtps (TLS1.3:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.92) (envelope-from ) id 1m9IDq-0001zi-W3 for pgsql-bugs@lists.postgresql.org; Fri, 30 Jul 2021 02:26:06 +0000 Received: by mail-io1-xd2c.google.com with SMTP id l126so9581848ioa.12 for ; Thu, 29 Jul 2021 19:26:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leadboat.com; s=google; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to; bh=o9SZrBH7OhudZjYj8flfNbizf4L9qLIXgAZklfkgdo0=; b=No+G+BinVxbVLgj8fFlMZ7u+RpX2ur4/HrTlGdH/T07/V72+Di7DUEmgzvcApx65qH kjLwBS9u9eyyHmTjV0e+Q8UElfXcpHvvanGmb+EenR27nc9VOPx8BI2+uMI4b5OZlq83 Nuy+QVVCa+NMv/D61je9hOexHJCN6/Gdo459Y= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to; bh=o9SZrBH7OhudZjYj8flfNbizf4L9qLIXgAZklfkgdo0=; b=uenAMCjgUIDPCNDRMK0QGrFcGFL+KO7iz4zqKSqjE7LL2cH9Z9tHFZ0mMnAC1PEBv9 QUkPFK4Y8bRT89rva5GwTRimv/sR/fcxazDsi08jaM8KiCeztSoGqY/XkyTQAjxv//UV jOpOoqfTRxIDiGwFAOjUu/JYNYRXdb0gskLuN1QOqdvD0HuTW/dugaQtrMLK/C6fu++7 W0U45lNXbXT5+esLIj3foMbxOdsbH5dEnPgq9/927Vaj1/DzDLcs775kdIJJqYhDeH3A EpeGA49UsElnhNVVn+q/mY4fouaNFqVoGhbnTZ6KVpKGMcI0omz3Dhpr/Ze3qAiN4MBG w4jQ== X-Gm-Message-State: AOAM532Q8rCgcNZ0wLlQAqJsiswM/vCR2gPZJKLO1Ka+wmrvZqHcxhE+ yxOmLdamYDB//lMJxTVOlU7nIQ== X-Google-Smtp-Source: ABdhPJzCvlpIC11Jeoyy6jGKf3UilriSVnKc/FN2UNlOcKswmxUxJNPBr1M2TBrGh2MxHyoB9MtBgQ== X-Received: by 2002:a5d:930b:: with SMTP id l11mr380976ion.177.1627611960909; Thu, 29 Jul 2021 19:26:00 -0700 (PDT) Received: from gust.leadboat.com ([172.243.183.102]) by smtp.gmail.com with ESMTPSA id h6sm130158iop.40.2021.07.29.19.25.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 29 Jul 2021 19:25:59 -0700 (PDT) Date: Thu, 29 Jul 2021 22:25:48 -0400 From: Noah Misch To: Andrey Borodin Cc: Michael Paquier , Tom Lane , pgsql-bugs@lists.postgresql.org Subject: Re: CREATE INDEX CONCURRENTLY does not index prepared xact's data Message-ID: <20210730022548.GA1940096@gust.leadboat.com> References: <1727EC55-0709-4E06-91C9-212682A134C7@yandex-team.ru> <20210720214936.GA1040253@gust.leadboat.com> <20210721175530.GA1090577@gust.leadboat.com> <946242CB-D63A-4D7B-A5A5-485165F5B78A@yandex-team.ru> <20210723223021.GA1354601@gust.leadboat.com> <845C714B-DBCF-49E9-9F95-238DB042F926@yandex-team.ru> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="3V7upXqbjpZ4EhLz" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --3V7upXqbjpZ4EhLz Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Thu, Jul 29, 2021 at 07:21:44PM +0500, Andrey Borodin wrote: > Currently CIC test breaks indexes even without 2PC. How is it supposed to work if vxid stays vxid through GetLockConflicts()\WaitForLockersMultiple() barrier and then suddenly converts to xid and commits before index validated? It's an inval race condition. Here's the sequence of events leading to a failure. "i:" lines are events within the INSERT backend, and "r:" lines are events within the REINDEX CONCURRENTLY backend: r: Phase 2 begins. i: Start INSERT. i: Acquire RowExclusiveLock on "idx_ccnew". i: RelationBuildDesc() begins for relation "idx_ccnew". i: RelationBuildDesc() calls RelationInitIndexAccessInfo(), which loads indisready=f. r: Phase 2 commits indisready=t for idx_ccnew. r: Start waiting for the INSERT to finish. i: LocalExecuteInvalidationMessage() calls RelationCacheInvalidateEntry() for the OID of relation "idx_ccnew". Its RelationIdCacheLookup() call returns NULL, because nothing has passed the relation to RelationCacheInsert(). i: RelationBuildDesc(), the same call that began above, calls RelationCacheInsert(). This is a problem, because the entry needs invalidation, but we already consumed the invalidation messages. i: Inserts heap tuple with xmin=xid1. Does not insert to idx_ccnew. Commits. r: validate_index() runs, adding the index tuple for xid1. i: Second INSERT runs, adding a heap tuple with xmin=xid2. It uses the stale relcache entry having indisready=f, so this insert also does not modify idx_ccnew. This time, it was responsible for making that index entry; nothing else will. In summary, the relcache is deaf to invals of a given relation during RelationBuildDesc() of that relation. One fix would be to make RelationBuildDesc() more like this pseudocode: retry: WatchInvalFor(targetRelId); ... complicated build steps that may call AcceptInvalidationMessages() ... if (ReceivedInvalFor(targetRelId)) goto retry; I'm attaching a proof-of-concept sufficient to let your v7 pgbench test pass. Among other things, I haven't checked whether other inval message types have the same hazard. What alternative fix designs should we consider? --3V7upXqbjpZ4EhLz Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="inval-build-race-v0.patch" diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 13d9994af3..e5f8809445 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -997,9 +997,16 @@ equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2) * (suggesting we are trying to access a just-deleted relation). * Any other error is reported via elog. */ +typedef struct InProgressRels { + Oid relid; + bool invalidated; +} InProgressRels; +static InProgressRels inProgress[100]; + static Relation RelationBuildDesc(Oid targetRelId, bool insertIt) { + int in_progress_offset; Relation relation; Oid relid; HeapTuple pg_class_tuple; @@ -1033,6 +1040,14 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) } #endif + for (in_progress_offset = 0; + OidIsValid(inProgress[in_progress_offset].relid); + in_progress_offset++) + ; + inProgress[in_progress_offset].relid = targetRelId; +retry: + inProgress[in_progress_offset].invalidated = false; + /* * find the tuple in pg_class corresponding to the given relation id */ @@ -1213,6 +1228,10 @@ RelationBuildDesc(Oid targetRelId, bool insertIt) */ heap_freetuple(pg_class_tuple); + if (inProgress[in_progress_offset].invalidated) + goto retry; /* TODO free old one */ + inProgress[in_progress_offset].relid = InvalidOid; + /* * Insert newly created relation into relcache hash table, if requested. * @@ -2805,6 +2824,13 @@ RelationCacheInvalidateEntry(Oid relationId) relcacheInvalsReceived++; RelationFlushRelation(relation); } + else + { + int i; + for (i = 0; OidIsValid(inProgress[i].relid); i++) + if (inProgress[i].relid == relationId) + inProgress[i].invalidated = true; + } } /* --3V7upXqbjpZ4EhLz--