From 9b6b517b9f9977999034d16b4dc33e91094ae7ba Mon Sep 17 00:00:00 2001 From: Masahiko Sawada Date: Tue, 12 Dec 2023 22:36:24 +0900 Subject: [PATCH v71 2/6] DEV: Debug TidStore. --- src/backend/access/common/tidstore.c | 203 ++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 2 deletions(-) diff --git a/src/backend/access/common/tidstore.c b/src/backend/access/common/tidstore.c index b725b62d4c..33753d8ed2 100644 --- a/src/backend/access/common/tidstore.c +++ b/src/backend/access/common/tidstore.c @@ -29,6 +29,11 @@ #include "utils/dsa.h" #include "utils/memutils.h" +/* Enable TidStore debugging if USE_ASSERT_CHECKING */ +#ifdef USE_ASSERT_CHECKING +#define TIDSTORE_DEBUG +#include "catalog/index.h" +#endif #define WORDNUM(x) ((x) / BITS_PER_BITMAPWORD) #define BITNUM(x) ((x) % BITS_PER_BITMAPWORD) @@ -88,6 +93,13 @@ struct TidStore /* DSA area for TidStore if using shared memory */ dsa_area *area; + +#ifdef TIDSTORE_DEBUG + ItemPointerData *tids; + int64 max_tids; + int64 num_tids; + bool tids_unordered; +#endif }; #define TidStoreIsShared(ts) ((ts)->area != NULL) @@ -105,11 +117,25 @@ struct TidStoreIter /* output for the caller */ TidStoreIterResult output; + +#ifdef TIDSTORE_DEBUG + /* iterator index for the ts->tids array */ + int64 tids_idx; +#endif }; static void tidstore_iter_extract_tids(TidStoreIter *iter, uint64 key, BlocktableEntry *page); +/* debug functions available only when TIDSTORE_DEBUG */ +#ifdef TIDSTORE_DEBUG +static void ts_debug_set_block_offsets(TidStore *ts, BlockNumber blkno, + OffsetNumber *offsets, int num_offsets); +static void ts_debug_iter_check_tids(TidStoreIter *iter); +static bool ts_debug_is_member(TidStore *ts, ItemPointer tid); +static int itemptr_cmp(const void *left, const void *right); +#endif + /* * Create a TidStore. The TidStore will live in the memory context that is * CurrentMemoryContext at the time of this call. The TID storage, backed @@ -154,6 +180,17 @@ TidStoreCreate(size_t max_bytes, dsa_area *area) else ts->tree.local = local_rt_create(ts->rt_context); +#ifdef TIDSTORE_DEBUG + { + int64 max_tids = max_bytes / sizeof(ItemPointerData); + + ts->tids = palloc(sizeof(ItemPointerData) * max_tids); + ts->max_tids = max_tids; + ts->num_tids = 0; + ts->tids_unordered = false; + } +#endif + return ts; } @@ -191,6 +228,7 @@ TidStoreDetach(TidStore *ts) Assert(TidStoreIsShared(ts)); shared_rt_detach(ts->tree.shared); + pfree(ts); } @@ -241,6 +279,11 @@ TidStoreDestroy(TidStore *ts) MemoryContextDelete(ts->rt_context); +#ifdef TIDSTORE_DEBUG + if (!TidStoreIsShared(ts)) + pfree(ts->tids); +#endif + pfree(ts); } @@ -297,6 +340,14 @@ TidStoreSetBlockOffsets(TidStore *ts, BlockNumber blkno, OffsetNumber *offsets, found = local_rt_set(ts->tree.local, blkno, page); Assert(!found); + +#ifdef TIDSTORE_DEBUG + if (!TidStoreIsShared(ts)) + { + /* Insert TIDs into the TID array too */ + ts_debug_set_block_offsets(ts, blkno, offsets, num_offsets); + } +#endif } /* Return true if the given TID is present in the TidStore */ @@ -310,6 +361,13 @@ TidStoreIsMember(TidStore *ts, ItemPointer tid) OffsetNumber off = ItemPointerGetOffsetNumber(tid); bool ret; +#ifdef TIDSTORE_DEBUG + bool ret_debug = false; + + if (!TidStoreIsShared(ts)) + ret_debug = ts_debug_is_member(ts, tid); +#endif + if (TidStoreIsShared(ts)) page = shared_rt_find(ts->tree.shared, blk); else @@ -317,17 +375,29 @@ TidStoreIsMember(TidStore *ts, ItemPointer tid) /* no entry for the blk */ if (page == NULL) - return false; + { + ret = false; + goto done; + } wordnum = WORDNUM(off); bitnum = BITNUM(off); /* no bitmap for the off */ if (wordnum >= page->nwords) - return false; + { + ret = false; + goto done; + } ret = (page->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0; +done: +#ifdef TIDSTORE_DEBUG + if (!TidStoreIsShared(ts)) + Assert(ret == ret_debug); +#endif + return ret; } @@ -360,6 +430,11 @@ TidStoreBeginIterate(TidStore *ts) else iter->tree_iter.local = local_rt_begin_iterate(ts->tree.local); +#ifdef TIDSTORE_DEBUG + if (!TidStoreIsShared(ts)) + iter->tids_idx = 0; +#endif + return iter; } @@ -387,6 +462,11 @@ TidStoreIterateNext(TidStoreIter *iter) /* Collect TIDs extracted from the key-value pair */ tidstore_iter_extract_tids(iter, key, page); +#ifdef TIDSTORE_DEBUG + if (!TidStoreIsShared(iter->ts)) + ts_debug_iter_check_tids(iter); +#endif + return result; } @@ -459,3 +539,122 @@ tidstore_iter_extract_tids(TidStoreIter *iter, uint64 key, BlocktableEntry *page } } } + +#ifdef TIDSTORE_DEBUG +/* Comparator routines for ItemPointer */ +static int +itemptr_cmp(const void *left, const void *right) +{ + BlockNumber lblk, + rblk; + OffsetNumber loff, + roff; + + lblk = ItemPointerGetBlockNumber((ItemPointer) left); + rblk = ItemPointerGetBlockNumber((ItemPointer) right); + + if (lblk < rblk) + return -1; + if (lblk > rblk) + return 1; + + loff = ItemPointerGetOffsetNumber((ItemPointer) left); + roff = ItemPointerGetOffsetNumber((ItemPointer) right); + + if (loff < roff) + return -1; + if (loff > roff) + return 1; + + return 0; +} + +/* Insert TIDs to the TID array for debugging */ +static void +ts_debug_set_block_offsets(TidStore *ts, BlockNumber blkno, OffsetNumber *offsets, + int num_offsets) +{ + if (ts->num_tids > 0 && + blkno < ItemPointerGetBlockNumber(&(ts->tids[ts->num_tids - 1]))) + { + /* The array will be sorted at ts_debug_is_member() */ + ts->tids_unordered = true; + } + + for (int i = 0; i < num_offsets; i++) + { + ItemPointer tid; + int idx = ts->num_tids + i; + + /* Enlarge the TID array if necessary */ + if (idx >= ts->max_tids) + { + ts->max_tids *= 2; + ts->tids = repalloc(ts->tids, sizeof(ItemPointerData) * ts->max_tids); + } + + tid = &(ts->tids[idx]); + + ItemPointerSetBlockNumber(tid, blkno); + ItemPointerSetOffsetNumber(tid, offsets[i]); + } + + ts->num_tids += num_offsets; +} + +/* Return true if the given TID is present in the TID array */ +static bool +ts_debug_is_member(TidStore *ts, ItemPointer tid) +{ + int64 litem, + ritem, + item; + ItemPointer res; + + if (ts->num_tids == 0) + return false; + + /* Make sure the TID array is sorted */ + if (ts->tids_unordered) + { + qsort(ts->tids, ts->num_tids, sizeof(ItemPointerData), itemptr_cmp); + ts->tids_unordered = false; + } + + litem = itemptr_encode(&ts->tids[0]); + ritem = itemptr_encode(&ts->tids[ts->num_tids - 1]); + item = itemptr_encode(tid); + + /* + * Doing a simple bound check before bsearch() is useful to avoid the + * extra cost of bsearch(), especially if dead items on the heap are + * concentrated in a certain range. Since this function is called for + * every index tuple, it pays to be really fast. + */ + if (item < litem || item > ritem) + return false; + + res = bsearch(tid, ts->tids, ts->num_tids, sizeof(ItemPointerData), + itemptr_cmp); + + return (res != NULL); +} + +/* Verify if the iterator output matches the TIDs in the array for debugging */ +static void +ts_debug_iter_check_tids(TidStoreIter *iter) +{ + BlockNumber blkno = iter->output.blkno; + + for (int i = 0; i < iter->output.num_offsets; i++) + { + ItemPointer tid = &(iter->ts->tids[iter->tids_idx + i]); + + Assert((iter->tids_idx + i) < iter->ts->max_tids); + Assert(ItemPointerGetBlockNumber(tid) == blkno); + Assert(ItemPointerGetOffsetNumber(tid) == iter->output.offsets[i]); + } + + iter->tids_idx += iter->output.num_offsets; +} +#endif -- 2.44.0