*** a/contrib/pg_buffercache/pg_buffercache_pages.c --- b/contrib/pg_buffercache/pg_buffercache_pages.c *************** *** 100,109 **** pg_buffercache_pages(PG_FUNCTION_ARGS) fctx->tupdesc = BlessTupleDesc(tupledesc); /* Allocate NBuffers worth of BufferCachePagesRec records. */ ! fctx->record = (BufferCachePagesRec *) palloc(sizeof(BufferCachePagesRec) * NBuffers); /* Set max calls and remember the user function context. */ ! funcctx->max_calls = NBuffers; funcctx->user_fctx = fctx; /* Return to original context when allocating transient memory */ --- 100,109 ---- fctx->tupdesc = BlessTupleDesc(tupledesc); /* Allocate NBuffers worth of BufferCachePagesRec records. */ ! fctx->record = (BufferCachePagesRec *) palloc(sizeof(BufferCachePagesRec) * NSharedBuffers); /* Set max calls and remember the user function context. */ ! funcctx->max_calls = NSharedBuffers; funcctx->user_fctx = fctx; /* Return to original context when allocating transient memory */ *************** *** 122,128 **** pg_buffercache_pages(PG_FUNCTION_ARGS) * Scan though all the buffers, saving the relevant fields in the * fctx->record structure. */ ! for (i = 0, bufHdr = BufferDescriptors; i < NBuffers; i++, bufHdr++) { /* Lock each buffer header before inspecting. */ LockBufHdr(bufHdr); --- 122,128 ---- * Scan though all the buffers, saving the relevant fields in the * fctx->record structure. */ ! for (i = 0, bufHdr = BufferDescriptors; i < NSharedBuffers; i++, bufHdr++) { /* Lock each buffer header before inspecting. */ LockBufHdr(bufHdr); *** a/src/backend/access/common/reloptions.c --- b/src/backend/access/common/reloptions.c *************** *** 33,38 **** --- 33,40 ---- #include "utils/memutils.h" #include "utils/rel.h" + static void validateBufferPoolOption(char *value); + /* * Contents of pg_class.reloptions * *************** *** 292,297 **** static relopt_string stringRelOpts[] = --- 294,310 ---- validateWithCheckOption, NULL }, + { + { + "buffer_pool", + "Table with buffer_pool option defined (default or priority).", + RELOPT_KIND_HEAP | RELOPT_KIND_BTREE + }, + 7, + false, + validateBufferPoolOption, + "default" + }, /* list terminator */ {{NULL}} }; *************** *** 1174,1179 **** default_reloptions(Datum reloptions, bool validate, relopt_kind kind) --- 1187,1194 ---- int numoptions; static const relopt_parse_elt tab[] = { {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}, + {"buffer_pool", RELOPT_TYPE_STRING, + offsetof(StdRdOptions, bufferpool_offset)}, {"autovacuum_enabled", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, enabled)}, {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT, *************** *** 1205,1211 **** default_reloptions(Datum reloptions, bool validate, relopt_kind kind) {"check_option", RELOPT_TYPE_STRING, offsetof(StdRdOptions, check_option_offset)}, {"user_catalog_table", RELOPT_TYPE_BOOL, ! offsetof(StdRdOptions, user_catalog_table)} }; options = parseRelOptions(reloptions, validate, kind, &numoptions); --- 1220,1226 ---- {"check_option", RELOPT_TYPE_STRING, offsetof(StdRdOptions, check_option_offset)}, {"user_catalog_table", RELOPT_TYPE_BOOL, ! offsetof(StdRdOptions, user_catalog_table)} }; options = parseRelOptions(reloptions, validate, kind, &numoptions); *************** *** 1356,1358 **** tablespace_reloptions(Datum reloptions, bool validate) --- 1371,1391 ---- return (bytea *) tsopts; } + + /* + * Validator for "bufferpool" reloption on Tables and views. The allowed values + * are "default" and "priority". + */ + static void + validateBufferPoolOption(char *value) + { + if (value == NULL || + (pg_strcasecmp(value, "default") != 0 && + pg_strcasecmp(value, "priority") != 0)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid value for \"bufferpool\" option"), + errdetail("Valid values are \"default\", and \"priority\"."))); + } + } *** a/src/backend/access/transam/clog.c --- b/src/backend/access/transam/clog.c *************** *** 437,443 **** TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn) Size CLOGShmemBuffers(void) { ! return Min(32, Max(4, NBuffers / 512)); } /* --- 437,443 ---- Size CLOGShmemBuffers(void) { ! return Min(32, Max(4, NSharedBuffers / 512)); } /* *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** *** 5048,5054 **** XLOGChooseNumBuffers(void) { int xbuffers; ! xbuffers = NBuffers / 32; if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ) xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ; if (xbuffers < 8) --- 5048,5054 ---- { int xbuffers; ! xbuffers = NSharedBuffers / 32; if (xbuffers > XLOG_SEG_SIZE / XLOG_BLCKSZ) xbuffers = XLOG_SEG_SIZE / XLOG_BLCKSZ; if (xbuffers < 8) *************** *** 8194,8200 **** LogCheckpointEnd(bool restartpoint) "write=%ld.%03d s, sync=%ld.%03d s, total=%ld.%03d s; " "sync files=%d, longest=%ld.%03d s, average=%ld.%03d s", CheckpointStats.ckpt_bufs_written, ! (double) CheckpointStats.ckpt_bufs_written * 100 / NBuffers, CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_recycled, --- 8194,8200 ---- "write=%ld.%03d s, sync=%ld.%03d s, total=%ld.%03d s; " "sync files=%d, longest=%ld.%03d s, average=%ld.%03d s", CheckpointStats.ckpt_bufs_written, ! (double) CheckpointStats.ckpt_bufs_written * 100 / NSharedBuffers, CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_recycled, *************** *** 8210,8216 **** LogCheckpointEnd(bool restartpoint) "write=%ld.%03d s, sync=%ld.%03d s, total=%ld.%03d s; " "sync files=%d, longest=%ld.%03d s, average=%ld.%03d s", CheckpointStats.ckpt_bufs_written, ! (double) CheckpointStats.ckpt_bufs_written * 100 / NBuffers, CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_recycled, --- 8210,8216 ---- "write=%ld.%03d s, sync=%ld.%03d s, total=%ld.%03d s; " "sync files=%d, longest=%ld.%03d s, average=%ld.%03d s", CheckpointStats.ckpt_bufs_written, ! (double) CheckpointStats.ckpt_bufs_written * 100 / NSharedBuffers, CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_recycled, *************** *** 8651,8657 **** CreateCheckPoint(int flags) LogCheckpointEnd(false); TRACE_POSTGRESQL_CHECKPOINT_DONE(CheckpointStats.ckpt_bufs_written, ! NBuffers, CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_recycled); --- 8651,8657 ---- LogCheckpointEnd(false); TRACE_POSTGRESQL_CHECKPOINT_DONE(CheckpointStats.ckpt_bufs_written, ! NSharedBuffers, CheckpointStats.ckpt_segs_added, CheckpointStats.ckpt_segs_removed, CheckpointStats.ckpt_segs_recycled); *** a/src/backend/optimizer/path/costsize.c --- b/src/backend/optimizer/path/costsize.c *************** *** 4118,4125 **** check_effective_cache_size(int *newval, void **extra, GucSource source) /* * Otherwise, substitute the auto-tune value, being wary of overflow. */ ! if (NBuffers < INT_MAX / 4) ! *newval = NBuffers * 4; else *newval = INT_MAX; } --- 4118,4125 ---- /* * Otherwise, substitute the auto-tune value, being wary of overflow. */ ! if (NSharedBuffers < INT_MAX / 4) ! *newval = NSharedBuffers * 4; else *newval = INT_MAX; } *** a/src/backend/postmaster/checkpointer.c --- b/src/backend/postmaster/checkpointer.c *************** *** 903,912 **** CheckpointerShmemSize(void) /* * Currently, the size of the requests[] array is arbitrarily set equal to ! * NBuffers. This may prove too large or small ... */ size = offsetof(CheckpointerShmemStruct, requests); ! size = add_size(size, mul_size(NBuffers, sizeof(CheckpointerRequest))); return size; } --- 903,912 ---- /* * Currently, the size of the requests[] array is arbitrarily set equal to ! * NSharedBuffers. This may prove too large or small ... */ size = offsetof(CheckpointerShmemStruct, requests); ! size = add_size(size, mul_size(NSharedBuffers, sizeof(CheckpointerRequest))); return size; } *************** *** 935,941 **** CheckpointerShmemInit(void) */ MemSet(CheckpointerShmem, 0, size); SpinLockInit(&CheckpointerShmem->ckpt_lck); ! CheckpointerShmem->max_requests = NBuffers; } } --- 935,941 ---- */ MemSet(CheckpointerShmem, 0, size); SpinLockInit(&CheckpointerShmem->ckpt_lck); ! CheckpointerShmem->max_requests = NSharedBuffers; } } *** a/src/backend/storage/buffer/buf_init.c --- b/src/backend/storage/buffer/buf_init.c *************** *** 17,25 **** #include "storage/bufmgr.h" #include "storage/buf_internals.h" - BufferDesc *BufferDescriptors; ! char *BufferBlocks; int32 *PrivateRefCount; --- 17,24 ---- #include "storage/bufmgr.h" #include "storage/buf_internals.h" BufferDesc *BufferDescriptors; ! char *BufferBlocks; int32 *PrivateRefCount; *************** *** 77,87 **** InitBufferPool(void) BufferDescriptors = (BufferDesc *) ShmemInitStruct("Buffer Descriptors", ! NBuffers * sizeof(BufferDesc), &foundDescs); BufferBlocks = (char *) ShmemInitStruct("Buffer Blocks", ! NBuffers * (Size) BLCKSZ, &foundBufs); if (foundDescs || foundBufs) { --- 76,86 ---- BufferDescriptors = (BufferDesc *) ShmemInitStruct("Buffer Descriptors", ! NSharedBuffers * sizeof(BufferDesc), &foundDescs); BufferBlocks = (char *) ShmemInitStruct("Buffer Blocks", ! NSharedBuffers * (Size) BLCKSZ, &foundBufs); if (foundDescs || foundBufs) { *************** *** 98,119 **** InitBufferPool(void) /* * Initialize all the buffer headers. ! */ ! for (i = 0; i < NBuffers; buf++, i++) { CLEAR_BUFFERTAG(buf->tag); buf->flags = 0; buf->usage_count = 0; buf->refcount = 0; buf->wait_backend_pid = 0; ! SpinLockInit(&buf->buf_hdr_lock); buf->buf_id = i; /* * Initially link all the buffers together as unused. Subsequent ! * management of this list is done by freelist.c. */ buf->freeNext = i + 1; --- 97,118 ---- /* * Initialize all the buffer headers. ! */ ! for (i = 0; i < NSharedBuffers; buf++, i++) { CLEAR_BUFFERTAG(buf->tag); buf->flags = 0; buf->usage_count = 0; buf->refcount = 0; buf->wait_backend_pid = 0; ! SpinLockInit(&buf->buf_hdr_lock); buf->buf_id = i; /* * Initially link all the buffers together as unused. Subsequent ! * management of this list is done by freelist.c. */ buf->freeNext = i + 1; *************** *** 122,134 **** InitBufferPool(void) } /* Correct last entry of linked list */ ! BufferDescriptors[NBuffers - 1].freeNext = FREENEXT_END_OF_LIST; } /* Init other shared buffer-management stuff */ StrategyInitialize(!foundDescs); } /* * Initialize access to shared buffer pool * --- 121,134 ---- } /* Correct last entry of linked list */ ! BufferDescriptors[NSharedBuffers - 1].freeNext = FREENEXT_END_OF_LIST; } /* Init other shared buffer-management stuff */ StrategyInitialize(!foundDescs); } + /* * Initialize access to shared buffer pool * *************** *** 147,153 **** InitBufferPoolAccess(void) /* * Allocate and zero local arrays of per-buffer info. */ ! PrivateRefCount = (int32 *) calloc(NBuffers, sizeof(int32)); if (!PrivateRefCount) ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), --- 147,153 ---- /* * Allocate and zero local arrays of per-buffer info. */ ! PrivateRefCount = (int32 *) calloc(NSharedBuffers, sizeof(int32)); if (!PrivateRefCount) ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), *************** *** 164,178 **** Size BufferShmemSize(void) { Size size = 0; /* size of buffer descriptors */ ! size = add_size(size, mul_size(NBuffers, sizeof(BufferDesc))); /* size of data pages */ ! size = add_size(size, mul_size(NBuffers, BLCKSZ)); /* size of stuff controlled by freelist.c */ size = add_size(size, StrategyShmemSize()); ! return size; } --- 164,180 ---- BufferShmemSize(void) { Size size = 0; + + NSharedBuffers = add_size(NBuffers, NPriorityBuffers); /* size of buffer descriptors */ ! size = add_size(size, mul_size(NSharedBuffers, sizeof(BufferDesc))); /* size of data pages */ ! size = add_size(size, mul_size(NSharedBuffers, BLCKSZ)); /* size of stuff controlled by freelist.c */ size = add_size(size, StrategyShmemSize()); ! return size; } *** a/src/backend/storage/buffer/buf_table.c --- b/src/backend/storage/buffer/buf_table.c *************** *** 34,40 **** typedef struct static HTAB *SharedBufHash; - /* * Estimate space needed for mapping hashtable * size is the desired hash table size (possibly more than NBuffers) --- 34,39 ---- *** a/src/backend/storage/buffer/bufmgr.c --- b/src/backend/storage/buffer/bufmgr.c *************** *** 87,93 **** static bool IsForInput; static volatile BufferDesc *PinCountWaitBuf = NULL; ! static Buffer ReadBuffer_common(SMgrRelation reln, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy, bool *hit); --- 87,93 ---- static volatile BufferDesc *PinCountWaitBuf = NULL; ! static Buffer ReadBuffer_common(int poolid, SMgrRelation reln, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy, bool *hit); *************** *** 102,108 **** static void TerminateBufferIO(volatile BufferDesc *buf, bool clear_dirty, int set_flag_bits); static void shared_buffer_write_error_callback(void *arg); static void local_buffer_write_error_callback(void *arg); ! static volatile BufferDesc *BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, --- 102,108 ---- int set_flag_bits); static void shared_buffer_write_error_callback(void *arg); static void local_buffer_write_error_callback(void *arg); ! static volatile BufferDesc *BufferAlloc(int poolid, SMgrRelation smgr, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, *************** *** 232,237 **** ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, --- 232,238 ---- ReadBufferMode mode, BufferAccessStrategy strategy) { bool hit; + int poolid; Buffer buf; /* Open it at the smgr level if not already done */ *************** *** 252,258 **** ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, * miss. */ pgstat_count_buffer_read(reln); ! buf = ReadBuffer_common(reln->rd_smgr, reln->rd_rel->relpersistence, forkNum, blockNum, mode, strategy, &hit); if (hit) pgstat_count_buffer_hit(reln); --- 253,261 ---- * miss. */ pgstat_count_buffer_read(reln); ! poolid = RelationIsInPriorityBufferPool(reln) ? PRIORITY_BUFFER_POOL : DEFAULT_BUFFER_POOL; ! ! buf = ReadBuffer_common(poolid, reln->rd_smgr, reln->rd_rel->relpersistence, forkNum, blockNum, mode, strategy, &hit); if (hit) pgstat_count_buffer_hit(reln); *************** *** 279,286 **** ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum, SMgrRelation smgr = smgropen(rnode, InvalidBackendId); Assert(InRecovery); ! ! return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum, mode, strategy, &hit); } --- 282,288 ---- SMgrRelation smgr = smgropen(rnode, InvalidBackendId); Assert(InRecovery); ! return ReadBuffer_common(DEFAULT_BUFFER_POOL, smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum, mode, strategy, &hit); } *************** *** 291,297 **** ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum, * *hit is set to true if the request was satisfied from shared buffer cache. */ static Buffer ! ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy, bool *hit) { --- 293,299 ---- * *hit is set to true if the request was satisfied from shared buffer cache. */ static Buffer ! ReadBuffer_common(int poolid, SMgrRelation smgr, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy, bool *hit) { *************** *** 333,339 **** ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, * lookup the buffer. IO_IN_PROGRESS is set if the requested block is * not currently in memory. */ ! bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum, strategy, &found); if (found) pgBufferUsage.shared_blks_hit++; --- 335,341 ---- * lookup the buffer. IO_IN_PROGRESS is set if the requested block is * not currently in memory. */ ! bufHdr = BufferAlloc(poolid, smgr, relpersistence, forkNum, blockNum, strategy, &found); if (found) pgBufferUsage.shared_blks_hit++; *************** *** 532,538 **** ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, * No locks are held either at entry or exit. */ static volatile BufferDesc * ! BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, BufferAccessStrategy strategy, bool *foundPtr) --- 534,540 ---- * No locks are held either at entry or exit. */ static volatile BufferDesc * ! BufferAlloc(int poolid, SMgrRelation smgr, char relpersistence, ForkNumber forkNum, BlockNumber blockNum, BufferAccessStrategy strategy, bool *foundPtr) *************** *** 613,619 **** BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, * still held, since it would be bad to hold the spinlock while * possibly waking up other processes. */ ! buf = StrategyGetBuffer(strategy, &lock_held); Assert(buf->refcount == 0); --- 615,621 ---- * still held, since it would be bad to hold the spinlock while * possibly waking up other processes. */ ! buf = StrategyGetBuffer(poolid, strategy, &lock_held); Assert(buf->refcount == 0); *************** *** 625,631 **** BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, /* Now it's safe to release the freelist lock */ if (lock_held) ! LWLockRelease(BufFreelistLock); /* * If the buffer was dirty, try to write it out. There is a race --- 627,633 ---- /* Now it's safe to release the freelist lock */ if (lock_held) ! LWLockRelease(BufFreelistLock(poolid)); /* * If the buffer was dirty, try to write it out. There is a race *************** *** 1246,1252 **** BufferSync(int flags) * certainly need to be written for the next checkpoint attempt, too. */ num_to_write = 0; ! for (buf_id = 0; buf_id < NBuffers; buf_id++) { volatile BufferDesc *bufHdr = &BufferDescriptors[buf_id]; --- 1248,1254 ---- * certainly need to be written for the next checkpoint attempt, too. */ num_to_write = 0; ! for (buf_id = 0; buf_id < NSharedBuffers; buf_id++) { volatile BufferDesc *bufHdr = &BufferDescriptors[buf_id]; *************** *** 1268,1274 **** BufferSync(int flags) if (num_to_write == 0) return; /* nothing to do */ ! TRACE_POSTGRESQL_BUFFER_SYNC_START(NBuffers, num_to_write); /* * Loop over all buffers again, and write the ones (still) marked with --- 1270,1276 ---- if (num_to_write == 0) return; /* nothing to do */ ! TRACE_POSTGRESQL_BUFFER_SYNC_START(NSharedBuffers, num_to_write); /* * Loop over all buffers again, and write the ones (still) marked with *************** *** 1278,1285 **** BufferSync(int flags) * Note that we don't read the buffer alloc count here --- that should be * left untouched till the next BgBufferSync() call. */ ! buf_id = StrategySyncStart(NULL, NULL); ! num_to_scan = NBuffers; num_written = 0; while (num_to_scan-- > 0) { --- 1280,1287 ---- * Note that we don't read the buffer alloc count here --- that should be * left untouched till the next BgBufferSync() call. */ ! buf_id = StrategySyncStart(0, NULL, NULL); ! num_to_scan = NSharedBuffers; num_written = 0; while (num_to_scan-- > 0) { *************** *** 1326,1342 **** BufferSync(int flags) } } ! if (++buf_id >= NBuffers) buf_id = 0; } - /* * Update checkpoint statistics. As noted above, this doesn't include * buffers written by other backends or bgwriter scan. */ CheckpointStats.ckpt_bufs_written += num_written; ! TRACE_POSTGRESQL_BUFFER_SYNC_DONE(NBuffers, num_written, num_to_write); } /* --- 1328,1343 ---- } } ! if (++buf_id >= NSharedBuffers) buf_id = 0; } /* * Update checkpoint statistics. As noted above, this doesn't include * buffers written by other backends or bgwriter scan. */ CheckpointStats.ckpt_bufs_written += num_written; ! TRACE_POSTGRESQL_BUFFER_SYNC_DONE(BufferPool[poolid].num_buffers, num_written, num_to_write); } /* *************** *** 1389,1635 **** BgBufferSync(void) int num_to_scan; int num_written; int reusable_buffers; /* Variables for final smoothed_density update */ long new_strategy_delta; uint32 new_recent_alloc; ! /* ! * Find out where the freelist clock sweep currently is, and how many ! * buffer allocations have happened since our last call. ! */ ! strategy_buf_id = StrategySyncStart(&strategy_passes, &recent_alloc); ! ! /* Report buffer alloc counts to pgstat */ ! BgWriterStats.m_buf_alloc += recent_alloc; ! ! /* ! * If we're not running the LRU scan, just stop after doing the stats ! * stuff. We mark the saved state invalid so that we can recover sanely ! * if LRU scan is turned back on later. ! */ ! if (bgwriter_lru_maxpages <= 0) { ! saved_info_valid = false; ! return true; ! } ! ! /* ! * Compute strategy_delta = how many buffers have been scanned by the ! * clock sweep since last time. If first time through, assume none. Then ! * see if we are still ahead of the clock sweep, and if so, how many ! * buffers we could scan before we'd catch up with it and "lap" it. Note: ! * weird-looking coding of xxx_passes comparisons are to avoid bogus ! * behavior when the passes counts wrap around. ! */ ! if (saved_info_valid) ! { ! int32 passes_delta = strategy_passes - prev_strategy_passes; ! ! strategy_delta = strategy_buf_id - prev_strategy_buf_id; ! strategy_delta += (long) passes_delta *NBuffers; ! Assert(strategy_delta >= 0); ! if ((int32) (next_passes - strategy_passes) > 0) { ! /* we're one pass ahead of the strategy point */ ! bufs_to_lap = strategy_buf_id - next_to_clean; ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter ahead: bgw %u-%u strategy %u-%u delta=%ld lap=%d", ! next_passes, next_to_clean, ! strategy_passes, strategy_buf_id, ! strategy_delta, bufs_to_lap); ! #endif } ! else if (next_passes == strategy_passes && ! next_to_clean >= strategy_buf_id) { ! /* on same pass, but ahead or at least not behind */ ! bufs_to_lap = NBuffers - (next_to_clean - strategy_buf_id); ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter ahead: bgw %u-%u strategy %u-%u delta=%ld lap=%d", ! next_passes, next_to_clean, ! strategy_passes, strategy_buf_id, ! strategy_delta, bufs_to_lap); ! #endif } else { /* ! * We're behind, so skip forward to the strategy point and start ! * cleaning from there. */ ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter behind: bgw %u-%u strategy %u-%u delta=%ld", ! next_passes, next_to_clean, ! strategy_passes, strategy_buf_id, ! strategy_delta); ! #endif next_to_clean = strategy_buf_id; next_passes = strategy_passes; ! bufs_to_lap = NBuffers; } - } - else - { - /* - * Initializing at startup or after LRU scanning had been off. Always - * start at the strategy point. - */ - #ifdef BGW_DEBUG - elog(DEBUG2, "bgwriter initializing: strategy %u-%u", - strategy_passes, strategy_buf_id); - #endif - strategy_delta = 0; - next_to_clean = strategy_buf_id; - next_passes = strategy_passes; - bufs_to_lap = NBuffers; - } ! /* Update saved info for next time */ ! prev_strategy_buf_id = strategy_buf_id; ! prev_strategy_passes = strategy_passes; ! saved_info_valid = true; ! /* ! * Compute how many buffers had to be scanned for each new allocation, ie, ! * 1/density of reusable buffers, and track a moving average of that. ! * ! * If the strategy point didn't move, we don't update the density estimate ! */ ! if (strategy_delta > 0 && recent_alloc > 0) ! { ! scans_per_alloc = (float) strategy_delta / (float) recent_alloc; ! smoothed_density += (scans_per_alloc - smoothed_density) / ! smoothing_samples; ! } ! ! /* ! * Estimate how many reusable buffers there are between the current ! * strategy point and where we've scanned ahead to, based on the smoothed ! * density estimate. ! */ ! bufs_ahead = NBuffers - bufs_to_lap; ! reusable_buffers_est = (float) bufs_ahead / smoothed_density; ! /* ! * Track a moving average of recent buffer allocations. Here, rather than ! * a true average we want a fast-attack, slow-decline behavior: we ! * immediately follow any increase. ! */ ! if (smoothed_alloc <= (float) recent_alloc) ! smoothed_alloc = recent_alloc; ! else ! smoothed_alloc += ((float) recent_alloc - smoothed_alloc) / ! smoothing_samples; ! /* Scale the estimate by a GUC to allow more aggressive tuning. */ ! upcoming_alloc_est = (int) (smoothed_alloc * bgwriter_lru_multiplier); ! /* ! * If recent_alloc remains at zero for many cycles, smoothed_alloc will ! * eventually underflow to zero, and the underflows produce annoying ! * kernel warnings on some platforms. Once upcoming_alloc_est has gone to ! * zero, there's no point in tracking smaller and smaller values of ! * smoothed_alloc, so just reset it to exactly zero to avoid this ! * syndrome. It will pop back up as soon as recent_alloc increases. ! */ ! if (upcoming_alloc_est == 0) ! smoothed_alloc = 0; ! /* ! * Even in cases where there's been little or no buffer allocation ! * activity, we want to make a small amount of progress through the buffer ! * cache so that as many reusable buffers as possible are clean after an ! * idle period. ! * ! * (scan_whole_pool_milliseconds / BgWriterDelay) computes how many times ! * the BGW will be called during the scan_whole_pool time; slice the ! * buffer pool into that many sections. ! */ ! min_scan_buffers = (int) (NBuffers / (scan_whole_pool_milliseconds / BgWriterDelay)); ! if (upcoming_alloc_est < (min_scan_buffers + reusable_buffers_est)) ! { ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter: alloc_est=%d too small, using min=%d + reusable_est=%d", ! upcoming_alloc_est, min_scan_buffers, reusable_buffers_est); ! #endif ! upcoming_alloc_est = min_scan_buffers + reusable_buffers_est; ! } ! /* ! * Now write out dirty reusable buffers, working forward from the ! * next_to_clean point, until we have lapped the strategy scan, or cleaned ! * enough buffers to match our estimate of the next cycle's allocation ! * requirements, or hit the bgwriter_lru_maxpages limit. ! */ ! /* Make sure we can handle the pin inside SyncOneBuffer */ ! ResourceOwnerEnlargeBuffers(CurrentResourceOwner); ! num_to_scan = bufs_to_lap; ! num_written = 0; ! reusable_buffers = reusable_buffers_est; ! /* Execute the LRU scan */ ! while (num_to_scan > 0 && reusable_buffers < upcoming_alloc_est) ! { ! int buffer_state = SyncOneBuffer(next_to_clean, true); ! if (++next_to_clean >= NBuffers) { ! next_to_clean = 0; ! next_passes++; ! } ! num_to_scan--; ! if (buffer_state & BUF_WRITTEN) ! { ! reusable_buffers++; ! if (++num_written >= bgwriter_lru_maxpages) { ! BgWriterStats.m_maxwritten_clean++; ! break; } } - else if (buffer_state & BUF_REUSABLE) - reusable_buffers++; - } ! BgWriterStats.m_buf_written_clean += num_written; ! #ifdef BGW_DEBUG ! elog(DEBUG1, "bgwriter: recent_alloc=%u smoothed=%.2f delta=%ld ahead=%d density=%.2f reusable_est=%d upcoming_est=%d scanned=%d wrote=%d reusable=%d", ! recent_alloc, smoothed_alloc, strategy_delta, bufs_ahead, ! smoothed_density, reusable_buffers_est, upcoming_alloc_est, ! bufs_to_lap - num_to_scan, ! num_written, ! reusable_buffers - reusable_buffers_est); ! #endif ! /* ! * Consider the above scan as being like a new allocation scan. ! * Characterize its density and update the smoothed one based on it. This ! * effectively halves the moving average period in cases where both the ! * strategy and the background writer are doing some useful scanning, ! * which is helpful because a long memory isn't as desirable on the ! * density estimates. ! */ ! new_strategy_delta = bufs_to_lap - num_to_scan; ! new_recent_alloc = reusable_buffers - reusable_buffers_est; ! if (new_strategy_delta > 0 && new_recent_alloc > 0) ! { ! scans_per_alloc = (float) new_strategy_delta / (float) new_recent_alloc; ! smoothed_density += (scans_per_alloc - smoothed_density) / ! smoothing_samples; ! ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter: cleaner density alloc=%u scan=%ld density=%.2f new smoothed=%.2f", ! new_recent_alloc, new_strategy_delta, ! scans_per_alloc, smoothed_density); ! #endif } /* Return true if OK to hibernate */ --- 1390,1640 ---- int num_to_scan; int num_written; int reusable_buffers; + int poolid; /* Variables for final smoothed_density update */ long new_strategy_delta; uint32 new_recent_alloc; ! for (poolid = 0; poolid < NUM_MAX_BUFFER_POOLS; poolid++) { ! /* ! * Find out where the freelist clock sweep currently is, and how many ! * buffer allocations have happened since our last call. ! */ ! strategy_buf_id = StrategySyncStart(poolid, &strategy_passes, &recent_alloc); ! /* Report buffer alloc counts to pgstat */ ! BgWriterStats.m_buf_alloc += recent_alloc; ! /* ! * If we're not running the LRU scan, just stop after doing the stats ! * stuff. We mark the saved state invalid so that we can recover sanely ! * if LRU scan is turned back on later. ! */ ! if (bgwriter_lru_maxpages <= 0) { ! saved_info_valid = false; ! return true; } ! ! /* ! * Compute strategy_delta = how many buffers have been scanned by the ! * clock sweep since last time. If first time through, assume none. Then ! * see if we are still ahead of the clock sweep, and if so, how many ! * buffers we could scan before we'd catch up with it and "lap" it. Note: ! * weird-looking coding of xxx_passes comparisons are to avoid bogus ! * behavior when the passes counts wrap around. ! */ ! if (saved_info_valid) { ! int32 passes_delta = strategy_passes - prev_strategy_passes; ! ! strategy_delta = strategy_buf_id - prev_strategy_buf_id; ! strategy_delta += (long) passes_delta * ((poolid == 0) ? NBuffers : NSharedBuffers); ! ! Assert(strategy_delta >= 0); ! ! if ((int32) (next_passes - strategy_passes) > 0) ! { ! /* we're one pass ahead of the strategy point */ ! bufs_to_lap = strategy_buf_id - next_to_clean; ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter ahead: bgw %u-%u strategy %u-%u delta=%ld lap=%d", ! next_passes, next_to_clean, ! strategy_passes, strategy_buf_id, ! strategy_delta, bufs_to_lap); ! #endif ! } ! else if (next_passes == strategy_passes && ! next_to_clean >= strategy_buf_id) ! { ! /* on same pass, but ahead or at least not behind */ ! bufs_to_lap = ((poolid == 0) ? NBuffers : NSharedBuffers) - (next_to_clean - strategy_buf_id); ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter ahead: bgw %u-%u strategy %u-%u delta=%ld lap=%d", ! next_passes, next_to_clean, ! strategy_passes, strategy_buf_id, ! strategy_delta, bufs_to_lap); ! #endif ! } ! else ! { ! /* ! * We're behind, so skip forward to the strategy point and start ! * cleaning from there. ! */ ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter behind: bgw %u-%u strategy %u-%u delta=%ld", ! next_passes, next_to_clean, ! strategy_passes, strategy_buf_id, ! strategy_delta); ! #endif ! next_to_clean = strategy_buf_id; ! next_passes = strategy_passes; ! bufs_to_lap = ((poolid == 0) ? NBuffers : NSharedBuffers); ! } } else { /* ! * Initializing at startup or after LRU scanning had been off. Always ! * start at the strategy point. */ ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter initializing: strategy %u-%u", ! strategy_passes, strategy_buf_id); ! #endif ! strategy_delta = 0; next_to_clean = strategy_buf_id; next_passes = strategy_passes; ! bufs_to_lap = ((poolid == 0) ? NBuffers : NSharedBuffers); } ! /* Update saved info for next time */ ! prev_strategy_buf_id = strategy_buf_id; ! prev_strategy_passes = strategy_passes; ! saved_info_valid = true; ! /* ! * Compute how many buffers had to be scanned for each new allocation, ie, ! * 1/density of reusable buffers, and track a moving average of that. ! * ! * If the strategy point didn't move, we don't update the density estimate ! */ ! if (strategy_delta > 0 && recent_alloc > 0) ! { ! scans_per_alloc = (float) strategy_delta / (float) recent_alloc; ! smoothed_density += (scans_per_alloc - smoothed_density) / ! smoothing_samples; ! } ! /* ! * Estimate how many reusable buffers there are between the current ! * strategy point and where we've scanned ahead to, based on the smoothed ! * density estimate. ! */ ! bufs_ahead = ((poolid == 0) ? NBuffers : NSharedBuffers) - bufs_to_lap; ! reusable_buffers_est = (float) bufs_ahead / smoothed_density; ! /* ! * Track a moving average of recent buffer allocations. Here, rather than ! * a true average we want a fast-attack, slow-decline behavior: we ! * immediately follow any increase. ! */ ! if (smoothed_alloc <= (float) recent_alloc) ! smoothed_alloc = recent_alloc; ! else ! smoothed_alloc += ((float) recent_alloc - smoothed_alloc) / ! smoothing_samples; ! /* Scale the estimate by a GUC to allow more aggressive tuning. */ ! upcoming_alloc_est = (int) (smoothed_alloc * bgwriter_lru_multiplier); ! /* ! * If recent_alloc remains at zero for many cycles, smoothed_alloc will ! * eventually underflow to zero, and the underflows produce annoying ! * kernel warnings on some platforms. Once upcoming_alloc_est has gone to ! * zero, there's no point in tracking smaller and smaller values of ! * smoothed_alloc, so just reset it to exactly zero to avoid this ! * syndrome. It will pop back up as soon as recent_alloc increases. ! */ ! if (upcoming_alloc_est == 0) ! smoothed_alloc = 0; ! /* ! * Even in cases where there's been little or no buffer allocation ! * activity, we want to make a small amount of progress through the buffer ! * cache so that as many reusable buffers as possible are clean after an ! * idle period. ! * ! * (scan_whole_pool_milliseconds / BgWriterDelay) computes how many times ! * the BGW will be called during the scan_whole_pool time; slice the ! * buffer pool into that many sections. ! */ ! min_scan_buffers = (int) (((poolid == 0) ? NBuffers : NPriorityBuffers) / (scan_whole_pool_milliseconds / BgWriterDelay)); ! if (upcoming_alloc_est < (min_scan_buffers + reusable_buffers_est)) ! { ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter: alloc_est=%d too small, using min=%d + reusable_est=%d", ! upcoming_alloc_est, min_scan_buffers, reusable_buffers_est); ! #endif ! upcoming_alloc_est = min_scan_buffers + reusable_buffers_est; ! } ! /* ! * Now write out dirty reusable buffers, working forward from the ! * next_to_clean point, until we have lapped the strategy scan, or cleaned ! * enough buffers to match our estimate of the next cycle's allocation ! * requirements, or hit the bgwriter_lru_maxpages limit. ! */ ! /* Make sure we can handle the pin inside SyncOneBuffer */ ! ResourceOwnerEnlargeBuffers(CurrentResourceOwner); ! num_to_scan = bufs_to_lap; ! num_written = 0; ! reusable_buffers = reusable_buffers_est; ! /* Execute the LRU scan */ ! while (num_to_scan > 0 && reusable_buffers < upcoming_alloc_est) { ! int buffer_state = SyncOneBuffer(next_to_clean, true); ! if (++next_to_clean >= ((poolid == 0) ? NBuffers : NSharedBuffers)) ! { ! next_to_clean = (poolid == 0) ? 0 : NBuffers; ! next_passes++; ! } ! num_to_scan--; ! ! if (buffer_state & BUF_WRITTEN) { ! reusable_buffers++; ! if (++num_written >= bgwriter_lru_maxpages) ! { ! BgWriterStats.m_maxwritten_clean++; ! break; ! } } + else if (buffer_state & BUF_REUSABLE) + reusable_buffers++; } ! BgWriterStats.m_buf_written_clean += num_written; ! #ifdef BGW_DEBUG ! elog(DEBUG1, "bgwriter: recent_alloc=%u smoothed=%.2f delta=%ld ahead=%d density=%.2f reusable_est=%d upcoming_est=%d scanned=%d wrote=%d reusable=%d", ! recent_alloc, smoothed_alloc, strategy_delta, bufs_ahead, ! smoothed_density, reusable_buffers_est, upcoming_alloc_est, ! bufs_to_lap - num_to_scan, ! num_written, ! reusable_buffers - reusable_buffers_est); ! #endif ! /* ! * Consider the above scan as being like a new allocation scan. ! * Characterize its density and update the smoothed one based on it. This ! * effectively halves the moving average period in cases where both the ! * strategy and the background writer are doing some useful scanning, ! * which is helpful because a long memory isn't as desirable on the ! * density estimates. ! */ ! new_strategy_delta = bufs_to_lap - num_to_scan; ! new_recent_alloc = reusable_buffers - reusable_buffers_est; ! if (new_strategy_delta > 0 && new_recent_alloc > 0) ! { ! scans_per_alloc = (float) new_strategy_delta / (float) new_recent_alloc; ! smoothed_density += (scans_per_alloc - smoothed_density) / ! smoothing_samples; ! ! #ifdef BGW_DEBUG ! elog(DEBUG2, "bgwriter: cleaner density alloc=%u scan=%ld density=%.2f new smoothed=%.2f", ! new_recent_alloc, new_strategy_delta, ! scans_per_alloc, smoothed_density); ! #endif ! } } /* Return true if OK to hibernate */ *************** *** 1717,1723 **** AtEOXact_Buffers(bool isCommit) int RefCountErrors = 0; Buffer b; ! for (b = 1; b <= NBuffers; b++) { if (PrivateRefCount[b - 1] != 0) { --- 1722,1728 ---- int RefCountErrors = 0; Buffer b; ! for (b = 1; b <= NSharedBuffers; b++) { if (PrivateRefCount[b - 1] != 0) { *************** *** 1763,1769 **** AtProcExit_Buffers(int code, Datum arg) int RefCountErrors = 0; Buffer b; ! for (b = 1; b <= NBuffers; b++) { if (PrivateRefCount[b - 1] != 0) { --- 1768,1774 ---- int RefCountErrors = 0; Buffer b; ! for (b = 1; b <= NSharedBuffers; b++) { if (PrivateRefCount[b - 1] != 0) { *************** *** 2144,2150 **** DropRelFileNodeBuffers(RelFileNodeBackend rnode, ForkNumber forkNum, return; } ! for (i = 0; i < NBuffers; i++) { volatile BufferDesc *bufHdr = &BufferDescriptors[i]; --- 2149,2155 ---- return; } ! for (i = 0; i < NSharedBuffers; i++) { volatile BufferDesc *bufHdr = &BufferDescriptors[i]; *************** *** 2233,2239 **** DropRelFileNodesAllBuffers(RelFileNodeBackend *rnodes, int nnodes) if (use_bsearch) pg_qsort(nodes, n, sizeof(RelFileNode), rnode_comparator); ! for (i = 0; i < NBuffers; i++) { RelFileNode *rnode = NULL; volatile BufferDesc *bufHdr = &BufferDescriptors[i]; --- 2238,2244 ---- if (use_bsearch) pg_qsort(nodes, n, sizeof(RelFileNode), rnode_comparator); ! for (i = 0; i < NSharedBuffers; i++) { RelFileNode *rnode = NULL; volatile BufferDesc *bufHdr = &BufferDescriptors[i]; *************** *** 2298,2304 **** DropDatabaseBuffers(Oid dbid) * database isn't our own. */ ! for (i = 0; i < NBuffers; i++) { volatile BufferDesc *bufHdr = &BufferDescriptors[i]; --- 2303,2309 ---- * database isn't our own. */ ! for (i = 0; i < NSharedBuffers; i++) { volatile BufferDesc *bufHdr = &BufferDescriptors[i]; *************** *** 2331,2337 **** PrintBufferDescs(void) int i; volatile BufferDesc *buf = BufferDescriptors; ! for (i = 0; i < NBuffers; ++i, ++buf) { /* theoretically we should lock the bufhdr here */ elog(LOG, --- 2336,2342 ---- int i; volatile BufferDesc *buf = BufferDescriptors; ! for (i = 0; i < NSharedBuffers; ++i, ++buf) { /* theoretically we should lock the bufhdr here */ elog(LOG, *************** *** 2352,2358 **** PrintPinnedBufs(void) int i; volatile BufferDesc *buf = BufferDescriptors; ! for (i = 0; i < NBuffers; ++i, ++buf) { if (PrivateRefCount[i] > 0) { --- 2357,2363 ---- int i; volatile BufferDesc *buf = BufferDescriptors; ! for (i = 0; i < NSharedBuffers; ++i, ++buf) { if (PrivateRefCount[i] > 0) { *************** *** 2437,2443 **** FlushRelationBuffers(Relation rel) /* Make sure we can handle the pin inside the loop */ ResourceOwnerEnlargeBuffers(CurrentResourceOwner); ! for (i = 0; i < NBuffers; i++) { bufHdr = &BufferDescriptors[i]; --- 2442,2448 ---- /* Make sure we can handle the pin inside the loop */ ResourceOwnerEnlargeBuffers(CurrentResourceOwner); ! for (i = 0; i < NSharedBuffers; i++) { bufHdr = &BufferDescriptors[i]; *************** *** 2487,2493 **** FlushDatabaseBuffers(Oid dbid) /* Make sure we can handle the pin inside the loop */ ResourceOwnerEnlargeBuffers(CurrentResourceOwner); ! for (i = 0; i < NBuffers; i++) { bufHdr = &BufferDescriptors[i]; --- 2492,2498 ---- /* Make sure we can handle the pin inside the loop */ ResourceOwnerEnlargeBuffers(CurrentResourceOwner); ! for (i = 0; i < NSharedBuffers; i++) { bufHdr = &BufferDescriptors[i]; *** a/src/backend/storage/buffer/freelist.c --- b/src/backend/storage/buffer/freelist.c *************** *** 49,55 **** typedef struct } BufferStrategyControl; /* Pointers to shared state */ ! static BufferStrategyControl *StrategyControl = NULL; /* * Private (non-shared) state for managing a ring of shared buffers to re-use. --- 49,66 ---- } BufferStrategyControl; /* Pointers to shared state */ ! static BufferStrategyControl *StrategyControl[NUM_MAX_BUFFER_POOLS]; ! ! struct bufferAccessStrategyStatus ! { ! char name[NAMEDATALEN]; ! int num_buffers; ! }; ! ! struct bufferAccessStrategyStatus BufferStrategyStatus[NUM_MAX_BUFFER_POOLS] = { ! {"Default Buffer Strategy status", 0}, ! {"Priority Buffer Strategy status", 0} ! }; /* * Private (non-shared) state for managing a ring of shared buffers to re-use. *************** *** 109,115 **** static void AddBufferToRing(BufferAccessStrategy strategy, * kernel calls while holding the buffer header spinlock. */ volatile BufferDesc * ! StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) { volatile BufferDesc *buf; Latch *bgwriterLatch; --- 120,126 ---- * kernel calls while holding the buffer header spinlock. */ volatile BufferDesc * ! StrategyGetBuffer(int poolid, BufferAccessStrategy strategy, bool *lock_held) { volatile BufferDesc *buf; Latch *bgwriterLatch; *************** *** 131,144 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) /* Nope, so lock the freelist */ *lock_held = true; ! LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * We count buffer allocation requests so that the bgwriter can estimate * the rate of buffer consumption. Note that buffers recycled by a * strategy object are intentionally not counted here. */ ! StrategyControl->numBufferAllocs++; /* * If bgwriterLatch is set, we need to waken the bgwriter, but we should --- 142,155 ---- /* Nope, so lock the freelist */ *lock_held = true; ! LWLockAcquire(BufFreelistLock(poolid), LW_EXCLUSIVE); /* * We count buffer allocation requests so that the bgwriter can estimate * the rate of buffer consumption. Note that buffers recycled by a * strategy object are intentionally not counted here. */ ! StrategyControl[poolid]->numBufferAllocs++; /* * If bgwriterLatch is set, we need to waken the bgwriter, but we should *************** *** 146,158 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) * is annoyingly tedious, but it happens at most once per bgwriter cycle, * so the performance hit is minimal. */ ! bgwriterLatch = StrategyControl->bgwriterLatch; if (bgwriterLatch) { ! StrategyControl->bgwriterLatch = NULL; ! LWLockRelease(BufFreelistLock); SetLatch(bgwriterLatch); ! LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); } /* --- 157,169 ---- * is annoyingly tedious, but it happens at most once per bgwriter cycle, * so the performance hit is minimal. */ ! bgwriterLatch = StrategyControl[poolid]->bgwriterLatch; if (bgwriterLatch) { ! StrategyControl[poolid]->bgwriterLatch = NULL; ! LWLockRelease(BufFreelistLock(poolid)); SetLatch(bgwriterLatch); ! LWLockAcquire(BufFreelistLock(poolid), LW_EXCLUSIVE); } /* *************** *** 161,173 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) * individual buffer spinlocks, so it's OK to manipulate them without * holding the spinlock. */ ! while (StrategyControl->firstFreeBuffer >= 0) { ! buf = &BufferDescriptors[StrategyControl->firstFreeBuffer]; Assert(buf->freeNext != FREENEXT_NOT_IN_LIST); /* Unconditionally remove buffer from freelist */ ! StrategyControl->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; /* --- 172,184 ---- * individual buffer spinlocks, so it's OK to manipulate them without * holding the spinlock. */ ! while (StrategyControl[poolid]->firstFreeBuffer >= 0) { ! buf = &BufferDescriptors[StrategyControl[poolid]->firstFreeBuffer]; Assert(buf->freeNext != FREENEXT_NOT_IN_LIST); /* Unconditionally remove buffer from freelist */ ! StrategyControl[poolid]->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; /* *************** *** 188,202 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) } /* Nothing on the freelist, so run the "clock sweep" algorithm */ ! trycounter = NBuffers; for (;;) { ! buf = &BufferDescriptors[StrategyControl->nextVictimBuffer]; ! if (++StrategyControl->nextVictimBuffer >= NBuffers) { ! StrategyControl->nextVictimBuffer = 0; ! StrategyControl->completePasses++; } /* --- 199,213 ---- } /* Nothing on the freelist, so run the "clock sweep" algorithm */ ! trycounter = BufferStrategyStatus[poolid].num_buffers; for (;;) { ! buf = &BufferDescriptors[StrategyControl[poolid]->nextVictimBuffer]; ! if (++StrategyControl[poolid]->nextVictimBuffer >= ((poolid == 0) ? BufferStrategyStatus[poolid].num_buffers : NSharedBuffers)) { ! StrategyControl[poolid]->nextVictimBuffer = (poolid == 0) ? 0 : BufferStrategyStatus[poolid - 1].num_buffers; ! StrategyControl[poolid]->completePasses++; } /* *************** *** 209,215 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) if (buf->usage_count > 0) { buf->usage_count--; ! trycounter = NBuffers; } else { --- 220,226 ---- if (buf->usage_count > 0) { buf->usage_count--; ! trycounter = BufferStrategyStatus[poolid].num_buffers; } else { *************** *** 241,247 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) void StrategyFreeBuffer(volatile BufferDesc *buf) { ! LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * It is possible that we are told to put something in the freelist that --- 252,260 ---- void StrategyFreeBuffer(volatile BufferDesc *buf) { ! int poolid = buf->buf_id < NBuffers ? DEFAULT_BUFFER_POOL : PRIORITY_BUFFER_POOL; ! ! LWLockAcquire(BufFreelistLock(poolid), LW_EXCLUSIVE); /* * It is possible that we are told to put something in the freelist that *************** *** 249,261 **** StrategyFreeBuffer(volatile BufferDesc *buf) */ if (buf->freeNext == FREENEXT_NOT_IN_LIST) { ! buf->freeNext = StrategyControl->firstFreeBuffer; if (buf->freeNext < 0) ! StrategyControl->lastFreeBuffer = buf->buf_id; ! StrategyControl->firstFreeBuffer = buf->buf_id; } ! LWLockRelease(BufFreelistLock); } /* --- 262,274 ---- */ if (buf->freeNext == FREENEXT_NOT_IN_LIST) { ! buf->freeNext = StrategyControl[poolid]->firstFreeBuffer; if (buf->freeNext < 0) ! StrategyControl[poolid]->lastFreeBuffer = buf->buf_id; ! StrategyControl[poolid]->firstFreeBuffer = buf->buf_id; } ! LWLockRelease(BufFreelistLock(poolid)); } /* *************** *** 270,289 **** StrategyFreeBuffer(volatile BufferDesc *buf) * being read. */ int ! StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc) { int result; ! LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); ! result = StrategyControl->nextVictimBuffer; if (complete_passes) ! *complete_passes = StrategyControl->completePasses; if (num_buf_alloc) { ! *num_buf_alloc = StrategyControl->numBufferAllocs; ! StrategyControl->numBufferAllocs = 0; } ! LWLockRelease(BufFreelistLock); return result; } --- 283,302 ---- * being read. */ int ! StrategySyncStart(int poolid, uint32 *complete_passes, uint32 *num_buf_alloc) { int result; ! LWLockAcquire(BufFreelistLock(poolid), LW_EXCLUSIVE); ! result = StrategyControl[poolid]->nextVictimBuffer; if (complete_passes) ! *complete_passes = StrategyControl[poolid]->completePasses; if (num_buf_alloc) { ! *num_buf_alloc = StrategyControl[poolid]->numBufferAllocs; ! StrategyControl[poolid]->numBufferAllocs = 0; } ! LWLockRelease(BufFreelistLock(poolid)); return result; } *************** *** 298,311 **** StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc) void StrategyNotifyBgWriter(Latch *bgwriterLatch) { ! /* ! * We acquire the BufFreelistLock just to ensure that the store appears ! * atomic to StrategyGetBuffer. The bgwriter should call this rather ! * infrequently, so there's no performance penalty from being safe. ! */ ! LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); ! StrategyControl->bgwriterLatch = bgwriterLatch; ! LWLockRelease(BufFreelistLock); } --- 311,329 ---- void StrategyNotifyBgWriter(Latch *bgwriterLatch) { ! int poolid; ! ! for (poolid = 0; poolid < NUM_MAX_BUFFER_POOLS; poolid++) ! { ! /* ! * We acquire the BufFreelistLock just to ensure that the store appears ! * atomic to StrategyGetBuffer. The bgwriter should call this rather ! * infrequently, so there's no performance penalty from being safe. ! */ ! LWLockAcquire(BufFreelistLock(poolid), LW_EXCLUSIVE); ! StrategyControl[poolid]->bgwriterLatch = bgwriterLatch; ! LWLockRelease(BufFreelistLock(poolid)); ! } } *************** *** 318,329 **** StrategyNotifyBgWriter(Latch *bgwriterLatch) * is also determined here. */ Size ! StrategyShmemSize(void) { Size size = 0; /* size of lookup hash table ... see comment in StrategyInitialize */ ! size = add_size(size, BufTableShmemSize(NBuffers + NUM_BUFFER_PARTITIONS)); /* size of the shared replacement strategy control block */ size = add_size(size, MAXALIGN(sizeof(BufferStrategyControl))); --- 336,347 ---- * is also determined here. */ Size ! StrategyShmemSize() { Size size = 0; /* size of lookup hash table ... see comment in StrategyInitialize */ ! size = add_size(size, BufTableShmemSize(NSharedBuffers + NUM_BUFFER_PARTITIONS)); /* size of the shared replacement strategy control block */ size = add_size(size, MAXALIGN(sizeof(BufferStrategyControl))); *************** *** 342,394 **** void StrategyInitialize(bool init) { bool found; /* * Initialize the shared buffer lookup hashtable. * * Since we can't tolerate running out of lookup table entries, we must be * sure to specify an adequate table size here. The maximum steady-state ! * usage is of course NBuffers entries, but BufferAlloc() tries to insert * a new entry before deleting the old. In principle this could be * happening in each partition concurrently, so we could need as many as ! * NBuffers + NUM_BUFFER_PARTITIONS entries. */ ! InitBufTable(NBuffers + NUM_BUFFER_PARTITIONS); ! /* ! * Get or create the shared strategy control block ! */ ! StrategyControl = (BufferStrategyControl *) ! ShmemInitStruct("Buffer Strategy Status", ! sizeof(BufferStrategyControl), ! &found); ! if (!found) { /* ! * Only done once, usually in postmaster */ ! Assert(init); ! /* ! * Grab the whole linked list of free buffers for our strategy. We ! * assume it was previously set up by InitBufferPool(). ! */ ! StrategyControl->firstFreeBuffer = 0; ! StrategyControl->lastFreeBuffer = NBuffers - 1; ! /* Initialize the clock sweep pointer */ ! StrategyControl->nextVictimBuffer = 0; ! /* Clear statistics */ ! StrategyControl->completePasses = 0; ! StrategyControl->numBufferAllocs = 0; ! /* No pending notification */ ! StrategyControl->bgwriterLatch = NULL; } - else - Assert(!init); } --- 360,419 ---- StrategyInitialize(bool init) { bool found; + int poolid; /* * Initialize the shared buffer lookup hashtable. * * Since we can't tolerate running out of lookup table entries, we must be * sure to specify an adequate table size here. The maximum steady-state ! * usage is of course NSharedBuffers entries, but BufferAlloc() tries to insert * a new entry before deleting the old. In principle this could be * happening in each partition concurrently, so we could need as many as ! * NSharedBuffers + NUM_BUFFER_PARTITIONS entries. */ ! InitBufTable(NSharedBuffers + NUM_BUFFER_PARTITIONS); ! BufferStrategyStatus[DEFAULT_BUFFER_POOL].num_buffers = NBuffers; ! BufferStrategyStatus[PRIORITY_BUFFER_POOL].num_buffers = NPriorityBuffers; ! for (poolid = 0; poolid < NUM_MAX_BUFFER_POOLS; poolid++) { /* ! * Get or create the shared strategy control block */ ! StrategyControl[poolid] = (BufferStrategyControl *) ! ShmemInitStruct(BufferStrategyStatus[poolid].name, ! sizeof(BufferStrategyControl), ! &found); ! if (!found) ! { ! /* ! * Only done once, usually in postmaster ! */ ! Assert(init); ! /* ! * Grab the whole linked list of free buffers for our strategy. We ! * assume it was previously set up by InitBufferPool(). ! */ ! StrategyControl[poolid]->firstFreeBuffer = (poolid == 0) ? 0 : BufferStrategyStatus[poolid - 1].num_buffers; ! StrategyControl[poolid]->lastFreeBuffer = BufferStrategyStatus[poolid].num_buffers - 1; ! /* Initialize the clock sweep pointer */ ! StrategyControl[poolid]->nextVictimBuffer = (poolid == 0) ? 0 : BufferStrategyStatus[poolid - 1].num_buffers;; ! /* Clear statistics */ ! StrategyControl[poolid]->completePasses = 0; ! StrategyControl[poolid]->numBufferAllocs = 0; ! ! /* No pending notification */ ! StrategyControl[poolid]->bgwriterLatch = NULL; ! } ! else ! Assert(!init); } } *************** *** 438,444 **** GetAccessStrategy(BufferAccessStrategyType btype) } /* Make sure ring isn't an undue fraction of shared buffers */ ! ring_size = Min(NBuffers / 8, ring_size); /* Allocate the object and initialize all elements to zeroes */ strategy = (BufferAccessStrategy) --- 463,469 ---- } /* Make sure ring isn't an undue fraction of shared buffers */ ! ring_size = Min(NSharedBuffers / 8, ring_size); /* Allocate the object and initialize all elements to zeroes */ strategy = (BufferAccessStrategy) *** a/src/backend/storage/lmgr/lwlock.c --- b/src/backend/storage/lmgr/lwlock.c *************** *** 219,225 **** NumLWLocks(void) numLocks = NUM_FIXED_LWLOCKS; /* bufmgr.c needs two for each shared buffer */ ! numLocks += 2 * NBuffers; /* proc.c needs one for each backend or auxiliary process */ numLocks += MaxBackends + NUM_AUXILIARY_PROCS; --- 219,225 ---- numLocks = NUM_FIXED_LWLOCKS; /* bufmgr.c needs two for each shared buffer */ ! numLocks += 2 * NSharedBuffers; /* proc.c needs one for each backend or auxiliary process */ numLocks += MaxBackends + NUM_AUXILIARY_PROCS; *** a/src/backend/tcop/postgres.c --- b/src/backend/tcop/postgres.c *************** *** 3538,3544 **** PostgresMain(int argc, char *argv[], MyStartTime = time(NULL); } ! SetProcessingMode(InitProcessing); /* Compute paths, if we didn't inherit them from postmaster */ --- 3538,3544 ---- MyStartTime = time(NULL); } ! SetProcessingMode(InitProcessing); /* Compute paths, if we didn't inherit them from postmaster */ *** a/src/backend/utils/init/globals.c --- b/src/backend/utils/init/globals.c *************** *** 107,112 **** int maintenance_work_mem = 16384; --- 107,114 ---- * register background workers. */ int NBuffers = 1000; + int NPriorityBuffers = 0; + int NSharedBuffers; int MaxConnections = 90; int max_worker_processes = 8; int MaxBackends = 0; *** a/src/backend/utils/misc/guc.c --- b/src/backend/utils/misc/guc.c *************** *** 1712,1717 **** static struct config_int ConfigureNamesInt[] = --- 1712,1728 ---- }, { + {"priority_buffers", PGC_POSTMASTER, RESOURCES_MEM, + gettext_noop("Sets the number of priority buffers used by the server."), + NULL, + GUC_UNIT_BLOCKS + }, + &NPriorityBuffers, + 1024, 16, INT_MAX / 2, + NULL, NULL, NULL + }, + + { {"temp_buffers", PGC_USERSET, RESOURCES_MEM, gettext_noop("Sets the maximum number of temporary buffers used by each session."), NULL, *** a/src/backend/utils/misc/postgresql.conf.sample --- b/src/backend/utils/misc/postgresql.conf.sample *************** *** 114,119 **** --- 114,121 ---- #shared_buffers = 32MB # min 128kB # (change requires restart) + #priority_buffers = 32MB #Buffers used for priority tables + # (change requires restart) #huge_pages = try # on, off, or try # (change requires restart) #temp_buffers = 8MB # min 800kB *** a/src/include/miscadmin.h --- b/src/include/miscadmin.h *************** *** 126,132 **** do { \ /***************************************************************************** * globals.h -- * *****************************************************************************/ - /* * from utils/init/globals.c */ --- 126,131 ---- *************** *** 140,146 **** extern bool ExitOnAnyError; extern PGDLLIMPORT char *DataDir; ! extern PGDLLIMPORT int NBuffers; extern int MaxBackends; extern int MaxConnections; extern int max_worker_processes; --- 139,145 ---- extern PGDLLIMPORT char *DataDir; ! extern PGDLLIMPORT int NSharedBuffers; extern int MaxBackends; extern int MaxConnections; extern int max_worker_processes; *** a/src/include/storage/buf_internals.h --- b/src/include/storage/buf_internals.h *************** *** 23,28 **** --- 23,31 ---- #include "storage/spin.h" #include "utils/relcache.h" + #define NUM_MAX_BUFFER_POOLS 2 + #define DEFAULT_BUFFER_POOL 0 + #define PRIORITY_BUFFER_POOL 1 /* * Flags for buffer descriptors *************** *** 185,197 **** extern BufferDesc *LocalBufferDescriptors; */ /* freelist.c */ ! extern volatile BufferDesc *StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held); extern void StrategyFreeBuffer(volatile BufferDesc *buf); extern bool StrategyRejectBuffer(BufferAccessStrategy strategy, volatile BufferDesc *buf); ! extern int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc); extern void StrategyNotifyBgWriter(Latch *bgwriterLatch); extern Size StrategyShmemSize(void); --- 188,200 ---- */ /* freelist.c */ ! extern volatile BufferDesc *StrategyGetBuffer(int poolid, BufferAccessStrategy strategy, bool *lock_held); extern void StrategyFreeBuffer(volatile BufferDesc *buf); extern bool StrategyRejectBuffer(BufferAccessStrategy strategy, volatile BufferDesc *buf); ! extern int StrategySyncStart(int poolid, uint32 *complete_passes, uint32 *num_buf_alloc); extern void StrategyNotifyBgWriter(Latch *bgwriterLatch); extern Size StrategyShmemSize(void); *** a/src/include/storage/bufmgr.h --- b/src/include/storage/bufmgr.h *************** *** 16,21 **** --- 16,22 ---- #include "storage/block.h" #include "storage/buf.h" + #include "storage/buf_internals.h" #include "storage/bufpage.h" #include "storage/relfilenode.h" #include "utils/relcache.h" *************** *** 44,50 **** typedef enum --- 45,53 ---- } ReadBufferMode; /* in globals.c ... this duplicates miscadmin.h */ + extern PGDLLIMPORT int NSharedBuffers; extern PGDLLIMPORT int NBuffers; + extern PGDLLIMPORT int NPriorityBuffers; /* in bufmgr.c */ extern bool zero_damaged_pages; *************** *** 97,103 **** extern PGDLLIMPORT int32 *LocalRefCount; */ #define BufferIsValid(bufnum) \ ( \ ! AssertMacro((bufnum) <= NBuffers && (bufnum) >= -NLocBuffer), \ (bufnum) != InvalidBuffer \ ) --- 100,106 ---- */ #define BufferIsValid(bufnum) \ ( \ ! AssertMacro((bufnum) <= NSharedBuffers && (bufnum) >= -NLocBuffer), \ (bufnum) != InvalidBuffer \ ) *** a/src/include/storage/lwlock.h --- b/src/include/storage/lwlock.h *************** *** 89,133 **** extern PGDLLIMPORT LWLockPadded *MainLWLockArray; * if you remove a lock, consider leaving a gap in the numbering sequence for * the benefit of DTrace and other external debugging scripts. */ ! #define BufFreelistLock (&MainLWLockArray[0].lock) ! #define ShmemIndexLock (&MainLWLockArray[1].lock) ! #define OidGenLock (&MainLWLockArray[2].lock) ! #define XidGenLock (&MainLWLockArray[3].lock) ! #define ProcArrayLock (&MainLWLockArray[4].lock) ! #define SInvalReadLock (&MainLWLockArray[5].lock) ! #define SInvalWriteLock (&MainLWLockArray[6].lock) ! #define WALBufMappingLock (&MainLWLockArray[7].lock) ! #define WALWriteLock (&MainLWLockArray[8].lock) ! #define ControlFileLock (&MainLWLockArray[9].lock) ! #define CheckpointLock (&MainLWLockArray[10].lock) ! #define CLogControlLock (&MainLWLockArray[11].lock) ! #define SubtransControlLock (&MainLWLockArray[12].lock) ! #define MultiXactGenLock (&MainLWLockArray[13].lock) ! #define MultiXactOffsetControlLock (&MainLWLockArray[14].lock) ! #define MultiXactMemberControlLock (&MainLWLockArray[15].lock) ! #define RelCacheInitLock (&MainLWLockArray[16].lock) ! #define CheckpointerCommLock (&MainLWLockArray[17].lock) ! #define TwoPhaseStateLock (&MainLWLockArray[18].lock) ! #define TablespaceCreateLock (&MainLWLockArray[19].lock) ! #define BtreeVacuumLock (&MainLWLockArray[20].lock) ! #define AddinShmemInitLock (&MainLWLockArray[21].lock) ! #define AutovacuumLock (&MainLWLockArray[22].lock) ! #define AutovacuumScheduleLock (&MainLWLockArray[23].lock) ! #define SyncScanLock (&MainLWLockArray[24].lock) ! #define RelationMappingLock (&MainLWLockArray[25].lock) ! #define AsyncCtlLock (&MainLWLockArray[26].lock) ! #define AsyncQueueLock (&MainLWLockArray[27].lock) ! #define SerializableXactHashLock (&MainLWLockArray[28].lock) ! #define SerializableFinishedListLock (&MainLWLockArray[29].lock) ! #define SerializablePredicateLockListLock (&MainLWLockArray[30].lock) ! #define OldSerXidLock (&MainLWLockArray[31].lock) ! #define SyncRepLock (&MainLWLockArray[32].lock) ! #define BackgroundWorkerLock (&MainLWLockArray[33].lock) ! #define DynamicSharedMemoryControlLock (&MainLWLockArray[34].lock) ! #define AutoFileLock (&MainLWLockArray[35].lock) ! #define ReplicationSlotAllocationLock (&MainLWLockArray[36].lock) ! #define ReplicationSlotControlLock (&MainLWLockArray[37].lock) ! #define NUM_INDIVIDUAL_LWLOCKS 38 /* * It's a bit odd to declare NUM_BUFFER_PARTITIONS and NUM_LOCK_PARTITIONS --- 89,135 ---- * if you remove a lock, consider leaving a gap in the numbering sequence for * the benefit of DTrace and other external debugging scripts. */ ! #define BufFreelistLock(poolid) (&MainLWLockArray[poolid].lock) ! #define NUM_FREELIST_LOCKS 2 ! ! #define ShmemIndexLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 1].lock) ! #define OidGenLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 2].lock) ! #define XidGenLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 3].lock) ! #define ProcArrayLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 4].lock) ! #define SInvalReadLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 5].lock) ! #define SInvalWriteLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 6].lock) ! #define WALBufMappingLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 7].lock) ! #define WALWriteLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 8].lock) ! #define ControlFileLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 9].lock) ! #define CheckpointLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 10].lock) ! #define CLogControlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 11].lock) ! #define SubtransControlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 12].lock) ! #define MultiXactGenLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 13].lock) ! #define MultiXactOffsetControlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 14].lock) ! #define MultiXactMemberControlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 15].lock) ! #define RelCacheInitLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 16].lock) ! #define CheckpointerCommLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 17].lock) ! #define TwoPhaseStateLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 18].lock) ! #define TablespaceCreateLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 19].lock) ! #define BtreeVacuumLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 20].lock) ! #define AddinShmemInitLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 21].lock) ! #define AutovacuumLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 22].lock) ! #define AutovacuumScheduleLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 23].lock) ! #define SyncScanLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 24].lock) ! #define RelationMappingLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 25].lock) ! #define AsyncCtlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 26].lock) ! #define AsyncQueueLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 27].lock) ! #define SerializableXactHashLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 28].lock) ! #define SerializableFinishedListLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 29].lock) ! #define SerializablePredicateLockListLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 30].lock) ! #define OldSerXidLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 31].lock) ! #define SyncRepLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 32].lock) ! #define BackgroundWorkerLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 33].lock) ! #define DynamicSharedMemoryControlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 34].lock) ! #define AutoFileLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 35].lock) ! #define ReplicationSlotAllocationLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 36].lock) ! #define ReplicationSlotControlLock (&MainLWLockArray[NUM_FREELIST_LOCKS + 37].lock) ! #define NUM_INDIVIDUAL_LWLOCKS NUM_FREELIST_LOCKS + 37 /* * It's a bit odd to declare NUM_BUFFER_PARTITIONS and NUM_LOCK_PARTITIONS *** a/src/include/utils/rel.h --- b/src/include/utils/rel.h *************** *** 217,222 **** typedef struct StdRdOptions --- 217,223 ---- { int32 vl_len_; /* varlena header (do not touch directly!) */ int fillfactor; /* page fill factor in percent (0..100) */ + int bufferpool_offset; /* Buffer Pool option */ AutoVacOpts autovacuum; /* autovacuum-related options */ bool security_barrier; /* for views */ int check_option_offset; /* for views */ *************** *** 249,254 **** typedef struct StdRdOptions --- 250,266 ---- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100) /* + * RelationIsInPriorityBufferPool + * Returns the relation's buffer pool. + */ + #define RelationIsInPriorityBufferPool(relation) \ + ((relation)->rd_options && \ + ((StdRdOptions *) (relation)->rd_options)->bufferpool_offset != 0 ? \ + strcmp((char *) (relation)->rd_options + \ + ((StdRdOptions *) (relation)->rd_options)->bufferpool_offset, \ + "priority") == 0 : false) + + /* * RelationIsSecurityView * Returns whether the relation is security view, or not */