diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 4e13d689f1..9e048deba6 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.102 2001/05/04 18:39:16 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.103 2001/06/19 19:42:15 tgl Exp $ * * NOTES * Transaction aborts can now occur two ways: @@ -544,7 +544,6 @@ CommandCounterIncrement(void) */ AtCommit_LocalCache(); AtStart_Cache(); - } void @@ -577,7 +576,7 @@ InitializeTransactionSystem(void) static void AtStart_Cache(void) { - DiscardInvalid(); + AcceptInvalidationMessages(); } /* -------------------------------- @@ -725,11 +724,10 @@ RecordTransactionCommit() static void AtCommit_Cache(void) { - /* - * Make catalog changes visible to all backend. + * Make catalog changes visible to all backends. */ - RegisterInvalid(true); + AtEOXactInvalidationMessages(true); } /* -------------------------------- @@ -739,11 +737,10 @@ AtCommit_Cache(void) static void AtCommit_LocalCache(void) { - /* * Make catalog changes visible to me for the next command. */ - ImmediateLocalInvalidation(true); + CommandEndInvalidationMessages(true); } /* -------------------------------- @@ -753,7 +750,6 @@ AtCommit_LocalCache(void) static void AtCommit_Locks(void) { - /* * XXX What if ProcReleaseLocks fails? (race condition?) * @@ -769,7 +765,6 @@ AtCommit_Locks(void) static void AtCommit_Memory(void) { - /* * Now that we're "out" of a transaction, have the system allocate * things in the top memory context instead of per-transaction @@ -844,7 +839,7 @@ static void AtAbort_Cache(void) { RelationCacheAbort(); - RegisterInvalid(false); + AtEOXactInvalidationMessages(false); } /* -------------------------------- diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index 9177a81604..6a3129ffd5 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.33 2001/06/16 22:58:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.34 2001/06/19 19:42:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,56 +55,31 @@ InitBackendSharedInvalidationState(void) } /* - * RegisterSharedInvalid + * SendSharedInvalidMessage * Add a shared-cache-invalidation message to the global SI message queue. - * - * Note: - * Assumes hash index is valid. - * Assumes item pointer is valid. */ void -RegisterSharedInvalid(int cacheId, /* XXX */ - Index hashIndex, - ItemPointer pointer) +SendSharedInvalidMessage(SharedInvalidationMessage *msg) { - SharedInvalidData newInvalid; bool insertOK; - /* - * This code has been hacked to accept two types of messages. This - * might be treated more generally in the future. - * - * (1) cacheId= system cache id hashIndex= system cache hash index for a - * (possibly) cached tuple pointer= pointer of (possibly) cached tuple - * - * (2) cacheId= special non-syscache id hashIndex= object id contained in - * (possibly) cached relation descriptor pointer= null - */ - - newInvalid.cacheId = cacheId; - newInvalid.hashIndex = hashIndex; - - if (ItemPointerIsValid(pointer)) - ItemPointerCopy(pointer, &newInvalid.pointerData); - else - ItemPointerSetInvalid(&newInvalid.pointerData); - SpinAcquire(SInvalLock); - insertOK = SIInsertDataEntry(shmInvalBuffer, &newInvalid); + insertOK = SIInsertDataEntry(shmInvalBuffer, msg); SpinRelease(SInvalLock); if (!insertOK) - elog(DEBUG, "RegisterSharedInvalid: SI buffer overflow"); + elog(DEBUG, "SendSharedInvalidMessage: SI buffer overflow"); } /* - * InvalidateSharedInvalid + * ReceiveSharedInvalidMessages * Process shared-cache-invalidation messages waiting for this backend */ void - InvalidateSharedInvalid(void (*invalFunction) (), - void (*resetFunction) ()) +ReceiveSharedInvalidMessages( + void (*invalFunction) (SharedInvalidationMessage *msg), + void (*resetFunction) (void)) { - SharedInvalidData data; + SharedInvalidationMessage data; int getResult; bool gotMessage = false; @@ -118,15 +93,13 @@ void if (getResult < 0) { /* got a reset message */ - elog(DEBUG, "InvalidateSharedInvalid: cache state reset"); + elog(DEBUG, "ReceiveSharedInvalidMessages: cache state reset"); resetFunction(); } else { /* got a normal data message */ - invalFunction(data.cacheId, - data.hashIndex, - &data.pointerData); + invalFunction(&data); } gotMessage = true; } diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index 63bc037bc9..3809619009 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.39 2001/06/16 22:58:15 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.40 2001/06/19 19:42:15 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,6 @@ #include "miscadmin.h" #include "storage/backendid.h" #include "storage/proc.h" -#include "storage/sinval.h" #include "storage/sinvaladt.h" SISeg *shmInvalBuffer; @@ -35,7 +34,6 @@ static void SISetProcStateInvalid(SISeg *segP); int SInvalShmemSize(int maxBackends) { - /* * Figure space needed. Note sizeof(SISeg) includes the first * ProcState entry. @@ -183,14 +181,13 @@ CleanupInvalidationState(int status, Datum arg) * Returns true for normal successful insertion, false if had to reset. */ bool -SIInsertDataEntry(SISeg *segP, SharedInvalidData *data) +SIInsertDataEntry(SISeg *segP, SharedInvalidationMessage *data) { int numMsgs = segP->maxMsgNum - segP->minMsgNum; /* Is the buffer full? */ if (numMsgs >= MAXNUMMESSAGES) { - /* * Don't panic just yet: slowest backend might have consumed some * messages but not yet have done SIDelExpiredDataEntries() to @@ -273,13 +270,12 @@ SISetProcStateInvalid(SISeg *segP) */ int SIGetDataEntry(SISeg *segP, int backendId, - SharedInvalidData *data) + SharedInvalidationMessage *data) { ProcState *stateP = &segP->procState[backendId - 1]; if (stateP->resetState) { - /* * Force reset. We can say we have dealt with any messages added * since the reset, as well... diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index 515b92c570..e645e92b22 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lmgr.c,v 1.46 2001/06/12 05:55:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lmgr.c,v 1.47 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -144,7 +144,7 @@ LockRelation(Relation relation, LOCKMODE lockmode) * rebuild it and not just delete it. */ RelationIncrementReferenceCount(relation); - DiscardInvalid(); + AcceptInvalidationMessages(); RelationDecrementReferenceCount(relation); } diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 4c1d908611..d6901ea839 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.79 2001/06/18 03:35:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.80 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,8 +31,18 @@ /* #define CACHEDEBUG */ /* turns DEBUG elogs on */ -/* voodoo constants */ -#define NCCBUCKETS 257 /* Hash buckets per CatCache (prime!) */ +/* + * Constants related to size of the catcache. + * + * NCCBUCKETS should be prime and must be less than 64K (because + * SharedInvalCatcacheMsg crams hash indexes into a uint16 field). In + * practice it should be a lot less, anyway, to avoid chewing up too much + * space on hash bucket headers. + * + * MAXCCTUPLES could be as small as a few hundred, if per-backend memory + * consumption is at a premium. + */ +#define NCCBUCKETS 257 /* Hash buckets per CatCache */ #define MAXCCTUPLES 5000 /* Maximum # of tuples in all caches */ @@ -218,6 +228,11 @@ CatalogCacheInitializeCache(CatCache *cache) */ tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation)); + /* + * get the relation's relisshared flag, too + */ + cache->cc_relisshared = RelationGetForm(relation)->relisshared; + /* * return to the caller's memory context and close the rel */ @@ -737,6 +752,7 @@ InitCatCache(int id, cp->cc_relname = relname; cp->cc_indname = indname; cp->cc_reloidattr = reloidattr; + cp->cc_relisshared = false; /* temporary */ cp->cc_tupdesc = (TupleDesc) NULL; cp->cc_ntup = 0; cp->cc_size = NCCBUCKETS; @@ -1116,7 +1132,8 @@ ReleaseCatCache(HeapTuple tuple) * * 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, so we have to be prepared to flush it. + * be by the end of the command --- or might be in other backends' caches + * --- so we have to be prepared to flush it. * * Also note that it's not an error if there are no catcaches for the * specified relation. inval.c doesn't know exactly which rels have @@ -1126,7 +1143,7 @@ ReleaseCatCache(HeapTuple tuple) void PrepareToInvalidateCacheTuple(Relation relation, HeapTuple tuple, - void (*function) (int, Index, ItemPointer)) + void (*function) (int, Index, ItemPointer, Oid)) { CatCache *ccp; @@ -1159,6 +1176,7 @@ PrepareToInvalidateCacheTuple(Relation relation, (*function) (ccp->id, CatalogCacheComputeTupleHashIndex(ccp, tuple), - &tuple->t_self); + &tuple->t_self, + ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId); } } diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 91884a4b9c..cd01bc9adc 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -22,22 +22,41 @@ * second lives till end of transaction. Finally, we need a third list of * all tuples outdated in the current transaction; if we commit, we send * those invalidation events to all other backends (via the SI message queue) - * so that they can flush obsolete entries from their caches. + * so that they can flush obsolete entries from their caches. This list + * definitely can't be processed until after we commit, otherwise the other + * backends won't see our updated tuples as good. * * We do not need to register EVERY tuple operation in this way, just those - * on tuples in relations that have associated catcaches. Also, whenever - * we see an operation on a pg_class or pg_attribute tuple, we register - * a relcache flush operation for the relation described by that tuple. + * on tuples in relations that have associated catcaches. We do, however, + * have to register every operation on every tuple that *could* be in a + * catcache, whether or not it currently is in our cache. Also, if the + * tuple is in a relation that has multiple catcaches, we need to register + * an invalidation message for each such catcache. catcache.c's + * PrepareToInvalidateCacheTuple() routine provides the knowledge of which + * catcaches may need invalidation for a given tuple. + * + * Also, whenever we see an operation on a pg_class or pg_attribute tuple, + * we register a relcache flush operation for the relation described by that + * tuple. + * + * We keep the relcache flush requests in lists separate from the catcache + * tuple flush requests. This allows us to issue all the pending catcache + * flushes before we issue relcache flushes, which saves us from loading + * a catcache tuple during relcache load only to flush it again right away. + * Also, we avoid queuing multiple relcache flush requests for the same + * relation, since a relcache flush is relatively expensive to do. + * (XXX is it worth testing likewise for duplicate catcache flush entries? + * Probably not.) + * + * All the request lists are kept in TopTransactionContext memory, since + * they need not live beyond the end of the current transaction. * * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.44 2001/06/18 03:35:07 tgl Exp $ - * - * Note - this code is real crufty... badly needs a rewrite to improve - * readability and portability. (Shouldn't assume Oid == Index, for example) + * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.45 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,65 +67,34 @@ #include "storage/sinval.h" #include "utils/catcache.h" #include "utils/inval.h" +#include "utils/memutils.h" #include "utils/relcache.h" /* - * private invalidation structures + * To minimize palloc traffic, we keep pending requests in successively- + * larger chunks (a slightly more sophisticated version of an expansible + * array). All request types can be stored as SharedInvalidationMessage + * records. */ - -typedef struct InvalidationUserData +typedef struct InvalidationChunk { - struct InvalidationUserData *dataP[1]; /* VARIABLE LENGTH */ -} InvalidationUserData; /* VARIABLE LENGTH STRUCTURE */ + struct InvalidationChunk *next; /* list link */ + int nitems; /* # items currently stored in chunk */ + int maxitems; /* size of allocated array in this chunk */ + SharedInvalidationMessage msgs[1]; /* VARIABLE LENGTH ARRAY */ +} InvalidationChunk; /* VARIABLE LENGTH STRUCTURE */ -typedef struct InvalidationEntryData +typedef struct InvalidationListHeader { - InvalidationUserData *nextP; - InvalidationUserData userData; /* VARIABLE LENGTH ARRAY */ -} InvalidationEntryData; /* VARIABLE LENGTH STRUCTURE */ - -typedef Pointer InvalidationEntry; - -typedef InvalidationEntry LocalInvalid; - -#define EmptyLocalInvalid NULL - -typedef struct CatalogInvalidationData -{ - Index cacheId; - Index hashIndex; - ItemPointerData pointerData; -} CatalogInvalidationData; - -typedef struct RelationInvalidationData -{ - Oid relationId; - Oid objectId; -} RelationInvalidationData; - -typedef union AnyInvalidation -{ - CatalogInvalidationData catalog; - RelationInvalidationData relation; -} AnyInvalidation; - -typedef struct InvalidationMessageData -{ - char kind; - AnyInvalidation any; -} InvalidationMessageData; - -typedef InvalidationMessageData *InvalidationMessage; - -/* - * variables and macros - */ + InvalidationChunk *cclist; /* list of chunks holding catcache msgs */ + InvalidationChunk *rclist; /* list of chunks holding relcache msgs */ +} InvalidationListHeader; /* * ---------------- * Invalidation info is divided into three parts. - * 1) shared invalidation to be registered for all backends + * 1) shared invalidation to be sent to all backends at commit * 2) local invalidation for the transaction itself (actually, just * for the current command within the transaction) * 3) rollback information for the transaction itself (in case we abort) @@ -114,367 +102,297 @@ typedef InvalidationMessageData *InvalidationMessage; */ /* - * head of invalidation linked list for all backends + * head of invalidation message list for all backends * eaten by AtCommit_Cache() in CommitTransaction() */ -static LocalInvalid InvalidForall = EmptyLocalInvalid; +static InvalidationListHeader GlobalInvalidMsgs; /* - * head of invalidation linked list for the backend itself + * head of invalidation message list for the current command * eaten by AtCommit_LocalCache() in CommandCounterIncrement() */ -static LocalInvalid InvalidLocal = EmptyLocalInvalid; +static InvalidationListHeader LocalInvalidMsgs; /* - * head of rollback linked list for the backend itself + * head of rollback message list for abort-time processing * eaten by AtAbort_Cache() in AbortTransaction() */ -static LocalInvalid RollbackStack = EmptyLocalInvalid; - - -static InvalidationEntry InvalidationEntryAllocate(uint16 size); -static void LocalInvalidInvalidate(LocalInvalid invalid, - void (*function) (InvalidationMessage), - bool freemember); -static LocalInvalid LocalInvalidRegister(LocalInvalid invalid, - InvalidationEntry entry); -static void DiscardInvalidStack(LocalInvalid *invalid); -static void InvalidationMessageRegisterSharedInvalid(InvalidationMessage message); +static InvalidationListHeader RollbackMsgs; /* ---------------------------------------------------------------- - * "local" invalidation support functions + * Invalidation list support functions + * + * These three routines encapsulate processing of the "chunked" + * representation of what is logically just a list of messages. * ---------------------------------------------------------------- */ /* - * InvalidationEntryAllocate - * Allocates an invalidation entry. - */ -static InvalidationEntry -InvalidationEntryAllocate(uint16 size) -{ - InvalidationEntryData *entryDataP; - - entryDataP = (InvalidationEntryData *) - malloc(sizeof(char *) + size); /* XXX alignment */ - if (entryDataP == NULL) - elog(ERROR, "Memory exhausted in InvalidationEntryAllocate"); - entryDataP->nextP = NULL; - return (Pointer) &entryDataP->userData; -} - -/* - * LocalInvalidRegister - * Link an invalidation entry into a chain of them. Really ugly - * coding here. - */ -static LocalInvalid -LocalInvalidRegister(LocalInvalid invalid, - InvalidationEntry entry) -{ - Assert(PointerIsValid(entry)); - - ((InvalidationUserData *) entry)->dataP[-1] = - (InvalidationUserData *) invalid; - - return entry; -} - -/* - * LocalInvalidInvalidate - * Processes, then frees all entries in a local cache - * invalidation list unless freemember parameter is false. + * AddInvalidationMessage + * Add an invalidation message to a list (of chunks). + * + * Note that we do not pay any great attention to maintaining the original + * ordering of the messages. */ static void -LocalInvalidInvalidate(LocalInvalid invalid, - void (*function) (InvalidationMessage), - bool freemember) +AddInvalidationMessage(InvalidationChunk **listHdr, + SharedInvalidationMessage *msg) { - InvalidationEntryData *entryDataP; + InvalidationChunk *chunk = *listHdr; - while (PointerIsValid(invalid)) + if (chunk == NULL) { - entryDataP = (InvalidationEntryData *) - &((InvalidationUserData *) invalid)->dataP[-1]; + /* First time through; create initial chunk */ +#define FIRSTCHUNKSIZE 16 + chunk = (InvalidationChunk *) + MemoryContextAlloc(TopTransactionContext, + sizeof(InvalidationChunk) + + (FIRSTCHUNKSIZE-1) * sizeof(SharedInvalidationMessage)); + chunk->nitems = 0; + chunk->maxitems = FIRSTCHUNKSIZE; + chunk->next = *listHdr; + *listHdr = chunk; + } + else if (chunk->nitems >= chunk->maxitems) + { + /* Need another chunk; double size of last chunk */ + int chunksize = 2 * chunk->maxitems; - if (PointerIsValid(function)) - (*function) ((InvalidationMessage) &entryDataP->userData); + chunk = (InvalidationChunk *) + MemoryContextAlloc(TopTransactionContext, + sizeof(InvalidationChunk) + + (chunksize-1) * sizeof(SharedInvalidationMessage)); + chunk->nitems = 0; + chunk->maxitems = chunksize; + chunk->next = *listHdr; + *listHdr = chunk; + } + /* Okay, add message to current chunk */ + chunk->msgs[chunk->nitems] = *msg; + chunk->nitems++; +} - invalid = (Pointer) entryDataP->nextP; +/* + * Free a list of inval message chunks. + * + * NOTE: when we are about to commit or abort a transaction, it's + * not really necessary to pfree the lists explicitly, since they will + * go away anyway when TopTransactionContext is destroyed. + */ +static void +FreeInvalidationMessageList(InvalidationChunk **listHdr) +{ + InvalidationChunk *chunk = *listHdr; - if (!freemember) - continue; - /* help catch errors */ - entryDataP->nextP = (InvalidationUserData *) NULL; + *listHdr = NULL; - free((Pointer) entryDataP); + while (chunk != NULL) + { + InvalidationChunk *nextchunk = chunk->next; + + pfree(chunk); + chunk = nextchunk; } } -static void -DiscardInvalidStack(LocalInvalid *invalid) -{ - LocalInvalid locinv; +/* + * Process a list of invalidation messages. + * + * This is a macro that executes the given code fragment for each message in + * a message chunk list. The fragment should refer to the message as *msg. + */ +#define ProcessMessageList(listHdr, codeFragment) \ + do { \ + InvalidationChunk *_chunk; \ + for (_chunk = (listHdr); _chunk != NULL; _chunk = _chunk->next) \ + { \ + int _cindex; \ + for (_cindex = 0; _cindex < _chunk->nitems; _cindex++) \ + { \ + SharedInvalidationMessage *msg = &_chunk->msgs[_cindex]; \ + codeFragment; \ + } \ + } \ + } while (0) - locinv = *invalid; - *invalid = EmptyLocalInvalid; - if (locinv) - LocalInvalidInvalidate(locinv, - (void (*) (InvalidationMessage)) NULL, - true); + +/* ---------------------------------------------------------------- + * Invalidation set support functions + * + * These routines understand about the division of a logical invalidation + * list into separate physical lists for catcache and relcache entries. + * ---------------------------------------------------------------- + */ + +/* + * Add a catcache inval entry + */ +static void +AddCatcacheInvalidationMessage(InvalidationListHeader *hdr, + int id, Index hashIndex, + ItemPointer tuplePtr, Oid dbId) +{ + SharedInvalidationMessage msg; + + msg.cc.id = (int16) id; + msg.cc.hashIndex = (uint16) hashIndex; + msg.cc.dbId = dbId; + msg.cc.tuplePtr = *tuplePtr; + AddInvalidationMessage(&hdr->cclist, &msg); +} + +/* + * Add a relcache inval entry + */ +static void +AddRelcacheInvalidationMessage(InvalidationListHeader *hdr, + Oid dbId, Oid relId) +{ + SharedInvalidationMessage msg; + + /* Don't add a duplicate item */ + /* We assume comparing relId is sufficient, needn't check dbId */ + ProcessMessageList(hdr->rclist, + if (msg->rc.relId == relId) return); + + /* OK, add the item */ + msg.rc.id = SHAREDINVALRELCACHE_ID; + msg.rc.dbId = dbId; + msg.rc.relId = relId; + AddInvalidationMessage(&hdr->rclist, &msg); +} + +/* + * Reset an invalidation list to empty + * + * physicalFree may be set false if caller knows transaction is ending + */ +static void +DiscardInvalidationMessages(InvalidationListHeader *hdr, bool physicalFree) +{ + if (physicalFree) + { + /* Physically pfree the list data */ + FreeInvalidationMessageList(&hdr->cclist); + FreeInvalidationMessageList(&hdr->rclist); + } + else + { + /* Assume the storage will go away at xact end, just reset pointers */ + hdr->cclist = NULL; + hdr->rclist = NULL; + } +} + +/* + * Execute the given function for all the messages in an invalidation list. + * The list is not altered. + * + * catcache entries are processed first, for reasons mentioned above. + */ +static void +ProcessInvalidationMessages(InvalidationListHeader *hdr, + void (*func) (SharedInvalidationMessage *msg)) +{ + ProcessMessageList(hdr->cclist, func(msg)); + ProcessMessageList(hdr->rclist, func(msg)); } /* ---------------------------------------------------------------- * private support functions * ---------------------------------------------------------------- */ -/* -------------------------------- - * CacheIdRegister....... - * RelationIdRegister.... - * -------------------------------- - */ -#ifdef INVALIDDEBUG -#define CacheIdRegisterSpecifiedLocalInvalid_DEBUG1 \ -elog(DEBUG, "CacheIdRegisterSpecifiedLocalInvalid(%d, %d, [%d, %d])", \ - cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \ - ItemPointerGetOffsetNumber(pointer)) -#define CacheIdRegisterLocalInvalid_DEBUG1 \ -elog(DEBUG, "CacheIdRegisterLocalInvalid(%d, %d, [%d, %d])", \ - cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \ - ItemPointerGetOffsetNumber(pointer)) -#define CacheIdRegisterLocalRollback_DEBUG1 \ -elog(DEBUG, "CacheIdRegisterLocalRollback(%d, %d, [%d, %d])", \ - cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \ - ItemPointerGetOffsetNumber(pointer)) -#else -#define CacheIdRegisterSpecifiedLocalInvalid_DEBUG1 -#define CacheIdRegisterLocalInvalid_DEBUG1 -#define CacheIdRegisterLocalRollback_DEBUG1 -#endif /* INVALIDDEBUG */ /* - * CacheIdRegisterSpecifiedLocalInvalid - */ -static LocalInvalid -CacheIdRegisterSpecifiedLocalInvalid(LocalInvalid invalid, - Index cacheId, - Index hashIndex, - ItemPointer pointer) -{ - InvalidationMessage message; - - /* - * debugging stuff - */ - CacheIdRegisterSpecifiedLocalInvalid_DEBUG1; - - /* - * create a message describing the system catalog tuple we wish to - * invalidate. - */ - message = (InvalidationMessage) - InvalidationEntryAllocate(sizeof(InvalidationMessageData)); - - message->kind = 'c'; - message->any.catalog.cacheId = cacheId; - message->any.catalog.hashIndex = hashIndex; - - ItemPointerCopy(pointer, &message->any.catalog.pointerData); - - /* - * Add message to linked list of unprocessed messages. - */ - invalid = LocalInvalidRegister(invalid, (InvalidationEntry) message); - return invalid; -} - -/* - * CacheIdRegisterLocalInvalid - */ -static void -CacheIdRegisterLocalInvalid(int cacheId, - Index hashIndex, - ItemPointer pointer) -{ - - /* - * debugging stuff - */ - CacheIdRegisterLocalInvalid_DEBUG1; - - /* - * Add message to InvalidForall linked list. - */ - InvalidForall = CacheIdRegisterSpecifiedLocalInvalid(InvalidForall, - cacheId, hashIndex, pointer); - - /* - * Add message to InvalidLocal linked list. - */ - InvalidLocal = CacheIdRegisterSpecifiedLocalInvalid(InvalidLocal, - cacheId, hashIndex, pointer); -} - -/* - * CacheIdRegisterLocalRollback - */ -static void -CacheIdRegisterLocalRollback(int cacheId, - Index hashIndex, - ItemPointer pointer) -{ - - /* - * debugging stuff - */ - CacheIdRegisterLocalRollback_DEBUG1; - - /* - * Add message to RollbackStack linked list. - */ - RollbackStack = CacheIdRegisterSpecifiedLocalInvalid( - RollbackStack, cacheId, hashIndex, pointer); -} - -/* - * RelationIdRegisterSpecifiedLocalInvalid - */ -static LocalInvalid -RelationIdRegisterSpecifiedLocalInvalid(LocalInvalid invalid, - Oid relationId, Oid objectId) -{ - InvalidationMessage message; - - /* - * debugging stuff - */ -#ifdef INVALIDDEBUG - elog(DEBUG, "RelationRegisterSpecifiedLocalInvalid(%u, %u)", relationId, - objectId); -#endif /* defined(INVALIDDEBUG) */ - - /* - * create a message describing the relation descriptor we wish to - * invalidate. - */ - message = (InvalidationMessage) - InvalidationEntryAllocate(sizeof(InvalidationMessageData)); - - message->kind = 'r'; - message->any.relation.relationId = relationId; - message->any.relation.objectId = objectId; - - /* - * Add message to linked list of unprocessed messages. - */ - invalid = LocalInvalidRegister(invalid, (InvalidationEntry) message); - return invalid; -} - -/* - * RelationIdRegisterLocalInvalid - */ -static void -RelationIdRegisterLocalInvalid(Oid relationId, Oid objectId) -{ - - /* - * debugging stuff - */ -#ifdef INVALIDDEBUG - elog(DEBUG, "RelationRegisterLocalInvalid(%u, %u)", relationId, - objectId); -#endif /* defined(INVALIDDEBUG) */ - - /* - * Add message to InvalidForall linked list. - */ - InvalidForall = RelationIdRegisterSpecifiedLocalInvalid(InvalidForall, - relationId, objectId); - - /* - * Add message to InvalidLocal linked list. - */ - InvalidLocal = RelationIdRegisterSpecifiedLocalInvalid(InvalidLocal, - relationId, objectId); -} - -/* - * RelationIdRegisterLocalRollback - */ -static void -RelationIdRegisterLocalRollback(Oid relationId, Oid objectId) -{ - - /* - * debugging stuff - */ -#ifdef INVALIDDEBUG - elog(DEBUG, "RelationRegisterLocalRollback(%u, %u)", relationId, - objectId); -#endif /* defined(INVALIDDEBUG) */ - - /* - * Add message to RollbackStack linked list. - */ - RollbackStack = RelationIdRegisterSpecifiedLocalInvalid( - RollbackStack, relationId, objectId); -} - -/* - * CacheIdInvalidate + * RegisterCatcacheInvalidation * - * This routine can invalidate a tuple in a system catalog cache - * or a cached relation descriptor. You pay your money and you - * take your chances... + * Register an invalidation event for an updated/deleted catcache entry. + * We insert the event into both GlobalInvalidMsgs (for transmission + * to other backends at transaction commit) and LocalInvalidMsgs (for + * my local invalidation at end of command within xact). */ -#ifdef INVALIDDEBUG -#define CacheIdInvalidate_DEBUG1 \ -elog(DEBUG, "CacheIdInvalidate(%d, %d, 0x%x[%d])", cacheId, hashIndex,\ - pointer, ItemPointerIsValid(pointer)) -#else -#define CacheIdInvalidate_DEBUG1 -#endif /* defined(INVALIDDEBUG) */ - static void -CacheIdInvalidate(Index cacheId, - Index hashIndex, - ItemPointer pointer) +RegisterCatcacheInvalidation(int cacheId, + Index hashIndex, + ItemPointer tuplePtr, + Oid dbId) { + AddCatcacheInvalidationMessage(&GlobalInvalidMsgs, + cacheId, hashIndex, tuplePtr, dbId); + AddCatcacheInvalidationMessage(&LocalInvalidMsgs, + cacheId, hashIndex, tuplePtr, dbId); +} - /* - * assume that if the item pointer is valid, then we are invalidating - * an item in the specified system catalog cache. - */ - if (ItemPointerIsValid(pointer)) +/* + * RegisterRelcacheInvalidation + * + * As above, but register a relcache invalidation event. + */ +static void +RegisterRelcacheInvalidation(Oid dbId, Oid relId) +{ + AddRelcacheInvalidationMessage(&GlobalInvalidMsgs, + dbId, relId); + AddRelcacheInvalidationMessage(&LocalInvalidMsgs, + dbId, relId); +} + +/* + * RegisterCatcacheRollback + * + * Register an invalidation event for an inserted catcache entry. + * This only needs to be flushed out of my local catcache, if I abort. + */ +static void +RegisterCatcacheRollback(int cacheId, + Index hashIndex, + ItemPointer tuplePtr, + Oid dbId) +{ + AddCatcacheInvalidationMessage(&RollbackMsgs, + cacheId, hashIndex, tuplePtr, dbId); +} + +/* + * RegisterRelcacheRollback + * + * As above, but register a relcache invalidation event. + */ +static void +RegisterRelcacheRollback(Oid dbId, Oid relId) +{ + AddRelcacheInvalidationMessage(&RollbackMsgs, + dbId, relId); +} + +/* + * LocalExecuteInvalidationMessage + * + * Process a single invalidation message (which could be either type). + * Only the local caches are flushed; this does not transmit the message + * to other backends. + */ +static void +LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) +{ + if (msg->id >= 0) { - CatalogCacheIdInvalidate(cacheId, hashIndex, pointer); - return; + if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == 0) + CatalogCacheIdInvalidate(msg->cc.id, + msg->cc.hashIndex, + &msg->cc.tuplePtr); } - - CacheIdInvalidate_DEBUG1; - - /* - * if the cacheId is the oid of any of the following system relations, - * then assume we are invalidating a relation descriptor - */ - if (cacheId == RelOid_pg_class) + else if (msg->id == SHAREDINVALRELCACHE_ID) { - RelationIdInvalidateRelationCacheByRelationId(hashIndex); - return; + if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == 0) + RelationIdInvalidateRelationCacheByRelationId(msg->rc.relId); } - - if (cacheId == RelOid_pg_attribute) + else { - RelationIdInvalidateRelationCacheByRelationId(hashIndex); - return; + elog(FATAL, "ExecuteInvalidationMessage: bogus message id %d", + msg->id); } - - /* - * Yow! the caller asked us to invalidate something else. - */ - elog(FATAL, "CacheIdInvalidate: cacheId=%d relation id?", cacheId); } /* @@ -495,278 +413,19 @@ InvalidateSystemCaches(void) RelationCacheInvalidate(); } -/* - * InvalidationMessageRegisterSharedInvalid - */ -#ifdef INVALIDDEBUG -#define InvalidationMessageRegisterSharedInvalid_DEBUG1 \ -elog(DEBUG,\ - "InvalidationMessageRegisterSharedInvalid(c, %d, %d, [%d, %d])",\ - message->any.catalog.cacheId,\ - message->any.catalog.hashIndex,\ - ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\ - ItemPointerGetOffsetNumber(&message->any.catalog.pointerData)) -#define InvalidationMessageRegisterSharedInvalid_DEBUG2 \ - elog(DEBUG, \ - "InvalidationMessageRegisterSharedInvalid(r, %u, %u)", \ - message->any.relation.relationId, \ - message->any.relation.objectId) -#else -#define InvalidationMessageRegisterSharedInvalid_DEBUG1 -#define InvalidationMessageRegisterSharedInvalid_DEBUG2 -#endif /* INVALIDDEBUG */ - -static void -InvalidationMessageRegisterSharedInvalid(InvalidationMessage message) -{ - Assert(PointerIsValid(message)); - - switch (message->kind) - { - case 'c': /* cached system catalog tuple */ - InvalidationMessageRegisterSharedInvalid_DEBUG1; - - RegisterSharedInvalid(message->any.catalog.cacheId, - message->any.catalog.hashIndex, - &message->any.catalog.pointerData); - break; - - case 'r': /* cached relation descriptor */ - InvalidationMessageRegisterSharedInvalid_DEBUG2; - - RegisterSharedInvalid(message->any.relation.relationId, - message->any.relation.objectId, - (ItemPointer) NULL); - break; - - default: - elog(FATAL, - "InvalidationMessageRegisterSharedInvalid: `%c' kind", - message->kind); - } -} - -/* - * InvalidationMessageCacheInvalidate - */ -#ifdef INVALIDDEBUG -#define InvalidationMessageCacheInvalidate_DEBUG1 \ -elog(DEBUG, "InvalidationMessageCacheInvalidate(c, %d, %d, [%d, %d])",\ - message->any.catalog.cacheId,\ - message->any.catalog.hashIndex,\ - ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\ - ItemPointerGetOffsetNumber(&message->any.catalog.pointerData)) -#define InvalidationMessageCacheInvalidate_DEBUG2 \ - elog(DEBUG, "InvalidationMessageCacheInvalidate(r, %u, %u)", \ - message->any.relation.relationId, \ - message->any.relation.objectId) -#else -#define InvalidationMessageCacheInvalidate_DEBUG1 -#define InvalidationMessageCacheInvalidate_DEBUG2 -#endif /* defined(INVALIDDEBUG) */ - -static void -InvalidationMessageCacheInvalidate(InvalidationMessage message) -{ - Assert(PointerIsValid(message)); - - switch (message->kind) - { - case 'c': /* cached system catalog tuple */ - InvalidationMessageCacheInvalidate_DEBUG1; - - CacheIdInvalidate(message->any.catalog.cacheId, - message->any.catalog.hashIndex, - &message->any.catalog.pointerData); - break; - - case 'r': /* cached relation descriptor */ - InvalidationMessageCacheInvalidate_DEBUG2; - - CacheIdInvalidate(message->any.relation.relationId, - message->any.relation.objectId, - (ItemPointer) NULL); - break; - - default: - elog(FATAL, "InvalidationMessageCacheInvalidate: `%c' kind", - message->kind); - } -} - -/* - * PrepareToInvalidateRelationCache - */ -static void -PrepareToInvalidateRelationCache(Relation relation, - HeapTuple tuple, - void (*function) (Oid, Oid)) -{ - Oid relationId; - Oid objectId; - - /* - * get the relation object id - */ - relationId = RelationGetRelid(relation); - - /* - * is it one of the ones we need to send an SI message for? - */ - if (relationId == RelOid_pg_class) - objectId = tuple->t_data->t_oid; - else if (relationId == RelOid_pg_attribute) - objectId = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; - else - return; - - /* - * register the relcache-invalidation action in the appropriate list - */ - Assert(PointerIsValid(function)); - - (*function) (relationId, objectId); -} - - -/* - * DiscardInvalid - * Causes the invalidated cache state to be discarded. - * - * Note: - * This should be called as the first step in processing a transaction. - */ -void -DiscardInvalid(void) -{ - - /* - * debugging stuff - */ -#ifdef INVALIDDEBUG - elog(DEBUG, "DiscardInvalid called"); -#endif /* defined(INVALIDDEBUG) */ - - InvalidateSharedInvalid(CacheIdInvalidate, InvalidateSystemCaches); -} - -/* - * RegisterInvalid - * Causes registration of invalidated state with other backends iff true. - * - * Note: - * This should be called as the last step in processing a transaction. - */ -void -RegisterInvalid(bool send) -{ - LocalInvalid invalid; - - /* - * debugging stuff - */ -#ifdef INVALIDDEBUG - elog(DEBUG, "RegisterInvalid(%d) called", send); -#endif /* defined(INVALIDDEBUG) */ - - /* - * Process and free the current list of inval messages. - */ - - DiscardInvalidStack(&InvalidLocal); - if (send) - { - DiscardInvalidStack(&RollbackStack); - invalid = InvalidForall; - InvalidForall = EmptyLocalInvalid; /* clear InvalidForall */ - LocalInvalidInvalidate(invalid, InvalidationMessageRegisterSharedInvalid, true); - } - else - { - DiscardInvalidStack(&InvalidForall); - invalid = RollbackStack; - RollbackStack = EmptyLocalInvalid; /* clear RollbackStack */ - LocalInvalidInvalidate(invalid, InvalidationMessageCacheInvalidate, true); - } - -} - -/* - * ImmediateLocalInvalidation - * Causes invalidation immediately for the next command of the transaction. - * - * Note: - * This should be called during CommandCounterIncrement(), - * after we have advanced the command ID. - */ -void -ImmediateLocalInvalidation(bool send) -{ - LocalInvalid invalid; - - /* - * debugging stuff - */ -#ifdef INVALIDDEBUG - elog(DEBUG, "ImmediateLocalInvalidation(%d) called", send); -#endif /* defined(INVALIDDEBUG) */ - - /* - * Process and free the local list of inval messages. - */ - - if (send) - { - invalid = InvalidLocal; - InvalidLocal = EmptyLocalInvalid; /* clear InvalidLocal */ - LocalInvalidInvalidate(invalid, InvalidationMessageCacheInvalidate, true); - } - else - { - - /* - * This may be used for rollback to a savepoint. Don't clear - * InvalidForall and RollbackStack here. - */ - DiscardInvalidStack(&InvalidLocal); - invalid = RollbackStack; - LocalInvalidInvalidate(invalid, InvalidationMessageCacheInvalidate, false); - } - -} - /* * PrepareForTupleInvalidation * Invoke functions for the tuple which register invalidation * of catalog/relation cache. - * Note: - * Assumes object id is valid. - * Assumes tuple is valid. */ -#ifdef INVALIDDEBUG -#define PrepareForTupleInvalidation_DEBUG1 \ -elog(DEBUG, "%s(%s, [%d,%d])", \ - funcname,\ - RelationGetPhysicalRelationName(relation), \ - ItemPointerGetBlockNumber(&tuple->t_self), \ - ItemPointerGetOffsetNumber(&tuple->t_self)) -#else -#define PrepareForTupleInvalidation_DEBUG1 -#endif /* defined(INVALIDDEBUG) */ - static void PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, void (*CacheIdRegisterFunc) (int, Index, - ItemPointer), - void (*RelationIdRegisterFunc) (Oid, Oid), - const char *funcname) + ItemPointer, Oid), + void (*RelationIdRegisterFunc) (Oid, Oid)) { - - /* - * sanity checks - */ - Assert(RelationIsValid(relation)); - Assert(HeapTupleIsValid(tuple)); + Oid tupleRelId; + Oid relationId; if (IsBootstrapProcessingMode()) return; @@ -780,15 +439,135 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, return; /* - * debugging stuff + * First let the catcache do its thing */ - PrepareForTupleInvalidation_DEBUG1; - PrepareToInvalidateCacheTuple(relation, tuple, CacheIdRegisterFunc); - PrepareToInvalidateRelationCache(relation, tuple, - RelationIdRegisterFunc); + /* + * Now, is this tuple one of the primary definers of a relcache entry? + */ + tupleRelId = RelationGetRelid(relation); + + if (tupleRelId == RelOid_pg_class) + relationId = tuple->t_data->t_oid; + else if (tupleRelId == RelOid_pg_attribute) + relationId = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; + else + return; + + /* + * Yes. We need to register a relcache invalidation event for the + * relation identified by relationId. + * + * KLUGE ALERT: we always send the relcache event with MyDatabaseId, + * even if the rel in question is shared. 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_class or pg_attribute change anyway. It looks a bit ugly + * though. + */ + (*RelationIdRegisterFunc) (MyDatabaseId, relationId); +} + + +/* ---------------------------------------------------------------- + * public functions + * ---------------------------------------------------------------- + */ + +/* + * AcceptInvalidationMessages + * Read and process invalidation messages from the shared invalidation + * message queue. + * + * Note: + * This should be called as the first step in processing a transaction. + */ +void +AcceptInvalidationMessages(void) +{ + ReceiveSharedInvalidMessages(LocalExecuteInvalidationMessage, + InvalidateSystemCaches); +} + +/* + * AtEOXactInvalidationMessages + * Process queued-up invalidation messages at end of transaction. + * + * If isCommit, we must send out the messages in our GlobalInvalidMsgs list + * to the shared invalidation message queue. Note that these will be read + * not only by other backends, but also by our own backend at the next + * transaction start (via AcceptInvalidationMessages). Therefore, it's okay + * to discard any pending LocalInvalidMsgs, since these will be redundant + * with the global list. + * + * If not isCommit, we are aborting, and must locally process the messages + * in our RollbackMsgs list. No messages need be sent to other backends, + * since they'll not have seen our changed tuples anyway. + * + * In any case, reset the various lists to empty. We need not physically + * free memory here, since TopTransactionContext is about to be emptied + * anyway. + * + * Note: + * This should be called as the last step in processing a transaction. + */ +void +AtEOXactInvalidationMessages(bool isCommit) +{ + if (isCommit) + { + ProcessInvalidationMessages(&GlobalInvalidMsgs, + SendSharedInvalidMessage); + } + else + { + ProcessInvalidationMessages(&RollbackMsgs, + LocalExecuteInvalidationMessage); + } + + DiscardInvalidationMessages(&GlobalInvalidMsgs, false); + DiscardInvalidationMessages(&LocalInvalidMsgs, false); + DiscardInvalidationMessages(&RollbackMsgs, false); +} + +/* + * CommandEndInvalidationMessages + * Process queued-up invalidation messages at end of one command + * in a transaction. + * + * Here, we send no messages to the shared queue, since we don't know yet if + * we will commit. But we do need to locally process the LocalInvalidMsgs + * list, so as to flush our caches of any tuples we have outdated in the + * current command. + * + * The isCommit = false case is not currently used, but may someday be + * needed to support rollback to a savepoint within a transaction. + * (I suspect it needs more work first --- tgl.) + * + * Note: + * This should be called during CommandCounterIncrement(), + * after we have advanced the command ID. + */ +void +CommandEndInvalidationMessages(bool isCommit) +{ + if (isCommit) + { + ProcessInvalidationMessages(&LocalInvalidMsgs, + LocalExecuteInvalidationMessage); + } + else + { + ProcessInvalidationMessages(&RollbackMsgs, + LocalExecuteInvalidationMessage); + } + /* + * LocalInvalidMsgs list is not interesting anymore, so flush it + * (for real). Do *not* clear GlobalInvalidMsgs or RollbackMsgs. + */ + DiscardInvalidationMessages(&LocalInvalidMsgs, true); } /* @@ -800,9 +579,8 @@ void RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple) { PrepareForTupleInvalidation(relation, tuple, - CacheIdRegisterLocalInvalid, - RelationIdRegisterLocalInvalid, - "RelationInvalidateHeapTuple"); + RegisterCatcacheInvalidation, + RegisterRelcacheInvalidation); } /* @@ -814,7 +592,6 @@ void RelationMark4RollbackHeapTuple(Relation relation, HeapTuple tuple) { PrepareForTupleInvalidation(relation, tuple, - CacheIdRegisterLocalRollback, - RelationIdRegisterLocalRollback, - "RelationMark4RollbackHeapTuple"); + RegisterCatcacheRollback, + RegisterRelcacheRollback); } diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h index 93a10e72fe..7d43c64578 100644 --- a/src/include/storage/sinval.h +++ b/src/include/storage/sinval.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: sinval.h,v 1.18 2001/02/26 00:50:08 tgl Exp $ + * $Id: sinval.h,v 1.19 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,15 +17,61 @@ #include "storage/itemptr.h" #include "storage/spin.h" + +/* + * We currently support two types of shared-invalidation messages: one that + * invalidates an entry in a catcache, and one that invalidates a relcache + * entry. More types could be added if needed. The message type is + * identified by the first "int16" field of the message struct. Zero or + * positive means a catcache inval message (and also serves as the catcache + * ID field). -1 means a relcache inval message. Other negative values + * are available to identify other inval message types. + * + * Shared-inval events are initially driven by detecting tuple inserts, + * updates and deletions in system catalogs (see RelationInvalidateHeapTuple + * and RelationMark4RollbackHeapTuple). Note that some system catalogs have + * multiple caches on them (with different indexes). On detecting a tuple + * invalidation in such a catalog, a separate catcache inval message must be + * generated for each of its caches. The catcache inval message carries the + * hash index for the target tuple, so that the catcache only needs to search + * one hash chain not all its chains. Of course this assumes that all the + * backends are using identical hashing code, but that should be OK. + */ + +typedef struct +{ + int16 id; /* cache ID --- must be first */ + uint16 hashIndex; /* hashchain index within this catcache */ + Oid dbId; /* database ID, or 0 if a shared relation */ + ItemPointerData tuplePtr; /* tuple identifier in cached relation */ +} SharedInvalCatcacheMsg; + +#define SHAREDINVALRELCACHE_ID (-1) + +typedef struct +{ + int16 id; /* type field --- must be first */ + Oid dbId; /* database ID, or 0 if a shared relation */ + Oid relId; /* relation ID */ +} SharedInvalRelcacheMsg; + +typedef union +{ + int16 id; /* type field --- must be first */ + SharedInvalCatcacheMsg cc; + SharedInvalRelcacheMsg rc; +} SharedInvalidationMessage; + + extern SPINLOCK SInvalLock; extern int SInvalShmemSize(int maxBackends); extern void CreateSharedInvalidationState(int maxBackends); extern void InitBackendSharedInvalidationState(void); -extern void RegisterSharedInvalid(int cacheId, Index hashIndex, - ItemPointer pointer); -extern void InvalidateSharedInvalid(void (*invalFunction) (), - void (*resetFunction) ()); +extern void SendSharedInvalidMessage(SharedInvalidationMessage *msg); +extern void ReceiveSharedInvalidMessages( + void (*invalFunction) (SharedInvalidationMessage *msg), + void (*resetFunction) (void)); extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself); extern bool TransactionIdIsInProgress(TransactionId xid); diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h index 22b073350d..606304ce31 100644 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@ -7,15 +7,15 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: sinvaladt.h,v 1.26 2001/03/22 04:01:09 momjian Exp $ + * $Id: sinvaladt.h,v 1.27 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef SINVALADT_H #define SINVALADT_H -#include "storage/itemptr.h" #include "storage/shmem.h" +#include "storage/sinval.h" /* * The shared cache invalidation manager is responsible for transmitting @@ -45,6 +45,9 @@ * large so that we don't need to do this often. It must be a multiple of * MAXNUMMESSAGES so that the existing circular-buffer entries don't need * to be moved when we do it. + * + * The struct type SharedInvalidationMessage, defining the contents of + * a single message, is defined in sinval.h. */ @@ -61,15 +64,6 @@ #define MAXNUMMESSAGES 4096 #define MSGNUMWRAPAROUND (MAXNUMMESSAGES * 4096) -/* The content of one shared-invalidation message */ -typedef struct SharedInvalidData -{ - int cacheId; /* XXX */ - Index hashIndex; - ItemPointerData pointerData; -} SharedInvalidData; - -typedef SharedInvalidData *SharedInvalid; /* Per-backend state in shared invalidation structure */ typedef struct ProcState @@ -83,7 +77,6 @@ typedef struct ProcState /* Shared cache invalidation memory segment */ typedef struct SISeg { - /* * General state information */ @@ -96,7 +89,7 @@ typedef struct SISeg /* * Circular buffer holding shared-inval messages */ - SharedInvalidData buffer[MAXNUMMESSAGES]; + SharedInvalidationMessage buffer[MAXNUMMESSAGES]; /* * Per-backend state info. @@ -117,9 +110,9 @@ extern SISeg *shmInvalBuffer; /* pointer to the shared inval buffer */ extern void SIBufferInit(int maxBackends); extern int SIBackendInit(SISeg *segP); -extern bool SIInsertDataEntry(SISeg *segP, SharedInvalidData *data); +extern bool SIInsertDataEntry(SISeg *segP, SharedInvalidationMessage *data); extern int SIGetDataEntry(SISeg *segP, int backendId, - SharedInvalidData *data); + SharedInvalidationMessage *data); extern void SIDelExpiredDataEntries(SISeg *segP); #endif /* SINVALADT_H */ diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h index a2d3064a8b..6b7a656e01 100644 --- a/src/include/utils/catcache.h +++ b/src/include/utils/catcache.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: catcache.h,v 1.33 2001/06/18 03:35:07 tgl Exp $ + * $Id: catcache.h,v 1.34 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,6 +36,7 @@ typedef struct catcache char *cc_relname; /* name of relation the tuples come from */ char *cc_indname; /* name of index matching cache keys */ int cc_reloidattr; /* AttrNumber of relation OID, or 0 */ + bool cc_relisshared; /* is relation shared? */ TupleDesc cc_tupdesc; /* tuple descriptor (copied from reldesc) */ int cc_ntup; /* # of tuples currently in this cache */ int cc_size; /* # of hash buckets in this cache */ @@ -98,7 +99,7 @@ extern void CatalogCacheFlushRelation(Oid relId); extern void CatalogCacheIdInvalidate(int cacheId, Index hashIndex, ItemPointer pointer); extern void PrepareToInvalidateCacheTuple(Relation relation, - HeapTuple tuple, - void (*function) (int, Index, ItemPointer)); + HeapTuple tuple, + void (*function) (int, Index, ItemPointer, Oid)); #endif /* CATCACHE_H */ diff --git a/src/include/utils/inval.h b/src/include/utils/inval.h index 7da14e5172..d6277e27e1 100644 --- a/src/include/utils/inval.h +++ b/src/include/utils/inval.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: inval.h,v 1.19 2001/01/24 19:43:28 momjian Exp $ + * $Id: inval.h,v 1.20 2001/06/19 19:42:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,11 +16,12 @@ #include "access/htup.h" -extern void DiscardInvalid(void); -extern void RegisterInvalid(bool send); +extern void AcceptInvalidationMessages(void); -extern void ImmediateLocalInvalidation(bool send); +extern void AtEOXactInvalidationMessages(bool isCommit); + +extern void CommandEndInvalidationMessages(bool isCommit); extern void RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple);