/*-------------------------------------------------------------------------
*
* sinval . c
* POSTGRES shared cache invalidation communication code .
*
* Portions Copyright ( c ) 1996 - 2025 , PostgreSQL Global Development Group
* Portions Copyright ( c ) 1994 , Regents of the University of California
*
*
* IDENTIFICATION
* src / backend / storage / ipc / sinval . c
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/xact.h"
#include "miscadmin.h"
#include "storage/latch.h"
#include "storage/sinvaladt.h"
#include "utils/inval.h"
uint64 SharedInvalidMessageCounter;
/*
* Because backends sitting idle will not be reading sinval events , we
* need a way to give an idle backend a swift kick in the rear and make
* it catch up before the sinval queue overflows and forces it to go
* through a cache reset exercise . This is done by sending
* PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind .
*
* The signal handler will set an interrupt pending flag and will set the
* processes latch . Whenever starting to read from the client , or when
* interrupted while doing so , ProcessClientReadInterrupt ( ) will call
* ProcessCatchupEvent ( ) .
*/
volatile sig_atomic_t catchupInterruptPending = false ;
/*
* SendSharedInvalidMessages
* Add shared - cache - invalidation message ( s ) to the global SI message queue .
*/
void
SendSharedInvalidMessages(const SharedInvalidationMessage *msgs, int n)
{
SIInsertDataEntries(msgs, n);
}
/*
* ReceiveSharedInvalidMessages
* Process shared - cache - invalidation messages waiting for this backend
*
* We guarantee to process all messages that had been queued before the
* routine was entered . It is of course possible for more messages to get
* queued right after our last SIGetDataEntries call .
*
* NOTE : it is entirely possible for this routine to be invoked recursively
* as a consequence of processing inside the invalFunction or resetFunction .
* Furthermore , such a recursive call must guarantee that all outstanding
* inval messages have been processed before it exits . This is the reason
* for the strange - looking choice to use a statically allocated buffer array
* and counters ; it ' s so that a recursive call can process messages already
* sucked out of sinvaladt . c .
*/
void
ReceiveSharedInvalidMessages(void (*invalFunction) (SharedInvalidationMessage *msg),
void (*resetFunction) (void ))
{
#define MAXINVALMSGS 32
static SharedInvalidationMessage messages[MAXINVALMSGS];
/*
* We use volatile here to prevent bugs if a compiler doesn ' t realize that
* recursion is a possibility . . .
*/
static volatile int nextmsg = 0 ;
static volatile int nummsgs = 0 ;
/* Deal with any messages still pending from an outer recursion */
while (nextmsg < nummsgs)
{
SharedInvalidationMessage msg = messages[nextmsg++];
SharedInvalidMessageCounter++;
invalFunction(&msg);
}
do
{
int getResult;
nextmsg = nummsgs = 0 ;
/* Try to get some more messages */
getResult = SIGetDataEntries(messages, MAXINVALMSGS);
if (getResult < 0 )
{
/* got a reset message */
elog(DEBUG4, "cache state reset" );
SharedInvalidMessageCounter++;
resetFunction();
break ; /* nothing more to do */
}
/* Process them, being wary that a recursive call might eat some */
nextmsg = 0 ;
nummsgs = getResult;
while (nextmsg < nummsgs)
{
SharedInvalidationMessage msg = messages[nextmsg++];
SharedInvalidMessageCounter++;
invalFunction(&msg);
}
/*
* We only need to loop if the last SIGetDataEntries call ( which might
* have been within a recursive call ) returned a full buffer .
*/
} while (nummsgs == MAXINVALMSGS);
/*
* We are now caught up . If we received a catchup signal , reset that
* flag , and call SICleanupQueue ( ) . This is not so much because we need
* to flush dead messages right now , as that we want to pass on the
* catchup signal to the next slowest backend . " Daisy chaining " the
* catchup signal this way avoids creating spikes in system load for what
* should be just a background maintenance activity .
*/
if (catchupInterruptPending)
{
catchupInterruptPending = false ;
elog(DEBUG4, "sinval catchup complete, cleaning queue" );
SICleanupQueue(false , 0 );
}
}
/*
* HandleCatchupInterrupt
*
* This is called when PROCSIG_CATCHUP_INTERRUPT is received .
*
* We used to directly call ProcessCatchupEvent directly when idle . These days
* we just set a flag to do it later and notify the process of that fact by
* setting the process ' s latch .
*/
void
HandleCatchupInterrupt(void )
{
/*
* Note : this is called by a SIGNAL HANDLER . You must be very wary what
* you do here .
*/
catchupInterruptPending = true ;
/* make sure the event is processed in due course */
SetLatch(MyLatch);
}
/*
* ProcessCatchupInterrupt
*
* The portion of catchup interrupt handling that runs outside of the signal
* handler , which allows it to actually process pending invalidations .
*/
void
ProcessCatchupInterrupt(void )
{
while (catchupInterruptPending)
{
/*
* What we need to do here is cause ReceiveSharedInvalidMessages ( ) to
* run , which will do the necessary work and also reset the
* catchupInterruptPending flag . If we are inside a transaction we
* can just call AcceptInvalidationMessages ( ) to do this . If we
* aren ' t , we start and immediately end a transaction ; the call to
* AcceptInvalidationMessages ( ) happens down inside transaction start .
*
* It is awfully tempting to just call AcceptInvalidationMessages ( )
* without the rest of the xact start / stop overhead , and I think that
* would actually work in the normal case ; but I am not sure that
* things would clean up nicely if we got an error partway through .
*/
if (IsTransactionOrTransactionBlock())
{
elog(DEBUG4, "ProcessCatchupEvent inside transaction" );
AcceptInvalidationMessages();
}
else
{
elog(DEBUG4, "ProcessCatchupEvent outside transaction" );
StartTransactionCommand();
CommitTransactionCommand();
}
}
}
Messung V0.5 in Prozent C=93 H=90 G=91
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland