1 /*-------------------------------------------------------------------------
4 * local buffer manager. Fast buffer manager for temporary tables,
5 * which never need to be WAL-logged or checkpointed, etc.
7 * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994-5, Regents of the University of California
12 * src/backend/storage/buffer/localbuf.c
14 *-------------------------------------------------------------------------
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"
30 /* entry for buffer lookup hashtable */
33 BufferTag key; /* Tag of a disk page */
34 int id; /* Associated local buffer's index */
35 } LocalBufferLookupEnt;
37 /* Note: this macro only works on local buffers, not shared ones! */
38 #define LocalBufHdrGetBlock(bufHdr) \
39 LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
41 int NLocBuffer = 0; /* until buffers are initialized */
43 BufferDesc *LocalBufferDescriptors = NULL;
44 Block *LocalBufferBlockPointers = NULL;
45 int32 *LocalRefCount = NULL;
47 static int nextFreeLocalBuf = 0;
49 static HTAB *LocalBufHash = NULL;
52 static void InitLocalBuffers(void);
53 static Block GetLocalBufferStorage(void);
57 * LocalPrefetchBuffer -
58 * initiate asynchronous read of a block of a relation
60 * Do PrefetchBuffer's work for temporary relations.
61 * No-op if prefetching isn't compiled in.
64 LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
68 BufferTag newTag; /* identity of requested block */
69 LocalBufferLookupEnt *hresult;
71 INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
73 /* Initialize local buffers if first request in this session */
74 if (LocalBufHash == NULL)
77 /* See if the desired buffer already exists */
78 hresult = (LocalBufferLookupEnt *)
79 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
83 /* Yes, so nothing to do */
87 /* Not in buffers, so initiate prefetch */
88 smgrprefetch(smgr, forkNum, blockNum);
89 #endif /* USE_PREFETCH */
95 * Find or create a local buffer for the given page of the given relation.
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).
103 LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
106 BufferTag newTag; /* identity of requested block */
107 LocalBufferLookupEnt *hresult;
113 INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);
115 /* Initialize local buffers if first request in this session */
116 if (LocalBufHash == NULL)
119 /* See if the desired buffer already exists */
120 hresult = (LocalBufferLookupEnt *)
121 hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
126 bufHdr = &LocalBufferDescriptors[b];
127 Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
129 fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
130 smgr->smgr_rnode.node.relNode, forkNum, blockNum, -b - 1);
132 /* this part is equivalent to PinBuffer for a shared buffer */
133 if (LocalRefCount[b] == 0)
135 if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
136 bufHdr->usage_count++;
139 ResourceOwnerRememberBuffer(CurrentResourceOwner,
140 BufferDescriptorGetBuffer(bufHdr));
141 if (bufHdr->flags & BM_VALID)
145 /* Previous read attempt must have failed; try again */
152 fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
153 smgr->smgr_rnode.node.relNode, forkNum, blockNum,
154 -nextFreeLocalBuf - 1);
158 * Need to get a new buffer. We use a clock sweep algorithm (essentially
159 * the same as what freelist.c does now...)
161 trycounter = NLocBuffer;
164 b = nextFreeLocalBuf;
166 if (++nextFreeLocalBuf >= NLocBuffer)
167 nextFreeLocalBuf = 0;
169 bufHdr = &LocalBufferDescriptors[b];
171 if (LocalRefCount[b] == 0)
173 if (bufHdr->usage_count > 0)
175 bufHdr->usage_count--;
176 trycounter = NLocBuffer;
180 /* Found a usable buffer */
182 ResourceOwnerRememberBuffer(CurrentResourceOwner,
183 BufferDescriptorGetBuffer(bufHdr));
187 else if (--trycounter == 0)
189 (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
190 errmsg("no empty local buffer available")));
194 * this buffer is not referenced but it might still be dirty. if that's
195 * the case, write it out before reusing it!
197 if (bufHdr->flags & BM_DIRTY)
201 /* Find smgr relation for buffer */
202 oreln = smgropen(bufHdr->tag.rnode, MyBackendId);
207 bufHdr->tag.blockNum,
208 (char *) LocalBufHdrGetBlock(bufHdr),
211 /* Mark not-dirty now in case we error out below */
212 bufHdr->flags &= ~BM_DIRTY;
214 pgBufferUsage.local_blks_written++;
218 * lazy memory allocation: allocate space on first use of a buffer.
220 if (LocalBufHdrGetBlock(bufHdr) == NULL)
222 /* Set pointer for use by BufferGetBlock() macro */
223 LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
227 * Update the hash table: remove old entry, if any, and make new one.
229 if (bufHdr->flags & BM_TAG_VALID)
231 hresult = (LocalBufferLookupEnt *)
232 hash_search(LocalBufHash, (void *) &bufHdr->tag,
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);
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");
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;
260 * MarkLocalBufferDirty -
261 * mark a local buffer dirty
264 MarkLocalBufferDirty(Buffer buffer)
269 Assert(BufferIsLocal(buffer));
272 fprintf(stderr, "LB DIRTY %d\n", buffer);
275 bufid = -(buffer + 1);
277 Assert(LocalRefCount[bufid] > 0);
279 bufHdr = &LocalBufferDescriptors[bufid];
280 bufHdr->flags |= BM_DIRTY;
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!
292 * See DropRelFileNodeBuffers in bufmgr.c for more notes.
295 DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
296 BlockNumber firstDelBlock)
300 for (i = 0; i < NLocBuffer; i++)
302 BufferDesc *bufHdr = &LocalBufferDescriptors[i];
303 LocalBufferLookupEnt *hresult;
305 if ((bufHdr->flags & BM_TAG_VALID) &&
306 RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
307 bufHdr->tag.forkNum == forkNum &&
308 bufHdr->tag.blockNum >= firstDelBlock)
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),
316 /* Remove entry from hashtable */
317 hresult = (LocalBufferLookupEnt *)
318 hash_search(LocalBufHash, (void *) &bufHdr->tag,
320 if (!hresult) /* shouldn't happen */
321 elog(ERROR, "local buffer hash table corrupted");
322 /* Mark buffer invalid */
323 CLEAR_BUFFERTAG(bufHdr->tag);
325 bufHdr->usage_count = 0;
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.
337 InitLocalBuffers(void)
339 int nbufs = num_temp_buffers;
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)
349 (errcode(ERRCODE_OUT_OF_MEMORY),
350 errmsg("out of memory")));
352 nextFreeLocalBuf = 0;
354 /* initialize fields that need to start off nonzero */
355 for (i = 0; i < nbufs; i++)
357 BufferDesc *buf = &LocalBufferDescriptors[i];
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
365 buf->buf_id = -i - 2;
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;
374 LocalBufHash = hash_create("Local Buffer Lookup Table",
377 HASH_ELEM | HASH_FUNCTION);
380 elog(ERROR, "could not initialize local buffer hash table");
382 /* Initialization done, mark buffers allocated */
387 * GetLocalBufferStorage - allocate memory for a local buffer
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
396 GetLocalBufferStorage(void)
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;
406 Assert(total_bufs_allocated < NLocBuffer);
408 if (next_buf_in_block >= num_bufs_in_block)
410 /* Need to make a new request to memmgr */
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.
418 if (LocalBufferContext == NULL)
420 AllocSetContextCreate(TopMemoryContext,
421 "LocalBufferContext",
422 ALLOCSET_DEFAULT_MINSIZE,
423 ALLOCSET_DEFAULT_INITSIZE,
424 ALLOCSET_DEFAULT_MAXSIZE);
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);
433 cur_block = (char *) MemoryContextAlloc(LocalBufferContext,
435 next_buf_in_block = 0;
436 num_bufs_in_block = num_bufs;
439 /* Allocate next buffer in current memory block */
440 this_buf = cur_block + next_buf_in_block * BLCKSZ;
442 total_bufs_allocated++;
444 return (Block) this_buf;
448 * AtEOXact_LocalBuffers - clean up at end of transaction.
450 * This is just like AtEOXact_Buffers, but for local buffers.
453 AtEOXact_LocalBuffers(bool isCommit)
455 #ifdef USE_ASSERT_CHECKING
460 for (i = 0; i < NLocBuffer; i++)
462 Assert(LocalRefCount[i] == 0);
469 * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
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
477 AtProcExit_LocalBuffers(void)
479 #ifdef USE_ASSERT_CHECKING
480 if (assert_enabled && LocalRefCount)
484 for (i = 0; i < NLocBuffer; i++)
486 Assert(LocalRefCount[i] == 0);