On 2020-Nov-18, Michael Paquier wrote: > On Tue, Nov 17, 2020 at 02:14:53PM -0300, Alvaro Herrera wrote: > > diff --git a/src/backend/replication/logical/logical.c > > b/src/backend/replication/logical/logical.c > > index f1f4df7d70..4324e32656 100644 > > --- a/src/backend/replication/logical/logical.c > > +++ b/src/backend/replication/logical/logical.c > > @@ -181,7 +181,7 @@ StartupDecodingContext(List *output_plugin_options, > > */ > > if (!IsTransactionOrTransactionBlock()) > > { > > - LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); > > + LWLockAcquire(ProcArrayLock, LW_SHARED); > > MyProc->statusFlags |= PROC_IN_LOGICAL_DECODING; > > ProcGlobal->statusFlags[MyProc->pgxactoff] = > > MyProc->statusFlags; > > LWLockRelease(ProcArrayLock); > > We don't really document that it is safe to update statusFlags while > holding a shared lock on ProcArrayLock, right? Could this be > clarified at least in proc.h?
Pushed that part with a comment addition. This stuff is completely undocumented ... > > + /* Determine whether we can call set_safe_index_flag */ > > + safe_index = indexInfo->ii_Expressions == NIL && > > + indexInfo->ii_Predicate == NIL; > > This should tell why we check after expressions and predicates, in > short giving an explanation about the snapshot issues with build and > validation when executing those expressions and/or predicates. Fair. It seems good to add a comment to the new function, and reference that in each place where we set the flag. > > + * Set the PROC_IN_SAFE_IC flag in my PGPROC entry. > Would it be better to document that this function had better be called > after beginning a transaction? I am wondering if we should not > enforce some conditions here, say this one or similar: > Assert(GetTopTransactionIdIfAny() == InvalidTransactionId); I went with checking MyProc->xid and MyProc->xmin, which is the same in practice but notionally closer to what we're doing. > > +static inline void > > +set_safe_index_flag(void) > > This routine name is rather close to index_set_state_flags(), could it > be possible to finish with a better name? I went with set_indexsafe_procflags(). Ugly ...
>From a1c9fb94a71f0bf9ec492e1715809315e38084b3 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Tue, 17 Nov 2020 11:41:19 -0300 Subject: [PATCH v7 1/2] CREATE INDEX CONCURRENTLY: don't wait for its kin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the various waiting phases of CREATE INDEX CONCURRENTLY, we wait for other processes to release their snapshots; in general terms this is necessary for correctness. However, processes doing CIC for other tables cannot possibly affect CIC done on "this" table, so we don't need to wait for *those*. This commit adds a flag in MyProc->statusFlags to indicate that the current process is doing CIC, so that other processes doing CIC can ignore it when waiting. This flag can potentially also be used by processes doing REINDEX CONCURRENTLY also to avoid waiting, and by VACUUM to ignore the process in CIC for the purposes of computing an Xmin. That's left for future commits. Author: Álvaro Herrera <alvhe...@alvh.no-ip.org> Author: Dimitry Dolgov <9erthali...@gmail.com> Reviewed-by: Michael Paquier <mich...@paquier.xyz> Discussion: https://postgr.es/m/20200810233815.GA18970@alvherre.pgsql --- src/backend/commands/indexcmds.c | 63 ++++++++++++++++++++++++++++++-- src/include/storage/proc.h | 5 ++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 02c7a0c7e1..9efb6b5420 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -93,6 +93,7 @@ static void ReindexPartitions(Oid relid, int options, bool isTopLevel); static void ReindexMultipleInternal(List *relids, int options); static bool ReindexRelationConcurrently(Oid relationOid, int options); static void update_relispartition(Oid relationId, bool newval); +static inline void set_indexsafe_procflags(void); /* * callback argument type for RangeVarCallbackForReindexIndex() @@ -384,7 +385,10 @@ CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts) * lazy VACUUMs, because they won't be fazed by missing index entries * either. (Manual ANALYZEs, however, can't be excluded because they * might be within transactions that are going to do arbitrary operations - * later.) + * later.) Processes running CREATE INDEX CONCURRENTLY + * on indexes that are neither expressional nor partial are also safe to + * ignore, since we know that those processes won't examine any data + * outside the table they're indexing. * * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not * check for that. @@ -405,7 +409,8 @@ WaitForOlderSnapshots(TransactionId limitXmin, bool progress) VirtualTransactionId *old_snapshots; old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false, - PROC_IS_AUTOVACUUM | PROC_IN_VACUUM, + PROC_IS_AUTOVACUUM | PROC_IN_VACUUM + | PROC_IN_SAFE_IC, &n_old_snapshots); if (progress) pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots); @@ -425,7 +430,8 @@ WaitForOlderSnapshots(TransactionId limitXmin, bool progress) newer_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false, - PROC_IS_AUTOVACUUM | PROC_IN_VACUUM, + PROC_IS_AUTOVACUUM | PROC_IN_VACUUM + | PROC_IN_SAFE_IC, &n_newer_snapshots); for (j = i; j < n_old_snapshots; j++) { @@ -518,6 +524,7 @@ DefineIndex(Oid relationId, bool amcanorder; amoptions_function amoptions; bool partitioned; + bool safe_index; Datum reloptions; int16 *coloptions; IndexInfo *indexInfo; @@ -1044,6 +1051,10 @@ DefineIndex(Oid relationId, } } + /* Is index safe for others to ignore? See set_indexsafe_procflags() */ + safe_index = indexInfo->ii_Expressions == NIL && + indexInfo->ii_Predicate == NIL; + /* * Report index creation if appropriate (delay this till after most of the * error checks) @@ -1430,6 +1441,10 @@ DefineIndex(Oid relationId, CommitTransactionCommand(); StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (safe_index) + set_indexsafe_procflags(); + /* * The index is now visible, so we can report the OID. */ @@ -1489,6 +1504,10 @@ DefineIndex(Oid relationId, CommitTransactionCommand(); StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (safe_index) + set_indexsafe_procflags(); + /* * Phase 3 of concurrent index build * @@ -1545,6 +1564,10 @@ DefineIndex(Oid relationId, CommitTransactionCommand(); StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (safe_index) + set_indexsafe_procflags(); + /* We should now definitely not be advertising any xmin. */ Assert(MyProc->xmin == InvalidTransactionId); @@ -3896,3 +3919,37 @@ update_relispartition(Oid relationId, bool newval) heap_freetuple(tup); table_close(classRel, RowExclusiveLock); } + +/* + * Set the PROC_IN_SAFE_IC flag in my PGPROC entry. + * + * When doing concurrent index builds, we can set this flag + * to tell other processes concurrently running CREATE + * INDEX CONCURRENTLY to ignore us when + * doing their waits for concurrent snapshots. On one hand it + * avoids pointlessly waiting for a process that's not interesting + * anyway; but more importantly it avoids deadlocks in some cases. + * + * This can only be done safely for indexes that don't execute any + * expressions that could access other tables, so index must not be + * expressional nor partial. Caller is responsible for only calling + * this routine when that assumption holds true. + * + * (The flag is reset automatically at transaction end, so it must be + * set for each transaction.) + */ +static inline void +set_indexsafe_procflags(void) +{ + /* + * This should only be called before installing xid or xmin in MyProc; + * otherwise, concurrent processes could see an Xmin that moves backwards. + */ + Assert(MyProc->xid == InvalidTransactionId && + MyProc->xmin == InvalidTransactionId); + + LWLockAcquire(ProcArrayLock, LW_SHARED); + MyProc->statusFlags |= PROC_IN_SAFE_IC; + ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags; + LWLockRelease(ProcArrayLock); +} diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 00bb244340..e75f6e8178 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -53,13 +53,16 @@ struct XidCache */ #define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */ #define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */ +#define PROC_IN_SAFE_IC 0x04 /* currently running CREATE INDEX + * CONCURRENTLY on non-expressional, + * non-partial index */ #define PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */ #define PROC_IN_LOGICAL_DECODING 0x10 /* currently doing logical * decoding outside xact */ /* flags reset at EOXact */ #define PROC_VACUUM_STATE_MASK \ - (PROC_IN_VACUUM | PROC_VACUUM_FOR_WRAPAROUND) + (PROC_IN_VACUUM | PROC_IN_SAFE_IC | PROC_VACUUM_FOR_WRAPAROUND) /* * We allow a small number of "weak" relation locks (AccessShareLock, -- 2.20.1
>From 127f6fd2b6c9a631da402531459bc06c86edad22 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera <alvhe...@alvh.no-ip.org> Date: Tue, 17 Nov 2020 11:42:22 -0300 Subject: [PATCH v7 2/2] Support safe flag in REINDEX CONCURRENTLY --- src/backend/commands/indexcmds.c | 27 +++++++++++++++++++++++++-- src/include/storage/proc.h | 1 + 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9efb6b5420..98ea2f477e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -385,7 +385,7 @@ CompareOpclassOptions(Datum *opts1, Datum *opts2, int natts) * lazy VACUUMs, because they won't be fazed by missing index entries * either. (Manual ANALYZEs, however, can't be excluded because they * might be within transactions that are going to do arbitrary operations - * later.) Processes running CREATE INDEX CONCURRENTLY + * later.) Processes running CREATE INDEX CONCURRENTLY or REINDEX CONCURRENTLY * on indexes that are neither expressional nor partial are also safe to * ignore, since we know that those processes won't examine any data * outside the table they're indexing. @@ -3043,6 +3043,7 @@ ReindexRelationConcurrently(Oid relationOid, int options) PROGRESS_CREATEIDX_ACCESS_METHOD_OID }; int64 progress_vals[4]; + bool all_indexes_safe = true; /* * Create a memory context that will survive forced transaction commits we @@ -3347,6 +3348,12 @@ ReindexRelationConcurrently(Oid relationOid, int options) */ newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock); + /* consider safety of this index for set_indexsafe_procflags */ + if (all_indexes_safe && + (newIndexRel->rd_indexprs != NIL || + newIndexRel->rd_indpred != NIL)) + all_indexes_safe = false; + /* * Save the list of OIDs and locks in private context */ @@ -3416,6 +3423,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) CommitTransactionCommand(); StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (all_indexes_safe) + set_indexsafe_procflags(); + /* * Phase 2 of REINDEX CONCURRENTLY * @@ -3479,6 +3490,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) } StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (all_indexes_safe) + set_indexsafe_procflags(); + /* * Phase 3 of REINDEX CONCURRENTLY * @@ -3632,6 +3647,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) CommitTransactionCommand(); StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (all_indexes_safe) + set_indexsafe_procflags(); + /* * Phase 5 of REINDEX CONCURRENTLY * @@ -3664,6 +3683,10 @@ ReindexRelationConcurrently(Oid relationOid, int options) CommitTransactionCommand(); StartTransactionCommand(); + /* Tell concurrent index builds to ignore us, if index qualifies */ + if (all_indexes_safe) + set_indexsafe_procflags(); + /* * Phase 6 of REINDEX CONCURRENTLY * @@ -3925,7 +3948,7 @@ update_relispartition(Oid relationId, bool newval) * * When doing concurrent index builds, we can set this flag * to tell other processes concurrently running CREATE - * INDEX CONCURRENTLY to ignore us when + * INDEX CONCURRENTLY and REINDEX CONCURRENTLY to ignore us when * doing their waits for concurrent snapshots. On one hand it * avoids pointlessly waiting for a process that's not interesting * anyway; but more importantly it avoids deadlocks in some cases. diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index e75f6e8178..532493bccc 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -54,6 +54,7 @@ struct XidCache #define PROC_IS_AUTOVACUUM 0x01 /* is it an autovac worker? */ #define PROC_IN_VACUUM 0x02 /* currently running lazy vacuum */ #define PROC_IN_SAFE_IC 0x04 /* currently running CREATE INDEX + * CONCURRENTLY or REINDEX * CONCURRENTLY on non-expressional, * non-partial index */ #define PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */ -- 2.20.1