hash_table: return TooOld outside the two-billion-version window (#98) #103

Open
weaselbot wants to merge 2 commits from weaselbot/conflict-set:weaselbot/issue-98 into main
pull from: weaselbot/conflict-set:weaselbot/issue-98
Member

Closes #98.

Problem

HashTable.cpp never tracked newestVersion, so it could not implement the
addWrites postcondition (newestVersion becomes writeVersion; oldestVersion
becomes the larger of its previous value and newestVersion - two billion) or
the class invariant oldestVersion >= newestVersion - two billion. Reads whose
readVersion fell out of the two-billion-version window were reported
Conflict/Commit instead of TooOld, so a caller that switched to the
hash_table implementation silently lost the "this read version is too old,
retry" signal.

Fix

Mirror SkipList.cpp / ReferenceImpl:

  • Impl gains an int64_t newestVersion, initialized to the constructor's
    oldestVersion.
  • addWrites asserts writeVersion >= newestVersion and sets
    newestVersion = writeVersion before the loop, so it advances even when
    count == 0.
  • check returns TooOld when
    version < oldestVersion || version < newestVersion - kVersionWindow, where
    kVersionWindow is 2'000'000'000 (integer arithmetic, no float rounding).

oldestVersion itself is left to setOldestVersion, exactly like the skip
list: the window is enforced at check time.

Tests

  • test_two_billion_versions now asserts its results (it previously asserted
    nothing, so it would have passed even if all implementations agreed on a
    wrong answer).
  • New test_two_billion_version_window exercises radix_tree, skip_list and
    hash_table with and without writes, at newestVersion, just inside the
    window (newestVersion - 2e9, still Conflict/Commit) and just outside
    it (newestVersion - 2e9 - 1, TooOld). It fails on main and passes here.

Full ctest suite passes in a Debug build (8603 tests, 0 failures), and
hash_table also builds cleanly with -DCMAKE_BUILD_TYPE=Release. clang-format
and black pass via pre-commit.

Closes #98. ## Problem `HashTable.cpp` never tracked `newestVersion`, so it could not implement the `addWrites` postcondition (`newestVersion` becomes `writeVersion`; `oldestVersion` becomes the larger of its previous value and `newestVersion - two billion`) or the class invariant `oldestVersion >= newestVersion - two billion`. Reads whose `readVersion` fell out of the two-billion-version window were reported `Conflict`/`Commit` instead of `TooOld`, so a caller that switched to the `hash_table` implementation silently lost the "this read version is too old, retry" signal. ## Fix Mirror `SkipList.cpp` / `ReferenceImpl`: * `Impl` gains an `int64_t newestVersion`, initialized to the constructor's `oldestVersion`. * `addWrites` asserts `writeVersion >= newestVersion` and sets `newestVersion = writeVersion` before the loop, so it advances even when `count == 0`. * `check` returns `TooOld` when `version < oldestVersion || version < newestVersion - kVersionWindow`, where `kVersionWindow` is `2'000'000'000` (integer arithmetic, no float rounding). `oldestVersion` itself is left to `setOldestVersion`, exactly like the skip list: the window is enforced at `check` time. ## Tests * `test_two_billion_versions` now asserts its results (it previously asserted nothing, so it would have passed even if all implementations agreed on a wrong answer). * New `test_two_billion_version_window` exercises `radix_tree`, `skip_list` and `hash_table` with and without writes, at `newestVersion`, just inside the window (`newestVersion - 2e9`, still `Conflict`/`Commit`) and just outside it (`newestVersion - 2e9 - 1`, `TooOld`). It fails on `main` and passes here. Full `ctest` suite passes in a `Debug` build (8603 tests, 0 failures), and `hash_table` also builds cleanly with `-DCMAKE_BUILD_TYPE=Release`. `clang-format` and `black` pass via pre-commit.
weaselbot added 2 commits 2026-11-10 20:29:07 +00:00
addWrites now advances newestVersion (even when count == 0), and check
treats a readVersion more than two billion versions behind newestVersion as
TooOld, matching the documented postcondition and the radix tree and skip
list implementations. Previously hash_table discarded writeVersion entirely,
so callers that switched implementations silently lost the "this read version
is too old, retry" signal.
Test the two-billion-version window in every implementation
CI / test (amd64, -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, 21, , gcc, ubuntu-latest-amd64) (pull_request) Waiting to run
CI / test (amd64, -DUSE_SIMD_FALLBACK=ON, 21, , simd-fallback, ubuntu-latest-amd64) (pull_request) Waiting to run
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Waiting to run
CI / coverage (pull_request) Waiting to run
CI / test (arm64, -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, 22, https://minio.weaselab.dev/public/aarch64/msan-toolchain-22.1.8.tar.zst, debug-arm64, ubuntu-latest-arm64) (pull_request) Successful in 4m6s
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 4m31s
CI / pre-commit (pull_request) Successful in 2m22s
CI / test (amd64, -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, 21, https://minio.weaselab.dev/public/x86_64/msan-toolchain-21.1.8.tar.zst, debug, ubuntu-latest-amd64) (pull_request) Successful in 4m10s
CI / test (amd64, -DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 21, , 64-bit-versions, ubuntu-latest-amd64) (pull_request) In progress
2f4de127c0
test_two_billion_versions asserted nothing about the results, and hash_table
was never exercised across a version jump larger than two billion. Assert the
expected results and add test_two_billion_version_window, which checks the
window boundary (and the count == 0 case) for radix_tree, skip_list and
hash_table.
Some checks are pending
CI / test (amd64, -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++, 21, , gcc, ubuntu-latest-amd64) (pull_request) Waiting to run
Required
Details
CI / test (amd64, -DUSE_SIMD_FALLBACK=ON, 21, , simd-fallback, ubuntu-latest-amd64) (pull_request) Waiting to run
Required
Details
CI / release (amd64, -DMSAN_TOOLCHAIN_PATH=/opt/msan, ubuntu-latest-amd64) (pull_request) Waiting to run
Required
Details
CI / coverage (pull_request) Waiting to run
Required
Details
CI / test (arm64, -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, 22, https://minio.weaselab.dev/public/aarch64/msan-toolchain-22.1.8.tar.zst, debug-arm64, ubuntu-latest-arm64) (pull_request) Successful in 4m6s
Required
Details
CI / release (arm64, , ubuntu-latest-arm64) (pull_request) Successful in 4m31s
Required
Details
CI / pre-commit (pull_request) Successful in 2m22s
Required
Details
CI / test (amd64, -DCMAKE_BUILD_TYPE=Debug -DMSAN_TOOLCHAIN_PATH=/opt/msan, 21, https://minio.weaselab.dev/public/x86_64/msan-toolchain-21.1.8.tar.zst, debug, ubuntu-latest-amd64) (pull_request) Successful in 4m10s
Required
Details
CI / test (amd64, -DCMAKE_CXX_FLAGS=-DUSE_64_BIT=1, 21, , 64-bit-versions, ubuntu-latest-amd64) (pull_request) In progress
Required
Details
Some required checks are missing.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u https://git.weaselab.dev/weaselbot/conflict-set weaselbot/issue-98:weaselbot-weaselbot/issue-98
git checkout weaselbot-weaselbot/issue-98
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: weaselab/conflict-set#103