1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +03:00

Revise sinval code to remove no-longer-used tuple TID from inval messages.

This requires adjusting the API for syscache callback functions: they now
get a hash value, not a TID, to identify the target tuple.  Most of them
weren't paying any attention to that argument anyway, but plancache did
require a small amount of fixing.

Also, improve performance a trifle by avoiding sending duplicate inval
messages when a heap_update isn't changing the catcache lookup columns.
This commit is contained in:
Tom Lane
2011-08-16 19:27:46 -04:00
parent 632ae6829f
commit b5282aa893
19 changed files with 210 additions and 211 deletions

View File

@ -53,7 +53,7 @@ typedef struct
* query execution), this seems OK.
*/
static void
InvalidateAttoptCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
{
HASH_SEQ_STATUS status;
AttoptCacheEntry *attopt;

View File

@ -435,21 +435,14 @@ CatCacheRemoveCList(CatCache *cache, CatCList *cl)
* target tuple that has to be invalidated has a different TID than it
* did when the event was created. So now we just compare hash values and
* accept the small risk of unnecessary invalidations due to false matches.
* (The ItemPointer argument is therefore useless and should get removed.)
*
* This routine is only quasi-public: it should only be used by inval.c.
*/
void
CatalogCacheIdInvalidate(int cacheId,
uint32 hashValue,
ItemPointer pointer)
CatalogCacheIdInvalidate(int cacheId, uint32 hashValue)
{
CatCache *ccp;
/*
* sanity checks
*/
Assert(ItemPointerIsValid(pointer));
CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called");
/*
@ -699,7 +692,7 @@ CatalogCacheFlushCatalog(Oid catId)
ResetCatalogCache(cache);
/* Tell inval.c to call syscache callbacks for this cache */
CallSyscacheCallbacks(cache->id, NULL);
CallSyscacheCallbacks(cache->id, 0);
}
}
@ -1708,11 +1701,16 @@ build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys)
* The lists of tuples that need to be flushed are kept by inval.c. This
* routine is a helper routine for inval.c. Given a tuple belonging to
* the specified relation, find all catcaches it could be in, compute the
* correct hash value for each such catcache, and call the specified function
* to record the cache id, hash value, and tuple ItemPointer in inval.c's
* lists. CatalogCacheIdInvalidate will be called later, if appropriate,
* correct hash value for each such catcache, and call the specified
* function to record the cache id and hash value in inval.c's lists.
* CatalogCacheIdInvalidate will be called later, if appropriate,
* using the recorded information.
*
* 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. We should make two list entries
* if the tuple's hash value changed, but only one if it didn't.
*
* Note that it is irrelevant whether the given tuple is actually loaded
* into the catcache at the moment. Even if it's not there now, it might
* be by the end of the command, or there might be a matching negative entry
@ -1727,7 +1725,8 @@ build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys)
void
PrepareToInvalidateCacheTuple(Relation relation,
HeapTuple tuple,
void (*function) (int, uint32, ItemPointer, Oid))
HeapTuple newtuple,
void (*function) (int, uint32, Oid))
{
CatCache *ccp;
Oid reloid;
@ -1747,13 +1746,16 @@ PrepareToInvalidateCacheTuple(Relation relation,
/* ----------------
* for each cache
* if the cache contains tuples from the specified relation
* compute the tuple's hash value in this cache,
* compute the tuple's hash value(s) in this cache,
* and call the passed function to register the information.
* ----------------
*/
for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
{
uint32 hashvalue;
Oid dbid;
if (ccp->cc_reloid != reloid)
continue;
@ -1761,10 +1763,20 @@ PrepareToInvalidateCacheTuple(Relation relation,
if (ccp->cc_tupdesc == NULL)
CatalogCacheInitializeCache(ccp);
(*function) (ccp->id,
CatalogCacheComputeTupleHashValue(ccp, tuple),
&tuple->t_self,
ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId);
hashvalue = CatalogCacheComputeTupleHashValue(ccp, tuple);
dbid = ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId;
(*function) (ccp->id, hashvalue, dbid);
if (newtuple)
{
uint32 newhashvalue;
newhashvalue = CatalogCacheComputeTupleHashValue(ccp, newtuple);
if (newhashvalue != hashvalue)
(*function) (ccp->id, newhashvalue, dbid);
}
}
}

View File

@ -39,8 +39,8 @@
*
* In short, we need to remember until xact end every insert or delete
* of a tuple that might be in the system caches. Updates are treated as
* two events, delete + insert, for simplicity. (There are cases where
* it'd be possible to record just one event, but we don't currently try.)
* two events, delete + insert, for simplicity. (If the update doesn't
* change the tuple hash value, catcache.c optimizes this into one event.)
*
* We do not need to register EVERY tuple operation in this way, just those
* on tuples in relations that have associated catcaches. We do, however,
@ -314,14 +314,12 @@ AppendInvalidationMessageList(InvalidationChunk **destHdr,
*/
static void
AddCatcacheInvalidationMessage(InvalidationListHeader *hdr,
int id, uint32 hashValue,
ItemPointer tuplePtr, Oid dbId)
int id, uint32 hashValue, Oid dbId)
{
SharedInvalidationMessage msg;
Assert(id < CHAR_MAX);
msg.cc.id = (int8) id;
msg.cc.tuplePtr = *tuplePtr;
msg.cc.dbId = dbId;
msg.cc.hashValue = hashValue;
AddInvalidationMessage(&hdr->cclist, &msg);
@ -416,11 +414,10 @@ ProcessInvalidationMessagesMulti(InvalidationListHeader *hdr,
static void
RegisterCatcacheInvalidation(int cacheId,
uint32 hashValue,
ItemPointer tuplePtr,
Oid dbId)
{
AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
cacheId, hashValue, tuplePtr, dbId);
cacheId, hashValue, dbId);
}
/*
@ -476,11 +473,9 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg)
{
if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
{
CatalogCacheIdInvalidate(msg->cc.id,
msg->cc.hashValue,
&msg->cc.tuplePtr);
CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue);
CallSyscacheCallbacks(msg->cc.id, &msg->cc.tuplePtr);
CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
}
}
else if (msg->id == SHAREDINVALCATALOG_ID)
@ -555,7 +550,7 @@ InvalidateSystemCaches(void)
{
struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
(*ccitem->function) (ccitem->arg, ccitem->id, NULL);
(*ccitem->function) (ccitem->arg, ccitem->id, 0);
}
for (i = 0; i < relcache_callback_count; i++)
@ -566,98 +561,6 @@ InvalidateSystemCaches(void)
}
}
/*
* PrepareForTupleInvalidation
* Detect whether invalidation of this tuple implies invalidation
* of catalog/relation cache entries; if so, register inval events.
*/
static void
PrepareForTupleInvalidation(Relation relation, HeapTuple tuple)
{
Oid tupleRelId;
Oid databaseId;
Oid relationId;
/* Do nothing during bootstrap */
if (IsBootstrapProcessingMode())
return;
/*
* We only need to worry about invalidation for tuples that are in system
* relations; user-relation tuples are never in catcaches and can't affect
* the relcache either.
*/
if (!IsSystemRelation(relation))
return;
/*
* TOAST tuples can likewise be ignored here. Note that TOAST tables are
* considered system relations so they are not filtered by the above test.
*/
if (IsToastRelation(relation))
return;
/*
* First let the catcache do its thing
*/
PrepareToInvalidateCacheTuple(relation, tuple,
RegisterCatcacheInvalidation);
/*
* Now, is this tuple one of the primary definers of a relcache entry?
*/
tupleRelId = RelationGetRelid(relation);
if (tupleRelId == RelationRelationId)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
relationId = HeapTupleGetOid(tuple);
if (classtup->relisshared)
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
}
else if (tupleRelId == AttributeRelationId)
{
Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
relationId = atttup->attrelid;
/*
* KLUGE ALERT: we always send the relcache event with MyDatabaseId,
* even if the rel in question is shared (which we can't easily tell).
* This essentially means that only backends in this same database
* will react to the relcache flush request. This is in fact
* appropriate, since only those backends could see our pg_attribute
* change anyway. It looks a bit ugly though. (In practice, shared
* relations can't have schema changes after bootstrap, so we should
* never come here for a shared rel anyway.)
*/
databaseId = MyDatabaseId;
}
else if (tupleRelId == IndexRelationId)
{
Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
/*
* When a pg_index row is updated, we should send out a relcache inval
* for the index relation. As above, we don't know the shared status
* of the index, but in practice it doesn't matter since indexes of
* shared catalogs can't have such updates.
*/
relationId = indextup->indexrelid;
databaseId = MyDatabaseId;
}
else
return;
/*
* Yes. We need to register a relcache invalidation event.
*/
RegisterRelcacheInvalidation(databaseId, relationId);
}
/* ----------------------------------------------------------------
* public functions
@ -1056,11 +959,103 @@ CommandEndInvalidationMessages(void)
* 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.
*/
void
CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple)
CacheInvalidateHeapTuple(Relation relation,
HeapTuple tuple,
HeapTuple newtuple)
{
PrepareForTupleInvalidation(relation, tuple);
Oid tupleRelId;
Oid databaseId;
Oid relationId;
/* Do nothing during bootstrap */
if (IsBootstrapProcessingMode())
return;
/*
* We only need to worry about invalidation for tuples that are in system
* relations; user-relation tuples are never in catcaches and can't affect
* the relcache either.
*/
if (!IsSystemRelation(relation))
return;
/*
* TOAST tuples can likewise be ignored here. Note that TOAST tables are
* considered system relations so they are not filtered by the above test.
*/
if (IsToastRelation(relation))
return;
/*
* First let the catcache do its thing
*/
PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
RegisterCatcacheInvalidation);
/*
* Now, is this tuple one of the primary definers of a relcache entry?
*
* Note we ignore newtuple here; we assume an update cannot move a tuple
* from being part of one relcache entry to being part of another.
*/
tupleRelId = RelationGetRelid(relation);
if (tupleRelId == RelationRelationId)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
relationId = HeapTupleGetOid(tuple);
if (classtup->relisshared)
databaseId = InvalidOid;
else
databaseId = MyDatabaseId;
}
else if (tupleRelId == AttributeRelationId)
{
Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple);
relationId = atttup->attrelid;
/*
* KLUGE ALERT: we always send the relcache event with MyDatabaseId,
* even if the rel in question is shared (which we can't easily tell).
* This essentially means that only backends in this same database
* will react to the relcache flush request. This is in fact
* appropriate, since only those backends could see our pg_attribute
* change anyway. It looks a bit ugly though. (In practice, shared
* relations can't have schema changes after bootstrap, so we should
* never come here for a shared rel anyway.)
*/
databaseId = MyDatabaseId;
}
else if (tupleRelId == IndexRelationId)
{
Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
/*
* When a pg_index row is updated, we should send out a relcache inval
* for the index relation. As above, we don't know the shared status
* of the index, but in practice it doesn't matter since indexes of
* shared catalogs can't have such updates.
*/
relationId = indextup->indexrelid;
databaseId = MyDatabaseId;
}
else
return;
/*
* Yes. We need to register a relcache invalidation event.
*/
RegisterRelcacheInvalidation(databaseId, relationId);
}
/*
@ -1094,7 +1089,7 @@ CacheInvalidateCatalog(Oid catalogId)
*
* This is used in places that need to force relcache rebuild but aren't
* changing any of the tuples recognized as contributors to the relcache
* entry by PrepareForTupleInvalidation. (An example is dropping an index.)
* entry by CacheInvalidateHeapTuple. (An example is dropping an index.)
*/
void
CacheInvalidateRelcache(Relation relation)
@ -1216,10 +1211,14 @@ CacheInvalidateRelmap(Oid databaseId)
* CacheRegisterSyscacheCallback
* Register the specified function to be called for all future
* invalidation events in the specified cache. The cache ID and the
* TID of the tuple being invalidated will be passed to the function.
* hash value of the tuple being invalidated will be passed to the
* function.
*
* NOTE: NULL will be passed for the TID if a cache reset request is received.
* NOTE: Hash value zero will be passed if a cache reset request is received.
* In this case the called routines should flush all cached state.
* Yes, there's a possibility of a false match to zero, but it doesn't seem
* worth troubling over, especially since most of the current callees just
* flush all cached state anyway.
*/
void
CacheRegisterSyscacheCallback(int cacheid,
@ -1265,7 +1264,7 @@ CacheRegisterRelcacheCallback(RelcacheCallbackFunction func,
* this module from knowing which catcache IDs correspond to which catalogs.
*/
void
CallSyscacheCallbacks(int cacheid, ItemPointer tuplePtr)
CallSyscacheCallbacks(int cacheid, uint32 hashvalue)
{
int i;
@ -1274,6 +1273,6 @@ CallSyscacheCallbacks(int cacheid, ItemPointer tuplePtr)
struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i;
if (ccitem->id == cacheid)
(*ccitem->function) (ccitem->arg, cacheid, tuplePtr);
(*ccitem->function) (ccitem->arg, cacheid, hashvalue);
}
}

View File

@ -71,8 +71,8 @@ static void ScanQueryForLocks(Query *parsetree, bool acquire);
static bool ScanQueryWalker(Node *node, bool *acquire);
static bool plan_list_is_transient(List *stmt_list);
static void PlanCacheRelCallback(Datum arg, Oid relid);
static void PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
static void PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue);
static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue);
/*
@ -1029,14 +1029,14 @@ PlanCacheRelCallback(Datum arg, Oid relid)
* PlanCacheFuncCallback
* Syscache inval callback function for PROCOID cache
*
* Invalidate all plans mentioning the given catalog entry, or all plans
* mentioning any member of this cache if tuplePtr == NULL.
* Invalidate all plans mentioning the object with the specified hash value,
* or all plans mentioning any member of this cache if hashvalue == 0.
*
* Note that the coding would support use for multiple caches, but right
* now only user-defined functions are tracked this way.
*/
static void
PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue)
{
ListCell *lc1;
@ -1060,8 +1060,8 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
if (item->cacheId != cacheid)
continue;
if (tuplePtr == NULL ||
ItemPointerEquals(tuplePtr, &item->tupleId))
if (hashvalue == 0 ||
item->hashValue == hashvalue)
{
/* Invalidate the plan! */
plan->dead = true;
@ -1086,8 +1086,8 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
if (item->cacheId != cacheid)
continue;
if (tuplePtr == NULL ||
ItemPointerEquals(tuplePtr, &item->tupleId))
if (hashvalue == 0 ||
item->hashValue == hashvalue)
{
/* Invalidate the plan! */
plan->dead = true;
@ -1108,7 +1108,7 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
* Just invalidate everything...
*/
static void
PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue)
{
ResetPlanCache();
}

View File

@ -50,7 +50,7 @@ typedef struct
* tablespaces, nor do we expect them to be frequently modified.
*/
static void
InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr)
InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, uint32 hashvalue)
{
HASH_SEQ_STATUS status;
TableSpaceCacheEntry *spc;

View File

@ -90,7 +90,7 @@ static Oid TSCurrentConfigCache = InvalidOid;
* table address as the "arg".
*/
static void
InvalidateTSCacheCallBack(Datum arg, int cacheid, ItemPointer tuplePtr)
InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
{
HTAB *hash = (HTAB *) DatumGetPointer(arg);
HASH_SEQ_STATUS status;