From 57ec97f2ee8ef815f706d5c952fcb0969a9a3a91 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Mon, 5 Feb 2024 13:59:10 -0800 Subject: [PATCH] Naive range reads implementation and test --- ConflictSet.cpp | 24 +++++++++++++-- Internal.h | 81 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/ConflictSet.cpp b/ConflictSet.cpp index b1f5aa2..202e4a9 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -815,8 +815,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { result[i] = TooOld; continue; } - // TODO support non-point reads - assert(r.end.len == 0); auto [l, c] = lastLeq(root, std::span(r.begin.p, r.begin.len)); #if DEBUG_VERBOSE && !defined(NDEBUG) @@ -830,6 +828,28 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl { r.readVersion ? Conflict : Commit; + if (result[i] == Commit && r.end.len > 0) { + auto [e, c] = + lastLeq(root, std::span(r.end.p, r.end.len)); +#if DEBUG_VERBOSE && !defined(NDEBUG) + Arena arena; + fprintf(stderr, "LastLeq for `%s' got `%s'\n", printable(r.end).c_str(), + printable(getSearchPath(arena, e)).c_str()); +#endif + if (l == e) { + continue; + } + if (c != 0) { + e = nextLogical(e); + } + for (auto iter = nextLogical(l); iter != e; iter = nextLogical(iter)) { + if (iter->entry.pointVersion > r.readVersion || + iter->entry.rangeVersion > r.readVersion) { + result[i] = Conflict; + break; + } + } + } } } void addWrites(const WriteRange *writes, int count) { diff --git a/Internal.h b/Internal.h index 7a3dffc..a933f26 100644 --- a/Internal.h +++ b/Internal.h @@ -441,7 +441,7 @@ inline std::string printable(const Key &key) { namespace { template struct TestDriver { - // TODO call setOldestVersion, and check range writes/reads + // TODO call setOldestVersion Arbitrary arbitrary; explicit TestDriver(const uint8_t *data, size_t size) : arbitrary({data, size}) {} @@ -536,11 +536,13 @@ template struct TestDriver { refImpl.addWrites(writes, numPointWrites + numRangeWrites); } { - int numReads = arbitrary.bounded(10); + int numPointReads = arbitrary.bounded(100); + int numRangeReads = arbitrary.bounded(100); int64_t v = std::max(writeVersion - arbitrary.bounded(10), 0); - auto *reads = new (arena) ConflictSet::ReadRange[numReads]; + auto *reads = + new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads]; auto keys = set(arena); - while (int(keys.size()) < numReads) { + while (int(keys.size()) < numPointReads + numRangeReads * 2) { if (!arbitrary.hasEntropy()) { return true; } @@ -549,29 +551,68 @@ template struct TestDriver { arbitrary.randomBytes(begin, keyLen); keys.insert(std::string_view((const char *)begin, keyLen)); } + auto iter = keys.begin(); - for (int i = 0; i < numReads; ++i) { - reads[i].begin.p = (const uint8_t *)iter->data(); - reads[i].begin.len = iter->size(); - ++iter; - reads[i].end.len = 0; + int i = 0; + for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads; + pointsRemaining > 0 || rangesRemaining > 0; ++i) { + bool pointRead = pointsRemaining > 0 && rangesRemaining > 0 + ? bool(arbitrary.bounded(2)) + : pointsRemaining > 0; + if (pointRead) { + assert(pointsRemaining > 0); + reads[i].begin.p = (const uint8_t *)iter->data(); + reads[i].begin.len = iter->size(); + reads[i].end.len = 0; + ++iter; + --pointsRemaining; + } else { + assert(rangesRemaining > 0); + reads[i].begin.p = (const uint8_t *)iter->data(); + reads[i].begin.len = iter->size(); + ++iter; + reads[i].end.p = (const uint8_t *)iter->data(); + reads[i].end.len = iter->size(); + ++iter; + --rangesRemaining; + } reads[i].readVersion = v; #if DEBUG_VERBOSE && !defined(NDEBUG) - fprintf(stderr, "Read: {%s} at %d\n", printable(reads[i].begin).c_str(), - int(reads[i].readVersion)); + if (reads[i].end.len == 0) { + fprintf(stderr, "Read: {%s} @ %d\n", + printable(reads[i].begin).c_str(), int(reads[i].readVersion)); + } else { + fprintf(stderr, "Read: [%s, %s) @ %d\n", + printable(reads[i].begin).c_str(), + printable(reads[i].end).c_str(), int(reads[i].readVersion)); + } #endif } assert(iter == keys.end()); - auto *results1 = new (arena) ConflictSet::Result[numReads]; - auto *results2 = new (arena) ConflictSet::Result[numReads]; - cs.check(reads, results1, numReads); - refImpl.check(reads, results2, numReads); - for (int i = 0; i < numReads; ++i) { + assert(i == numPointReads + numRangeReads); + auto *results1 = + new (arena) ConflictSet::Result[numPointReads + numRangeReads]; + auto *results2 = + new (arena) ConflictSet::Result[numPointReads + numRangeReads]; + cs.check(reads, results1, numPointReads + numRangeReads); + refImpl.check(reads, results2, numPointReads + numRangeReads); + for (int i = 0; i < numPointReads + numRangeReads; ++i) { if (results1[i] != results2[i]) { - fprintf(stderr, - "Expected %s, got %s for read of %s at version %" PRId64 "\n", - resultToStr(results2[i]), resultToStr(results1[i]), - printable(reads[i].begin).c_str(), reads[i].readVersion); + if (reads[i].end.len == 0) { + fprintf(stderr, + "Expected %s, got %s for read of {%s} at version %" PRId64 + "\n", + resultToStr(results2[i]), resultToStr(results1[i]), + printable(reads[i].begin).c_str(), reads[i].readVersion); + } else { + fprintf( + stderr, + "Expected %s, got %s for read of [%s, %s) at version %" PRId64 + "\n", + resultToStr(results2[i]), resultToStr(results1[i]), + printable(reads[i].begin).c_str(), + printable(reads[i].end).c_str(), reads[i].readVersion); + } ok = false; return true; }