Commit e9568083 authored by Teodor Sigaev's avatar Teodor Sigaev

Add pages deleted from pending list to FSM

Add pages deleted from GIN's pending list during cleanup to free space map
immediately. Clean up process could be initiated by ordinary insert but adding
page to FSM might occur only at vacuum. On some workload like never-vacuumed
insert-only tables it could cause a huge bloat.

Jeff Janes <jeff.janes@gmail.com>
parent a1c44e1a
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/rel.h" #include "utils/rel.h"
#include "storage/indexfsm.h"
/* GUC parameter */ /* GUC parameter */
int gin_pending_list_limit = 0; int gin_pending_list_limit = 0;
...@@ -521,10 +522,12 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, ...@@ -521,10 +522,12 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
int64 nDeletedHeapTuples = 0; int64 nDeletedHeapTuples = 0;
ginxlogDeleteListPages data; ginxlogDeleteListPages data;
Buffer buffers[GIN_NDELETE_AT_ONCE]; Buffer buffers[GIN_NDELETE_AT_ONCE];
BlockNumber freespace[GIN_NDELETE_AT_ONCE];
data.ndeleted = 0; data.ndeleted = 0;
while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead) while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead)
{ {
freespace[data.ndeleted] = blknoToDelete;
buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete); buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete);
LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE); LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
page = BufferGetPage(buffers[data.ndeleted]); page = BufferGetPage(buffers[data.ndeleted]);
...@@ -609,6 +612,10 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead, ...@@ -609,6 +612,10 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
UnlockReleaseBuffer(buffers[i]); UnlockReleaseBuffer(buffers[i]);
END_CRIT_SECTION(); END_CRIT_SECTION();
for (i = 0; i < data.ndeleted; i++)
RecordFreeIndexPage(index, freespace[i]);
} while (blknoToDelete != newHead); } while (blknoToDelete != newHead);
return false; return false;
...@@ -744,6 +751,7 @@ ginInsertCleanup(GinState *ginstate, ...@@ -744,6 +751,7 @@ ginInsertCleanup(GinState *ginstate,
BuildAccumulator accum; BuildAccumulator accum;
KeyArray datums; KeyArray datums;
BlockNumber blkno; BlockNumber blkno;
bool fsm_vac = false;
metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO); metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
LockBuffer(metabuffer, GIN_SHARE); LockBuffer(metabuffer, GIN_SHARE);
...@@ -793,6 +801,7 @@ ginInsertCleanup(GinState *ginstate, ...@@ -793,6 +801,7 @@ ginInsertCleanup(GinState *ginstate,
{ {
/* another cleanup process is running concurrently */ /* another cleanup process is running concurrently */
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(buffer);
fsm_vac = false;
break; break;
} }
...@@ -857,6 +866,7 @@ ginInsertCleanup(GinState *ginstate, ...@@ -857,6 +866,7 @@ ginInsertCleanup(GinState *ginstate,
/* another cleanup process is running concurrently */ /* another cleanup process is running concurrently */
UnlockReleaseBuffer(buffer); UnlockReleaseBuffer(buffer);
LockBuffer(metabuffer, GIN_UNLOCK); LockBuffer(metabuffer, GIN_UNLOCK);
fsm_vac = false;
break; break;
} }
...@@ -895,9 +905,13 @@ ginInsertCleanup(GinState *ginstate, ...@@ -895,9 +905,13 @@ ginInsertCleanup(GinState *ginstate,
{ {
/* another cleanup process is running concurrently */ /* another cleanup process is running concurrently */
LockBuffer(metabuffer, GIN_UNLOCK); LockBuffer(metabuffer, GIN_UNLOCK);
fsm_vac = false;
break; break;
} }
/* At this point, some pending pages have been freed up */
fsm_vac = true;
Assert(blkno == metadata->head); Assert(blkno == metadata->head);
LockBuffer(metabuffer, GIN_UNLOCK); LockBuffer(metabuffer, GIN_UNLOCK);
...@@ -931,6 +945,15 @@ ginInsertCleanup(GinState *ginstate, ...@@ -931,6 +945,15 @@ ginInsertCleanup(GinState *ginstate,
ReleaseBuffer(metabuffer); ReleaseBuffer(metabuffer);
/*
* As pending list pages can have a high churn rate, it is
* desirable to recycle them immediately to the FreeSpace Map when
* ordinary backends clean the list.
*/
if (fsm_vac && !vac_delay)
IndexFreeSpaceMapVacuum(index);
/* Clean up temporary space */ /* Clean up temporary space */
MemoryContextSwitchTo(oldCtx); MemoryContextSwitchTo(oldCtx);
MemoryContextDelete(opCtx); MemoryContextDelete(opCtx);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment