KeyRangeMap: introduce Unaligned KeyRangMap functions
This allows us to return: [firstKey, transition points, endKey] where firstKey <= keys.begin and endKey >= keys.end. This also fixes a prior issue where we were ending the range with a larger key's value rather than carrying the largest value within the range to the end.
This commit is contained in:
parent
65112fbdc0
commit
5b6a1f0675
|
@ -23,6 +23,7 @@
|
|||
#include "fdbclient/CommitTransaction.h"
|
||||
#include "fdbclient/FDBTypes.h"
|
||||
#include "fdbclient/ReadYourWrites.h"
|
||||
#include "flow/UnitTest.h"
|
||||
#include "flow/actorcompiler.h" // has to be last include
|
||||
|
||||
void KeyRangeActorMap::getRangesAffectedByInsertion(const KeyRangeRef& keys, std::vector<KeyRange>& affectedRanges) {
|
||||
|
@ -35,32 +36,54 @@ void KeyRangeActorMap::getRangesAffectedByInsertion(const KeyRangeRef& keys, std
|
|||
affectedRanges.push_back(KeyRangeRef(keys.end, e.end()));
|
||||
}
|
||||
|
||||
RangeResult krmDecodeRanges(KeyRef mapPrefix, KeyRange keys, RangeResult kv) {
|
||||
RangeResult krmDecodeRanges(KeyRef mapPrefix, KeyRange keys, RangeResult kv, bool align) {
|
||||
ASSERT(!kv.more || kv.size() > 1);
|
||||
KeyRange withPrefix =
|
||||
KeyRangeRef(mapPrefix.toString() + keys.begin.toString(), mapPrefix.toString() + keys.end.toString());
|
||||
|
||||
ValueRef beginValue, endValue;
|
||||
if (kv.size() && kv[0].key.startsWith(mapPrefix))
|
||||
beginValue = kv[0].value;
|
||||
if (kv.size() && kv.end()[-1].key.startsWith(mapPrefix))
|
||||
endValue = kv.end()[-1].value;
|
||||
|
||||
RangeResult result;
|
||||
result.arena().dependsOn(kv.arena());
|
||||
result.arena().dependsOn(keys.arena());
|
||||
|
||||
result.push_back(result.arena(), KeyValueRef(keys.begin, beginValue));
|
||||
// Always push a kv pair <= keys.begin.
|
||||
KeyRef beginKey = keys.begin;
|
||||
if (!align && !kv.empty() && kv.front().key.startsWith(mapPrefix) && kv.front().key < withPrefix.begin) {
|
||||
beginKey = kv[0].key.removePrefix(mapPrefix);
|
||||
}
|
||||
ValueRef beginValue;
|
||||
if (!kv.empty() && kv.front().key.startsWith(mapPrefix) && kv.front().key <= withPrefix.begin) {
|
||||
beginValue = kv.front().value;
|
||||
}
|
||||
result.push_back(result.arena(), KeyValueRef(beginKey, beginValue));
|
||||
|
||||
for (int i = 0; i < kv.size(); i++) {
|
||||
if (kv[i].key > withPrefix.begin && kv[i].key < withPrefix.end) {
|
||||
KeyRef k = kv[i].key.removePrefix(mapPrefix);
|
||||
result.push_back(result.arena(), KeyValueRef(k, kv[i].value));
|
||||
} else if (kv[i].key >= withPrefix.end)
|
||||
} else if (kv[i].key >= withPrefix.end) {
|
||||
kv.more = false;
|
||||
// There should be at most 1 value past mapPrefix + keys.end.
|
||||
ASSERT(i == kv.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!kv.more)
|
||||
result.push_back(result.arena(), KeyValueRef(keys.end, endValue));
|
||||
if (!kv.more) {
|
||||
KeyRef endKey = keys.end;
|
||||
if (!align && !kv.empty() && kv.back().key.startsWith(mapPrefix) && kv.back().key >= withPrefix.end) {
|
||||
endKey = kv.back().key.removePrefix(mapPrefix);
|
||||
}
|
||||
ValueRef endValue;
|
||||
if (!kv.empty()) {
|
||||
// In the aligned case, carry the last value to be the end value.
|
||||
if (align && kv.back().key.startsWith(mapPrefix) && kv.back().key > withPrefix.end) {
|
||||
endValue = result.back().value;
|
||||
} else {
|
||||
endValue = kv.back().value;
|
||||
}
|
||||
}
|
||||
result.push_back(result.arena(), KeyValueRef(endKey, endValue));
|
||||
}
|
||||
result.more = kv.more;
|
||||
|
||||
return result;
|
||||
|
@ -93,6 +116,37 @@ ACTOR Future<RangeResult> krmGetRanges(Reference<ReadYourWritesTransaction> tr,
|
|||
return krmDecodeRanges(mapPrefix, keys, kv);
|
||||
}
|
||||
|
||||
// Returns keys.begin, all transitional points in keys, and keys.end, and their values
|
||||
ACTOR Future<RangeResult> krmGetRangesUnaligned(Transaction* tr,
|
||||
Key mapPrefix,
|
||||
KeyRange keys,
|
||||
int limit,
|
||||
int limitBytes) {
|
||||
KeyRange withPrefix =
|
||||
KeyRangeRef(mapPrefix.toString() + keys.begin.toString(), mapPrefix.toString() + keys.end.toString());
|
||||
|
||||
state GetRangeLimits limits(limit, limitBytes);
|
||||
limits.minRows = 2;
|
||||
RangeResult kv = wait(tr->getRange(lastLessOrEqual(withPrefix.begin), firstGreaterThan(withPrefix.end), limits));
|
||||
|
||||
return krmDecodeRanges(mapPrefix, keys, kv, false);
|
||||
}
|
||||
|
||||
ACTOR Future<RangeResult> krmGetRangesUnaligned(Reference<ReadYourWritesTransaction> tr,
|
||||
Key mapPrefix,
|
||||
KeyRange keys,
|
||||
int limit,
|
||||
int limitBytes) {
|
||||
KeyRange withPrefix =
|
||||
KeyRangeRef(mapPrefix.toString() + keys.begin.toString(), mapPrefix.toString() + keys.end.toString());
|
||||
|
||||
state GetRangeLimits limits(limit, limitBytes);
|
||||
limits.minRows = 2;
|
||||
RangeResult kv = wait(tr->getRange(lastLessOrEqual(withPrefix.begin), firstGreaterThan(withPrefix.end), limits));
|
||||
|
||||
return krmDecodeRanges(mapPrefix, keys, kv, false);
|
||||
}
|
||||
|
||||
void krmSetPreviouslyEmptyRange(Transaction* tr,
|
||||
const KeyRef& mapPrefix,
|
||||
const KeyRangeRef& keys,
|
||||
|
@ -254,3 +308,87 @@ Future<Void> krmSetRangeCoalescing(Reference<ReadYourWritesTransaction> const& t
|
|||
Value const& value) {
|
||||
return holdWhile(tr, krmSetRangeCoalescing_(tr.getPtr(), mapPrefix, range, maxRange, value));
|
||||
}
|
||||
|
||||
TEST_CASE("/keyrangemap/decoderange/aligned") {
|
||||
Arena arena;
|
||||
Key prefix = LiteralStringRef("/prefix/");
|
||||
StringRef fullKeyA = StringRef(arena, LiteralStringRef("/prefix/a"));
|
||||
StringRef fullKeyB = StringRef(arena, LiteralStringRef("/prefix/b"));
|
||||
StringRef fullKeyC = StringRef(arena, LiteralStringRef("/prefix/c"));
|
||||
StringRef fullKeyD = StringRef(arena, LiteralStringRef("/prefix/d"));
|
||||
|
||||
StringRef keyA = StringRef(arena, LiteralStringRef("a"));
|
||||
StringRef keyB = StringRef(arena, LiteralStringRef("b"));
|
||||
StringRef keyC = StringRef(arena, LiteralStringRef("c"));
|
||||
StringRef keyD = StringRef(arena, LiteralStringRef("d"));
|
||||
StringRef keyE = StringRef(arena, LiteralStringRef("e"));
|
||||
StringRef keyAB = StringRef(arena, LiteralStringRef("ab"));
|
||||
StringRef keyCD = StringRef(arena, LiteralStringRef("cd"));
|
||||
|
||||
// Fake getRange() call.
|
||||
RangeResult kv;
|
||||
kv.push_back(arena, KeyValueRef(fullKeyA, keyA));
|
||||
kv.push_back(arena, KeyValueRef(fullKeyB, keyB));
|
||||
kv.push_back(arena, KeyValueRef(fullKeyC, keyC));
|
||||
kv.push_back(arena, KeyValueRef(fullKeyD, keyD));
|
||||
|
||||
// [A, AB(start), B, C, CD(end), D]
|
||||
RangeResult decodedRanges = krmDecodeRanges(prefix, KeyRangeRef(keyAB, keyCD), kv);
|
||||
ASSERT(decodedRanges.size() == 4);
|
||||
ASSERT(decodedRanges.front().key == keyAB);
|
||||
ASSERT(decodedRanges.front().value == keyA);
|
||||
ASSERT(decodedRanges.back().key == keyCD);
|
||||
ASSERT(decodedRanges.back().value == keyC);
|
||||
|
||||
// [""(start), A, B, C, D, E(end)]
|
||||
decodedRanges = krmDecodeRanges(prefix, KeyRangeRef(StringRef(), keyE), kv);
|
||||
ASSERT(decodedRanges.size() == 6);
|
||||
ASSERT(decodedRanges.front().key == StringRef());
|
||||
ASSERT(decodedRanges.front().value == StringRef());
|
||||
ASSERT(decodedRanges.back().key == keyE);
|
||||
ASSERT(decodedRanges.back().value == keyD);
|
||||
|
||||
return Void();
|
||||
}
|
||||
|
||||
TEST_CASE("/keyrangemap/decoderange/unaligned") {
|
||||
Arena arena;
|
||||
Key prefix = LiteralStringRef("/prefix/");
|
||||
StringRef fullKeyA = StringRef(arena, LiteralStringRef("/prefix/a"));
|
||||
StringRef fullKeyB = StringRef(arena, LiteralStringRef("/prefix/b"));
|
||||
StringRef fullKeyC = StringRef(arena, LiteralStringRef("/prefix/c"));
|
||||
StringRef fullKeyD = StringRef(arena, LiteralStringRef("/prefix/d"));
|
||||
|
||||
StringRef keyA = StringRef(arena, LiteralStringRef("a"));
|
||||
StringRef keyB = StringRef(arena, LiteralStringRef("b"));
|
||||
StringRef keyC = StringRef(arena, LiteralStringRef("c"));
|
||||
StringRef keyD = StringRef(arena, LiteralStringRef("d"));
|
||||
StringRef keyE = StringRef(arena, LiteralStringRef("e"));
|
||||
StringRef keyAB = StringRef(arena, LiteralStringRef("ab"));
|
||||
StringRef keyCD = StringRef(arena, LiteralStringRef("cd"));
|
||||
|
||||
// Fake getRange() call.
|
||||
RangeResult kv;
|
||||
kv.push_back(arena, KeyValueRef(fullKeyA, keyA));
|
||||
kv.push_back(arena, KeyValueRef(fullKeyB, keyB));
|
||||
kv.push_back(arena, KeyValueRef(fullKeyC, keyC));
|
||||
kv.push_back(arena, KeyValueRef(fullKeyD, keyD));
|
||||
|
||||
// [A, AB(start), B, C, CD(end), D]
|
||||
RangeResult decodedRanges = krmDecodeRanges(prefix, KeyRangeRef(keyAB, keyCD), kv, false);
|
||||
ASSERT(decodedRanges.size() == 4);
|
||||
ASSERT(decodedRanges.front().key == keyA);
|
||||
ASSERT(decodedRanges.front().value == keyA);
|
||||
ASSERT(decodedRanges.back().key == keyD);
|
||||
ASSERT(decodedRanges.back().value == keyD);
|
||||
|
||||
// [""(start), A, B, C, D, E(end)]
|
||||
decodedRanges = krmDecodeRanges(prefix, KeyRangeRef(StringRef(), keyE), kv, false);
|
||||
ASSERT(decodedRanges.size() == 6);
|
||||
ASSERT(decodedRanges.front().key == StringRef());
|
||||
ASSERT(decodedRanges.front().value == StringRef());
|
||||
ASSERT(decodedRanges.back().key == keyE);
|
||||
ASSERT(decodedRanges.back().value == keyD);
|
||||
|
||||
return Void();
|
||||
}
|
|
@ -136,6 +136,16 @@ Future<RangeResult> krmGetRanges(Reference<ReadYourWritesTransaction> const& tr,
|
|||
KeyRange const& keys,
|
||||
int const& limit = CLIENT_KNOBS->KRM_GET_RANGE_LIMIT,
|
||||
int const& limitBytes = CLIENT_KNOBS->KRM_GET_RANGE_LIMIT_BYTES);
|
||||
Future<RangeResult> krmGetRangesUnaligned(Transaction* const& tr,
|
||||
Key const& mapPrefix,
|
||||
KeyRange const& keys,
|
||||
int const& limit = CLIENT_KNOBS->KRM_GET_RANGE_LIMIT,
|
||||
int const& limitBytes = CLIENT_KNOBS->KRM_GET_RANGE_LIMIT_BYTES);
|
||||
Future<RangeResult> krmGetRangesUnaligned(Reference<ReadYourWritesTransaction> const& tr,
|
||||
Key const& mapPrefix,
|
||||
KeyRange const& keys,
|
||||
int const& limit = CLIENT_KNOBS->KRM_GET_RANGE_LIMIT,
|
||||
int const& limitBytes = CLIENT_KNOBS->KRM_GET_RANGE_LIMIT_BYTES);
|
||||
void krmSetPreviouslyEmptyRange(Transaction* tr,
|
||||
const KeyRef& mapPrefix,
|
||||
const KeyRangeRef& keys,
|
||||
|
@ -162,7 +172,7 @@ Future<Void> krmSetRangeCoalescing(Reference<ReadYourWritesTransaction> const& t
|
|||
KeyRange const& range,
|
||||
KeyRange const& maxRange,
|
||||
Value const& value);
|
||||
RangeResult krmDecodeRanges(KeyRef mapPrefix, KeyRange keys, RangeResult kv);
|
||||
RangeResult krmDecodeRanges(KeyRef mapPrefix, KeyRange keys, RangeResult kv, bool align = true);
|
||||
|
||||
template <class Val, class Metric, class MetricFunc>
|
||||
std::vector<KeyRangeWith<Val>> KeyRangeMap<Val, Metric, MetricFunc>::getAffectedRangesAfterInsertion(
|
||||
|
|
Loading…
Reference in New Issue