Merge pull request #3380 from sfc-gh-clin/update-cross-module-read-error-handler

Update special_keys_cross_module_read error handler
This commit is contained in:
Meng Xu 2020-06-17 18:17:32 -07:00 committed by GitHub
commit e0fbbea79e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 181 deletions

View File

@ -103,47 +103,36 @@ ACTOR Future<Void> moveKeySelectorOverRangeActor(const SpecialKeyRangeBaseImpl*
return Void();
}
void onModuleRead(ReadYourWritesTransaction* ryw, SpecialKeySpace::MODULE module,
Optional<SpecialKeySpace::MODULE>& lastModuleRead) {
if (ryw && !ryw->specialKeySpaceRelaxed() && lastModuleRead.present() && lastModuleRead.get() != module) {
throw special_keys_cross_module_read();
}
lastModuleRead = module;
}
// This function will normalize the given KeySelector to a standard KeySelector:
// orEqual == false && offset == 1 (Standard form)
// If the corresponding key is outside the whole space, it will move to the begin or the end
// It does have overhead here since we query all keys twice in the worst case.
// However, moving the KeySelector while handling other parameters like limits makes the code much more complex and hard
// to maintain; Thus, separate each part to make the code easy to understand and more compact
// Boundary is the range of the legal key space, which, by default is the range of the module
// And (\xff\xff, \xff\xff\xff) if SPECIAL_KEY_SPACE_RELAXED is turned on
ACTOR Future<Void> normalizeKeySelectorActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ryw, KeySelector* ks,
Optional<SpecialKeySpace::MODULE>* lastModuleRead, int* actualOffset,
KeyRangeRef boundary, int* actualOffset,
Standalone<RangeResultRef>* result,
Optional<Standalone<RangeResultRef>>* cache) {
state RangeMap<Key, SpecialKeyRangeBaseImpl*, KeyRangeRef>::Iterator iter =
ks->offset < 1 ? sks->getImpls().rangeContainingKeyBefore(ks->getKey())
: sks->getImpls().rangeContaining(ks->getKey());
while ((ks->offset < 1 && iter != sks->getImpls().ranges().begin()) ||
(ks->offset > 1 && iter != sks->getImpls().ranges().end())) {
onModuleRead(ryw, sks->getModules().rangeContaining(iter->begin())->value(), *lastModuleRead);
while ((ks->offset < 1 && iter->begin() > boundary.begin) || (ks->offset > 1 && iter->begin() < boundary.end)) {
if (iter->value() != nullptr) {
wait(moveKeySelectorOverRangeActor(iter->value(), ryw, ks, cache));
}
ks->offset < 1 ? --iter : ++iter;
}
*actualOffset = ks->offset;
if (iter == sks->getImpls().ranges().begin())
ks->setKey(sks->getKeyRange().begin);
else if (iter == sks->getImpls().ranges().end())
ks->setKey(sks->getKeyRange().end);
if (iter->begin() == boundary.begin || iter->begin() == boundary.end) ks->setKey(iter->begin());
if (!ks->isFirstGreaterOrEqual()) {
// The Key Selector points to key outside the whole special key space
TraceEvent(SevInfo, "KeySelectorPointsOutside")
// The Key Selector clamps up to the legal key space
TraceEvent(SevInfo, "ReadToBoundary")
.detail("TerminateKey", ks->getKey())
.detail("TerminateOffset", ks->offset);
if (ks->offset < 1 && iter == sks->getImpls().ranges().begin())
if (ks->offset < 1)
result->readToBegin = true;
else
result->readThroughEnd = true;
@ -152,28 +141,25 @@ ACTOR Future<Void> normalizeKeySelectorActor(SpecialKeySpace* sks, ReadYourWrite
return Void();
}
ACTOR Future<Standalone<RangeResultRef>> SpecialKeySpace::checkModuleFound(SpecialKeySpace* sks,
ReadYourWritesTransaction* ryw,
KeySelector begin, KeySelector end,
GetRangeLimits limits, bool reverse) {
ACTOR Future<Standalone<RangeResultRef>> SpecialKeySpace::checkRYWValid(SpecialKeySpace* sks,
ReadYourWritesTransaction* ryw,
KeySelector begin, KeySelector end,
GetRangeLimits limits, bool reverse) {
ASSERT(ryw);
choose {
when(std::pair<Standalone<RangeResultRef>, Optional<SpecialKeySpace::MODULE>> result =
when(Standalone<RangeResultRef> result =
wait(SpecialKeySpace::getRangeAggregationActor(sks, ryw, begin, end, limits, reverse))) {
if (ryw && !ryw->specialKeySpaceRelaxed()) {
auto module = result.second;
if (!module.present() || module.get() == SpecialKeySpace::MODULE::UNKNOWN) {
throw special_keys_no_module_found();
}
}
return result.first;
return result;
}
when(wait(ryw ? ryw->resetFuture() : Never())) { throw internal_error(); }
when(wait(ryw->resetFuture())) { throw internal_error(); }
}
}
ACTOR Future<std::pair<Standalone<RangeResultRef>, Optional<SpecialKeySpace::MODULE>>>
SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ryw, KeySelector begin,
KeySelector end, GetRangeLimits limits, bool reverse) {
ACTOR Future<Standalone<RangeResultRef>> SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks,
ReadYourWritesTransaction* ryw,
KeySelector begin, KeySelector end,
GetRangeLimits limits,
bool reverse) {
// This function handles ranges which cover more than one keyrange and aggregates all results
// KeySelector, GetRangeLimits and reverse are all handled here
state Standalone<RangeResultRef> result;
@ -181,22 +167,41 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, ReadYourWritesTr
state RangeMap<Key, SpecialKeyRangeBaseImpl*, KeyRangeRef>::Iterator iter;
state int actualBeginOffset;
state int actualEndOffset;
state Optional<SpecialKeySpace::MODULE> lastModuleRead;
state KeyRangeRef moduleBoundary;
// used to cache result from potential first read
state Optional<Standalone<RangeResultRef>> cache;
wait(normalizeKeySelectorActor(sks, ryw, &begin, &lastModuleRead, &actualBeginOffset, &result, &cache));
wait(normalizeKeySelectorActor(sks, ryw, &end, &lastModuleRead, &actualEndOffset, &result, &cache));
if (ryw->specialKeySpaceRelaxed()) {
moduleBoundary = sks->range;
} else {
auto beginIter = sks->getModules().rangeContaining(begin.getKey());
if (beginIter->begin() <= end.getKey() && end.getKey() <= beginIter->end()) {
if (beginIter->value() == SpecialKeySpace::MODULE::UNKNOWN)
throw special_keys_no_module_found();
else
moduleBoundary = beginIter->range();
} else {
TraceEvent(SevInfo, "SpecialKeyCrossModuleRead")
.detail("Begin", begin.toString())
.detail("End", end.toString())
.detail("BoundaryBegin", beginIter->begin())
.detail("BoundaryEnd", beginIter->end());
throw special_keys_cross_module_read();
}
}
wait(normalizeKeySelectorActor(sks, ryw, &begin, moduleBoundary, &actualBeginOffset, &result, &cache));
wait(normalizeKeySelectorActor(sks, ryw, &end, moduleBoundary, &actualEndOffset, &result, &cache));
// Handle all corner cases like what RYW does
// return if range inverted
if (actualBeginOffset >= actualEndOffset && begin.getKey() >= end.getKey()) {
TEST(true);
return std::make_pair(RangeResultRef(false, false), lastModuleRead);
return RangeResultRef(false, false);
}
// If touches begin or end, return with readToBegin and readThroughEnd flags
if (begin.getKey() == sks->range.end || end.getKey() == sks->range.begin) {
if (begin.getKey() == moduleBoundary.end || end.getKey() == moduleBoundary.begin) {
TEST(true);
return std::make_pair(result, lastModuleRead);
return result;
}
state RangeMap<Key, SpecialKeyRangeBaseImpl*, KeyRangeRef>::Ranges ranges =
sks->impls.intersectingRanges(KeyRangeRef(begin.getKey(), end.getKey()));
@ -206,7 +211,6 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, ReadYourWritesTr
if (reverse) {
while (iter != ranges.begin()) {
--iter;
onModuleRead(ryw, sks->getModules().rangeContaining(iter->begin())->value(), lastModuleRead);
if (iter->value() == nullptr) continue;
KeyRangeRef kr = iter->range();
KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin;
@ -230,13 +234,12 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, ReadYourWritesTr
if (limits.isReached()) {
result.more = true;
result.readToBegin = false;
return std::make_pair(result, lastModuleRead);
return result;
};
}
}
} else {
for (iter = ranges.begin(); iter != ranges.end(); ++iter) {
onModuleRead(ryw, sks->getModules().rangeContaining(iter->begin())->value(), lastModuleRead);
if (iter->value() == nullptr) continue;
KeyRangeRef kr = iter->range();
KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin;
@ -260,12 +263,12 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, ReadYourWritesTr
if (limits.isReached()) {
result.more = true;
result.readThroughEnd = false;
return std::make_pair(result, lastModuleRead);
return result;
};
}
}
}
return std::make_pair(result, lastModuleRead);
return result;
}
Future<Standalone<RangeResultRef>> SpecialKeySpace::getRange(ReadYourWritesTransaction* ryw, KeySelector begin,
@ -285,7 +288,7 @@ Future<Standalone<RangeResultRef>> SpecialKeySpace::getRange(ReadYourWritesTrans
return Standalone<RangeResultRef>();
}
return checkModuleFound(this, ryw, begin, end, limits, reverse);
return checkRYWValid(this, ryw, begin, end, limits, reverse);
}
ACTOR Future<Optional<Value>> SpecialKeySpace::getActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ryw,
@ -374,112 +377,3 @@ DDStatsRangeImpl::DDStatsRangeImpl(KeyRangeRef kr) : SpecialKeyRangeAsyncImpl(kr
Future<Standalone<RangeResultRef>> DDStatsRangeImpl::getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const {
return ddMetricsGetRangeActor(ryw, kr);
}
class SpecialKeyRangeTestImpl : public SpecialKeyRangeBaseImpl {
public:
explicit SpecialKeyRangeTestImpl(KeyRangeRef kr, const std::string& prefix, int size)
: SpecialKeyRangeBaseImpl(kr), prefix(prefix), size(size) {
ASSERT(size > 0);
for (int i = 0; i < size; ++i) {
kvs.push_back_deep(kvs.arena(),
KeyValueRef(getKeyForIndex(i), deterministicRandom()->randomAlphaNumeric(16)));
}
}
KeyValueRef getKeyValueForIndex(int idx) { return kvs[idx]; }
Key getKeyForIndex(int idx) { return Key(prefix + format("%010d", idx)).withPrefix(range.begin); }
int getSize() { return size; }
Future<Standalone<RangeResultRef>> getRange(ReadYourWritesTransaction* ryw, KeyRangeRef kr) const override {
int startIndex = 0, endIndex = size;
while (startIndex < size && kvs[startIndex].key < kr.begin) ++startIndex;
while (endIndex > startIndex && kvs[endIndex - 1].key >= kr.end) --endIndex;
if (startIndex == endIndex)
return Standalone<RangeResultRef>();
else
return Standalone<RangeResultRef>(RangeResultRef(kvs.slice(startIndex, endIndex), false));
}
private:
Standalone<VectorRef<KeyValueRef>> kvs;
std::string prefix;
int size;
};
TEST_CASE("/fdbclient/SpecialKeySpace/Unittest") {
SpecialKeySpace sks(normalKeys.begin, normalKeys.end);
SpecialKeyRangeTestImpl pkr1(KeyRangeRef(LiteralStringRef("/cat/"), LiteralStringRef("/cat/\xff")), "small", 10);
SpecialKeyRangeTestImpl pkr2(KeyRangeRef(LiteralStringRef("/dog/"), LiteralStringRef("/dog/\xff")), "medium", 100);
SpecialKeyRangeTestImpl pkr3(KeyRangeRef(LiteralStringRef("/pig/"), LiteralStringRef("/pig/\xff")), "large", 1000);
sks.registerKeyRange(SpecialKeySpace::MODULE::TESTONLY, pkr1.getKeyRange(), &pkr1);
sks.registerKeyRange(SpecialKeySpace::MODULE::TESTONLY, pkr2.getKeyRange(), &pkr2);
sks.registerKeyRange(SpecialKeySpace::MODULE::TESTONLY, pkr3.getKeyRange(), &pkr3);
// get
{
auto resultFuture = sks.get(nullptr, LiteralStringRef("/cat/small0000000009"));
ASSERT(resultFuture.isReady());
auto result = resultFuture.getValue().get();
ASSERT(result == pkr1.getKeyValueForIndex(9).value);
auto emptyFuture = sks.get(nullptr, LiteralStringRef("/cat/small0000000010"));
ASSERT(emptyFuture.isReady());
auto emptyResult = emptyFuture.getValue();
ASSERT(!emptyResult.present());
}
// general getRange
{
KeySelector start = KeySelectorRef(LiteralStringRef("/elepant"), false, -9);
KeySelector end = KeySelectorRef(LiteralStringRef("/frog"), false, +11);
auto resultFuture = sks.getRange(nullptr, start, end, GetRangeLimits());
ASSERT(resultFuture.isReady());
auto result = resultFuture.getValue();
ASSERT(result.size() == 20);
ASSERT(result[0].key == pkr2.getKeyForIndex(90));
ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(9));
}
// KeySelector points outside
{
KeySelector start = KeySelectorRef(pkr3.getKeyForIndex(999), true, -1110);
KeySelector end = KeySelectorRef(pkr1.getKeyForIndex(0), false, +1112);
auto resultFuture = sks.getRange(nullptr, start, end, GetRangeLimits());
ASSERT(resultFuture.isReady());
auto result = resultFuture.getValue();
ASSERT(result.size() == 1110);
ASSERT(result[0].key == pkr1.getKeyForIndex(0));
ASSERT(result[result.size() - 1].key == pkr3.getKeyForIndex(999));
}
// GetRangeLimits with row limit
{
KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0);
KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0);
auto resultFuture = sks.getRange(nullptr, start, end, GetRangeLimits(2));
ASSERT(resultFuture.isReady());
auto result = resultFuture.getValue();
ASSERT(result.size() == 2);
ASSERT(result[0].key == pkr2.getKeyForIndex(0));
ASSERT(result[1].key == pkr2.getKeyForIndex(1));
}
// GetRangeLimits with byte limit
{
KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0);
KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(0), false, 0);
auto resultFuture = sks.getRange(nullptr, start, end, GetRangeLimits(10, 100));
ASSERT(resultFuture.isReady());
auto result = resultFuture.getValue();
int bytes = 0;
for (int i = 0; i < result.size() - 1; ++i) bytes += 8 + pkr2.getKeyValueForIndex(i).expectedSize();
ASSERT(bytes < 100);
ASSERT(bytes + 8 + pkr2.getKeyValueForIndex(result.size()).expectedSize() >= 100);
}
// reverse test with overlapping key range
{
KeySelector start = KeySelectorRef(pkr2.getKeyForIndex(0), true, 0);
KeySelector end = KeySelectorRef(pkr3.getKeyForIndex(999), true, +1);
auto resultFuture = sks.getRange(nullptr, start, end, GetRangeLimits(1100), true);
ASSERT(resultFuture.isReady());
auto result = resultFuture.getValue();
for (int i = 0; i < pkr3.getSize(); ++i) ASSERT(result[i] == pkr3.getKeyValueForIndex(pkr3.getSize() - 1 - i));
for (int i = 0; i < pkr2.getSize(); ++i)
ASSERT(result[i + pkr3.getSize()] == pkr2.getKeyValueForIndex(pkr2.getSize() - 1 - i));
}
return Void();
}

