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-2008, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994-5, Regents of the University of California
14 *-------------------------------------------------------------------------
18 #include "storage/buf_internals.h"
19 #include "storage/bufmgr.h"
20 #include "storage/smgr.h"
21 #include "utils/guc.h"
22 #include "utils/memutils.h"
23 #include "utils/resowner.h"
28 /* entry for buffer lookup hashtable */
31 BufferTag key
; /* Tag of a disk page */
32 int id
; /* Associated local buffer's index */
33 } LocalBufferLookupEnt
;
35 /* Note: this macro only works on local buffers, not shared ones! */
36 #define LocalBufHdrGetBlock(bufHdr) \
37 LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
39 int NLocBuffer
= 0; /* until buffers are initialized */
41 BufferDesc
*LocalBufferDescriptors
= NULL
;
42 Block
*LocalBufferBlockPointers
= NULL
;
43 int32
*LocalRefCount
= NULL
;
45 static int nextFreeLocalBuf
= 0;
47 static HTAB
*LocalBufHash
= NULL
;
50 static void InitLocalBuffers(void);
51 static Block
GetLocalBufferStorage(void);
56 * Find or create a local buffer for the given page of the given relation.
58 * API is similar to bufmgr.c's BufferAlloc, except that we do not need
59 * to do any locking since this is all local. Also, IO_IN_PROGRESS
60 * does not get set. Lastly, we support only default access strategy
61 * (hence, usage_count is always advanced).
64 LocalBufferAlloc(SMgrRelation smgr
, ForkNumber forkNum
, BlockNumber blockNum
,
67 BufferTag newTag
; /* identity of requested block */
68 LocalBufferLookupEnt
*hresult
;
74 INIT_BUFFERTAG(newTag
, smgr
->smgr_rnode
, forkNum
, blockNum
);
76 /* Initialize local buffers if first request in this session */
77 if (LocalBufHash
== NULL
)
80 /* See if the desired buffer already exists */
81 hresult
= (LocalBufferLookupEnt
*)
82 hash_search(LocalBufHash
, (void *) &newTag
, HASH_FIND
, NULL
);
87 bufHdr
= &LocalBufferDescriptors
[b
];
88 Assert(BUFFERTAGS_EQUAL(bufHdr
->tag
, newTag
));
90 fprintf(stderr
, "LB ALLOC (%u,%d) %d\n",
91 smgr
->smgr_rnode
.relNode
, blockNum
, -b
- 1);
93 /* this part is equivalent to PinBuffer for a shared buffer */
94 if (LocalRefCount
[b
] == 0)
96 if (bufHdr
->usage_count
< BM_MAX_USAGE_COUNT
)
97 bufHdr
->usage_count
++;
100 ResourceOwnerRememberBuffer(CurrentResourceOwner
,
101 BufferDescriptorGetBuffer(bufHdr
));
102 if (bufHdr
->flags
& BM_VALID
)
106 /* Previous read attempt must have failed; try again */
113 fprintf(stderr
, "LB ALLOC (%u,%d) %d\n",
114 RelationGetRelid(reln
), blockNum
, -nextFreeLocalBuf
- 1);
118 * Need to get a new buffer. We use a clock sweep algorithm (essentially
119 * the same as what freelist.c does now...)
121 trycounter
= NLocBuffer
;
124 b
= nextFreeLocalBuf
;
126 if (++nextFreeLocalBuf
>= NLocBuffer
)
127 nextFreeLocalBuf
= 0;
129 bufHdr
= &LocalBufferDescriptors
[b
];
131 if (LocalRefCount
[b
] == 0)
133 if (bufHdr
->usage_count
> 0)
135 bufHdr
->usage_count
--;
136 trycounter
= NLocBuffer
;
140 /* Found a usable buffer */
142 ResourceOwnerRememberBuffer(CurrentResourceOwner
,
143 BufferDescriptorGetBuffer(bufHdr
));
147 else if (--trycounter
== 0)
149 (errcode(ERRCODE_INSUFFICIENT_RESOURCES
),
150 errmsg("no empty local buffer available")));
154 * this buffer is not referenced but it might still be dirty. if that's
155 * the case, write it out before reusing it!
157 if (bufHdr
->flags
& BM_DIRTY
)
161 /* Find smgr relation for buffer */
162 oreln
= smgropen(bufHdr
->tag
.rnode
);
167 bufHdr
->tag
.blockNum
,
168 (char *) LocalBufHdrGetBlock(bufHdr
),
171 /* Mark not-dirty now in case we error out below */
172 bufHdr
->flags
&= ~BM_DIRTY
;
174 LocalBufferFlushCount
++;
178 * lazy memory allocation: allocate space on first use of a buffer.
180 if (LocalBufHdrGetBlock(bufHdr
) == NULL
)
182 /* Set pointer for use by BufferGetBlock() macro */
183 LocalBufHdrGetBlock(bufHdr
) = GetLocalBufferStorage();
187 * Update the hash table: remove old entry, if any, and make new one.
189 if (bufHdr
->flags
& BM_TAG_VALID
)
191 hresult
= (LocalBufferLookupEnt
*)
192 hash_search(LocalBufHash
, (void *) &bufHdr
->tag
,
194 if (!hresult
) /* shouldn't happen */
195 elog(ERROR
, "local buffer hash table corrupted");
196 /* mark buffer invalid just in case hash insert fails */
197 CLEAR_BUFFERTAG(bufHdr
->tag
);
198 bufHdr
->flags
&= ~(BM_VALID
| BM_TAG_VALID
);
201 hresult
= (LocalBufferLookupEnt
*)
202 hash_search(LocalBufHash
, (void *) &newTag
, HASH_ENTER
, &found
);
203 if (found
) /* shouldn't happen */
204 elog(ERROR
, "local buffer hash table corrupted");
210 bufHdr
->tag
= newTag
;
211 bufHdr
->flags
&= ~(BM_VALID
| BM_DIRTY
| BM_JUST_DIRTIED
| BM_IO_ERROR
);
212 bufHdr
->flags
|= BM_TAG_VALID
;
213 bufHdr
->usage_count
= 1;
220 * MarkLocalBufferDirty -
221 * mark a local buffer dirty
224 MarkLocalBufferDirty(Buffer buffer
)
229 Assert(BufferIsLocal(buffer
));
232 fprintf(stderr
, "LB DIRTY %d\n", buffer
);
235 bufid
= -(buffer
+ 1);
237 Assert(LocalRefCount
[bufid
] > 0);
239 bufHdr
= &LocalBufferDescriptors
[bufid
];
240 bufHdr
->flags
|= BM_DIRTY
;
244 * DropRelFileNodeLocalBuffers
245 * This function removes from the buffer pool all the pages of the
246 * specified relation that have block numbers >= firstDelBlock.
247 * (In particular, with firstDelBlock = 0, all pages are removed.)
248 * Dirty pages are simply dropped, without bothering to write them
249 * out first. Therefore, this is NOT rollback-able, and so should be
250 * used only with extreme caution!
252 * See DropRelFileNodeBuffers in bufmgr.c for more notes.
255 DropRelFileNodeLocalBuffers(RelFileNode rnode
, ForkNumber forkNum
,
256 BlockNumber firstDelBlock
)
260 for (i
= 0; i
< NLocBuffer
; i
++)
262 BufferDesc
*bufHdr
= &LocalBufferDescriptors
[i
];
263 LocalBufferLookupEnt
*hresult
;
265 if ((bufHdr
->flags
& BM_TAG_VALID
) &&
266 RelFileNodeEquals(bufHdr
->tag
.rnode
, rnode
) &&
267 bufHdr
->tag
.forkNum
== forkNum
&&
268 bufHdr
->tag
.blockNum
>= firstDelBlock
)
270 if (LocalRefCount
[i
] != 0)
271 elog(ERROR
, "block %u of %u/%u/%u is still referenced (local %u)",
272 bufHdr
->tag
.blockNum
,
273 bufHdr
->tag
.rnode
.spcNode
,
274 bufHdr
->tag
.rnode
.dbNode
,
275 bufHdr
->tag
.rnode
.relNode
,
277 /* Remove entry from hashtable */
278 hresult
= (LocalBufferLookupEnt
*)
279 hash_search(LocalBufHash
, (void *) &bufHdr
->tag
,
281 if (!hresult
) /* shouldn't happen */
282 elog(ERROR
, "local buffer hash table corrupted");
283 /* Mark buffer invalid */
284 CLEAR_BUFFERTAG(bufHdr
->tag
);
286 bufHdr
->usage_count
= 0;
293 * init the local buffer cache. Since most queries (esp. multi-user ones)
294 * don't involve local buffers, we delay allocating actual memory for the
295 * buffers until we need them; just make the buffer headers here.
298 InitLocalBuffers(void)
300 int nbufs
= num_temp_buffers
;
304 /* Allocate and zero buffer headers and auxiliary arrays */
305 LocalBufferDescriptors
= (BufferDesc
*) calloc(nbufs
, sizeof(BufferDesc
));
306 LocalBufferBlockPointers
= (Block
*) calloc(nbufs
, sizeof(Block
));
307 LocalRefCount
= (int32
*) calloc(nbufs
, sizeof(int32
));
308 if (!LocalBufferDescriptors
|| !LocalBufferBlockPointers
|| !LocalRefCount
)
310 (errcode(ERRCODE_OUT_OF_MEMORY
),
311 errmsg("out of memory")));
313 nextFreeLocalBuf
= 0;
315 /* initialize fields that need to start off nonzero */
316 for (i
= 0; i
< nbufs
; i
++)
318 BufferDesc
*buf
= &LocalBufferDescriptors
[i
];
321 * negative to indicate local buffer. This is tricky: shared buffers
322 * start with 0. We have to start with -2. (Note that the routine
323 * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
326 buf
->buf_id
= -i
- 2;
329 /* Create the lookup hash table */
330 MemSet(&info
, 0, sizeof(info
));
331 info
.keysize
= sizeof(BufferTag
);
332 info
.entrysize
= sizeof(LocalBufferLookupEnt
);
333 info
.hash
= tag_hash
;
335 LocalBufHash
= hash_create("Local Buffer Lookup Table",
338 HASH_ELEM
| HASH_FUNCTION
);
341 elog(ERROR
, "could not initialize local buffer hash table");
343 /* Initialization done, mark buffers allocated */
348 * GetLocalBufferStorage - allocate memory for a local buffer
350 * The idea of this function is to aggregate our requests for storage
351 * so that the memory manager doesn't see a whole lot of relatively small
352 * requests. Since we'll never give back a local buffer once it's created
353 * within a particular process, no point in burdening memmgr with separately
357 GetLocalBufferStorage(void)
359 static char *cur_block
= NULL
;
360 static int next_buf_in_block
= 0;
361 static int num_bufs_in_block
= 0;
362 static int total_bufs_allocated
= 0;
366 Assert(total_bufs_allocated
< NLocBuffer
);
368 if (next_buf_in_block
>= num_bufs_in_block
)
370 /* Need to make a new request to memmgr */
373 /* Start with a 16-buffer request; subsequent ones double each time */
374 num_bufs
= Max(num_bufs_in_block
* 2, 16);
375 /* But not more than what we need for all remaining local bufs */
376 num_bufs
= Min(num_bufs
, NLocBuffer
- total_bufs_allocated
);
377 /* And don't overflow MaxAllocSize, either */
378 num_bufs
= Min(num_bufs
, MaxAllocSize
/ BLCKSZ
);
380 /* Allocate space from TopMemoryContext so it never goes away */
381 cur_block
= (char *) MemoryContextAlloc(TopMemoryContext
,
383 next_buf_in_block
= 0;
384 num_bufs_in_block
= num_bufs
;
387 /* Allocate next buffer in current memory block */
388 this_buf
= cur_block
+ next_buf_in_block
* BLCKSZ
;
390 total_bufs_allocated
++;
392 return (Block
) this_buf
;
396 * AtEOXact_LocalBuffers - clean up at end of transaction.
398 * This is just like AtEOXact_Buffers, but for local buffers.
401 AtEOXact_LocalBuffers(bool isCommit
)
403 #ifdef USE_ASSERT_CHECKING
408 for (i
= 0; i
< NLocBuffer
; i
++)
410 Assert(LocalRefCount
[i
] == 0);
417 * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
419 * This is just like AtProcExit_Buffers, but for local buffers. We have
420 * to drop pins to ensure that any attempt to drop temp files doesn't
421 * fail in DropRelFileNodeBuffers.
424 AtProcExit_LocalBuffers(void)
426 /* just zero the refcounts ... */
428 MemSet(LocalRefCount
, 0, NLocBuffer
* sizeof(*LocalRefCount
));