diff options
Diffstat (limited to 'src/backend/access/gist/gistvacuum.c')
-rw-r--r-- | src/backend/access/gist/gistvacuum.c | 160 |
1 files changed, 67 insertions, 93 deletions
diff --git a/src/backend/access/gist/gistvacuum.c b/src/backend/access/gist/gistvacuum.c index def74fdaa37..a9c616c7724 100644 --- a/src/backend/access/gist/gistvacuum.c +++ b/src/backend/access/gist/gistvacuum.c @@ -24,58 +24,34 @@ #include "storage/lmgr.h" #include "utils/memutils.h" -/* - * State kept across vacuum stages. - */ +/* Working state needed by gistbulkdelete */ typedef struct { - IndexBulkDeleteResult stats; /* must be first */ + IndexVacuumInfo *info; + IndexBulkDeleteResult *stats; + IndexBulkDeleteCallback callback; + void *callback_state; + GistNSN startNSN; /* - * These are used to memorize all internal and empty leaf pages in the 1st - * vacuum stage. They are used in the 2nd stage, to delete all the empty - * pages. + * These are used to memorize all internal and empty leaf pages. They are + * used for deleting all the empty pages. */ IntegerSet *internal_page_set; IntegerSet *empty_leaf_set; MemoryContext page_set_context; -} GistBulkDeleteResult; - -/* Working state needed by gistbulkdelete */ -typedef struct -{ - IndexVacuumInfo *info; - GistBulkDeleteResult *stats; - IndexBulkDeleteCallback callback; - void *callback_state; - GistNSN startNSN; } GistVacState; -static void gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, +static void gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state); static void gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno); static void gistvacuum_delete_empty_pages(IndexVacuumInfo *info, - GistBulkDeleteResult *stats); -static bool gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats, + GistVacState *vstate); +static bool gistdeletepage(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, Buffer buffer, OffsetNumber downlink, Buffer leafBuffer); -/* allocate the 'stats' struct that's kept over vacuum stages */ -static GistBulkDeleteResult * -create_GistBulkDeleteResult(void) -{ - GistBulkDeleteResult *gist_stats; - - gist_stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult)); - gist_stats->page_set_context = - GenerationContextCreate(CurrentMemoryContext, - "GiST VACUUM page set context", - 16 * 1024); - - return gist_stats; -} - /* * VACUUM bulkdelete stage: remove index entries. */ @@ -83,15 +59,13 @@ IndexBulkDeleteResult * gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state) { - GistBulkDeleteResult *gist_stats = (GistBulkDeleteResult *) stats; - /* allocate stats if first time through, else re-use existing struct */ - if (gist_stats == NULL) - gist_stats = create_GistBulkDeleteResult(); + if (stats == NULL) + stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); - gistvacuumscan(info, gist_stats, callback, callback_state); + gistvacuumscan(info, stats, callback, callback_state); - return (IndexBulkDeleteResult *) gist_stats; + return stats; } /* @@ -100,8 +74,6 @@ gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteResult * gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) { - GistBulkDeleteResult *gist_stats = (GistBulkDeleteResult *) stats; - /* No-op in ANALYZE ONLY mode */ if (info->analyze_only) return stats; @@ -111,25 +83,13 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) * stats from the latest gistbulkdelete call. If it wasn't called, we * still need to do a pass over the index, to obtain index statistics. */ - if (gist_stats == NULL) + if (stats == NULL) { - gist_stats = create_GistBulkDeleteResult(); - gistvacuumscan(info, gist_stats, NULL, NULL); + stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult)); + gistvacuumscan(info, stats, NULL, NULL); } /* - * If we saw any empty pages, try to unlink them from the tree so that - * they can be reused. - */ - gistvacuum_delete_empty_pages(info, gist_stats); - - /* we don't need the internal and empty page sets anymore */ - MemoryContextDelete(gist_stats->page_set_context); - gist_stats->page_set_context = NULL; - gist_stats->internal_page_set = NULL; - gist_stats->empty_leaf_set = NULL; - - /* * It's quite possible for us to be fooled by concurrent page splits into * double-counting some index tuples, so disbelieve any total that exceeds * the underlying heap's count ... if we know that accurately. Otherwise @@ -137,11 +97,11 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) */ if (!info->estimated_count) { - if (gist_stats->stats.num_index_tuples > info->num_heap_tuples) - gist_stats->stats.num_index_tuples = info->num_heap_tuples; + if (stats->num_index_tuples > info->num_heap_tuples) + stats->num_index_tuples = info->num_heap_tuples; } - return (IndexBulkDeleteResult *) gist_stats; + return stats; } /* @@ -153,15 +113,16 @@ gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats) * occurred). * * This also makes note of any empty leaf pages, as well as all internal - * pages. The second stage, gistvacuum_delete_empty_pages(), needs that - * information. Any deleted pages are added directly to the free space map. - * (They should've been added there when they were originally deleted, already, - * but it's possible that the FSM was lost at a crash, for example.) + * pages while looping over all index pages. After scanning all the pages, we + * remove the empty pages so that they can be reused. Any deleted pages are + * added directly to the free space map. (They should've been added there + * when they were originally deleted, already, but it's possible that the FSM + * was lost at a crash, for example.) * * The caller is responsible for initially allocating/zeroing a stats struct. */ static void -gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, +gistvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback, void *callback_state) { Relation rel = info->index; @@ -175,11 +136,10 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, * Reset counts that will be incremented during the scan; needed in case * of multiple scans during a single VACUUM command. */ - stats->stats.estimated_count = false; - stats->stats.num_index_tuples = 0; - stats->stats.pages_deleted = 0; - stats->stats.pages_free = 0; - MemoryContextReset(stats->page_set_context); + stats->estimated_count = false; + stats->num_index_tuples = 0; + stats->pages_deleted = 0; + stats->pages_free = 0; /* * Create the integer sets to remember all the internal and the empty leaf @@ -187,9 +147,12 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, * this context so that the subsequent allocations for these integer sets * will be done from the same context. */ - oldctx = MemoryContextSwitchTo(stats->page_set_context); - stats->internal_page_set = intset_create(); - stats->empty_leaf_set = intset_create(); + vstate.page_set_context = GenerationContextCreate(CurrentMemoryContext, + "GiST VACUUM page set context", + 16 * 1024); + oldctx = MemoryContextSwitchTo(vstate.page_set_context); + vstate.internal_page_set = intset_create(); + vstate.empty_leaf_set = intset_create(); MemoryContextSwitchTo(oldctx); /* Set up info to pass down to gistvacuumpage */ @@ -257,11 +220,23 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, * Note that if no recyclable pages exist, we don't bother vacuuming the * FSM at all. */ - if (stats->stats.pages_free > 0) + if (stats->pages_free > 0) IndexFreeSpaceMapVacuum(rel); /* update statistics */ - stats->stats.num_pages = num_pages; + stats->num_pages = num_pages; + + /* + * If we saw any empty pages, try to unlink them from the tree so that + * they can be reused. + */ + gistvacuum_delete_empty_pages(info, &vstate); + + /* we don't need the internal and empty page sets anymore */ + MemoryContextDelete(vstate.page_set_context); + vstate.page_set_context = NULL; + vstate.internal_page_set = NULL; + vstate.empty_leaf_set = NULL; } /* @@ -278,7 +253,6 @@ gistvacuumscan(IndexVacuumInfo *info, GistBulkDeleteResult *stats, static void gistvacuumpage(GistVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno) { - GistBulkDeleteResult *stats = vstate->stats; IndexVacuumInfo *info = vstate->info; IndexBulkDeleteCallback callback = vstate->callback; void *callback_state = vstate->callback_state; @@ -307,13 +281,13 @@ restart: { /* Okay to recycle this page */ RecordFreeIndexPage(rel, blkno); - stats->stats.pages_free++; - stats->stats.pages_deleted++; + vstate->stats->pages_free++; + vstate->stats->pages_deleted++; } else if (GistPageIsDeleted(page)) { /* Already deleted, but can't recycle yet */ - stats->stats.pages_deleted++; + vstate->stats->pages_deleted++; } else if (GistPageIsLeaf(page)) { @@ -388,7 +362,7 @@ restart: END_CRIT_SECTION(); - stats->stats.tuples_removed += ntodelete; + vstate->stats->tuples_removed += ntodelete; /* must recompute maxoff */ maxoff = PageGetMaxOffsetNumber(page); } @@ -405,10 +379,10 @@ restart: * it up. */ if (blkno == orig_blkno) - intset_add_member(stats->empty_leaf_set, blkno); + intset_add_member(vstate->empty_leaf_set, blkno); } else - stats->stats.num_index_tuples += nremain; + vstate->stats->num_index_tuples += nremain; } else { @@ -443,7 +417,7 @@ restart: * parents of empty leaf pages. */ if (blkno == orig_blkno) - intset_add_member(stats->internal_page_set, blkno); + intset_add_member(vstate->internal_page_set, blkno); } UnlockReleaseBuffer(buffer); @@ -466,7 +440,7 @@ restart: * Scan all internal pages, and try to delete their empty child pages. */ static void -gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats) +gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistVacState *vstate) { Relation rel = info->index; BlockNumber empty_pages_remaining; @@ -475,10 +449,10 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats /* * Rescan all inner pages to find those that have empty child pages. */ - empty_pages_remaining = intset_num_entries(stats->empty_leaf_set); - intset_begin_iterate(stats->internal_page_set); + empty_pages_remaining = intset_num_entries(vstate->empty_leaf_set); + intset_begin_iterate(vstate->internal_page_set); while (empty_pages_remaining > 0 && - intset_iterate_next(stats->internal_page_set, &blkno)) + intset_iterate_next(vstate->internal_page_set, &blkno)) { Buffer buffer; Page page; @@ -521,7 +495,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats BlockNumber leafblk; leafblk = ItemPointerGetBlockNumber(&(idxtuple->t_tid)); - if (intset_is_member(stats->empty_leaf_set, leafblk)) + if (intset_is_member(vstate->empty_leaf_set, leafblk)) { leafs_to_delete[ntodelete] = leafblk; todelete[ntodelete++] = off; @@ -561,7 +535,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats gistcheckpage(rel, leafbuf); LockBuffer(buffer, GIST_EXCLUSIVE); - if (gistdeletepage(info, stats, + if (gistdeletepage(info, vstate->stats, buffer, todelete[i] - deleted, leafbuf)) deleted++; @@ -573,7 +547,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats ReleaseBuffer(buffer); /* update stats */ - stats->stats.pages_removed += deleted; + vstate->stats->pages_removed += deleted; /* * We can stop the scan as soon as we have seen the downlinks, even if @@ -596,7 +570,7 @@ gistvacuum_delete_empty_pages(IndexVacuumInfo *info, GistBulkDeleteResult *stats * prevented it. */ static bool -gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats, +gistdeletepage(IndexVacuumInfo *info, IndexBulkDeleteResult *stats, Buffer parentBuffer, OffsetNumber downlink, Buffer leafBuffer) { @@ -665,7 +639,7 @@ gistdeletepage(IndexVacuumInfo *info, GistBulkDeleteResult *stats, /* mark the page as deleted */ MarkBufferDirty(leafBuffer); GistPageSetDeleted(leafPage, txid); - stats->stats.pages_deleted++; + stats->pages_deleted++; /* remove the downlink from the parent */ MarkBufferDirty(parentBuffer); |