diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 3538392..7dda302 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -5091,7 +5091,7 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { if (oldestExtantVersion < writeVersion - kMaxCorrectVersionWindow) [[unlikely]] { - if (writeVersion > newestVersionFullPrecision + kNominalVersionWindow) { + if (writeVersion - kNominalVersionWindow > newestVersionFullPrecision) { eraseTree(rootParent->children[0], &writeContext); init(writeVersion - kNominalVersionWindow); } diff --git a/conflict_set_cxx_api_test.cpp b/conflict_set_cxx_api_test.cpp index 97804ed..4e621fd 100644 --- a/conflict_set_cxx_api_test.cpp +++ b/conflict_set_cxx_api_test.cpp @@ -2,6 +2,7 @@ #include #include +#include using namespace weaselab; @@ -23,6 +24,28 @@ int main(void) { int64_t bytes = cs.getBytes(); assert(bytes > 0); + // Regression: a write just below INT64_MAX must not overflow the nominal + // version window comparison and erase entries that reads still observe. + // Versions are only required to be >= 0, so this is valid input. + { + const int64_t imax = std::numeric_limits::max(); + const int64_t v0 = imax - int64_t(1000000000); + ConflictSet nearMax(0); + ConflictSet::WriteRange w2; + w2.begin.p = (const uint8_t *)"key"; + w2.begin.len = 3; + w2.end.len = 0; + nearMax.addWrites(&w2, 1, v0); + nearMax.addWrites(&w2, 0, imax); + ConflictSet::ReadRange r2; + r2.begin.p = (const uint8_t *)"key"; + r2.begin.len = 3; + r2.end.len = 0; + r2.readVersion = v0 - 1; + nearMax.check(&r2, &result, 1); + assert(result == ConflictSet::Conflict); + } + ConflictSet::MetricsV1 *metrics; int metricsCount; cs.getMetricsV1(&metrics, &metricsCount);