*** ./src/include/utils/memutils.h.orig 2009-09-30 01:54:36.000000000 -0700 --- ./src/include/utils/memutils.h 2009-09-30 03:33:44.000000000 -0700 *************** *** 114,119 **** --- 114,122 ---- */ /* aset.c */ + + extern int max_allocated_mem; + extern MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, *** ./src/backend/utils/mmgr/aset.c.orig 2009-09-29 16:14:23.000000000 -0700 --- ./src/backend/utils/mmgr/aset.c 2009-10-01 03:07:34.000000000 -0700 *************** *** 168,173 **** --- 168,187 ---- } AllocBlockData; /* + * AllocBlock accounting maintains total allocated memory to enforce the memory use limit. + */ + int max_allocated_mem = 0; + Size AllocBlockAccountingMemUsed = 0; + + #define AllocBlockAccountingFree(block) \ + (AllocBlockAccountingMemUsed -= block->endptr - (char *) (block)) + #define AllocBlockAccountingAlloc(block) \ + (AllocBlockAccountingMemUsed += block->endptr - (char *) (block)) + #define AllocBlockAccountingOverLimit() \ + (max_allocated_mem != 0 \ + && AllocBlockAccountingMemUsed / 1024 > max_allocated_mem) + + /* * AllocChunk * The prefix of each piece of memory in an AllocBlock * *************** *** 393,398 **** --- 407,423 ---- context->blocks = block; /* Mark block as not to be released at reset time */ context->keeper = block; + + AllocBlockAccountingAlloc(block); + if (AllocBlockAccountingOverLimit()) + { + MemoryContextStats(TopMemoryContext); + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("memory limit exceeded"), + errdetail("Failed while creating memory context \"%s\".", + name))); + } } context->isReset = true; *************** *** 476,481 **** --- 501,507 ---- else { /* Normal case, release the block */ + AllocBlockAccountingFree(block); #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(block, 0x7F, block->freeptr - ((char *) block)); *************** *** 521,526 **** --- 547,553 ---- { AllocBlock next = block->next; + AllocBlockAccountingFree(block); #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(block, 0x7F, block->freeptr - ((char *) block)); *************** *** 597,602 **** --- 624,640 ---- set->blocks = block; } + AllocBlockAccountingAlloc(block); + if (AllocBlockAccountingOverLimit()) + { + MemoryContextStats(TopMemoryContext); + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("memory limit exceeded"), + errdetail("Failed on request of size %lu.", + (unsigned long) size))); + } + set->isReset = false; AllocAllocInfo(set, chunk); *************** *** 767,772 **** --- 805,821 ---- block->next = set->blocks; set->blocks = block; + + AllocBlockAccountingAlloc(block); + if (AllocBlockAccountingOverLimit()) + { + MemoryContextStats(TopMemoryContext); + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("memory limit exceeded"), + errdetail("Failed on request of size %lu.", + (unsigned long) size))); + } } /* *************** *** 843,848 **** --- 892,898 ---- set->blocks = block->next; else prevblock->next = block->next; + AllocBlockAccountingFree(block); #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(block, 0x7F, block->freeptr - ((char *) block)); *** ./src/backend/utils/misc/guc.c.orig 2009-09-30 01:39:18.000000000 -0700 --- ./src/backend/utils/misc/guc.c 2009-10-01 03:03:13.000000000 -0700 *************** *** 167,172 **** --- 167,173 ---- static bool assign_maxconnections(int newval, bool doit, GucSource source); static bool assign_autovacuum_max_workers(int newval, bool doit, GucSource source); static bool assign_effective_io_concurrency(int newval, bool doit, GucSource source); + static bool assign_max_allocated_mem(int newval, bool doit, GucSource source); static const char *assign_pgstat_temp_directory(const char *newval, bool doit, GucSource source); static char *config_enum_get_options(struct config_enum * record, *************** *** 1415,1420 **** --- 1416,1431 ---- }, { + {"max_allocated_mem", PGC_USERSET, RESOURCES_MEM, + gettext_noop("Sets the maximum memory that can be allocated by a session."), + gettext_noop("Exceeding this limit will cause the current operation to fail."), + GUC_UNIT_KB + }, + &max_allocated_mem, + 0, 0, MAX_KILOBYTES, assign_max_allocated_mem, NULL + }, + + { {"max_stack_depth", PGC_SUSET, RESOURCES_MEM, gettext_noop("Sets the maximum stack depth, in kilobytes."), NULL, *************** *** 7673,7676 **** --- 7684,7696 ---- return newval; } + static bool + assign_max_allocated_mem(int newval, bool doit, GucSource source) + { + /* minimum enforceable limit is 16MB */ + if (newval != 0 && newval < 16384) + return false; + return true; + } + #include "guc-file.c" *** ./doc/src/sgml/config.sgml.orig 2009-10-01 02:15:56.000000000 -0700 --- ./doc/src/sgml/config.sgml 2009-10-01 03:02:19.000000000 -0700 *************** *** 859,864 **** --- 859,887 ---- + + max_allocated_mem (integer) + + max_allocated_mem configuration parameter + + + + Limits the maximum amount of memory that an individual backend process + can allocate for its own use. This prevents a single session running a large + or badly behaved query from consuming excessive system memory. The default + setting is zero (0) which disables this limit. This is + appropriate for most situations. + + The minimum non-zero setting is 16 megabytes (16MB). It should + always be set larger than maintenance_work_mem, and + usually several times larger than work_mem. Useful + settings will depend on the nature of the workload. A starting point might + be one fourth the total memory on the host machine. Setting this too small + may prevent large or complex queries from running. + + + + max_stack_depth (integer)