]> granicus.if.org Git - postgresql/commitdiff
Add pages deleted from pending list to FSM
authorTeodor Sigaev <teodor@sigaev.ru>
Mon, 7 Sep 2015 13:24:01 +0000 (16:24 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Mon, 7 Sep 2015 13:24:01 +0000 (16:24 +0300)
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>

src/backend/access/gin/ginfast.c

index ae446653a956a3b830d18013dd5e812c13448759..fa8d8a756ebc901850504ddb7b78a97db0e3dd3e 100644 (file)
@@ -24,6 +24,7 @@
 #include "miscadmin.h"
 #include "utils/memutils.h"
 #include "utils/rel.h"
+#include "storage/indexfsm.h"
 
 /* GUC parameter */
 int                    gin_pending_list_limit = 0;
@@ -521,10 +522,12 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
                int64           nDeletedHeapTuples = 0;
                ginxlogDeleteListPages data;
                Buffer          buffers[GIN_NDELETE_AT_ONCE];
+               BlockNumber     freespace[GIN_NDELETE_AT_ONCE];
 
                data.ndeleted = 0;
                while (data.ndeleted < GIN_NDELETE_AT_ONCE && blknoToDelete != newHead)
                {
+                       freespace[data.ndeleted] = blknoToDelete;
                        buffers[data.ndeleted] = ReadBuffer(index, blknoToDelete);
                        LockBuffer(buffers[data.ndeleted], GIN_EXCLUSIVE);
                        page = BufferGetPage(buffers[data.ndeleted]);
@@ -609,6 +612,10 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
                        UnlockReleaseBuffer(buffers[i]);
 
                END_CRIT_SECTION();
+
+               for (i = 0; i < data.ndeleted; i++)
+                       RecordFreeIndexPage(index, freespace[i]);
+
        } while (blknoToDelete != newHead);
 
        return false;
@@ -744,6 +751,7 @@ ginInsertCleanup(GinState *ginstate,
        BuildAccumulator accum;
        KeyArray        datums;
        BlockNumber blkno;
+       bool            fsm_vac = false;
 
        metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
        LockBuffer(metabuffer, GIN_SHARE);
@@ -793,6 +801,7 @@ ginInsertCleanup(GinState *ginstate,
                {
                        /* another cleanup process is running concurrently */
                        UnlockReleaseBuffer(buffer);
+                       fsm_vac = false;
                        break;
                }
 
@@ -857,6 +866,7 @@ ginInsertCleanup(GinState *ginstate,
                                /* another cleanup process is running concurrently */
                                UnlockReleaseBuffer(buffer);
                                LockBuffer(metabuffer, GIN_UNLOCK);
+                               fsm_vac = false;
                                break;
                        }
 
@@ -895,9 +905,13 @@ ginInsertCleanup(GinState *ginstate,
                        {
                                /* another cleanup process is running concurrently */
                                LockBuffer(metabuffer, GIN_UNLOCK);
+                               fsm_vac = false;
                                break;
                        }
 
+                       /* At this point, some pending pages have been freed up */
+                       fsm_vac = true;
+
                        Assert(blkno == metadata->head);
                        LockBuffer(metabuffer, GIN_UNLOCK);
 
@@ -931,6 +945,15 @@ ginInsertCleanup(GinState *ginstate,
 
        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 */
        MemoryContextSwitchTo(oldCtx);
        MemoryContextDelete(opCtx);