diff --git a/HashTable.cpp b/HashTable.cpp index b1dfc92..58c50cf 100644 --- a/HashTable.cpp +++ b/HashTable.cpp @@ -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 { - Impl(int64_t oldestVersion) : oldestVersion(oldestVersion) {} + Impl(int64_t oldestVersion) + : oldestVersion(oldestVersion), newestVersion(oldestVersion) {} void check(const ConflictSet::ReadRange *reads, ConflictSet::Result *results, int count) const { for (int i = 0; i < count; ++i) { auto key = std::string_view((const char *)reads[i].begin.p, reads[i].begin.len); auto version = reads[i].readVersion; - if (version < oldestVersion) { + if (version < oldestVersion || version < newestVersion - kVersionWindow) { results[i] = TooOld; continue; } @@ -38,6 +44,10 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { void addWrites(const ConflictSet::WriteRange *writes, int count, 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) { auto &max = map[std::string((const char *)writes[i].begin.p, writes[i].begin.len)]; @@ -84,6 +94,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { private: int64_t keyUpdates = 0; int64_t oldestVersion; + int64_t newestVersion; std::unordered_map> map; std::string removalKey; };