diff options
Diffstat (limited to 'src/backend/utils/cache/inval.c')
-rw-r--r-- | src/backend/utils/cache/inval.c | 304 |
1 files changed, 86 insertions, 218 deletions
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 93bfa1e11a6..603aa4157be 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -94,10 +94,6 @@ * worth trying to avoid sending such inval traffic in the future, if those * problems can be overcome cheaply. * - * When making a nontransactional change to a cacheable object, we must - * likewise send the invalidation immediately, before ending the change's - * critical section. This includes inplace heap updates, relmap, and smgr. - * * When wal_level=logical, write invalidations into WAL at each command end to * support the decoding of the in-progress transactions. See * CommandEndInvalidationMessages. @@ -134,15 +130,13 @@ /* * Pending requests are stored as ready-to-send SharedInvalidationMessages. - * We keep the messages themselves in arrays in TopTransactionContext (there - * are separate arrays for catcache and relcache messages). For transactional - * messages, control information is kept in a chain of TransInvalidationInfo - * structs, also allocated in TopTransactionContext. (We could keep a - * subtransaction's TransInvalidationInfo in its CurTransactionContext; but - * that's more wasteful not less so, since in very many scenarios it'd be the - * only allocation in the subtransaction's CurTransactionContext.) For - * inplace update messages, control information appears in an - * InvalidationInfo, allocated in CurrentMemoryContext. + * We keep the messages themselves in arrays in TopTransactionContext + * (there are separate arrays for catcache and relcache messages). Control + * information is kept in a chain of TransInvalidationInfo structs, also + * allocated in TopTransactionContext. (We could keep a subtransaction's + * TransInvalidationInfo in its CurTransactionContext; but that's more + * wasteful not less so, since in very many scenarios it'd be the only + * allocation in the subtransaction's CurTransactionContext.) * * We can store the message arrays densely, and yet avoid moving data around * within an array, because within any one subtransaction we need only @@ -153,9 +147,7 @@ * struct. Similarly, we need distinguish messages of prior subtransactions * from those of the current subtransaction only until the subtransaction * completes, after which we adjust the array indexes in the parent's - * TransInvalidationInfo to include the subtransaction's messages. Inplace - * invalidations don't need a concept of command or subtransaction boundaries, - * since we send them during the WAL insertion critical section. + * TransInvalidationInfo to include the subtransaction's messages. * * The ordering of the individual messages within a command's or * subtransaction's output is not considered significant, although this @@ -208,7 +200,7 @@ typedef struct InvalidationMsgsGroup /*---------------- - * Transactional invalidation messages are divided into two groups: + * Invalidation messages are divided into two groups: * 1) events so far in current command, not yet reflected to caches. * 2) events in previous commands of current transaction; these have * been reflected to local caches, and must be either broadcast to @@ -224,36 +216,26 @@ typedef struct InvalidationMsgsGroup *---------------- */ -/* fields common to both transactional and inplace invalidation */ -typedef struct InvalidationInfo -{ - /* Events emitted by current command */ - InvalidationMsgsGroup CurrentCmdInvalidMsgs; - - /* init file must be invalidated? */ - bool RelcacheInitFileInval; -} InvalidationInfo; - -/* subclass adding fields specific to transactional invalidation */ typedef struct TransInvalidationInfo { - /* Base class */ - struct InvalidationInfo ii; - - /* Events emitted by previous commands of this (sub)transaction */ - InvalidationMsgsGroup PriorCmdInvalidMsgs; - /* Back link to parent transaction's info */ struct TransInvalidationInfo *parent; /* Subtransaction nesting depth */ int my_level; + + /* Events emitted by current command */ + InvalidationMsgsGroup CurrentCmdInvalidMsgs; + + /* Events emitted by previous commands of this (sub)transaction */ + InvalidationMsgsGroup PriorCmdInvalidMsgs; + + /* init file must be invalidated? */ + bool RelcacheInitFileInval; } TransInvalidationInfo; static TransInvalidationInfo *transInvalInfo = NULL; -static InvalidationInfo *inplaceInvalInfo = NULL; - /* GUC storage */ int debug_discard_caches = 0; @@ -561,12 +543,9 @@ ProcessInvalidationMessagesMulti(InvalidationMsgsGroup *group, static void RegisterCatcacheInvalidation(int cacheId, uint32 hashValue, - Oid dbId, - void *context) + Oid dbId) { - InvalidationInfo *info = (InvalidationInfo *) context; - - AddCatcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs, + AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs, cacheId, hashValue, dbId); } @@ -576,9 +555,10 @@ RegisterCatcacheInvalidation(int cacheId, * Register an invalidation event for all catcache entries from a catalog. */ static void -RegisterCatalogInvalidation(InvalidationInfo *info, Oid dbId, Oid catId) +RegisterCatalogInvalidation(Oid dbId, Oid catId) { - AddCatalogInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, catId); + AddCatalogInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs, + dbId, catId); } /* @@ -587,9 +567,10 @@ RegisterCatalogInvalidation(InvalidationInfo *info, Oid dbId, Oid catId) * As above, but register a relcache invalidation event. */ static void -RegisterRelcacheInvalidation(InvalidationInfo *info, Oid dbId, Oid relId) +RegisterRelcacheInvalidation(Oid dbId, Oid relId) { - AddRelcacheInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId); + AddRelcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs, + dbId, relId); /* * Most of the time, relcache invalidation is associated with system @@ -606,7 +587,7 @@ RegisterRelcacheInvalidation(InvalidationInfo *info, Oid dbId, Oid relId) * as well. Also zap when we are invalidating whole relcache. */ if (relId == InvalidOid || RelationIdIsInInitFile(relId)) - info->RelcacheInitFileInval = true; + transInvalInfo->RelcacheInitFileInval = true; } /* @@ -616,27 +597,24 @@ RegisterRelcacheInvalidation(InvalidationInfo *info, Oid dbId, Oid relId) * Only needed for catalogs that don't have catcaches. */ static void -RegisterSnapshotInvalidation(InvalidationInfo *info, Oid dbId, Oid relId) +RegisterSnapshotInvalidation(Oid dbId, Oid relId) { - AddSnapshotInvalidationMessage(&info->CurrentCmdInvalidMsgs, dbId, relId); + AddSnapshotInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs, + dbId, relId); } /* * PrepareInvalidationState * Initialize inval data for the current (sub)transaction. */ -static InvalidationInfo * +static void PrepareInvalidationState(void) { TransInvalidationInfo *myInfo; - Assert(IsTransactionState()); - /* Can't queue transactional message while collecting inplace messages. */ - Assert(inplaceInvalInfo == NULL); - if (transInvalInfo != NULL && transInvalInfo->my_level == GetCurrentTransactionNestLevel()) - return (InvalidationInfo *) transInvalInfo; + return; myInfo = (TransInvalidationInfo *) MemoryContextAllocZero(TopTransactionContext, @@ -659,7 +637,7 @@ PrepareInvalidationState(void) * counter. This is a convenient place to check for that, as well as * being important to keep management of the message arrays simple. */ - if (NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs) != 0) + if (NumMessagesInGroup(&transInvalInfo->CurrentCmdInvalidMsgs) != 0) elog(ERROR, "cannot start a subtransaction when there are unprocessed inval messages"); /* @@ -668,8 +646,8 @@ PrepareInvalidationState(void) * to update them to follow whatever is already in the arrays. */ SetGroupToFollow(&myInfo->PriorCmdInvalidMsgs, - &transInvalInfo->ii.CurrentCmdInvalidMsgs); - SetGroupToFollow(&myInfo->ii.CurrentCmdInvalidMsgs, + &transInvalInfo->CurrentCmdInvalidMsgs); + SetGroupToFollow(&myInfo->CurrentCmdInvalidMsgs, &myInfo->PriorCmdInvalidMsgs); } else @@ -685,41 +663,6 @@ PrepareInvalidationState(void) } transInvalInfo = myInfo; - return (InvalidationInfo *) myInfo; -} - -/* - * PrepareInplaceInvalidationState - * Initialize inval data for an inplace update. - * - * See previous function for more background. - */ -static InvalidationInfo * -PrepareInplaceInvalidationState(void) -{ - InvalidationInfo *myInfo; - - Assert(IsTransactionState()); - /* limit of one inplace update under assembly */ - Assert(inplaceInvalInfo == NULL); - - /* gone after WAL insertion CritSection ends, so use current context */ - myInfo = (InvalidationInfo *) palloc0(sizeof(InvalidationInfo)); - - /* Stash our messages past end of the transactional messages, if any. */ - if (transInvalInfo != NULL) - SetGroupToFollow(&myInfo->CurrentCmdInvalidMsgs, - &transInvalInfo->ii.CurrentCmdInvalidMsgs); - else - { - InvalMessageArrays[CatCacheMsgs].msgs = NULL; - InvalMessageArrays[CatCacheMsgs].maxmsgs = 0; - InvalMessageArrays[RelCacheMsgs].msgs = NULL; - InvalMessageArrays[RelCacheMsgs].maxmsgs = 0; - } - - inplaceInvalInfo = myInfo; - return myInfo; } /* ---------------------------------------------------------------- @@ -959,7 +902,7 @@ xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs, * after we send the SI messages. However, we need not do anything unless * we committed. */ - *RelcacheInitFileInval = transInvalInfo->ii.RelcacheInitFileInval; + *RelcacheInitFileInval = transInvalInfo->RelcacheInitFileInval; /* * Collect all the pending messages into a single contiguous array of @@ -970,7 +913,7 @@ xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs, * not new ones. */ nummsgs = NumMessagesInGroup(&transInvalInfo->PriorCmdInvalidMsgs) + - NumMessagesInGroup(&transInvalInfo->ii.CurrentCmdInvalidMsgs); + NumMessagesInGroup(&transInvalInfo->CurrentCmdInvalidMsgs); *msgs = msgarray = (SharedInvalidationMessage *) MemoryContextAlloc(CurTransactionContext, @@ -983,7 +926,7 @@ xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs, msgs, n * sizeof(SharedInvalidationMessage)), nmsgs += n)); - ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs, + ProcessMessageSubGroupMulti(&transInvalInfo->CurrentCmdInvalidMsgs, CatCacheMsgs, (memcpy(msgarray + nmsgs, msgs, @@ -995,7 +938,7 @@ xactGetCommittedInvalidationMessages(SharedInvalidationMessage **msgs, msgs, n * sizeof(SharedInvalidationMessage)), nmsgs += n)); - ProcessMessageSubGroupMulti(&transInvalInfo->ii.CurrentCmdInvalidMsgs, + ProcessMessageSubGroupMulti(&transInvalInfo->CurrentCmdInvalidMsgs, RelCacheMsgs, (memcpy(msgarray + nmsgs, msgs, @@ -1081,9 +1024,7 @@ ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs, void AtEOXact_Inval(bool isCommit) { - inplaceInvalInfo = NULL; - - /* Quick exit if no transactional messages */ + /* Quick exit if no messages */ if (transInvalInfo == NULL) return; @@ -1097,16 +1038,16 @@ AtEOXact_Inval(bool isCommit) * after we send the SI messages. However, we need not do anything * unless we committed. */ - if (transInvalInfo->ii.RelcacheInitFileInval) + if (transInvalInfo->RelcacheInitFileInval) RelationCacheInitFilePreInvalidate(); AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs, - &transInvalInfo->ii.CurrentCmdInvalidMsgs); + &transInvalInfo->CurrentCmdInvalidMsgs); ProcessInvalidationMessagesMulti(&transInvalInfo->PriorCmdInvalidMsgs, SendSharedInvalidMessages); - if (transInvalInfo->ii.RelcacheInitFileInval) + if (transInvalInfo->RelcacheInitFileInval) RelationCacheInitFilePostInvalidate(); } else @@ -1120,44 +1061,6 @@ AtEOXact_Inval(bool isCommit) } /* - * PreInplace_Inval - * Process queued-up invalidation before inplace update critical section. - * - * Tasks belong here if they are safe even if the inplace update does not - * complete. Currently, this just unlinks a cache file, which can fail. The - * sum of this and AtInplace_Inval() mirrors AtEOXact_Inval(isCommit=true). - */ -void -PreInplace_Inval(void) -{ - Assert(CritSectionCount == 0); - - if (inplaceInvalInfo && inplaceInvalInfo->RelcacheInitFileInval) - RelationCacheInitFilePreInvalidate(); -} - -/* - * AtInplace_Inval - * Process queued-up invalidations after inplace update buffer mutation. - */ -void -AtInplace_Inval(void) -{ - Assert(CritSectionCount > 0); - - if (inplaceInvalInfo == NULL) - return; - - ProcessInvalidationMessagesMulti(&inplaceInvalInfo->CurrentCmdInvalidMsgs, - SendSharedInvalidMessages); - - if (inplaceInvalInfo->RelcacheInitFileInval) - RelationCacheInitFilePostInvalidate(); - - inplaceInvalInfo = NULL; -} - -/* * AtEOSubXact_Inval * Process queued-up invalidation messages at end of subtransaction. * @@ -1179,20 +1082,9 @@ void AtEOSubXact_Inval(bool isCommit) { int my_level; - TransInvalidationInfo *myInfo; + TransInvalidationInfo *myInfo = transInvalInfo; - /* - * Successful inplace update must clear this, but we clear it on abort. - * Inplace updates allocate this in CurrentMemoryContext, which has - * lifespan <= subtransaction lifespan. Hence, don't free it explicitly. - */ - if (isCommit) - Assert(inplaceInvalInfo == NULL); - else - inplaceInvalInfo = NULL; - - /* Quick exit if no transactional messages. */ - myInfo = transInvalInfo; + /* Quick exit if no messages. */ if (myInfo == NULL) return; @@ -1233,12 +1125,12 @@ AtEOSubXact_Inval(bool isCommit) &myInfo->PriorCmdInvalidMsgs); /* Must readjust parent's CurrentCmdInvalidMsgs indexes now */ - SetGroupToFollow(&myInfo->parent->ii.CurrentCmdInvalidMsgs, + SetGroupToFollow(&myInfo->parent->CurrentCmdInvalidMsgs, &myInfo->parent->PriorCmdInvalidMsgs); /* Pending relcache inval becomes parent's problem too */ - if (myInfo->ii.RelcacheInitFileInval) - myInfo->parent->ii.RelcacheInitFileInval = true; + if (myInfo->RelcacheInitFileInval) + myInfo->parent->RelcacheInitFileInval = true; /* Pop the transaction state stack */ transInvalInfo = myInfo->parent; @@ -1285,7 +1177,7 @@ CommandEndInvalidationMessages(void) if (transInvalInfo == NULL) return; - ProcessInvalidationMessages(&transInvalInfo->ii.CurrentCmdInvalidMsgs, + ProcessInvalidationMessages(&transInvalInfo->CurrentCmdInvalidMsgs, LocalExecuteInvalidationMessage); /* WAL Log per-command invalidation messages for wal_level=logical */ @@ -1293,21 +1185,26 @@ CommandEndInvalidationMessages(void) LogLogicalInvalidations(); AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs, - &transInvalInfo->ii.CurrentCmdInvalidMsgs); + &transInvalInfo->CurrentCmdInvalidMsgs); } /* - * CacheInvalidateHeapTupleCommon - * Common logic for end-of-command and inplace variants. + * CacheInvalidateHeapTuple + * Register the given tuple for invalidation at end of command + * (ie, current command is creating or outdating this tuple). + * Also, detect whether a relcache invalidation is implied. + * + * For an insert or delete, tuple is the target tuple and newtuple is NULL. + * For an update, we are called just once, with tuple being the old tuple + * version and newtuple the new version. This allows avoidance of duplicate + * effort during an update. */ -static void -CacheInvalidateHeapTupleCommon(Relation relation, - HeapTuple tuple, - HeapTuple newtuple, - InvalidationInfo *(*prepare_callback) (void)) +void +CacheInvalidateHeapTuple(Relation relation, + HeapTuple tuple, + HeapTuple newtuple) { - InvalidationInfo *info; Oid tupleRelId; Oid databaseId; Oid relationId; @@ -1331,8 +1228,11 @@ CacheInvalidateHeapTupleCommon(Relation relation, if (IsToastRelation(relation)) return; - /* Allocate any required resources. */ - info = prepare_callback(); + /* + * If we're not prepared to queue invalidation messages for this + * subtransaction level, get ready now. + */ + PrepareInvalidationState(); /* * First let the catcache do its thing @@ -1341,12 +1241,11 @@ CacheInvalidateHeapTupleCommon(Relation relation, if (RelationInvalidatesSnapshotsOnly(tupleRelId)) { databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId; - RegisterSnapshotInvalidation(info, databaseId, tupleRelId); + RegisterSnapshotInvalidation(databaseId, tupleRelId); } else PrepareToInvalidateCacheTuple(relation, tuple, newtuple, - RegisterCatcacheInvalidation, - (void *) info); + RegisterCatcacheInvalidation); /* * Now, is this tuple one of the primary definers of a relcache entry? See @@ -1419,44 +1318,7 @@ CacheInvalidateHeapTupleCommon(Relation relation, /* * Yes. We need to register a relcache invalidation event. */ - RegisterRelcacheInvalidation(info, databaseId, relationId); -} - -/* - * CacheInvalidateHeapTuple - * Register the given tuple for invalidation at end of command - * (ie, current command is creating or outdating this tuple) and end of - * transaction. Also, detect whether a relcache invalidation is implied. - * - * For an insert or delete, tuple is the target tuple and newtuple is NULL. - * For an update, we are called just once, with tuple being the old tuple - * version and newtuple the new version. This allows avoidance of duplicate - * effort during an update. - */ -void -CacheInvalidateHeapTuple(Relation relation, - HeapTuple tuple, - HeapTuple newtuple) -{ - CacheInvalidateHeapTupleCommon(relation, tuple, newtuple, - PrepareInvalidationState); -} - -/* - * CacheInvalidateHeapTupleInplace - * Register the given tuple for nontransactional invalidation pertaining - * to an inplace update. Also, detect whether a relcache invalidation is - * implied. - * - * Like CacheInvalidateHeapTuple(), but for inplace updates. - */ -void -CacheInvalidateHeapTupleInplace(Relation relation, - HeapTuple tuple, - HeapTuple newtuple) -{ - CacheInvalidateHeapTupleCommon(relation, tuple, newtuple, - PrepareInplaceInvalidationState); + RegisterRelcacheInvalidation(databaseId, relationId); } /* @@ -1475,13 +1337,14 @@ CacheInvalidateCatalog(Oid catalogId) { Oid databaseId; + PrepareInvalidationState(); + if (IsSharedRelation(catalogId)) databaseId = InvalidOid; else databaseId = MyDatabaseId; - RegisterCatalogInvalidation(PrepareInvalidationState(), - databaseId, catalogId); + RegisterCatalogInvalidation(databaseId, catalogId); } /* @@ -1499,14 +1362,15 @@ CacheInvalidateRelcache(Relation relation) Oid databaseId; Oid relationId; + PrepareInvalidationState(); + relationId = RelationGetRelid(relation); if (relation->rd_rel->relisshared) databaseId = InvalidOid; else databaseId = MyDatabaseId; - RegisterRelcacheInvalidation(PrepareInvalidationState(), - databaseId, relationId); + RegisterRelcacheInvalidation(databaseId, relationId); } /* @@ -1519,8 +1383,9 @@ CacheInvalidateRelcache(Relation relation) void CacheInvalidateRelcacheAll(void) { - RegisterRelcacheInvalidation(PrepareInvalidationState(), - InvalidOid, InvalidOid); + PrepareInvalidationState(); + + RegisterRelcacheInvalidation(InvalidOid, InvalidOid); } /* @@ -1534,13 +1399,14 @@ CacheInvalidateRelcacheByTuple(HeapTuple classTuple) Oid databaseId; Oid relationId; + PrepareInvalidationState(); + relationId = classtup->oid; if (classtup->relisshared) databaseId = InvalidOid; else databaseId = MyDatabaseId; - RegisterRelcacheInvalidation(PrepareInvalidationState(), - databaseId, relationId); + RegisterRelcacheInvalidation(databaseId, relationId); } /* @@ -1554,6 +1420,8 @@ CacheInvalidateRelcacheByRelid(Oid relid) { HeapTuple tup; + PrepareInvalidationState(); + tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for relation %u", relid); @@ -1743,7 +1611,7 @@ LogLogicalInvalidations(void) if (transInvalInfo == NULL) return; - group = &transInvalInfo->ii.CurrentCmdInvalidMsgs; + group = &transInvalInfo->CurrentCmdInvalidMsgs; nmsgs = NumMessagesInGroup(group); if (nmsgs > 0) |