v71-0002-DEV-Debug-TidStore.patch
application/x-patch
Filename: v71-0002-DEV-Debug-TidStore.patch
Type: application/x-patch
Part: 0
Patch
Format: format-patch
Series: patch v71-0002
Subject: DEV: Debug TidStore.
| File | + | − |
|---|---|---|
| src/backend/access/common/tidstore.c | 201 | 2 |
From 9b6b517b9f9977999034d16b4dc33e91094ae7ba Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
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