diff -ruN ../../work/postgresql-9.0.3/src/backend/storage/buffer/buf_init.c src/backend/storage/buffer/buf_init.c --- ../../work/postgresql-9.0.3/src/backend/storage/buffer/buf_init.c 2011-01-28 11:21:31.000000000 +0900 +++ src/backend/storage/buffer/buf_init.c 2011-05-04 12:56:48.000000000 +0900 @@ -127,6 +127,16 @@ /* Init other shared buffer-management stuff */ StrategyInitialize(!foundDescs); + + if (EnableBufferCacheHibernation) + { + ResisterBufferCacheHibernation(BUFFER_CACHE_HIBERNATION_DESCRIPTERS, + (char *)BufferDescriptors, sizeof(BufferDesc), NBuffers); + ResisterBufferCacheHibernation(BUFFER_CACHE_HIBERNATION_BLOCKS, + (char *)BufferBlocks, BLCKSZ, NBuffers); + + ResumeBufferCacheHibernation(); + } } /* diff -ruN ../../work/postgresql-9.0.3/src/backend/storage/buffer/bufmgr.c src/backend/storage/buffer/bufmgr.c --- ../../work/postgresql-9.0.3/src/backend/storage/buffer/bufmgr.c 2011-01-28 11:21:31.000000000 +0900 +++ src/backend/storage/buffer/bufmgr.c 2011-05-04 19:27:21.000000000 +0900 @@ -31,6 +31,7 @@ #include "postgres.h" #include +#include #include #include "catalog/catalog.h" @@ -61,6 +62,11 @@ #define BUF_WRITTEN 0x01 #define BUF_REUSABLE 0x02 +/* enable this to debug buffer cache hibernation. */ +#if 0 +#define DEBUG_BUFFER_CACHE_HIBERNATION +#endif + /* GUC variables */ bool zero_damaged_pages = false; @@ -756,6 +762,14 @@ } } +#ifdef DEBUG_BUFFER_CACHE_HIBERNATION + elog(DEBUG5, + "alloc: hash[%08x] id[%d]\t%d,%d,%d,%d,%d", + newHash, buf->buf_id, newTag.rnode.spcNode, + newTag.rnode.dbNode, newTag.rnode.relNode, + newTag.forkNum, newTag.blockNum); +#endif + return buf; } @@ -817,6 +831,14 @@ else *foundPtr = TRUE; +#ifdef DEBUG_BUFFER_CACHE_HIBERNATION + elog(DEBUG5, + "rename:hash[%08x] id[%d]\t%d,%d,%d,%d,%d", + newHash, buf->buf_id, newTag.rnode.spcNode, + newTag.rnode.dbNode, newTag.rnode.relNode, + newTag.forkNum, newTag.blockNum); +#endif + return buf; } @@ -2731,3 +2753,268 @@ pfree(path); } } + +/* ---------------------------------------------------------------- + * buffer cache hibernation support stuff + * + * Suspend/resume buffer cache data structure using hibernation files + * at shutdown/startup. + * ---------------------------------------------------------------- + */ + +bool EnableBufferCacheHibernation = false; + +static struct +{ + char *hibernation_file; + char *data_ptr; + Size record_length; + Size num_records; +} BufferCacheHibernationData[] = +{ + /* BufferStrategyControl */ + { + "global/pg_buffer_cache_hibernation_strategy", + NULL, 0, 0 + }, + + /* BufferDescriptors */ + { + "global/pg_buffer_cache_hibernation_descriptors", + NULL, 0, 0 + }, + + /* BufferBlocks */ + { + "global/pg_buffer_cache_hibernation_blocks", + NULL, 0, 0 + }, + + /* End-of-list marker */ + { + NULL, + NULL, 0, 0 + }, +}; + +/* + * AtProcExit_BufferCacheHibernation: + * store buffer cache into hibernation files at shutdown. + */ +static void +AtProcExit_BufferCacheHibernation(int code, Datum arg) +{ + BufferHibernationFileType id; + int i; + + if (EnableBufferCacheHibernation == false) + { + return; + } + + /* + * suspend buffer cache data structure into hibernation files. + */ + for (id = 0; BufferCacheHibernationData[id].hibernation_file != NULL; id++) + { + int fd; + Size record_length; + Size num_records; + char *ptr; + + if (BufferCacheHibernationData[id].data_ptr == NULL || + BufferCacheHibernationData[id].record_length == 0 || + BufferCacheHibernationData[id].num_records == 0) + { + return; + } + + elog(NOTICE, + "buffer cache hibernate into %s", + BufferCacheHibernationData[id].hibernation_file); + + fd = BasicOpenFile(BufferCacheHibernationData[id].hibernation_file, + O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, S_IRUSR | S_IWUSR); + if (fd < 0) + { + return; + } + + record_length = BufferCacheHibernationData[id].record_length; + num_records = BufferCacheHibernationData[id].num_records; + + for (i = 0; i < num_records; i++) + { + ptr = BufferCacheHibernationData[id].data_ptr + (i * record_length); + write(fd, (void *)ptr, record_length); + } + + close(fd); + } +} + +/* + * ResisterBufferCacheHibernation: + * register buffer cache data structure info. + */ +void +ResisterBufferCacheHibernation(BufferHibernationFileType id, char *ptr, Size record_length, Size num_records) +{ + if (EnableBufferCacheHibernation == false) + { + return; + } + + if (id != BUFFER_CACHE_HIBERNATION_STRATEGY && + id != BUFFER_CACHE_HIBERNATION_DESCRIPTERS && + id != BUFFER_CACHE_HIBERNATION_BLOCKS) + { + return; + } + + BufferCacheHibernationData[id].data_ptr = ptr; + BufferCacheHibernationData[id].record_length = record_length; + BufferCacheHibernationData[id].num_records = num_records; +} + +/* + * ResumeBufferCacheHibernation: + * resume buffer cache from hibernation file at startup. + */ +void +ResumeBufferCacheHibernation(void) +{ + BufferHibernationFileType id; + int i; + + if (EnableBufferCacheHibernation == false) + { + return; + } + + /* + * AtProcExit_BufferCacheHibernation to be called at shutdown. + */ + on_shmem_exit(AtProcExit_BufferCacheHibernation, 0); + + /* + * check if all hibernation files are valid. + */ + for (id = 0; BufferCacheHibernationData[id].hibernation_file != NULL; id++) + { + int fd; + Size record_length; + Size num_records; + struct stat sb; + + if (BufferCacheHibernationData[id].data_ptr == NULL || + BufferCacheHibernationData[id].record_length == 0 || + BufferCacheHibernationData[id].num_records == 0) + { + return; + } + + fd = BasicOpenFile(BufferCacheHibernationData[id].hibernation_file, + O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR); + if (fd < 0) + { + return; + } + + if (fstat(fd, &sb) < 0) + { + elog(WARNING, + "could not get stats of buffer cache hibernation file: %s", + BufferCacheHibernationData[id].hibernation_file); + close(fd); + return; + } + + record_length = BufferCacheHibernationData[id].record_length; + num_records = BufferCacheHibernationData[id].num_records; + + if (sb.st_size != (record_length * num_records)) + { + elog(WARNING, + "size mismatch on buffer cache hibernation file: %s", + BufferCacheHibernationData[id].hibernation_file); + close(fd); + return; + } + + close(fd); + } + + /* + * resume buffer cache data structure from hibernation files. + */ + for (id = 0; BufferCacheHibernationData[id].hibernation_file != NULL; id++) + { + int fd; + Size record_length; + Size num_records; + char *ptr; + + record_length = BufferCacheHibernationData[id].record_length; + num_records = BufferCacheHibernationData[id].num_records; + + elog(NOTICE, + "buffer cache resume from %s(%d * %d)", + BufferCacheHibernationData[id].hibernation_file, + record_length, num_records); + + fd = BasicOpenFile(BufferCacheHibernationData[id].hibernation_file, + O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR); + if (fd < 0) + { + return; + } + + for (i = 0; i < num_records; i++) + { + ptr = BufferCacheHibernationData[id].data_ptr + (i * record_length); + read(fd, (void *)ptr, record_length); + } + + close(fd); + } + + /* + * setup lookup hashtable based on buffer descriptors. + */ + for (i = 0; i < NBuffers; i++) + { + BufferDesc *buf; + BufferTag newTag; + uint32 newHash; + int buf_id; + + buf = &BufferDescriptors[i]; + if (buf->tag.rnode.spcNode == InvalidOid && + buf->tag.rnode.dbNode == InvalidOid && + buf->tag.rnode.relNode == InvalidOid) + { + continue; + } + + INIT_BUFFERTAG(newTag, buf->tag.rnode, buf->tag.forkNum, buf->tag.blockNum); + newHash = BufTableHashCode(&newTag); + buf_id = BufTableInsert(&newTag, newHash, buf->buf_id); + if (buf_id != -1) + { + /* the entry exists already, return it to the freelist. */ + buf->refcount = 0; + buf->flags = 0; + InvalidateBuffer(buf); + continue; + } + +#ifdef DEBUG_BUFFER_CACHE_HIBERNATION + elog(DEBUG5, + "buffer hash[%08x] id[%d]\t%d,%d,%d,%d,%d", + newHash, buf->buf_id, newTag.rnode.spcNode, + newTag.rnode.dbNode, newTag.rnode.relNode, + newTag.forkNum, newTag.blockNum); +#endif + } +} diff -ruN ../../work/postgresql-9.0.3/src/backend/storage/buffer/freelist.c src/backend/storage/buffer/freelist.c --- ../../work/postgresql-9.0.3/src/backend/storage/buffer/freelist.c 2011-01-28 11:21:31.000000000 +0900 +++ src/backend/storage/buffer/freelist.c 2011-05-04 12:59:14.000000000 +0900 @@ -347,6 +347,12 @@ } else Assert(!init); + + if (EnableBufferCacheHibernation) + { + ResisterBufferCacheHibernation(BUFFER_CACHE_HIBERNATION_STRATEGY, + (char *)StrategyControl, sizeof(BufferStrategyControl), 1); + } } diff -ruN ../../work/postgresql-9.0.3/src/backend/utils/misc/guc.c src/backend/utils/misc/guc.c --- ../../work/postgresql-9.0.3/src/backend/utils/misc/guc.c 2011-01-28 11:21:31.000000000 +0900 +++ src/backend/utils/misc/guc.c 2011-05-04 13:20:18.000000000 +0900 @@ -1270,6 +1270,16 @@ false, NULL, NULL }, + { + {"enable_buffer_cache_hibernation", PGC_POSTMASTER, UNGROUPED, + gettext_noop("Enables buffer cache hibernation."), + gettext_noop("Suspend/resume buffer cache data structure using hibernation files " + "at shutdown/startup.") + }, + &EnableBufferCacheHibernation, + false, NULL, NULL + }, + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL diff -ruN ../../work/postgresql-9.0.3/src/backend/utils/misc/postgresql.conf.sample src/backend/utils/misc/postgresql.conf.sample --- ../../work/postgresql-9.0.3/src/backend/utils/misc/postgresql.conf.sample 2011-05-04 04:50:53.000000000 +0900 +++ src/backend/utils/misc/postgresql.conf.sample 2011-05-04 03:52:34.000000000 +0900 @@ -118,6 +118,10 @@ #work_mem = 1MB # min 64kB #maintenance_work_mem = 16MB # min 1MB #max_stack_depth = 2MB # min 100kB +#enable_buffer_cache_hibernation = off # "on" allows buffer cache hibernation + # support (suspend/resume buffer cache + # data structure using hibernation files + # at shutdown/startup) # - Kernel Resource Usage - diff -ruN ../../work/postgresql-9.0.3/src/include/storage/bufmgr.h src/include/storage/bufmgr.h --- ../../work/postgresql-9.0.3/src/include/storage/bufmgr.h 2011-01-28 11:21:31.000000000 +0900 +++ src/include/storage/bufmgr.h 2011-05-04 13:02:01.000000000 +0900 @@ -207,6 +207,20 @@ extern void AtProcExit_LocalBuffers(void); +/* buffer cache hibernation support stuff */ +extern bool EnableBufferCacheHibernation; + +typedef enum BufferHibernationFileType +{ + BUFFER_CACHE_HIBERNATION_STRATEGY, + BUFFER_CACHE_HIBERNATION_DESCRIPTERS, + BUFFER_CACHE_HIBERNATION_BLOCKS +} BufferHibernationFileType; + +extern void ResisterBufferCacheHibernation(BufferHibernationFileType id, + char *ptr, Size record_length, Size num_records); +extern void ResumeBufferCacheHibernation(void); + /* in freelist.c */ extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype); extern void FreeAccessStrategy(BufferAccessStrategy strategy);