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);