PostgreSQL Source Code  git master
sinval.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * sinval.c
4  * POSTGRES shared cache invalidation communication code.
5  *
6  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/storage/ipc/sinval.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/xact.h"
18 #include "commands/async.h"
19 #include "miscadmin.h"
20 #include "storage/ipc.h"
21 #include "storage/proc.h"
22 #include "storage/sinvaladt.h"
23 #include "utils/inval.h"
24 
25 
27 
28 
29 /*
30  * Because backends sitting idle will not be reading sinval events, we
31  * need a way to give an idle backend a swift kick in the rear and make
32  * it catch up before the sinval queue overflows and forces it to go
33  * through a cache reset exercise. This is done by sending
34  * PROCSIG_CATCHUP_INTERRUPT to any backend that gets too far behind.
35  *
36  * The signal handler will set an interrupt pending flag and will set the
37  * processes latch. Whenever starting to read from the client, or when
38  * interrupted while doing so, ProcessClientReadInterrupt() will call
39  * ProcessCatchupEvent().
40  */
41 volatile sig_atomic_t catchupInterruptPending = false;
42 
43 
44 /*
45  * SendSharedInvalidMessages
46  * Add shared-cache-invalidation message(s) to the global SI message queue.
47  */
48 void
50 {
51  SIInsertDataEntries(msgs, n);
52 }
53 
54 /*
55  * ReceiveSharedInvalidMessages
56  * Process shared-cache-invalidation messages waiting for this backend
57  *
58  * We guarantee to process all messages that had been queued before the
59  * routine was entered. It is of course possible for more messages to get
60  * queued right after our last SIGetDataEntries call.
61  *
62  * NOTE: it is entirely possible for this routine to be invoked recursively
63  * as a consequence of processing inside the invalFunction or resetFunction.
64  * Furthermore, such a recursive call must guarantee that all outstanding
65  * inval messages have been processed before it exits. This is the reason
66  * for the strange-looking choice to use a statically allocated buffer array
67  * and counters; it's so that a recursive call can process messages already
68  * sucked out of sinvaladt.c.
69  */
70 void
72  void (*invalFunction) (SharedInvalidationMessage *msg),
73  void (*resetFunction) (void))
74 {
75 #define MAXINVALMSGS 32
76  static SharedInvalidationMessage messages[MAXINVALMSGS];
77 
78  /*
79  * We use volatile here to prevent bugs if a compiler doesn't realize that
80  * recursion is a possibility ...
81  */
82  static volatile int nextmsg = 0;
83  static volatile int nummsgs = 0;
84 
85  /* Deal with any messages still pending from an outer recursion */
86  while (nextmsg < nummsgs)
87  {
88  SharedInvalidationMessage msg = messages[nextmsg++];
89 
91  invalFunction(&msg);
92  }
93 
94  do
95  {
96  int getResult;
97 
98  nextmsg = nummsgs = 0;
99 
100  /* Try to get some more messages */
101  getResult = SIGetDataEntries(messages, MAXINVALMSGS);
102 
103  if (getResult < 0)
104  {
105  /* got a reset message */
106  elog(DEBUG4, "cache state reset");
108  resetFunction();
109  break; /* nothing more to do */
110  }
111 
112  /* Process them, being wary that a recursive call might eat some */
113  nextmsg = 0;
114  nummsgs = getResult;
115 
116  while (nextmsg < nummsgs)
117  {
118  SharedInvalidationMessage msg = messages[nextmsg++];
119 
121  invalFunction(&msg);
122  }
123 
124  /*
125  * We only need to loop if the last SIGetDataEntries call (which might
126  * have been within a recursive call) returned a full buffer.
127  */
128  } while (nummsgs == MAXINVALMSGS);
129 
130  /*
131  * We are now caught up. If we received a catchup signal, reset that
132  * flag, and call SICleanupQueue(). This is not so much because we need
133  * to flush dead messages right now, as that we want to pass on the
134  * catchup signal to the next slowest backend. "Daisy chaining" the
135  * catchup signal this way avoids creating spikes in system load for what
136  * should be just a background maintenance activity.
137  */
139  {
140  catchupInterruptPending = false;
141  elog(DEBUG4, "sinval catchup complete, cleaning queue");
142  SICleanupQueue(false, 0);
143  }
144 }
145 
146 
147 /*
148  * HandleCatchupInterrupt
149  *
150  * This is called when PROCSIG_CATCHUP_INTERRUPT is received.
151  *
152  * We used to directly call ProcessCatchupEvent directly when idle. These days
153  * we just set a flag to do it later and notify the process of that fact by
154  * setting the process's latch.
155  */
156 void
158 {
159  /*
160  * Note: this is called by a SIGNAL HANDLER. You must be very wary what
161  * you do here.
162  */
163 
165 
166  /* make sure the event is processed in due course */
167  SetLatch(MyLatch);
168 }
169 
170 /*
171  * ProcessCatchupInterrupt
172  *
173  * The portion of catchup interrupt handling that runs outside of the signal
174  * handler, which allows it to actually process pending invalidations.
175  */
176 void
178 {
180  {
181  /*
182  * What we need to do here is cause ReceiveSharedInvalidMessages() to
183  * run, which will do the necessary work and also reset the
184  * catchupInterruptPending flag. If we are inside a transaction we
185  * can just call AcceptInvalidationMessages() to do this. If we
186  * aren't, we start and immediately end a transaction; the call to
187  * AcceptInvalidationMessages() happens down inside transaction start.
188  *
189  * It is awfully tempting to just call AcceptInvalidationMessages()
190  * without the rest of the xact start/stop overhead, and I think that
191  * would actually work in the normal case; but I am not sure that
192  * things would clean up nicely if we got an error partway through.
193  */
195  {
196  elog(DEBUG4, "ProcessCatchupEvent inside transaction");
198  }
199  else
200  {
201  elog(DEBUG4, "ProcessCatchupEvent outside transaction");
204  }
205  }
206 }
void ProcessCatchupInterrupt(void)
Definition: sinval.c:177
void AcceptInvalidationMessages(void)
Definition: inval.c:679
void CommitTransactionCommand(void)
Definition: xact.c:2745
bool IsTransactionOrTransactionBlock(void)
Definition: xact.c:4466
#define DEBUG4
Definition: elog.h:22
int SIGetDataEntries(SharedInvalidationMessage *data, int datasize)
Definition: sinvaladt.c:539
void ReceiveSharedInvalidMessages(void(*invalFunction)(SharedInvalidationMessage *msg), void(*resetFunction)(void))
Definition: sinval.c:71
#define MAXINVALMSGS
void SICleanupQueue(bool callerHasWriteLock, int minFree)
Definition: sinvaladt.c:643
uint64 SharedInvalidMessageCounter
Definition: sinval.c:26
void SetLatch(volatile Latch *latch)
Definition: latch.c:414
void StartTransactionCommand(void)
Definition: xact.c:2674
void SendSharedInvalidMessages(const SharedInvalidationMessage *msgs, int n)
Definition: sinval.c:49
struct Latch * MyLatch
Definition: globals.c:52
void SIInsertDataEntries(const SharedInvalidationMessage *data, int n)
Definition: sinvaladt.c:436
volatile sig_atomic_t catchupInterruptPending
Definition: sinval.c:41
#define elog
Definition: elog.h:219
void HandleCatchupInterrupt(void)
Definition: sinval.c:157