View File

@ -108,12 +108,11 @@ public:
Future<Standalone<RangeResultRef>> getRange(ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end,
GetRangeLimits limits, bool reverse = false);
SpecialKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = normalKeys.end, bool testOnly = true) {
// Default value is nullptr, begin of KeyRangeMap is Key()
impls = KeyRangeMap<SpecialKeyRangeBaseImpl*>(nullptr, spaceEndKey);
range = KeyRangeRef(spaceStartKey, spaceEndKey);
modules = KeyRangeMap<SpecialKeySpace::MODULE>(
testOnly ? SpecialKeySpace::MODULE::TESTONLY : SpecialKeySpace::MODULE::UNKNOWN, spaceEndKey);
SpecialKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = normalKeys.end, bool testOnly = true)
: range(KeyRangeRef(spaceStartKey, spaceEndKey)), impls(nullptr, spaceEndKey),
modules(testOnly ? SpecialKeySpace::MODULE::TESTONLY : SpecialKeySpace::MODULE::UNKNOWN, spaceEndKey) {
// Default begin of KeyRangeMap is Key(), insert the range to update start key if needed
impls.insert(range, nullptr);
if (!testOnly) modulesBoundaryInit(); // testOnly is used in the correctness workload
}
// Initialize module boundaries, used to handle cross_module_read
@ -152,17 +151,17 @@ public:
private:
ACTOR static Future<Optional<Value>> getActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ryw, KeyRef key);
ACTOR static Future<Standalone<RangeResultRef>> checkModuleFound(SpecialKeySpace* sks,
ACTOR static Future<Standalone<RangeResultRef>> checkRYWValid(SpecialKeySpace* sks,
ReadYourWritesTransaction* ryw, KeySelector begin,
KeySelector end, GetRangeLimits limits,
bool reverse);
ACTOR static Future<std::pair<Standalone<RangeResultRef>, Optional<SpecialKeySpace::MODULE>>>
getRangeAggregationActor(SpecialKeySpace* sks, ReadYourWritesTransaction* ryw, KeySelector begin, KeySelector end,
GetRangeLimits limits, bool reverse);
ACTOR static Future<Standalone<RangeResultRef>> getRangeAggregationActor(SpecialKeySpace* sks,
ReadYourWritesTransaction* ryw,
KeySelector begin, KeySelector end,
GetRangeLimits limits, bool reverse);
KeyRange range;
KeyRangeMap<SpecialKeyRangeBaseImpl*> impls;
KeyRangeMap<SpecialKeySpace::MODULE> modules;
KeyRange range;
static std::unordered_map<SpecialKeySpace::MODULE, KeyRange> moduleToBoundary;
};

