hash_table: return TooOld outside the two-billion-version window

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.
This commit is contained in:
weaselbot committed 2026-11-10 15:28:58 -05:00
1 parent e23018c952
commit 78a5e8a5d5
1 file changed
+13 -2
+13 -2
View File
@@ -18,15 +18,21 @@ struct string_hash {
} }
}; };
// Reads more than this many versions behind `newestVersion` are `TooOld`, so
// that a caller whose read version fell out of the version window is told to
// retry. See the `addWrites` postcondition in ConflictSet.h.
constexpr int64_t kVersionWindow = 2'000'000'000;
struct __attribute__((visibility("hidden"))) ConflictSet::Impl { struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {} Impl(int64_t oldestVersion)
: oldestVersion(oldestVersion), newestVersion(oldestVersion) {}
void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results, void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results,
int count) const { int count) const {
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
auto key = auto key =
std::string_view((const char *)reads[i].begin.p, reads[i].begin.len); std::string_view((const char *)reads[i].begin.p, reads[i].begin.len);
auto version = reads[i].readVersion; auto version = reads[i].readVersion;
if (version < oldestVersion) { if (version < oldestVersion || version < newestVersion - kVersionWindow) {
results[i] = TooOld; results[i] = TooOld;
continue; continue;
} }
@@ -38,6 +44,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
void addWrites(const ConflictSet::WriteRange *writes, int count, void addWrites(const ConflictSet::WriteRange *writes, int count,
int64_t writeVersion) { int64_t writeVersion) {
// Advance newestVersion before the loop, so that it also advances when
// count == 0, as documented in ConflictSet.h.
assert(writeVersion >= newestVersion);
newestVersion = writeVersion;
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
auto &max = map[std::string((const char *)writes[i].begin.p, auto &max = map[std::string((const char *)writes[i].begin.p,
writes[i].begin.len)]; writes[i].begin.len)];
@@ -84,6 +94,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
private: private:
int64_t keyUpdates = 0; int64_t keyUpdates = 0;
int64_t oldestVersion; int64_t oldestVersion;
int64_t newestVersion;
std::unordered_map<std::string, int64_t, string_hash, std::equal_to<>> map; std::unordered_map<std::string, int64_t, string_hash, std::equal_to<>> map;
std::string removalKey; std::string removalKey;
}; };