diff options
Diffstat (limited to 'src/backend/storage/freespace/freespace.c')
-rw-r--r-- | src/backend/storage/freespace/freespace.c | 121 |
1 files changed, 87 insertions, 34 deletions
diff --git a/src/backend/storage/freespace/freespace.c b/src/backend/storage/freespace/freespace.c index 62e5252aac1..3309ba11dac 100644 --- a/src/backend/storage/freespace/freespace.c +++ b/src/backend/storage/freespace/freespace.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.54 2006/07/14 14:52:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/storage/freespace/freespace.c,v 1.55 2006/09/21 20:31:22 tgl Exp $ * * * NOTES: @@ -24,7 +24,7 @@ * behavior keeps track of which relation is least recently used. * * For each known relation, we track the average request size given to - * GetPageWithFreeSpace() as well as the most recent number of pages given + * GetPageWithFreeSpace() as well as the most recent number of pages reported * to RecordRelationFreeSpace(). The average request size is not directly * used in this module, but we expect VACUUM to use it to filter out * uninteresting amounts of space before calling RecordRelationFreeSpace(). @@ -82,7 +82,7 @@ * relfilenode * isIndex * avgRequest - * lastPageCount + * interestingPages * storedPages * arena data array of storedPages FSMPageData or IndexFSMPageData *---------- @@ -111,7 +111,7 @@ typedef struct FsmCacheRelHeader RelFileNode key; /* hash key (must be first) */ bool isIndex; /* if true, we store only page numbers */ uint32 avgRequest; /* moving average of space requests */ - int32 lastPageCount; /* pages passed to RecordRelationFreeSpace */ + BlockNumber interestingPages; /* # of pages with useful free space */ int32 storedPages; /* # of pages stored in arena */ } FsmCacheRelHeader; @@ -128,7 +128,8 @@ static void CheckFreeSpaceMapStatistics(int elevel, int numRels, static FSMRelation *lookup_fsm_rel(RelFileNode *rel); static FSMRelation *create_fsm_rel(RelFileNode *rel); static void delete_fsm_rel(FSMRelation *fsmrel); -static int realloc_fsm_rel(FSMRelation *fsmrel, int nPages, bool isIndex); +static int realloc_fsm_rel(FSMRelation *fsmrel, BlockNumber interestingPages, + bool isIndex); static void link_fsm_rel_usage(FSMRelation *fsmrel); static void unlink_fsm_rel_usage(FSMRelation *fsmrel); static void link_fsm_rel_storage(FSMRelation *fsmrel); @@ -146,6 +147,7 @@ static void pack_incoming_pages(FSMPageData *newLocation, int newPages, static void pack_existing_pages(FSMPageData *newLocation, int newPages, FSMPageData *oldLocation, int oldPages); static int fsm_calc_request(FSMRelation *fsmrel); +static int fsm_calc_request_unclamped(FSMRelation *fsmrel); static int fsm_calc_target_allocation(int myRequest); static int fsm_current_chunks(FSMRelation *fsmrel); static int fsm_current_allocation(FSMRelation *fsmrel); @@ -361,11 +363,17 @@ GetAvgFSMRequestSize(RelFileNode *rel) * * Any pre-existing info about the relation is assumed obsolete and discarded. * + * interestingPages is the total number of pages in the relation that have + * at least threshold free space; nPages is the number actually reported in + * pageSpaces[] (may be less --- in particular, callers typically clamp their + * space usage to MaxFSMPages). + * * The given pageSpaces[] array must be sorted in order by blkno. Note that * the FSM is at liberty to discard some or all of the data. */ void RecordRelationFreeSpace(RelFileNode *rel, + BlockNumber interestingPages, int nPages, PageFreeSpaceInfo *pageSpaces) { @@ -392,7 +400,7 @@ RecordRelationFreeSpace(RelFileNode *rel, int curAllocPages; FSMPageData *newLocation; - curAlloc = realloc_fsm_rel(fsmrel, nPages, false); + curAlloc = realloc_fsm_rel(fsmrel, interestingPages, false); curAllocPages = curAlloc * CHUNKPAGES; /* @@ -455,6 +463,7 @@ GetFreeIndexPage(RelFileNode *rel) */ void RecordIndexFreeSpace(RelFileNode *rel, + BlockNumber interestingPages, int nPages, BlockNumber *pages) { @@ -481,7 +490,7 @@ RecordIndexFreeSpace(RelFileNode *rel, int i; IndexFSMPageData *newLocation; - curAlloc = realloc_fsm_rel(fsmrel, nPages, true); + curAlloc = realloc_fsm_rel(fsmrel, interestingPages, true); curAllocPages = curAlloc * INDEXCHUNKPAGES; /* @@ -530,7 +539,7 @@ FreeSpaceMapTruncateRel(RelFileNode *rel, BlockNumber nblocks) (void) lookup_fsm_page_entry(fsmrel, nblocks, &pageIndex); /* Delete all such entries */ fsmrel->storedPages = pageIndex; - /* XXX should we adjust rel's lastPageCount and sumRequests? */ + /* XXX should we adjust rel's interestingPages and sumRequests? */ } LWLockRelease(FreeSpaceLock); } @@ -587,20 +596,24 @@ PrintFreeSpaceMapStatistics(int elevel) { FSMRelation *fsmrel; int storedPages = 0; + double sumRequests = 0; int numRels; - double sumRequests; double needed; LWLockAcquire(FreeSpaceLock, LW_EXCLUSIVE); - /* Count total space used --- tedious, but seems useful */ + /* + * Count total space actually used, as well as the unclamped request total + */ for (fsmrel = FreeSpaceMap->firstRel; fsmrel != NULL; fsmrel = fsmrel->nextPhysical) + { storedPages += fsmrel->storedPages; + sumRequests += fsm_calc_request_unclamped(fsmrel); + } /* Copy other stats before dropping lock */ numRels = FreeSpaceMap->numRels; - sumRequests = FreeSpaceMap->sumRequests; LWLockRelease(FreeSpaceLock); /* Convert stats to actual number of page slots needed */ @@ -613,7 +626,8 @@ PrintFreeSpaceMapStatistics(int elevel) "%.0f page slots are required to track all free space.\n" "Current limits are: %d page slots, %d relations, using %.0f KB.", Min(needed, MaxFSMPages), - needed, MaxFSMPages, MaxFSMRelations, + needed, + MaxFSMPages, MaxFSMRelations, (double) FreeSpaceShmemSize() / 1024.0))); CheckFreeSpaceMapStatistics(NOTICE, numRels, needed); @@ -687,7 +701,7 @@ DumpFreeSpaceMap(int code, Datum arg) relheader.key = fsmrel->key; relheader.isIndex = fsmrel->isIndex; relheader.avgRequest = fsmrel->avgRequest; - relheader.lastPageCount = fsmrel->lastPageCount; + relheader.interestingPages = fsmrel->interestingPages; relheader.storedPages = fsmrel->storedPages; if (fwrite(&relheader, 1, sizeof(relheader), fp) != sizeof(relheader)) goto write_failed; @@ -792,17 +806,12 @@ LoadFreeSpaceMap(void) if (fread(&relheader, 1, sizeof(relheader), fp) != sizeof(relheader) || (relheader.isIndex != false && relheader.isIndex != true) || relheader.avgRequest >= BLCKSZ || - relheader.lastPageCount < 0 || relheader.storedPages < 0) { elog(LOG, "bogus rel header in \"%s\"", FSM_CACHE_FILENAME); goto read_failed; } - /* Make sure lastPageCount doesn't exceed current MaxFSMPages */ - if (relheader.lastPageCount > MaxFSMPages) - relheader.lastPageCount = MaxFSMPages; - /* Read the per-page data */ nPages = relheader.storedPages; if (relheader.isIndex) @@ -827,7 +836,7 @@ LoadFreeSpaceMap(void) fsmrel = create_fsm_rel(&relheader.key); fsmrel->avgRequest = relheader.avgRequest; - curAlloc = realloc_fsm_rel(fsmrel, relheader.lastPageCount, + curAlloc = realloc_fsm_rel(fsmrel, relheader.interestingPages, relheader.isIndex); if (relheader.isIndex) { @@ -932,7 +941,7 @@ create_fsm_rel(RelFileNode *rel) /* New hashtable entry, initialize it (hash_search set the key) */ fsmrel->isIndex = false; /* until we learn different */ fsmrel->avgRequest = INITIAL_AVERAGE; - fsmrel->lastPageCount = 0; + fsmrel->interestingPages = 0; fsmrel->firstChunk = -1; /* no space allocated */ fsmrel->storedPages = 0; fsmrel->nextPage = 0; @@ -988,7 +997,8 @@ delete_fsm_rel(FSMRelation *fsmrel) * The return value is the actual new allocation, in chunks. */ static int -realloc_fsm_rel(FSMRelation *fsmrel, int nPages, bool isIndex) +realloc_fsm_rel(FSMRelation *fsmrel, BlockNumber interestingPages, + bool isIndex) { int myRequest; int myAlloc; @@ -999,7 +1009,7 @@ realloc_fsm_rel(FSMRelation *fsmrel, int nPages, bool isIndex) */ fsmrel->storedPages = 0; FreeSpaceMap->sumRequests -= fsm_calc_request(fsmrel); - fsmrel->lastPageCount = nPages; + fsmrel->interestingPages = interestingPages; fsmrel->isIndex = isIndex; myRequest = fsm_calc_request(fsmrel); FreeSpaceMap->sumRequests += myRequest; @@ -1012,7 +1022,7 @@ realloc_fsm_rel(FSMRelation *fsmrel, int nPages, bool isIndex) * new data in-place. */ curAlloc = fsm_current_allocation(fsmrel); - if (myAlloc > curAlloc && (myRequest + 1) > curAlloc && nPages > 0) + if (myAlloc > curAlloc && (myRequest + 1) > curAlloc && interestingPages > 0) { /* Remove entry from storage list, and compact */ unlink_fsm_rel_storage(fsmrel); @@ -1649,27 +1659,70 @@ pack_existing_pages(FSMPageData *newLocation, int newPages, } /* - * Calculate number of chunks "requested" by a rel. + * Calculate number of chunks "requested" by a rel. The "request" is + * anything beyond the rel's one guaranteed chunk. * - * Rel's lastPageCount and isIndex settings must be up-to-date when called. + * Rel's interestingPages and isIndex settings must be up-to-date when called. * * See notes at top of file for details. */ static int fsm_calc_request(FSMRelation *fsmrel) { - int chunkCount; + int req; /* Convert page count to chunk count */ if (fsmrel->isIndex) - chunkCount = (fsmrel->lastPageCount - 1) / INDEXCHUNKPAGES + 1; + { + /* test to avoid unsigned underflow at zero */ + if (fsmrel->interestingPages <= INDEXCHUNKPAGES) + return 0; + /* quotient will fit in int, even if interestingPages doesn't */ + req = (fsmrel->interestingPages - 1) / INDEXCHUNKPAGES; + } else - chunkCount = (fsmrel->lastPageCount - 1) / CHUNKPAGES + 1; - /* "Request" is anything beyond our one guaranteed chunk */ - if (chunkCount <= 0) - return 0; + { + if (fsmrel->interestingPages <= CHUNKPAGES) + return 0; + req = (fsmrel->interestingPages - 1) / CHUNKPAGES; + } + + /* + * We clamp the per-relation requests to at most half the arena size; + * this is intended to prevent a single bloated relation from crowding + * out FSM service for every other rel. + */ + req = Min(req, FreeSpaceMap->totalChunks / 2); + + return req; +} + +/* + * Same as above, but without the clamp ... this is just intended for + * reporting the total space needed to store all information. + */ +static int +fsm_calc_request_unclamped(FSMRelation *fsmrel) +{ + int req; + + /* Convert page count to chunk count */ + if (fsmrel->isIndex) + { + /* test to avoid unsigned underflow at zero */ + if (fsmrel->interestingPages <= INDEXCHUNKPAGES) + return 0; + /* quotient will fit in int, even if interestingPages doesn't */ + req = (fsmrel->interestingPages - 1) / INDEXCHUNKPAGES; + } else - return chunkCount - 1; + { + if (fsmrel->interestingPages <= CHUNKPAGES) + return 0; + req = (fsmrel->interestingPages - 1) / CHUNKPAGES; + } + + return req; } /* @@ -1769,11 +1822,11 @@ DumpFreeSpace(void) for (fsmrel = FreeSpaceMap->usageList; fsmrel; fsmrel = fsmrel->nextUsage) { relNum++; - fprintf(stderr, "Map %d: rel %u/%u/%u isIndex %d avgRequest %u lastPageCount %d nextPage %d\nMap= ", + fprintf(stderr, "Map %d: rel %u/%u/%u isIndex %d avgRequest %u interestingPages %u nextPage %d\nMap= ", relNum, fsmrel->key.spcNode, fsmrel->key.dbNode, fsmrel->key.relNode, (int) fsmrel->isIndex, fsmrel->avgRequest, - fsmrel->lastPageCount, fsmrel->nextPage); + fsmrel->interestingPages, fsmrel->nextPage); if (fsmrel->isIndex) { IndexFSMPageData *page; |