mirror of
https://github.com/postgres/postgres.git
synced 2025-06-22 02:52:08 +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:
221
src/backend/utils/cache/inval.c
vendored
221
src/backend/utils/cache/inval.c
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user