@ -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 RecordRelationFreeS pace */
BlockNumber interestingPages ; /* # of pages with useful free s pace */
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 , i nteresting Pages, 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 , i nteresting Pages, 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 = n Pages;
fsmrel - > interestingPages = interesting Pages;
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 & & i nteresting Pages > 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 \n Map= " ,
fprintf ( stderr , " Map %d: rel %u/%u/%u isIndex %d avgRequest %u interestingPages %u nextPage %d \n Map= " ,
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 ;