mirror of
https://github.com/postgres/postgres.git
synced 2026-02-16 17:19:08 -05:00
The local buffer manager is no longer used for newly-created relations (unless they are TEMP); a new non-TEMP relation goes through the shared bufmgr and thus will participate normally in checkpoints. But TEMP relations use the local buffer manager throughout their lifespan. Also, operations in TEMP relations are not logged in WAL, thus improving performance. Since it's no longer necessary to fsync relations as they move out of the local buffers into shared buffers, quite a lot of smgr.c/md.c/fd.c code is no longer needed and has been removed: there's no concept of a dirty relation anymore in md.c/fd.c, and we never fsync anything but WAL. Still TODO: improve local buffer management algorithms so that it would be reasonable to increase NLocBuffer.
236 lines
5.7 KiB
C
236 lines
5.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* localbuf.c
|
|
* local buffer manager. Fast buffer manager for temporary tables,
|
|
* which never need to be WAL-logged or checkpointed, etc.
|
|
*
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994-5, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/localbuf.c,v 1.45 2002/08/06 02:36:34 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "storage/buf_internals.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/smgr.h"
|
|
#include "utils/relcache.h"
|
|
|
|
|
|
/*#define LBDEBUG*/
|
|
|
|
/* should be a GUC parameter some day */
|
|
int NLocBuffer = 64;
|
|
|
|
BufferDesc *LocalBufferDescriptors = NULL;
|
|
Block *LocalBufferBlockPointers = NULL;
|
|
long *LocalRefCount = NULL;
|
|
|
|
static int nextFreeLocalBuf = 0;
|
|
|
|
|
|
/*
|
|
* LocalBufferAlloc -
|
|
* allocate a local buffer. We do round robin allocation for now.
|
|
*/
|
|
BufferDesc *
|
|
LocalBufferAlloc(Relation reln, BlockNumber blockNum, bool *foundPtr)
|
|
{
|
|
int i;
|
|
BufferDesc *bufHdr = (BufferDesc *) NULL;
|
|
|
|
/* a low tech search for now -- not optimized for scans */
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
if (LocalBufferDescriptors[i].tag.rnode.relNode ==
|
|
reln->rd_node.relNode &&
|
|
LocalBufferDescriptors[i].tag.blockNum == blockNum)
|
|
{
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
|
|
RelationGetRelid(reln), blockNum, -i - 1);
|
|
#endif
|
|
|
|
LocalRefCount[i]++;
|
|
*foundPtr = TRUE;
|
|
return &LocalBufferDescriptors[i];
|
|
}
|
|
}
|
|
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB ALLOC (%u,%d) %d\n",
|
|
RelationGetRelid(reln), blockNum, -nextFreeLocalBuf - 1);
|
|
#endif
|
|
|
|
/* need to get a new buffer (round robin for now) */
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
int b = (nextFreeLocalBuf + i) % NLocBuffer;
|
|
|
|
if (LocalRefCount[b] == 0)
|
|
{
|
|
bufHdr = &LocalBufferDescriptors[b];
|
|
LocalRefCount[b]++;
|
|
nextFreeLocalBuf = (b + 1) % NLocBuffer;
|
|
break;
|
|
}
|
|
}
|
|
if (bufHdr == NULL)
|
|
elog(ERROR, "no empty local buffer.");
|
|
|
|
/*
|
|
* this buffer is not referenced but it might still be dirty.
|
|
* if that's the case, write it out before reusing it!
|
|
*/
|
|
if (bufHdr->flags & BM_DIRTY || bufHdr->cntxDirty)
|
|
{
|
|
Relation bufrel = RelationNodeCacheGetRelation(bufHdr->tag.rnode);
|
|
|
|
/*
|
|
* The relcache is not supposed to throw away temp rels, so this
|
|
* should always succeed.
|
|
*/
|
|
Assert(bufrel != NULL);
|
|
|
|
/* flush this page */
|
|
smgrwrite(DEFAULT_SMGR, bufrel, bufHdr->tag.blockNum,
|
|
(char *) MAKE_PTR(bufHdr->data));
|
|
LocalBufferFlushCount++;
|
|
|
|
/* drop refcount incremented by RelationNodeCacheGetRelation */
|
|
RelationDecrementReferenceCount(bufrel);
|
|
}
|
|
|
|
/*
|
|
* lazy memory allocation: allocate space on first use of a buffer.
|
|
*
|
|
* Note this path cannot be taken for a buffer that was previously
|
|
* in use, so it's okay to do it (and possibly error out) before
|
|
* marking the buffer as valid.
|
|
*/
|
|
if (bufHdr->data == (SHMEM_OFFSET) 0)
|
|
{
|
|
char *data = (char *) malloc(BLCKSZ);
|
|
|
|
if (data == NULL)
|
|
elog(ERROR, "Out of memory in LocalBufferAlloc");
|
|
|
|
/*
|
|
* This is a bit of a hack: bufHdr->data needs to be a shmem
|
|
* offset for consistency with the shared-buffer case, so make it
|
|
* one even though it's not really a valid shmem offset.
|
|
*/
|
|
bufHdr->data = MAKE_OFFSET(data);
|
|
|
|
/*
|
|
* Set pointer for use by BufferGetBlock() macro.
|
|
*/
|
|
LocalBufferBlockPointers[-(bufHdr->buf_id + 2)] = (Block) data;
|
|
}
|
|
|
|
/*
|
|
* it's all ours now.
|
|
*
|
|
* We need not in tblNode currently but will in future I think, when
|
|
* we'll give up rel->rd_fd to fmgr cache.
|
|
*/
|
|
bufHdr->tag.rnode = reln->rd_node;
|
|
bufHdr->tag.blockNum = blockNum;
|
|
bufHdr->flags &= ~BM_DIRTY;
|
|
bufHdr->cntxDirty = false;
|
|
|
|
*foundPtr = FALSE;
|
|
return bufHdr;
|
|
}
|
|
|
|
/*
|
|
* WriteLocalBuffer -
|
|
* writes out a local buffer (actually, just marks it dirty)
|
|
*/
|
|
void
|
|
WriteLocalBuffer(Buffer buffer, bool release)
|
|
{
|
|
int bufid;
|
|
|
|
Assert(BufferIsLocal(buffer));
|
|
|
|
#ifdef LBDEBUG
|
|
fprintf(stderr, "LB WRITE %d\n", buffer);
|
|
#endif
|
|
|
|
bufid = -(buffer + 1);
|
|
LocalBufferDescriptors[bufid].flags |= BM_DIRTY;
|
|
|
|
if (release)
|
|
{
|
|
Assert(LocalRefCount[bufid] > 0);
|
|
LocalRefCount[bufid]--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* InitLocalBuffer -
|
|
* init the local buffer cache. Since most queries (esp. multi-user ones)
|
|
* don't involve local buffers, we delay allocating actual memory for the
|
|
* buffers until we need them; just make the buffer headers here.
|
|
*/
|
|
void
|
|
InitLocalBuffer(void)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* these aren't going away. I'm not gonna use palloc.
|
|
*/
|
|
LocalBufferDescriptors =
|
|
(BufferDesc *) calloc(NLocBuffer, sizeof(BufferDesc));
|
|
LocalBufferBlockPointers = (Block *) calloc(NLocBuffer, sizeof(Block));
|
|
LocalRefCount = (long *) calloc(NLocBuffer, sizeof(long));
|
|
nextFreeLocalBuf = 0;
|
|
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
BufferDesc *buf = &LocalBufferDescriptors[i];
|
|
|
|
/*
|
|
* negative to indicate local buffer. This is tricky: shared
|
|
* buffers start with 0. We have to start with -2. (Note that the
|
|
* routine BufferDescriptorGetBuffer adds 1 to buf_id so our first
|
|
* buffer id is -1.)
|
|
*/
|
|
buf->buf_id = -i - 2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* AtEOXact_LocalBuffers - clean up at end of transaction.
|
|
*
|
|
* This is just like AtEOXact_Buffers, but for local buffers.
|
|
*/
|
|
void
|
|
AtEOXact_LocalBuffers(bool isCommit)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < NLocBuffer; i++)
|
|
{
|
|
if (LocalRefCount[i] != 0)
|
|
{
|
|
BufferDesc *buf = &(LocalBufferDescriptors[i]);
|
|
|
|
if (isCommit)
|
|
elog(WARNING,
|
|
"Local Buffer Leak: [%03d] (rel=%u/%u, blockNum=%u, flags=0x%x, refcount=%d %ld)",
|
|
i,
|
|
buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
|
|
buf->tag.blockNum, buf->flags,
|
|
buf->refcount, LocalRefCount[i]);
|
|
|
|
LocalRefCount[i] = 0;
|
|
}
|
|
}
|
|
}
|