Re: 7.4RC2 PANIC: insufficient room in FSM

From: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
To: "Arthur Ward" <award(at)dominionsciences(dot)com>
Cc: pgsql-bugs(at)postgresql(dot)org
Subject: Re: 7.4RC2 PANIC: insufficient room in FSM
Date: 2003-11-26 20:55:10
Message-ID: 17424.1069880110@sss.pgh.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

"Arthur Ward" <award(at)dominionsciences(dot)com> writes:
> [ 7.4RC2 produced this: ]
> Nov 24 20:37:19 x postgres: [13904] PANIC: insufficient room in FSM

After further study I've concluded that this means the fix I put in
place here:

2003-10-29 12:36 tgl

* src/backend/storage/freespace/freespace.c: compact_fsm_storage()
does need to handle the case where a relation's FSM data has to be
both moved down and compressed. Per report from Dror Matalon.

was incomplete, and that in fact there is no can't-happen case for this
routine. I've applied the attached patch for 7.4.1.

regards, tom lane

*** src/backend/storage/freespace/freespace.c.orig Wed Oct 29 12:36:57 2003
--- src/backend/storage/freespace/freespace.c Wed Nov 26 13:43:16 2003
***************
*** 1394,1399 ****
--- 1394,1400 ----
compact_fsm_storage(void)
{
int nextChunkIndex = 0;
+ bool did_push = false;
FSMRelation *fsmrel;

for (fsmrel = FreeSpaceMap->firstRel;
***************
*** 1419,1434 ****
newAllocPages = newAlloc * INDEXCHUNKPAGES;
else
newAllocPages = newAlloc * CHUNKPAGES;
- newChunkIndex = nextChunkIndex;
- nextChunkIndex += newAlloc;

/*
* Determine current size, current and new locations
*/
curChunks = fsm_current_chunks(fsmrel);
oldChunkIndex = fsmrel->firstChunk;
- newLocation = FreeSpaceMap->arena + newChunkIndex * CHUNKBYTES;
oldLocation = FreeSpaceMap->arena + oldChunkIndex * CHUNKBYTES;

/*
* It's possible that we have to move data down, not up, if the
--- 1420,1434 ----
newAllocPages = newAlloc * INDEXCHUNKPAGES;
else
newAllocPages = newAlloc * CHUNKPAGES;

/*
* Determine current size, current and new locations
*/
curChunks = fsm_current_chunks(fsmrel);
oldChunkIndex = fsmrel->firstChunk;
oldLocation = FreeSpaceMap->arena + oldChunkIndex * CHUNKBYTES;
+ newChunkIndex = nextChunkIndex;
+ newLocation = FreeSpaceMap->arena + newChunkIndex * CHUNKBYTES;

/*
* It's possible that we have to move data down, not up, if the
***************
*** 1440,1449 ****
* more than once, so pack everything against the end of the arena
* if so.
*
! * In corner cases where roundoff has affected our allocation, it's
! * possible that we have to move down and compress our data too.
! * Since this case is extremely infrequent, we do not try to be smart
! * about it --- we just drop pages from the end of the rel's data.
*/
if (newChunkIndex > oldChunkIndex)
{
--- 1440,1455 ----
* more than once, so pack everything against the end of the arena
* if so.
*
! * In corner cases where we are on the short end of a roundoff choice
! * that we were formerly on the long end of, it's possible that we
! * have to move down and compress our data too. In fact, even after
! * pushing down the following rels, there might not be as much space
! * as we computed for this rel above --- that would imply that some
! * following rel(s) are also on the losing end of roundoff choices.
! * We could handle this fairly by doing the per-rel compactions
! * out-of-order, but that seems like way too much complexity to deal
! * with a very infrequent corner case. Instead, we simply drop pages
! * from the end of the current rel's data until it fits.
*/
if (newChunkIndex > oldChunkIndex)
{
***************
*** 1455,1475 ****
fsmrel->storedPages = newAllocPages;
curChunks = fsm_current_chunks(fsmrel);
}
if (fsmrel->nextPhysical != NULL)
limitChunkIndex = fsmrel->nextPhysical->firstChunk;
else
limitChunkIndex = FreeSpaceMap->totalChunks;
if (newChunkIndex + curChunks > limitChunkIndex)
{
! /* need to push down additional rels */
! push_fsm_rels_after(fsmrel);
! /* recheck for safety */
if (fsmrel->nextPhysical != NULL)
limitChunkIndex = fsmrel->nextPhysical->firstChunk;
else
limitChunkIndex = FreeSpaceMap->totalChunks;
if (newChunkIndex + curChunks > limitChunkIndex)
! elog(PANIC, "insufficient room in FSM");
}
memmove(newLocation, oldLocation, curChunks * CHUNKBYTES);
}
--- 1461,1504 ----
fsmrel->storedPages = newAllocPages;
curChunks = fsm_current_chunks(fsmrel);
}
+ /* is there enough space? */
if (fsmrel->nextPhysical != NULL)
limitChunkIndex = fsmrel->nextPhysical->firstChunk;
else
limitChunkIndex = FreeSpaceMap->totalChunks;
if (newChunkIndex + curChunks > limitChunkIndex)
{
! /* not enough space, push down following rels */
! if (!did_push)
! {
! push_fsm_rels_after(fsmrel);
! did_push = true;
! }
! /* now is there enough space? */
if (fsmrel->nextPhysical != NULL)
limitChunkIndex = fsmrel->nextPhysical->firstChunk;
else
limitChunkIndex = FreeSpaceMap->totalChunks;
if (newChunkIndex + curChunks > limitChunkIndex)
! {
! /* uh-oh, forcibly cut the allocation to fit */
! newAlloc = limitChunkIndex - newChunkIndex;
! /*
! * If newAlloc < 0 at this point, we are moving the rel's
! * firstChunk into territory currently assigned to a later
! * rel. This is okay so long as we do not copy any data.
! * The rels will be back in nondecreasing firstChunk order
! * at completion of the compaction pass.
! */
! if (newAlloc < 0)
! newAlloc = 0;
! if (fsmrel->isIndex)
! newAllocPages = newAlloc * INDEXCHUNKPAGES;
! else
! newAllocPages = newAlloc * CHUNKPAGES;
! fsmrel->storedPages = newAllocPages;
! curChunks = fsm_current_chunks(fsmrel);
! }
}
memmove(newLocation, oldLocation, curChunks * CHUNKBYTES);
}
***************
*** 1504,1509 ****
--- 1533,1539 ----
memmove(newLocation, oldLocation, curChunks * CHUNKBYTES);
}
fsmrel->firstChunk = newChunkIndex;
+ nextChunkIndex += newAlloc;
}
Assert(nextChunkIndex <= FreeSpaceMap->totalChunks);
FreeSpaceMap->usedChunks = nextChunkIndex;
***************
*** 1544,1551 ****
oldChunkIndex = fsmrel->firstChunk;
if (newChunkIndex < oldChunkIndex)
{
! /* trouble... */
! elog(PANIC, "insufficient room in FSM");
}
else if (newChunkIndex > oldChunkIndex)
{
--- 1574,1581 ----
oldChunkIndex = fsmrel->firstChunk;
if (newChunkIndex < oldChunkIndex)
{
! /* we're pushing down, how can it move up? */
! elog(PANIC, "inconsistent entry sizes in FSM");
}
else if (newChunkIndex > oldChunkIndex)
{
***************
*** 1758,1771 ****
{
int chunkCount;

/* Convert page count to chunk count */
if (fsmrel->isIndex)
chunkCount = (fsmrel->storedPages - 1) / INDEXCHUNKPAGES + 1;
else
chunkCount = (fsmrel->storedPages - 1) / CHUNKPAGES + 1;
- /* Make sure storedPages==0 produces right answer */
- if (chunkCount < 0)
- chunkCount = 0;
return chunkCount;
}

--- 1788,1801 ----
{
int chunkCount;

+ /* Make sure storedPages==0 produces right answer */
+ if (fsmrel->storedPages <= 0)
+ return 0;
/* Convert page count to chunk count */
if (fsmrel->isIndex)
chunkCount = (fsmrel->storedPages - 1) / INDEXCHUNKPAGES + 1;
else
chunkCount = (fsmrel->storedPages - 1) / CHUNKPAGES + 1;
return chunkCount;
}

In response to

Browse pgsql-bugs by date

  From Date Subject
Next Message Steve Thames 2003-11-26 23:36:53 Problem using LIMIT 1 when only 1 record exists.
Previous Message Javier Carlos 2003-11-26 19:42:02 Re: SELECT with MANY tables