From 838d08c70b6c402d2df2aacce6b5babf7f6aa687 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 14 Feb 2024 16:11:57 -0800 Subject: [PATCH] WIP checkRightOfPyramid seems to be correct --- ConflictSet.cpp | 284 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 243 insertions(+), 41 deletions(-) diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 5f29b5d..1ccfa7a 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -649,6 +649,10 @@ Iterator firstGeq(Node *n, const std::span 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 key, int64_t readVersion) { auto remaining = key; for (;;) { - if (n->partialKeyLen > 0) { - int commonLen = std::min(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 key, } } } + if (n->partialKeyLen > 0) { + int commonLen = std::min(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::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::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::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::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 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(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 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(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 getSearchPath(Arena &arena, Node *n) { @@ -919,18 +1117,8 @@ descend(int &depth, int &lcp, Node *newNode, std::span 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::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 begin, - const std::span end, int64_t readVersion, +bool checkRangeRead(Node *n, std::span begin, + std::span end, int64_t readVersion, Arena &arena) { int lcp = longestCommonPrefix(begin.data(), end.data(), @@ -955,6 +1143,10 @@ bool checkRangeRead(Node *n, const std::span 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 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(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; + // } + // } } }