Add a test for special_keys_cross_module_read, fix two bugs, add several TODOs

This commit is contained in:
Chaoguang Lin 2020-05-14 17:30:48 -07:00
parent 8cc8ceb09c
commit 4a13107805
4 changed files with 99 additions and 18 deletions

View File

@ -529,6 +529,7 @@ struct WorkerInterfacesSpecialKeyImpl : SpecialKeyRangeBaseImpl {
struct SingleSpecialKeyImpl : SpecialKeyRangeBaseImpl {
Future<Standalone<RangeResultRef>> getRange(Reference<ReadYourWritesTransaction> ryw,
KeyRangeRef kr) const override {
if (!kr.contains(k)) return Standalone<RangeResultRef>();
return map(f(ryw), [k = k](Optional<Value> v) {
Standalone<RangeResultRef> result;
if (v.present()) {
@ -583,7 +584,7 @@ DatabaseContext::DatabaseContext(Reference<AsyncVar<Reference<ClusterConnectionF
transactionsProcessBehind("ProcessBehind", cc), outstandingWatches(0), latencies(1000), readLatencies(1000),
commitLatencies(1000), GRVLatencies(1000), mutationsPerCommit(1000), bytesPerCommit(1000), mvCacheInsertLocation(0),
healthMetricsLastUpdated(0), detailedHealthMetricsLastUpdated(0), internal(internal),
specialKeySpace(std::make_unique<SpecialKeySpace>(specialKeys.begin, specialKeys.end)) {
specialKeySpace(std::make_unique<SpecialKeySpace>(specialKeys.begin, specialKeys.end, /* test */ false)) {
dbId = deterministicRandom()->randomUniqueID();
connected = clientInfo->get().proxies.size() ? Void() : clientInfo->onChange();

View File

@ -23,7 +23,6 @@
#include "flow/actorcompiler.h" // This must be the last #include.
std::unordered_map<SpecialKeySpace::MODULE, KeyRange> SpecialKeySpace::moduleToBoundary = {
{ SpecialKeySpace::MODULE::TESTONLY, normalKeys }, // all test keys are supposed to be in normalKeys
{ SpecialKeySpace::MODULE::TRANSACTION,
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")) },
{ SpecialKeySpace::MODULE::WORKERINTERFACE,
@ -46,7 +45,7 @@ ACTOR Future<Void> moveKeySelectorOverRangeActor(const SpecialKeyRangeBaseImpl*
if (ks->offset < 1) {
// less than the given key
if (skrImpl->getKeyRange().contains(ks->getKey())) endKey = keyAfter(ks->getKey());
if (skrImpl->getKeyRange().contains(ks->getKey())) endKey = ks->getKey(); // keyAfter(ks->getKey());
} else {
// greater than the given key
if (skrImpl->getKeyRange().contains(ks->getKey())) startKey = ks->getKey();
@ -60,7 +59,7 @@ ACTOR Future<Void> moveKeySelectorOverRangeActor(const SpecialKeyRangeBaseImpl*
Standalone<RangeResultRef> result = wait(skrImpl->getRange(ryw, KeyRangeRef(startKey, endKey)));
if (result.size() == 0) {
TraceEvent("ZeroElementsIntheRange").detail("Start", startKey).detail("End", endKey);
TraceEvent(SevDebug, "ZeroElementsIntheRange").detail("Start", startKey).detail("End", endKey);
return Void();
}
// Note : KeySelector::setKey has byte limit according to the knobs, customize it if needed
@ -110,9 +109,9 @@ ACTOR Future<Void> normalizeKeySelectorActor(SpecialKeySpace* sks, Reference<Rea
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);
if (iter->value() != nullptr) {
wait(moveKeySelectorOverRangeActor(iter->value(), ryw, ks));
onModuleRead(ryw, sks->getImplToModuleMap().at(iter->value()), *lastModuleRead);
}
ks->offset < 1 ? --iter : ++iter;
}
@ -142,15 +141,16 @@ ACTOR Future<Standalone<RangeResultRef>> SpecialKeySpace::checkModuleFound(Speci
GetRangeLimits limits, bool reverse) {
std::pair<Standalone<RangeResultRef>, Optional<SpecialKeySpace::MODULE>> result =
wait(SpecialKeySpace::getRangeAggregationActor(sks, ryw, begin, end, limits, reverse));
// TODO : all cross_module_read or no_module_found error is better to check at the beginning, improve this later
if (ryw && !ryw->specialKeySpaceRelaxed()) {
auto module = result.second;
if (!module.present()) {
throw special_keys_no_module_found();
} else {
auto boundary = sks->moduleToBoundary.at(module.get());
if (!boundary.contains(begin.getKey()) || end.getKey() > boundary.end)
throw special_keys_cross_module_read();
}
} /*else {
auto boundary = sks->moduleToBoundary.at(module.get());
if (!boundary.contains(begin.getKey()) || end.getKey() < boundary.begin || end.getKey() > boundary.end)
throw special_keys_cross_module_read();
}*/
}
return result.first;
}
@ -167,6 +167,7 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, Reference<ReadYo
state Optional<SpecialKeySpace::MODULE> lastModuleRead;
wait(normalizeKeySelectorActor(sks, ryw, &begin, &lastModuleRead, &actualBeginOffset, &result));
// TODO : check if end the boundary of a module
wait(normalizeKeySelectorActor(sks, ryw, &end, &lastModuleRead, &actualEndOffset, &result));
// Handle all corner cases like what RYW does
// return if range inverted
@ -187,8 +188,8 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, Reference<ReadYo
if (reverse) {
while (iter != ranges.begin()) {
--iter;
onModuleRead(ryw, sks->getModules().rangeContaining(iter->begin())->value(), lastModuleRead);
if (iter->value() == nullptr) continue;
onModuleRead(ryw, sks->getImplToModuleMap().at(iter->value()), lastModuleRead);
KeyRangeRef kr = iter->range();
KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin;
KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end;
@ -210,8 +211,8 @@ SpecialKeySpace::getRangeAggregationActor(SpecialKeySpace* sks, Reference<ReadYo
}
} else {
for (iter = ranges.begin(); iter != ranges.end(); ++iter) {
onModuleRead(ryw, sks->getModules().rangeContaining(iter->begin())->value(), lastModuleRead);
if (iter->value() == nullptr) continue;
onModuleRead(ryw, sks->getImplToModuleMap().at(iter->value()), lastModuleRead);
KeyRangeRef kr = iter->range();
KeyRef keyStart = kr.contains(begin.getKey()) ? begin.getKey() : kr.begin;
KeyRef keyEnd = kr.contains(end.getKey()) ? end.getKey() : kr.end;
@ -248,6 +249,11 @@ Future<Standalone<RangeResultRef>> SpecialKeySpace::getRange(Reference<ReadYourW
begin.removeOrEqual(begin.arena());
end.removeOrEqual(end.arena());
// if( begin.offset >= end.offset && begin.getKey() >= end.getKey() ) {
// TEST(true); // RYW range inverted
// return Standalone<RangeResultRef>();
// }
return checkModuleFound(this, ryw, begin, end, limits, reverse);
}

View File

@ -51,6 +51,8 @@ protected:
class SpecialKeySpace {
public:
enum class MODULE {
UNKNOWN, // used for all unregistered range, a cross_module_read will happen if your range read contains any
// UNKKNOWN module range
TESTONLY, // only used by tests
TRANSACTION,
WORKERINTERFACE,
@ -64,23 +66,42 @@ public:
Future<Standalone<RangeResultRef>> getRange(Reference<ReadYourWritesTransaction> ryw, KeySelector begin,
KeySelector end, GetRangeLimits limits, bool reverse = false);
SpecialKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = normalKeys.end) {
SpecialKeySpace(KeyRef spaceStartKey = Key(), KeyRef spaceEndKey = normalKeys.end, bool test = true) {
// Default value is nullptr, begin of KeyRangeMap is Key()
impls = KeyRangeMap<SpecialKeyRangeBaseImpl*>(nullptr, spaceEndKey);
range = KeyRangeRef(spaceStartKey, spaceEndKey);
modules = KeyRangeMap<SpecialKeySpace::MODULE>(SpecialKeySpace::MODULE::UNKNOWN, spaceEndKey);
if (!test) modulesBoundaryInit();
// TODO : Handle testonly here
}
void modulesBoundaryInit() {
for (const auto& pair : moduleToBoundary) {
ASSERT(range.contains(pair.second));
// Make sure the module is not overlapping with any registered modules
ASSERT(modules.rangeContaining(pair.second.begin) == modules.rangeContaining(pair.second.end) &&
modules[pair.second.begin] == SpecialKeySpace::MODULE::UNKNOWN);
modules.insert(pair.second, pair.first);
impls.insert(pair.second, nullptr);
}
}
void registerKeyRange(SpecialKeySpace::MODULE module, const KeyRangeRef& kr, SpecialKeyRangeBaseImpl* impl) {
// range check
// TODO: add range check not to be replaced by overlapped ones
ASSERT(kr.begin >= range.begin && kr.end <= range.end);
ASSERT(range.contains(kr));
// make sure the registered range is not overlapping with existing ones
// Note: kr.end should not be the same as another range's begin, although it should work even they are the same
ASSERT(impls.rangeContaining(kr.begin) == impls.rangeContaining(kr.end) && impls[kr.begin] == nullptr);
// ASSERT(impls.rangeContaining(kr.begin) == impls.rangeContaining(kr.end) && impls[kr.begin] == nullptr);
for (auto iter = impls.rangeContaining(kr.begin); true; ++iter) {
ASSERT(iter->value() == nullptr);
if (iter == impls.rangeContaining(kr.end)) break; // relax the end to be another one's start if needed
}
impls.insert(kr, impl);
// Set module for the range
implToModule[impl] = module;
implToModule[impl] = module; // TODO : check do we really need this map
}
KeyRangeMap<SpecialKeyRangeBaseImpl*>& getImpls() { return impls; }
KeyRangeMap<SpecialKeySpace::MODULE>& getModules() { return modules; }
KeyRangeRef getKeyRange() const { return range; }
const std::unordered_map<SpecialKeyRangeBaseImpl*, SpecialKeySpace::MODULE>& getImplToModuleMap() const {
return implToModule;
@ -99,6 +120,7 @@ private:
KeySelector end, GetRangeLimits limits, bool reverse);
KeyRangeMap<SpecialKeyRangeBaseImpl*> impls;
KeyRangeMap<SpecialKeySpace::MODULE> modules;
KeyRange range;
std::unordered_map<SpecialKeyRangeBaseImpl*, SpecialKeySpace::MODULE> implToModule;

View File

@ -108,8 +108,8 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
return Void();
}
ACTOR Future<Void> _start(Database cx, SpecialKeySpaceCorrectnessWorkload* self) {
wait(timeout(self->getRangeCallActor(cx, self) && testConflictRanges(cx, /*read*/ true, self) &&
testConflictRanges(cx, /*read*/ false, self),
wait(timeout(self->testCrossModuleRead(cx, self) && self->getRangeCallActor(cx, self) &&
testConflictRanges(cx, /*read*/ true, self) && testConflictRanges(cx, /*read*/ false, self),
self->testDuration, Void()));
return Void();
}
@ -224,6 +224,58 @@ struct SpecialKeySpaceCorrectnessWorkload : TestWorkload {
return GetRangeLimits(rowLimits, byteLimits);
}
ACTOR Future<Void> testCrossModuleRead(Database cx_, SpecialKeySpaceCorrectnessWorkload* self) {
Database cx = cx_->clone();
state Reference<ReadYourWritesTransaction> tx = Reference(new ReadYourWritesTransaction(cx));
try {
wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transactio"), LiteralStringRef("\xff\xff/transaction0")),
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);
}
try {
wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction1")),
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);
}
try {
wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transaction"), LiteralStringRef("\xff\xff/transaction1")),
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);
}
try {
wait(success(tx->getRange(
KeyRangeRef(LiteralStringRef("\xff\xff/transaction/"), LiteralStringRef("\xff\xff/transaction0")),
CLIENT_KNOBS->TOO_MANY)));
ASSERT(true);
} catch (Error& e) {
throw;
}
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);
}
return Void();
}
ACTOR static Future<Void> testConflictRanges(Database cx_, bool read, SpecialKeySpaceCorrectnessWorkload* self) {
state StringRef prefix = read ? readConflictRangeKeysRange.begin : writeConflictRangeKeysRange.begin;
TEST(read); // test read conflict range special key implementation