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-2009, PostgreSQL Global Development Group
8
* Portions Copyright (c) 1994-5, Regents of the University of California
14
*-------------------------------------------------------------------------
18
#include "catalog/catalog.h"
19
#include "storage/buf_internals.h"
20
#include "storage/bufmgr.h"
21
#include "storage/smgr.h"
22
#include "utils/guc.h"
23
#include "utils/memutils.h"
24
#include "utils/resowner.h"
29
/* entry for buffer lookup hashtable */
32
BufferTag key; /* Tag of a disk page */
33
int id; /* Associated local buffer's index */
34
} LocalBufferLookupEnt;
36
/* Note: this macro only works on local buffers, not shared ones! */
37
#define LocalBufHdrGetBlock(bufHdr) \
38
LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
40
int NLocBuffer = 0; /* until buffers are initialized */
42
BufferDesc *LocalBufferDescriptors = NULL;
43
Block *LocalBufferBlockPointers = NULL;
44
int32 *LocalRefCount = NULL;
46
static int nextFreeLocalBuf = 0;
48
static HTAB *LocalBufHash = NULL;
51
static void InitLocalBuffers(void);
52
static Block GetLocalBufferStorage(void);
56
* LocalPrefetchBuffer -
57
* initiate asynchronous read of a block of a relation
59
* Do PrefetchBuffer's work for temporary relations.
60
* No-op if prefetching isn't compiled in.
63
LocalPrefetchBuffer(SMgrRelation smgr, ForkNumber forkNum,
67
BufferTag newTag; /* identity of requested block */
68
LocalBufferLookupEnt *hresult;
70
INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
72
/* Initialize local buffers if first request in this session */
73
if (LocalBufHash == NULL)
76
/* See if the desired buffer already exists */
77
hresult = (LocalBufferLookupEnt *)
78
hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
82
/* Yes, so nothing to do */
86
/* Not in buffers, so initiate prefetch */
87
smgrprefetch(smgr, forkNum, blockNum);
88
#endif /* USE_PREFETCH */
94
* Find or create a local buffer for the given page of the given relation.
96
* API is similar to bufmgr.c's BufferAlloc, except that we do not need
97
* to do any locking since this is all local. Also, IO_IN_PROGRESS
98
* does not get set. Lastly, we support only default access strategy
99
* (hence, usage_count is always advanced).
102
LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
105
BufferTag newTag; /* identity of requested block */
106
LocalBufferLookupEnt *hresult;
112
INIT_BUFFERTAG(newTag, smgr->smgr_rnode, forkNum, blockNum);
114
/* Initialize local buffers if first request in this session */
115
if (LocalBufHash == NULL)
118
/* See if the desired buffer already exists */
119
hresult = (LocalBufferLookupEnt *)
120
hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);
125
bufHdr = &LocalBufferDescriptors[b];
126
Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
128
fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
129
smgr->smgr_rnode.relNode, forkNum, blockNum, -b - 1);
131
/* this part is equivalent to PinBuffer for a shared buffer */
132
if (LocalRefCount[b] == 0)
134
if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
135
bufHdr->usage_count++;
138
ResourceOwnerRememberBuffer(CurrentResourceOwner,
139
BufferDescriptorGetBuffer(bufHdr));
140
if (bufHdr->flags & BM_VALID)
144
/* Previous read attempt must have failed; try again */
151
fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
152
smgr->smgr_rnode.relNode, forkNum, blockNum, -nextFreeLocalBuf - 1);
156
* Need to get a new buffer. We use a clock sweep algorithm (essentially
157
* the same as what freelist.c does now...)
159
trycounter = NLocBuffer;
162
b = nextFreeLocalBuf;
164
if (++nextFreeLocalBuf >= NLocBuffer)
165
nextFreeLocalBuf = 0;
167
bufHdr = &LocalBufferDescriptors[b];
169
if (LocalRefCount[b] == 0)
171
if (bufHdr->usage_count > 0)
173
bufHdr->usage_count--;
174
trycounter = NLocBuffer;
178
/* Found a usable buffer */
180
ResourceOwnerRememberBuffer(CurrentResourceOwner,
181
BufferDescriptorGetBuffer(bufHdr));
185
else if (--trycounter == 0)
187
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
188
errmsg("no empty local buffer available")));
192
* this buffer is not referenced but it might still be dirty. if that's
193
* the case, write it out before reusing it!
195
if (bufHdr->flags & BM_DIRTY)
199
/* Find smgr relation for buffer */
200
oreln = smgropen(bufHdr->tag.rnode);
205
bufHdr->tag.blockNum,
206
(char *) LocalBufHdrGetBlock(bufHdr),
209
/* Mark not-dirty now in case we error out below */
210
bufHdr->flags &= ~BM_DIRTY;
212
LocalBufferFlushCount++;
216
* lazy memory allocation: allocate space on first use of a buffer.
218
if (LocalBufHdrGetBlock(bufHdr) == NULL)
220
/* Set pointer for use by BufferGetBlock() macro */
221
LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
225
* Update the hash table: remove old entry, if any, and make new one.
227
if (bufHdr->flags & BM_TAG_VALID)
229
hresult = (LocalBufferLookupEnt *)
230
hash_search(LocalBufHash, (void *) &bufHdr->tag,
232
if (!hresult) /* shouldn't happen */
233
elog(ERROR, "local buffer hash table corrupted");
234
/* mark buffer invalid just in case hash insert fails */
235
CLEAR_BUFFERTAG(bufHdr->tag);
236
bufHdr->flags &= ~(BM_VALID | BM_TAG_VALID);
239
hresult = (LocalBufferLookupEnt *)
240
hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found);
241
if (found) /* shouldn't happen */
242
elog(ERROR, "local buffer hash table corrupted");
248
bufHdr->tag = newTag;
249
bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
250
bufHdr->flags |= BM_TAG_VALID;
251
bufHdr->usage_count = 1;
258
* MarkLocalBufferDirty -
259
* mark a local buffer dirty
262
MarkLocalBufferDirty(Buffer buffer)
267
Assert(BufferIsLocal(buffer));
270
fprintf(stderr, "LB DIRTY %d\n", buffer);
273
bufid = -(buffer + 1);
275
Assert(LocalRefCount[bufid] > 0);
277
bufHdr = &LocalBufferDescriptors[bufid];
278
bufHdr->flags |= BM_DIRTY;
282
* DropRelFileNodeLocalBuffers
283
* This function removes from the buffer pool all the pages of the
284
* specified relation that have block numbers >= firstDelBlock.
285
* (In particular, with firstDelBlock = 0, all pages are removed.)
286
* Dirty pages are simply dropped, without bothering to write them
287
* out first. Therefore, this is NOT rollback-able, and so should be
288
* used only with extreme caution!
290
* See DropRelFileNodeBuffers in bufmgr.c for more notes.
293
DropRelFileNodeLocalBuffers(RelFileNode rnode, ForkNumber forkNum,
294
BlockNumber firstDelBlock)
298
for (i = 0; i < NLocBuffer; i++)
300
BufferDesc *bufHdr = &LocalBufferDescriptors[i];
301
LocalBufferLookupEnt *hresult;
303
if ((bufHdr->flags & BM_TAG_VALID) &&
304
RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
305
bufHdr->tag.forkNum == forkNum &&
306
bufHdr->tag.blockNum >= firstDelBlock)
308
if (LocalRefCount[i] != 0)
309
elog(ERROR, "block %u of %s is still referenced (local %u)",
310
bufHdr->tag.blockNum,
311
relpath(bufHdr->tag.rnode, bufHdr->tag.forkNum),
313
/* Remove entry from hashtable */
314
hresult = (LocalBufferLookupEnt *)
315
hash_search(LocalBufHash, (void *) &bufHdr->tag,
317
if (!hresult) /* shouldn't happen */
318
elog(ERROR, "local buffer hash table corrupted");
319
/* Mark buffer invalid */
320
CLEAR_BUFFERTAG(bufHdr->tag);
322
bufHdr->usage_count = 0;
329
* init the local buffer cache. Since most queries (esp. multi-user ones)
330
* don't involve local buffers, we delay allocating actual memory for the
331
* buffers until we need them; just make the buffer headers here.
334
InitLocalBuffers(void)
336
int nbufs = num_temp_buffers;
340
/* Allocate and zero buffer headers and auxiliary arrays */
341
LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
342
LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
343
LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
344
if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
346
(errcode(ERRCODE_OUT_OF_MEMORY),
347
errmsg("out of memory")));
349
nextFreeLocalBuf = 0;
351
/* initialize fields that need to start off nonzero */
352
for (i = 0; i < nbufs; i++)
354
BufferDesc *buf = &LocalBufferDescriptors[i];
357
* negative to indicate local buffer. This is tricky: shared buffers
358
* start with 0. We have to start with -2. (Note that the routine
359
* BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
362
buf->buf_id = -i - 2;
365
/* Create the lookup hash table */
366
MemSet(&info, 0, sizeof(info));
367
info.keysize = sizeof(BufferTag);
368
info.entrysize = sizeof(LocalBufferLookupEnt);
369
info.hash = tag_hash;
371
LocalBufHash = hash_create("Local Buffer Lookup Table",
374
HASH_ELEM | HASH_FUNCTION);
377
elog(ERROR, "could not initialize local buffer hash table");
379
/* Initialization done, mark buffers allocated */
384
* GetLocalBufferStorage - allocate memory for a local buffer
386
* The idea of this function is to aggregate our requests for storage
387
* so that the memory manager doesn't see a whole lot of relatively small
388
* requests. Since we'll never give back a local buffer once it's created
389
* within a particular process, no point in burdening memmgr with separately
393
GetLocalBufferStorage(void)
395
static char *cur_block = NULL;
396
static int next_buf_in_block = 0;
397
static int num_bufs_in_block = 0;
398
static int total_bufs_allocated = 0;
402
Assert(total_bufs_allocated < NLocBuffer);
404
if (next_buf_in_block >= num_bufs_in_block)
406
/* Need to make a new request to memmgr */
409
/* Start with a 16-buffer request; subsequent ones double each time */
410
num_bufs = Max(num_bufs_in_block * 2, 16);
411
/* But not more than what we need for all remaining local bufs */
412
num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
413
/* And don't overflow MaxAllocSize, either */
414
num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
416
/* Allocate space from TopMemoryContext so it never goes away */
417
cur_block = (char *) MemoryContextAlloc(TopMemoryContext,
419
next_buf_in_block = 0;
420
num_bufs_in_block = num_bufs;
423
/* Allocate next buffer in current memory block */
424
this_buf = cur_block + next_buf_in_block * BLCKSZ;
426
total_bufs_allocated++;
428
return (Block) this_buf;
432
* AtEOXact_LocalBuffers - clean up at end of transaction.
434
* This is just like AtEOXact_Buffers, but for local buffers.
437
AtEOXact_LocalBuffers(bool isCommit)
439
#ifdef USE_ASSERT_CHECKING
444
for (i = 0; i < NLocBuffer; i++)
446
Assert(LocalRefCount[i] == 0);
453
* AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
455
* This is just like AtProcExit_Buffers, but for local buffers. We have
456
* to drop pins to ensure that any attempt to drop temp files doesn't
457
* fail in DropRelFileNodeBuffers.
460
AtProcExit_LocalBuffers(void)
462
/* just zero the refcounts ... */
464
MemSet(LocalRefCount, 0, NLocBuffer * sizeof(*LocalRefCount));