From: Robert Haas Date: Fri, 15 Oct 2021 18:54:18 +0000 (-0400) Subject: start of code to identify obsolete index entries + remove them X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=b7a66e83efd92f8a6cddc0e827f7acb2c99d7121;p=users%2Frhaas%2Fpostgres.git start of code to identify obsolete index entries + remove them --- diff --git a/src/backend/access/conveyor/cbmetapage.c b/src/backend/access/conveyor/cbmetapage.c index 4076c5cf2c..68fdd412b6 100644 --- a/src/backend/access/conveyor/cbmetapage.c +++ b/src/backend/access/conveyor/cbmetapage.c @@ -483,6 +483,129 @@ cb_metapage_remove_index_segment(CBMetapageData *meta, CBSegNo segno) } } +/* + * Examine the metapage state to determine how to go about recycling space. + * + * If the return value is CBM_OBSOLETE_SEGMENT_ENTRIES, then + * *oldest_index_segment will be set, and the caller should remove obsolete + * entries from that segment and/or the segment itself. + * + * If the return value is CBM_OBSOLETE_METAPAGE_ENTRIES, then *metapage_segno + * will be set to a payload segment that can be deallocated, and + * *metapage_offset to the location in the metapage where the index entry + * referencing that segment is stored. + * + * If the return value is CBM_OBSOLETE_METAPAGE_START, then there are + * no index segments and no uncleared index entries in the metapage that + * are obsolete, but some cleared index entries can be discarded. + * + * If the return value is CBM_OBSOLETE_NOTHING, there's nothing to do. + */ +CBMObsoleteState +cb_metapage_get_obsolete_state(CBMetapageData *meta, + CBSegNo *oldest_index_segment, + CBSegNo *metapage_segno, + unsigned *metapage_offset) +{ + CBPageNo istart = meta->cbm_index_start; + CBPageNo imstart = meta->cbm_index_metapage_start; + CBPageNo olpage = meta->cbm_oldest_logical_page; + uint16 pps = meta->cbm_pages_per_segment; + unsigned keep_offset; + unsigned offset; + + /* Sanity checks. */ + if (olpage < istart) + elog(ERROR, + "index starts at " UINT64_FORMAT " but oldest logical page is " UINT64_FORMAT, + istart, olpage); + if (imstart < istart) + elog(ERROR, + "metapage index starts at " UINT64_FORMAT " but index starts at " UINT64_FORMAT, + imstart, istart); + if (istart % pps != 0) + elog(ERROR, + "index start " UINT64_FORMAT " is not a multiple of pages per segment", + istart); + if (imstart % pps != 0) + elog(ERROR, + "index metapage start " UINT64_FORMAT " is not a multiple of pages per segment", + imstart); + + /* + * Detect the case where there is no obsolete data in the index. + * + * This happens if the oldest logical page is either equal to the start + * of the index, or follows it by less than the number of pages per + * segment. In the latter case, some but not all of the pages in the + * oldest payload segment are obsolete. We can only clean up entire + * payload semgents, so in such cases there is nothing to do. + */ + if (istart + pps > olpage) + return CBM_OBSOLETE_NOTHING; + + /* + * If there are any index segments, then the first step is to remove + * index entries from those segments, and the second step is to remove + * the segments themselves if they end up containing no useful entries. + * We need not consider doing anything in the metapage itself until no + * index segments remain. + */ + if (meta->cbm_oldest_index_segment != CB_INVALID_SEGMENT) + { + *oldest_index_segment = meta->cbm_oldest_index_segment; + return CBM_OBSOLETE_SEGMENT_ENTRIES; + } + + /* + * Since there are no index pages, the whole index is in the metapage, + * and therefore the logical page number should be somewhere in the range + * of pages covered by the metapage. + */ + if (olpage < imstart) + elog(ERROR, + "oldest logical page " UINT64_FORMAT " precedes metapage start " UINT64_FORMAT " but there are no index segments", + olpage, imstart); + + /* Search for obsolete index entries that have not yet been cleared. */ + keep_offset = (olpage - imstart) / pps; + for (offset = 0; offset < keep_offset; ++offset) + { + if (meta->cbm_index[offset] != CB_INVALID_SEGMENT) + { + *metapage_segno = meta->cbm_index[offset]; + *metapage_offset = offset; + return CBM_OBSOLETE_METAPAGE_ENTRIES; + } + } + + /* + * Apparently, there's nothing left to do but discard already-cleared + * index entries. + */ + return CBM_OBSOLETE_METAPAGE_START; +} + +/* + * Clear a single index entry from the metapage. + * + * We require that the caller provide not only the offset but the segment + * number that is expected to be found at that offset. That lets us check + * that nothing unexpected has occurred. + */ +void +cb_metapage_clear_obsolete_index_entry(CBMetapageData *meta, + CBSegNo segno, + unsigned offset) +{ + if (meta->cbm_index[offset] != offset) + elog(ERROR, + "index entry at offset %u was expected to be %u but found %u", + offset, segno, meta->cbm_index[offset]); + + meta->cbm_index[offset] = CB_INVALID_SEGMENT; +} + /* * Returns the lowest unused segment number covered by the metapage, * or CB_INVALID_SEGMENT if none. diff --git a/src/include/access/cbmetapage.h b/src/include/access/cbmetapage.h index 9157677bfe..50e3c79227 100644 --- a/src/include/access/cbmetapage.h +++ b/src/include/access/cbmetapage.h @@ -84,6 +84,33 @@ typedef enum CBM_INSERT_NEEDS_INDEX_SEGMENT } CBMInsertState; +/* + * Possible states of the metapage with regard to obsoleting index entries. + * + * CBM_OBSOLETE_SEGMENT_ENTRIES means that there may be index entries which + * are no longer required in the oldest index segment. + * + * CBM_OBSOLETE_METAPAGE_ENTRIES means that there are no index segments in + * existence and that there is at least one index entry in the metapage + * that is no longer required. + * + * CBM_OBSOLETE_METAPAGE_START means that there are no index segments in + * in existence and that all index entries in the metapage prior to the + * logical truncation point have been cleared; however, the metapage's + * notion of where the index begins should be advanced to free up space + * in the metapage. + * + * CBM_OBSOLETE_NOTHING means that there is no cleanup work of this type + * to be done. + */ +typedef enum +{ + CBM_OBSOLETE_SEGMENT_ENTRIES, + CBM_OBSOLETE_METAPAGE_ENTRIES, + CBM_OBSOLETE_METAPAGE_START, + CBM_OBSOLETE_NOTHING +} CBMObsoleteState; + /* * Function prototypes. */ @@ -124,10 +151,19 @@ extern void cb_metapage_get_index_info(CBMetapageData *meta, CBSegNo *oldest_index_segment, CBSegNo *newest_index_segment, uint64 *index_segments_moved); + extern void cb_metapage_add_index_segment(CBMetapageData *meta, CBSegNo segno); extern void cb_metapage_remove_index_segment(CBMetapageData *meta, CBSegNo segno); +extern CBMObsoleteState cb_metapage_get_obsolete_state(CBMetapageData *meta, + CBSegNo *oldest_index_segment, + CBSegNo *metapage_segno, + unsigned *metapage_offset); +extern void cb_metapage_clear_obsolete_index_entry(CBMetapageData *meta, + CBSegNo segno, + unsigned offset); + extern CBSegNo cb_metapage_find_free_segment(CBMetapageData *meta); extern bool cb_metapage_get_fsm_bit(CBMetapageData *meta, CBSegNo segno); extern void cb_metapage_set_fsm_bit(CBMetapageData *meta, CBSegNo segno,