View File

@ -203,7 +203,6 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
KeySelector randomKeySelector() {
Key randomKey;
// TODO : add randomness to pickup existing keys
if (deterministicRandom()->random01() < absoluteRandomProb) {
Key prefix;
if (deterministicRandom()->random01() < absoluteRandomProb)
@ -282,17 +281,18 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
} catch (Error& e) {
throw;
}
// begin keySelector outside module range
// cross module read with option turned on
try {
const KeyRef key = LiteralStringRef("\xff\xff/cluster_file_path");
KeySelector begin = KeySelectorRef(key, false, 0);
KeySelector end = KeySelectorRef(keyAfter(key), false, 1);
wait(success(tx->getRange(begin, end, GetRangeLimits(CLIENT_KNOBS->TOO_MANY))));
ASSERT(false);
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) throw;
ASSERT(e.code() == error_code_special_keys_cross_module_read);
tx->setOption(FDBTransactionOptions::SPECIAL_KEY_SPACE_RELAXED);
const KeyRef startKey = LiteralStringRef("\xff\xff/transactio");
const KeyRef endKey = LiteralStringRef("\xff\xff/transaction1");
Standalone<RangeResultRef> result =
wait(tx->getRange(KeyRangeRef(startKey, endKey), GetRangeLimits(CLIENT_KNOBS->TOO_MANY)));
// The whole transaction module should be empty
ASSERT(!result.size());
tx->reset();
} catch (Error& e) {
throw;
}
// end keySelector inside module range, *** a tricky corner case ***
try {
@ -307,9 +307,9 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
}
// No module found error case with keys
try {
wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/A_no_module_related_prefix"), LiteralStringRef("\xff\xff/I_am_also_not_in_any_module")),
CLIENT_KNOBS->TOO_MANY)));
wait(success(tx->getRange(KeyRangeRef(LiteralStringRef("\xff\xff/A_no_module_related_prefix"),
LiteralStringRef("\xff\xff/I_am_also_not_in_any_module")),
CLIENT_KNOBS->TOO_MANY)));
ASSERT(false);
} catch (Error& e) {
if (e.code() == error_code_actor_cancelled) throw;
@ -327,6 +327,29 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
ASSERT(e.code() == error_code_special_keys_no_module_found);
tx->reset();
}
// begin and end keySelectors clamp up to the boundary of the module
try {
const KeyRef key = LiteralStringRef("\xff\xff/cluster_file_path");
KeySelector begin = KeySelectorRef(key, false, 0);
KeySelector end = KeySelectorRef(keyAfter(key), false, 2);
Standalone<RangeResultRef> result = wait(tx->getRange(begin, end, GetRangeLimits(CLIENT_KNOBS->TOO_MANY)));
ASSERT(result.readToBegin && result.readThroughEnd);
tx->reset();
} catch (Error& e) {
throw;
}
try {
tx->addReadConflictRange(singleKeyRange(LiteralStringRef("readKey")));
const KeyRef key = LiteralStringRef("\xff\xff/transaction/a_to_be_the_first");
KeySelector begin = KeySelectorRef(key, false, 0);
KeySelector end = KeySelectorRef(key, false, 2);
Standalone<RangeResultRef> result = wait(tx->getRange(begin, end, GetRangeLimits(CLIENT_KNOBS->TOO_MANY)));
ASSERT(result.readToBegin && !result.readThroughEnd);
tx->reset();
} catch (Error& e) {
throw;
}
return Void();
}