WIP checkRightOfPyramid seems to be correct
This commit is contained in:
284
ConflictSet.cpp
284
ConflictSet.cpp
@@ -649,6 +649,10 @@ Iterator firstGeq(Node *n, const std::span<const uint8_t> key) {
|
||||
return {stepwise.n, stepwise.cmp};
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string getSearchPathPrintable(Node *n);
|
||||
}
|
||||
|
||||
// TODO rewrite in terms of FirstGeqStepwise?
|
||||
//
|
||||
// Logically this is the same as performing firstGeq and then checking against
|
||||
@@ -658,29 +662,6 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
||||
int64_t readVersion) {
|
||||
auto remaining = key;
|
||||
for (;;) {
|
||||
if (n->partialKeyLen > 0) {
|
||||
int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
|
||||
for (int i = 0; i < commonLen; ++i) {
|
||||
auto c = n->partialKey[i] <=> remaining[i];
|
||||
if (c == 0) {
|
||||
continue;
|
||||
}
|
||||
if (c > 0) {
|
||||
goto downLeftSpine;
|
||||
} else {
|
||||
n = nextSibling(n);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
if (commonLen == n->partialKeyLen) {
|
||||
// partial key matches
|
||||
remaining = remaining.subspan(commonLen, remaining.size() - commonLen);
|
||||
} else if (n->partialKeyLen > int(remaining.size())) {
|
||||
// n is the first physical node greater than remaining, and there's no
|
||||
// eq node
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
if (n->maxVersion <= readVersion) {
|
||||
return true;
|
||||
}
|
||||
@@ -707,6 +688,29 @@ bool checkPointRead(Node *n, const std::span<const uint8_t> key,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n->partialKeyLen > 0) {
|
||||
int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
|
||||
for (int i = 0; i < commonLen; ++i) {
|
||||
auto c = n->partialKey[i] <=> remaining[i];
|
||||
if (c == 0) {
|
||||
continue;
|
||||
}
|
||||
if (c > 0) {
|
||||
goto downLeftSpine;
|
||||
} else {
|
||||
n = nextSibling(n);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
if (commonLen == n->partialKeyLen) {
|
||||
// partial key matches
|
||||
remaining = remaining.subspan(commonLen, remaining.size() - commonLen);
|
||||
} else if (n->partialKeyLen > int(remaining.size())) {
|
||||
// n is the first physical node greater than remaining, and there's no
|
||||
// eq node
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
if (n == nullptr) {
|
||||
@@ -722,8 +726,202 @@ downLeftSpine:
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string getSearchPathPrintable(Node *n);
|
||||
// Precondition: node has a child at index begin
|
||||
int64_t maxRightOf(Node *n, int begin) {
|
||||
int64_t result = std::numeric_limits<int64_t>::lowest();
|
||||
while (begin >= 0) {
|
||||
result = std::max(result, getChildExists(n, begin)->maxVersion);
|
||||
begin = getChildGeq(n, begin + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t maxRightOfExclusive(Node *n, int begin) {
|
||||
int64_t result = std::numeric_limits<int64_t>::lowest();
|
||||
for (;;) {
|
||||
begin = getChildGeq(n, begin + 1);
|
||||
if (begin < 0) {
|
||||
break;
|
||||
}
|
||||
result = std::max(result, getChildExists(n, begin)->maxVersion);
|
||||
}
|
||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
fprintf(stderr, "At `%s', max version right of %02x is %" PRId64 "\n",
|
||||
getSearchPathPrintable(n).c_str(), begin, result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return the maximum version among all keys starting with the search path of
|
||||
// `n` + a child < `end`
|
||||
int64_t maxLeftOfExclusive(Node *n, int end) {
|
||||
int begin = -1;
|
||||
int64_t result = std::numeric_limits<int64_t>::lowest();
|
||||
for (;;) {
|
||||
begin = getChildGeq(n, begin + 1);
|
||||
if (begin < 0 || begin >= end) {
|
||||
break;
|
||||
}
|
||||
result = std::max(result, getChildExists(n, begin)->maxVersion);
|
||||
}
|
||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
fprintf(stderr, "At `%s', max version left of %02x is %" PRId64 "\n",
|
||||
getSearchPathPrintable(n).c_str(), end, result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// Precondition: child exists at `end`
|
||||
int64_t maxBetweenExclusive(Node *n, int begin, int end) {
|
||||
int64_t result = std::numeric_limits<int64_t>::lowest();
|
||||
int next = begin;
|
||||
for (;;) {
|
||||
next = getChildGeq(n, next + 1);
|
||||
if (next < 0 || next >= end) {
|
||||
break;
|
||||
}
|
||||
result = std::max(result, getChildExists(n, next)->maxVersion);
|
||||
}
|
||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
fprintf(stderr, "At `%s', max version in (%02x, %02x) is %" PRId64 "\n",
|
||||
getSearchPathPrintable(n).c_str(), begin, end, result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns true if the version of all keys >= key in n is <= readVersion
|
||||
bool checkLeftOfPyramid(Node *n, const std::span<const uint8_t> key,
|
||||
int64_t readVersion) {
|
||||
auto remaining = key;
|
||||
for (;;) {
|
||||
|
||||
if (n->maxVersion <= readVersion) {
|
||||
return true;
|
||||
}
|
||||
if (remaining.size() == 0) {
|
||||
if (n->entryPresent) {
|
||||
return n->entry.pointVersion <= readVersion;
|
||||
}
|
||||
int c = getChildGeq(n, 0);
|
||||
assert(c >= 0);
|
||||
n = getChildExists(n, c);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
|
||||
int c = getChildGeq(n, remaining[0]);
|
||||
if (c == remaining[0]) {
|
||||
auto v = maxRightOfExclusive(n, c);
|
||||
if (v > readVersion) {
|
||||
return false;
|
||||
}
|
||||
n = getChildExists(n, c);
|
||||
remaining = remaining.subspan(1, remaining.size() - 1);
|
||||
} else {
|
||||
if (c >= 0) {
|
||||
n = getChildExists(n, c);
|
||||
goto downLeftSpine;
|
||||
} else {
|
||||
n = nextSibling(n);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
|
||||
if (n->partialKeyLen > 0) {
|
||||
int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
|
||||
for (int i = 0; i < commonLen; ++i) {
|
||||
auto c = n->partialKey[i] <=> remaining[i];
|
||||
if (c == 0) {
|
||||
continue;
|
||||
}
|
||||
if (c > 0) {
|
||||
goto downLeftSpine;
|
||||
} else {
|
||||
n = nextSibling(n);
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
if (commonLen == n->partialKeyLen) {
|
||||
// partial key matches
|
||||
remaining = remaining.subspan(commonLen, remaining.size() - commonLen);
|
||||
} else if (n->partialKeyLen > int(remaining.size())) {
|
||||
// n is the first physical node greater than remaining, and there's no
|
||||
// eq node
|
||||
goto downLeftSpine;
|
||||
}
|
||||
}
|
||||
}
|
||||
downLeftSpine:
|
||||
if (n == nullptr) {
|
||||
return true;
|
||||
}
|
||||
for (;;) {
|
||||
if (n->entryPresent) {
|
||||
return n->entry.rangeVersion <= readVersion;
|
||||
}
|
||||
int c = getChildGeq(n, 0);
|
||||
assert(c >= 0);
|
||||
n = getChildExists(n, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the version of all keys < key in n is <= readVersion
|
||||
bool checkRightOfPyramid(Node *n, const std::span<const uint8_t> key,
|
||||
int64_t readVersion) {
|
||||
assert(key.size() > 0);
|
||||
auto remaining = key;
|
||||
for (bool first = true;; first = false) {
|
||||
|
||||
if (n->maxVersion <= readVersion) {
|
||||
return true;
|
||||
}
|
||||
if (!first && n->entryPresent && n->entry.rangeVersion > readVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (remaining.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (n->entryPresent && n->entry.pointVersion > readVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto v = maxLeftOfExclusive(n, remaining[0]);
|
||||
if (v > readVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int c = getChildGeq(n, remaining[0]);
|
||||
if (c == remaining[0]) {
|
||||
n = getChildExists(n, c);
|
||||
remaining = remaining.subspan(1, remaining.size() - 1);
|
||||
} else {
|
||||
if (c >= 0) {
|
||||
return true;
|
||||
}
|
||||
return n->maxVersion <= readVersion;
|
||||
}
|
||||
|
||||
if (n->partialKeyLen > 0) {
|
||||
int commonLen = std::min<int>(n->partialKeyLen, remaining.size());
|
||||
for (int i = 0; i < commonLen; ++i) {
|
||||
auto c = n->partialKey[i] <=> remaining[i];
|
||||
if (c == 0) {
|
||||
continue;
|
||||
}
|
||||
if (c > 0) {
|
||||
return true;
|
||||
}
|
||||
return n->maxVersion <= readVersion;
|
||||
}
|
||||
if (commonLen == n->partialKeyLen) {
|
||||
// partial key matches
|
||||
remaining = remaining.subspan(commonLen, remaining.size() - commonLen);
|
||||
} else if (n->partialKeyLen > int(remaining.size())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> getSearchPath(Arena &arena, Node *n) {
|
||||
@@ -919,18 +1117,8 @@ descend(int &depth, int &lcp, Node *newNode, std::span<const uint8_t> end,
|
||||
newNode->partialKey + newNode->partialKeyLen);
|
||||
}
|
||||
|
||||
// Precondition: node has a child at index begin
|
||||
int64_t maxRightOf(Node *n, int begin) {
|
||||
int64_t result = std::numeric_limits<int64_t>::lowest();
|
||||
while (begin >= 0) {
|
||||
result = std::max(result, getChildExists(n, begin)->maxVersion);
|
||||
begin = getChildGeq(n, begin + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool checkRangeRead(Node *n, const std::span<const uint8_t> begin,
|
||||
const std::span<const uint8_t> end, int64_t readVersion,
|
||||
bool checkRangeRead(Node *n, std::span<const uint8_t> begin,
|
||||
std::span<const uint8_t> end, int64_t readVersion,
|
||||
Arena &arena) {
|
||||
|
||||
int lcp = longestCommonPrefix(begin.data(), end.data(),
|
||||
@@ -955,6 +1143,10 @@ bool checkRangeRead(Node *n, const std::span<const uint8_t> begin,
|
||||
// Check that we can start FirstGeq where Search left off
|
||||
const int consumed = lcp - search.remaining.size();
|
||||
assert(consumed >= 0);
|
||||
|
||||
auto trimmedBegin = begin.subspan(consumed, int(begin.size()) - consumed);
|
||||
auto trimmedEnd = end.subspan(consumed, int(end.size()) - consumed);
|
||||
|
||||
auto left =
|
||||
firstGeq(search.n, begin.subspan(consumed, int(begin.size()) - consumed));
|
||||
#ifndef NDEBUG
|
||||
@@ -989,10 +1181,6 @@ bool checkRangeRead(Node *n, const std::span<const uint8_t> begin,
|
||||
break;
|
||||
}
|
||||
|
||||
#if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
fprintf(stderr, "Visit %s\n", printable(searchPath).c_str());
|
||||
#endif
|
||||
|
||||
if (iter->entryPresent) {
|
||||
if (!first && iter->entry.rangeVersion > readVersion) {
|
||||
return false;
|
||||
@@ -1168,6 +1356,20 @@ struct __attribute__((visibility("hidden"))) ConflictSet::Impl {
|
||||
reads[i].readVersion))
|
||||
? Commit
|
||||
: Conflict;
|
||||
// auto k = std::span<const uint8_t>(reads[i].begin.p,
|
||||
// reads[i].begin.len); if (k.size() > 0) {
|
||||
// bool expected =
|
||||
// checkRangeRead(root, {}, k, reads[i].readVersion, arena);
|
||||
// bool actual = checkRightOfPyramid(root, k,
|
||||
// reads[i].readVersion); if (expected != actual) {
|
||||
// #if DEBUG_VERBOSE && !defined(NDEBUG)
|
||||
// fprintf(stderr, "Expected %d, got %d for [,%s)\n",
|
||||
// int(expected),
|
||||
// int(actual), printable(k).c_str());
|
||||
// #endif
|
||||
// result[i] = TooOld;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user