OSDN Git Service

Fix typo in sslmode documentation
[pg-rex/syncrep.git] / src / backend / storage / buffer / localbuf.c
1 /*-------------------------------------------------------------------------
2  *
3  * localbuf.c
4  *        local buffer manager. Fast buffer manager for temporary tables,
5  *        which never need to be WAL-logged or checkpointed, etc.
6  *
7  * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994-5, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        src/backend/storage/buffer/localbuf.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include "catalog/catalog.h"
19 #include "executor/instrument.h"
20 #include "storage/buf_internals.h"
21 #include "storage/bufmgr.h"
22 #include "storage/smgr.h"
23 #include "utils/guc.h"
24 #include "utils/memutils.h"
25 #include "utils/resowner.h"
26
27
28 /*#define LBDEBUG*/
29
30 /* entry for buffer lookup hashtable */
31 typedef struct
32 {
33         BufferTag       key;                    /* Tag of a disk page */
34         int                     id;                             /* Associated local buffer's index */
35 } LocalBufferLookupEnt;
36
37 /* Note: this macro only works on local buffers, not shared ones! */
38 #define LocalBufHdrGetBlock(bufHdr) \
39         LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
40
41 int                     NLocBuffer = 0;         /* until buffers are initialized */
42
43 BufferDesc *LocalBufferDescriptors = NULL;
44 Block      *LocalBufferBlockPointers = NULL;
45 int32      *LocalRefCount = NULL;
46
47 static int      nextFreeLocalBuf = 0;
48
49 static HTAB *LocalBufHash = NULL;
50
51
52 static void InitLocalBuffers(void);
53 static Block GetLocalBufferStorage(void);
54
55
56 /*
57  * LocalPrefetchBuffer -
58  *        initiate asynchronous read of a block of a relation
59  *
60  * Do PrefetchBuffer's work for temporary relations.
61  * No-op if prefetching isn't compiled in.
62  */
63 void
64 LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
65                                         BlockNumber blockNum)
66 {
67 #ifdef USE_PREFETCH
68         BufferTag       newTag;                 /* identity of requested block */
69         LocalBufferLookupEnt *hresult;
70
71         INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
72
73         /* Initialize local buffers if first request in this session */
74         if (LocalBufHash == NULL)
75                 InitLocalBuffers();
76
77         /* See if the desired buffer already exists */
78         hresult = (LocalBufferLookupEnt *)
79                 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
80
81         if (hresult)
82         {
83                 /* Yes, so nothing to do */
84                 return;
85         }
86
87         /* Not in buffers, so initiate prefetch */
88         smgrprefetch(smgr, forkNum, blockNum);
89 #endif   /* USE_PREFETCH */
90 }
91
92
93 /*
94  * LocalBufferAlloc -
95  *        Find or create a local buffer for the given page of the given relation.
96  *
97  * API is similar to bufmgr.c's BufferAlloc, except that we do not need
98  * to do any locking since this is all local.   Also, IO_IN_PROGRESS
99  * does not get set.  Lastly, we support only default access strategy
100  * (hence, usage_count is always advanced).
101  */
102 BufferDesc *
103 LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
104                                  bool *foundPtr)
105 {
106         BufferTag       newTag;                 /* identity of requested block */
107         LocalBufferLookupEnt *hresult;
108         BufferDesc *bufHdr;
109         int                     b;
110         int                     trycounter;
111         bool            found;
112
113         INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
114
115         /* Initialize local buffers if first request in this session */
116         if (LocalBufHash == NULL)
117                 InitLocalBuffers();
118
119         /* See if the desired buffer already exists */
120         hresult = (LocalBufferLookupEnt *)
121                 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
122
123         if (hresult)
124         {
125                 b = hresult->id;
126                 bufHdr = &LocalBufferDescriptors[b];
127                 Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
128 #ifdef LBDEBUG
129                 fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
130                                 smgr->smgr_rnode.node.relNode, forkNum, blockNum, -b - 1);
131 #endif
132                 /* this part is equivalent to PinBuffer for a shared buffer */
133                 if (LocalRefCount[b] == 0)
134                 {
135                         if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
136                                 bufHdr->usage_count++;
137                 }
138                 LocalRefCount[b]++;
139                 ResourceOwnerRememberBuffer(CurrentResourceOwner,
140                                                                         BufferDescriptorGetBuffer(bufHdr));
141                 if (bufHdr->flags & BM_VALID)
142                         *foundPtr = TRUE;
143                 else
144                 {
145                         /* Previous read attempt must have failed; try again */
146                         *foundPtr = FALSE;
147                 }
148                 return bufHdr;
149         }
150
151 #ifdef LBDEBUG
152         fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
153                         smgr->smgr_rnode.node.relNode, forkNum, blockNum,
154                         -nextFreeLocalBuf - 1);
155 #endif
156
157         /*
158          * Need to get a new buffer.  We use a clock sweep algorithm (essentially
159          * the same as what freelist.c does now...)
160          */
161         trycounter = NLocBuffer;
162         for (;;)
163         {
164                 b = nextFreeLocalBuf;
165
166                 if (++nextFreeLocalBuf >= NLocBuffer)
167                         nextFreeLocalBuf = 0;
168
169                 bufHdr = &LocalBufferDescriptors[b];
170
171                 if (LocalRefCount[b] == 0)
172                 {
173                         if (bufHdr->usage_count > 0)
174                         {
175                                 bufHdr->usage_count--;
176                                 trycounter = NLocBuffer;
177                         }
178                         else
179                         {
180                                 /* Found a usable buffer */
181                                 LocalRefCount[b]++;
182                                 ResourceOwnerRememberBuffer(CurrentResourceOwner,
183                                                                                   BufferDescriptorGetBuffer(bufHdr));
184                                 break;
185                         }
186                 }
187                 else if (--trycounter == 0)
188                         ereport(ERROR,
189                                         (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
190                                          errmsg("no empty local buffer available")));
191         }
192
193         /*
194          * this buffer is not referenced but it might still be dirty. if that's
195          * the case, write it out before reusing it!
196          */
197         if (bufHdr->flags & BM_DIRTY)
198         {
199                 SMgrRelation oreln;
200
201                 /* Find smgr relation for buffer */
202                 oreln = smgropen(bufHdr->tag.rnode, MyBackendId);
203
204                 /* And write... */
205                 smgrwrite(oreln,
206                                   bufHdr->tag.forkNum,
207                                   bufHdr->tag.blockNum,
208                                   (char *) LocalBufHdrGetBlock(bufHdr),
209                                   false);
210
211                 /* Mark not-dirty now in case we error out below */
212                 bufHdr->flags &= ~BM_DIRTY;
213
214                 pgBufferUsage.local_blks_written++;
215         }
216
217         /*
218          * lazy memory allocation: allocate space on first use of a buffer.
219          */
220         if (LocalBufHdrGetBlock(bufHdr) == NULL)
221         {
222                 /* Set pointer for use by BufferGetBlock() macro */
223                 LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
224         }
225
226         /*
227          * Update the hash table: remove old entry, if any, and make new one.
228          */
229         if (bufHdr->flags & BM_TAG_VALID)
230         {
231                 hresult = (LocalBufferLookupEnt *)
232                         hash_search(LocalBufHash, (void *) &bufHdr->tag,
233                                                 HASH_REMOVE, NULL);
234                 if (!hresult)                   /* shouldn't happen */
235                         elog(ERROR, "local buffer hash table corrupted");
236                 /* mark buffer invalid just in case hash insert fails */
237                 CLEAR_BUFFERTAG(bufHdr->tag);
238                 bufHdr->flags &= ~(BM_VALID | BM_TAG_VALID);
239         }
240
241         hresult = (LocalBufferLookupEnt *)
242                 hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found);
243         if (found)                                      /* shouldn't happen */
244                 elog(ERROR, "local buffer hash table corrupted");
245         hresult->id = b;
246
247         /*
248          * it's all ours now.
249          */
250         bufHdr->tag = newTag;
251         bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
252         bufHdr->flags |= BM_TAG_VALID;
253         bufHdr->usage_count = 1;
254
255         *foundPtr = FALSE;
256         return bufHdr;
257 }
258
259 /*
260  * MarkLocalBufferDirty -
261  *        mark a local buffer dirty
262  */
263 void
264 MarkLocalBufferDirty(Buffer buffer)
265 {
266         int                     bufid;
267         BufferDesc *bufHdr;
268
269         Assert(BufferIsLocal(buffer));
270
271 #ifdef LBDEBUG
272         fprintf(stderr, "LB DIRTY %d\n", buffer);
273 #endif
274
275         bufid = -(buffer + 1);
276
277         Assert(LocalRefCount[bufid] > 0);
278
279         bufHdr = &LocalBufferDescriptors[bufid];
280         bufHdr->flags |= BM_DIRTY;
281 }
282
283 /*
284  * DropRelFileNodeLocalBuffers
285  *              This function removes from the buffer pool all the pages of the
286  *              specified relation that have block numbers >= firstDelBlock.
287  *              (In particular, with firstDelBlock = 0, all pages are removed.)
288  *              Dirty pages are simply dropped, without bothering to write them
289  *              out first.      Therefore, this is NOT rollback-able, and so should be
290  *              used only with extreme caution!
291  *
292  *              See DropRelFileNodeBuffers in bufmgr.c for more notes.
293  */
294 void
295 DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
296                                                         BlockNumber firstDelBlock)
297 {
298         int                     i;
299
300         for (i = 0; i < NLocBuffer; i++)
301         {
302                 BufferDesc *bufHdr = &LocalBufferDescriptors[i];
303                 LocalBufferLookupEnt *hresult;
304
305                 if ((bufHdr->flags & BM_TAG_VALID) &&
306                         RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
307                         bufHdr->tag.forkNum == forkNum &&
308                         bufHdr->tag.blockNum >= firstDelBlock)
309                 {
310                         if (LocalRefCount[i] != 0)
311                                 elog(ERROR, "block %u of %s is still referenced (local %u)",
312                                          bufHdr->tag.blockNum,
313                                          relpathbackend(bufHdr->tag.rnode, MyBackendId,
314                                                                         bufHdr->tag.forkNum),
315                                          LocalRefCount[i]);
316                         /* Remove entry from hashtable */
317                         hresult = (LocalBufferLookupEnt *)
318                                 hash_search(LocalBufHash, (void *) &bufHdr->tag,
319                                                         HASH_REMOVE, NULL);
320                         if (!hresult)           /* shouldn't happen */
321                                 elog(ERROR, "local buffer hash table corrupted");
322                         /* Mark buffer invalid */
323                         CLEAR_BUFFERTAG(bufHdr->tag);
324                         bufHdr->flags = 0;
325                         bufHdr->usage_count = 0;
326                 }
327         }
328 }
329
330 /*
331  * InitLocalBuffers -
332  *        init the local buffer cache. Since most queries (esp. multi-user ones)
333  *        don't involve local buffers, we delay allocating actual memory for the
334  *        buffers until we need them; just make the buffer headers here.
335  */
336 static void
337 InitLocalBuffers(void)
338 {
339         int                     nbufs = num_temp_buffers;
340         HASHCTL         info;
341         int                     i;
342
343         /* Allocate and zero buffer headers and auxiliary arrays */
344         LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
345         LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
346         LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
347         if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
348                 ereport(FATAL,
349                                 (errcode(ERRCODE_OUT_OF_MEMORY),
350                                  errmsg("out of memory")));
351
352         nextFreeLocalBuf = 0;
353
354         /* initialize fields that need to start off nonzero */
355         for (i = 0; i < nbufs; i++)
356         {
357                 BufferDesc *buf = &LocalBufferDescriptors[i];
358
359                 /*
360                  * negative to indicate local buffer. This is tricky: shared buffers
361                  * start with 0. We have to start with -2. (Note that the routine
362                  * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
363                  * is -1.)
364                  */
365                 buf->buf_id = -i - 2;
366         }
367
368         /* Create the lookup hash table */
369         MemSet(&info, 0, sizeof(info));
370         info.keysize = sizeof(BufferTag);
371         info.entrysize = sizeof(LocalBufferLookupEnt);
372         info.hash = tag_hash;
373
374         LocalBufHash = hash_create("Local Buffer Lookup Table",
375                                                            nbufs,
376                                                            &info,
377                                                            HASH_ELEM | HASH_FUNCTION);
378
379         if (!LocalBufHash)
380                 elog(ERROR, "could not initialize local buffer hash table");
381
382         /* Initialization done, mark buffers allocated */
383         NLocBuffer = nbufs;
384 }
385
386 /*
387  * GetLocalBufferStorage - allocate memory for a local buffer
388  *
389  * The idea of this function is to aggregate our requests for storage
390  * so that the memory manager doesn't see a whole lot of relatively small
391  * requests.  Since we'll never give back a local buffer once it's created
392  * within a particular process, no point in burdening memmgr with separately
393  * managed chunks.
394  */
395 static Block
396 GetLocalBufferStorage(void)
397 {
398         static char *cur_block = NULL;
399         static int      next_buf_in_block = 0;
400         static int      num_bufs_in_block = 0;
401         static int      total_bufs_allocated = 0;
402         static MemoryContext LocalBufferContext = NULL;
403
404         char       *this_buf;
405
406         Assert(total_bufs_allocated < NLocBuffer);
407
408         if (next_buf_in_block >= num_bufs_in_block)
409         {
410                 /* Need to make a new request to memmgr */
411                 int                     num_bufs;
412
413                 /*
414                  * We allocate local buffers in a context of their own, so that the
415                  * space eaten for them is easily recognizable in MemoryContextStats
416                  * output.      Create the context on first use.
417                  */
418                 if (LocalBufferContext == NULL)
419                         LocalBufferContext =
420                                 AllocSetContextCreate(TopMemoryContext,
421                                                                           "LocalBufferContext",
422                                                                           ALLOCSET_DEFAULT_MINSIZE,
423                                                                           ALLOCSET_DEFAULT_INITSIZE,
424                                                                           ALLOCSET_DEFAULT_MAXSIZE);
425
426                 /* Start with a 16-buffer request; subsequent ones double each time */
427                 num_bufs = Max(num_bufs_in_block * 2, 16);
428                 /* But not more than what we need for all remaining local bufs */
429                 num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
430                 /* And don't overflow MaxAllocSize, either */
431                 num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
432
433                 cur_block = (char *) MemoryContextAlloc(LocalBufferContext,
434                                                                                                 num_bufs * BLCKSZ);
435                 next_buf_in_block = 0;
436                 num_bufs_in_block = num_bufs;
437         }
438
439         /* Allocate next buffer in current memory block */
440         this_buf = cur_block + next_buf_in_block * BLCKSZ;
441         next_buf_in_block++;
442         total_bufs_allocated++;
443
444         return (Block) this_buf;
445 }
446
447 /*
448  * AtEOXact_LocalBuffers - clean up at end of transaction.
449  *
450  * This is just like AtEOXact_Buffers, but for local buffers.
451  */
452 void
453 AtEOXact_LocalBuffers(bool isCommit)
454 {
455 #ifdef USE_ASSERT_CHECKING
456         if (assert_enabled)
457         {
458                 int                     i;
459
460                 for (i = 0; i < NLocBuffer; i++)
461                 {
462                         Assert(LocalRefCount[i] == 0);
463                 }
464         }
465 #endif
466 }
467
468 /*
469  * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
470  *
471  * This is just like AtProcExit_Buffers, but for local buffers.  We shouldn't
472  * be holding any remaining pins; if we are, and assertions aren't enabled,
473  * we'll fail later in DropRelFileNodeBuffers while trying to drop the temp
474  * rels.
475  */
476 void
477 AtProcExit_LocalBuffers(void)
478 {
479 #ifdef USE_ASSERT_CHECKING
480         if (assert_enabled && LocalRefCount)
481         {
482                 int                     i;
483
484                 for (i = 0; i < NLocBuffer; i++)
485                 {
486                         Assert(LocalRefCount[i] == 0);
487                 }
488         }
489 #endif
490 }