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