Naive range reads implementation and test

This commit is contained in:
2024-02-05 13:59:10 -08:00
parent e3e0e7ba44
commit 57ec97f2ee
2 changed files with 83 additions and 22 deletions

View File

@@ -815,8 +815,6 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
result[i] = TooOld; result[i] = TooOld;
continue; continue;
} }
// TODO support non-point reads
assert(r.end.len == 0);
auto [l, c] = auto [l, c] =
lastLeq(root, std::span<const uint8_t>(r.begin.p, r.begin.len)); lastLeq(root, std::span<const uint8_t>(r.begin.p, r.begin.len));
#if DEBUG_VERBOSE && !defined(NDEBUG) #if DEBUG_VERBOSE && !defined(NDEBUG)
@@ -830,6 +828,28 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
r.readVersion r.readVersion
? Conflict ? Conflict
: Commit; : Commit;
if (result[i] == Commit && r.end.len > 0) {
auto [e, c] =
lastLeq(root, std::span<const uint8_t>(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) { void addWrites(const WriteRange *writes, int count) {

View File

@@ -441,7 +441,7 @@ inline std::string printable(const Key &key) {
namespace { namespace {
template <class ConflictSetImpl> struct TestDriver { template <class ConflictSetImpl> struct TestDriver {
// TODO call setOldestVersion, and check range writes/reads // TODO call setOldestVersion
Arbitrary arbitrary; Arbitrary arbitrary;
explicit TestDriver(const uint8_t *data, size_t size) explicit TestDriver(const uint8_t *data, size_t size)
: arbitrary({data, size}) {} : arbitrary({data, size}) {}
@@ -536,11 +536,13 @@ template <class ConflictSetImpl> struct TestDriver {
refImpl.addWrites(writes, numPointWrites + numRangeWrites); 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<int64_t>(writeVersion - arbitrary.bounded(10), 0); int64_t v = std::max<int64_t>(writeVersion - arbitrary.bounded(10), 0);
auto *reads = new (arena) ConflictSet::ReadRange[numReads]; auto *reads =
new (arena) ConflictSet::ReadRange[numPointReads + numRangeReads];
auto keys = set<std::string_view>(arena); auto keys = set<std::string_view>(arena);
while (int(keys.size()) < numReads) { while (int(keys.size()) < numPointReads + numRangeReads * 2) {
if (!arbitrary.hasEntropy()) { if (!arbitrary.hasEntropy()) {
return true; return true;
} }
@@ -549,29 +551,68 @@ template <class ConflictSetImpl> struct TestDriver {
arbitrary.randomBytes(begin, keyLen); arbitrary.randomBytes(begin, keyLen);
keys.insert(std::string_view((const char *)begin, keyLen)); keys.insert(std::string_view((const char *)begin, keyLen));
} }
auto iter = keys.begin(); auto iter = keys.begin();
for (int i = 0; i < numReads; ++i) { int i = 0;
reads[i].begin.p = (const uint8_t *)iter->data(); for (int pointsRemaining = numPointReads, rangesRemaining = numRangeReads;
reads[i].begin.len = iter->size(); pointsRemaining > 0 || rangesRemaining > 0; ++i) {
++iter; bool pointRead = pointsRemaining > 0 && rangesRemaining > 0
reads[i].end.len = 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; reads[i].readVersion = v;
#if DEBUG_VERBOSE && !defined(NDEBUG) #if DEBUG_VERBOSE && !defined(NDEBUG)
fprintf(stderr, "Read: {%s} at %d\n", printable(reads[i].begin).c_str(), if (reads[i].end.len == 0) {
int(reads[i].readVersion)); 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 #endif
} }
assert(iter == keys.end()); assert(iter == keys.end());
auto *results1 = new (arena) ConflictSet::Result[numReads]; assert(i == numPointReads + numRangeReads);
auto *results2 = new (arena) ConflictSet::Result[numReads]; auto *results1 =
cs.check(reads, results1, numReads); new (arena) ConflictSet::Result[numPointReads + numRangeReads];
refImpl.check(reads, results2, numReads); auto *results2 =
for (int i = 0; i < numReads; ++i) { 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]) { if (results1[i] != results2[i]) {
fprintf(stderr, if (reads[i].end.len == 0) {
"Expected %s, got %s for read of %s at version %" PRId64 "\n", fprintf(stderr,
resultToStr(results2[i]), resultToStr(results1[i]), "Expected %s, got %s for read of {%s} at version %" PRId64
printable(reads[i].begin).c_str(), reads[i].readVersion); "\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; ok = false;
return true; return true;
} }