start of code to identify obsolete index entries + remove them
authorRobert Haas <[email protected]>
Fri, 15 Oct 2021 18:54:18 +0000 (14:54 -0400)
committerRobert Haas <[email protected]>
Fri, 15 Oct 2021 18:54:18 +0000 (14:54 -0400)
src/backend/access/conveyor/cbmetapage.c
src/include/access/cbmetapage.h

index 4076c5cf2c2827075e2aa5f152d8843b8f99acae..68fdd412b6d08a403ef099295258d06ab783bf0a 100644 (file)
@@ -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.
index 9157677bfe02ed05bb4eb6767bb6cd3495654fd4..50e3c79227a7793c013882059aaa7605c81bdcbb 100644 (file)
@@ -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,