On Wed, Feb 22, 2023 at 6:55 PM John Naylor <john.nay...@enterprisedb.com> wrote: > > > On Wed, Feb 22, 2023 at 3:29 PM Masahiko Sawada <sawada.m...@gmail.com> wrote: > > > > On Wed, Feb 22, 2023 at 4:35 PM John Naylor > > <john.nay...@enterprisedb.com> wrote: > > > > > > I don't think any vacuum calls in regression tests would stress any of > > > this code very much, so it's not worth carrying the old way forward. I > > > was thinking of only doing this as a short-time sanity check for testing > > > a real-world workload. > > > > I guess that It would also be helpful at least until the GA release. > > People will be able to test them easily on their workloads or their > > custom test scenarios. > > That doesn't seem useful to me. If we've done enough testing to reassure us > the new way always gives the same answer, the old way is not needed at commit > time. If there is any doubt it will always give the same answer, then the > whole patchset won't be committed.
True. Even if we're done enough testing we cannot claim there is no bug. My idea is to make the bug investigation easier but on reflection, it seems not the best idea given this purpose. Instead, it seems to be better to add more necessary assertions. What do you think about the attached patch? Please note that it also includes the changes for minimum memory requirement. Regards, -- Masahiko Sawada Amazon Web Services: https://aws.amazon.com
diff --git a/src/backend/access/common/tidstore.c b/src/backend/access/common/tidstore.c index 9360520482..fc20e58a95 100644 --- a/src/backend/access/common/tidstore.c +++ b/src/backend/access/common/tidstore.c @@ -75,6 +75,14 @@ typedef uint64 offsetbm; #define LOWER_OFFSET_NBITS 6 /* log(sizeof(offsetbm), 2) */ #define LOWER_OFFSET_MASK ((1 << LOWER_OFFSET_NBITS) - 1) +/* + * The minimum amount of memory required by TidStore is 2MB, the current minimum + * valid value for the maintenance_work_mem GUC. This is required to allocate the + * DSA initial segment, 1MB, and some meta data. This number is applied also to + * the local TidStore cases for simplicity. + */ +#define TIDSTORE_MIN_MEMORY (2 * 1024 * 1024L) /* 2MB */ + /* A magic value used to identify our TidStore. */ #define TIDSTORE_MAGIC 0x826f6a10 @@ -101,7 +109,7 @@ typedef struct TidStoreControl /* These values are never changed after creation */ size_t max_bytes; /* the maximum bytes a TidStore can use */ - int max_off; /* the maximum offset number */ + OffsetNumber max_off; /* the maximum offset number */ int max_off_nbits; /* the number of bits required for offset * numbers */ int upper_off_nbits; /* the number of bits of offset numbers @@ -174,10 +182,17 @@ static inline tidkey encode_tid(TidStore *ts, ItemPointer tid, offsetbm *off_bit * The radix tree for storage is allocated in DSA area is 'area' is non-NULL. */ TidStore * -TidStoreCreate(size_t max_bytes, int max_off, dsa_area *area) +TidStoreCreate(size_t max_bytes, OffsetNumber max_off, dsa_area *area) { TidStore *ts; + Assert(max_off <= MaxOffsetNumber); + + /* Sanity check for the max_bytes */ + if (max_bytes < TIDSTORE_MIN_MEMORY) + elog(ERROR, "memory for TidStore must be at least %ld, but %zu provided", + TIDSTORE_MIN_MEMORY, max_bytes); + ts = palloc0(sizeof(TidStore)); /* @@ -192,8 +207,8 @@ TidStoreCreate(size_t max_bytes, int max_off, dsa_area *area) * In local TidStore cases, the radix tree uses slab allocators for each kind * of node class. The most memory consuming case while adding Tids associated * with one page (i.e. during TidStoreSetBlockOffsets()) is that we allocate a new - * slab block for a new radix tree node, which is approximately 70kB. Therefore, - * we deduct 70kB from the max_bytes. + * slab block for a new radix tree node, which is approximately 70kB at most. + * Therefore, we deduct 70kB from the max_bytes. * * In shared cases, DSA allocates the memory segments big enough to follow * a geometric series that approximately doubles the total DSA size (see @@ -378,6 +393,7 @@ TidStoreSetBlockOffsets(TidStore *ts, BlockNumber blkno, OffsetNumber *offsets, const int nkeys = UINT64CONST(1) << ts->control->upper_off_nbits; Assert(!TidStoreIsShared(ts) || ts->control->magic == TIDSTORE_MAGIC); + Assert(BlockNumberIsValid(blkno)); bitmaps = palloc(sizeof(offsetbm) * nkeys); key = prev_key = key_base; @@ -386,6 +402,8 @@ TidStoreSetBlockOffsets(TidStore *ts, BlockNumber blkno, OffsetNumber *offsets, { offsetbm off_bit; + Assert(offsets[i] <= ts->control->max_off); + /* encode the tid to a key and partial offset */ key = encode_blk_off(ts, blkno, offsets[i], &off_bit); @@ -452,6 +470,8 @@ TidStoreIsMember(TidStore *ts, ItemPointer tid) offsetbm off_bit; bool found; + Assert(ItemPointerIsValid(tid)); + key = encode_tid(ts, tid, &off_bit); if (TidStoreIsShared(ts)) @@ -535,6 +555,7 @@ TidStoreIterateNext(TidStoreIter *iter) while (tidstore_iter(iter, &key, &off_bitmap)) { BlockNumber blkno = key_get_blkno(iter->ts, key); + Assert(BlockNumberIsValid(blkno)); if (BlockNumberIsValid(output->blkno) && output->blkno != blkno) { @@ -586,6 +607,7 @@ TidStoreNumTids(TidStore *ts) num_tids = ts->control->num_tids; LWLockRelease(&ts->control->lock); + Assert(num_tids >= 0); return num_tids; } diff --git a/src/include/access/tidstore.h b/src/include/access/tidstore.h index 66f0fdd482..d1cc93cbb6 100644 --- a/src/include/access/tidstore.h +++ b/src/include/access/tidstore.h @@ -30,7 +30,7 @@ typedef struct TidStoreIterResult OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER]; } TidStoreIterResult; -extern TidStore *TidStoreCreate(size_t max_bytes, int max_off, dsa_area *dsa); +extern TidStore *TidStoreCreate(size_t max_bytes, OffsetNumber max_off, dsa_area *dsa); extern TidStore *TidStoreAttach(dsa_area *dsa, dsa_pointer handle); extern void TidStoreDetach(TidStore *ts); extern void TidStoreDestroy(TidStore *ts);