/* * ConfigDatabaseUnitTests.actor.cpp * * This source file is part of the FoundationDB open source project * * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fdbclient/CoordinationInterface.h" #include "fdbclient/IConfigTransaction.h" #include "fdbclient/TestKnobCollection.h" #include "fdbserver/ConfigBroadcaster.h" #include "fdbserver/ConfigNode.h" #include "fdbserver/LocalConfiguration.h" #include "fdbclient/Tuple.h" #include "flow/UnitTest.h" #include "flow/actorcompiler.h" // must be last include namespace { Key encodeConfigKey(Optional configClass, KeyRef knobName) { Tuple tuple; if (configClass.present()) { tuple.append(configClass.get()); } else { tuple.appendNull(); } tuple << knobName; return tuple.pack(); } void appendVersionedMutation(Standalone>& versionedMutations, Version version, Optional configClass, KeyRef knobName, Optional knobValue) { auto configKey = ConfigKeyRef(configClass, knobName); auto mutation = ConfigMutationRef(configKey, knobValue); versionedMutations.emplace_back_deep(versionedMutations.arena(), version, mutation); } class WriteToTransactionEnvironment { std::string dataDir; ConfigTransactionInterface cti; ConfigFollowerInterface cfi; Reference node; Future ctiServer; Future cfiServer; Version lastWrittenVersion{ 0 }; static Value longToValue(int64_t v) { auto s = format("%ld", v); return StringRef(reinterpret_cast(s.c_str()), s.size()); } ACTOR static Future set(WriteToTransactionEnvironment* self, Optional configClass, int64_t value, KeyRef knobName) { state Reference tr = IConfigTransaction::createTestSimple(self->cti); auto configKey = encodeConfigKey(configClass, knobName); tr->set(configKey, longToValue(value)); wait(tr->commit()); self->lastWrittenVersion = tr->getCommittedVersion(); return Void(); } ACTOR static Future clear(WriteToTransactionEnvironment* self, Optional configClass) { state Reference tr = IConfigTransaction::createTestSimple(self->cti); auto configKey = encodeConfigKey(configClass, "test_long"_sr); tr->clear(configKey); wait(tr->commit()); self->lastWrittenVersion = tr->getCommittedVersion(); return Void(); } void setup() { ctiServer = node->serve(cti); cfiServer = node->serve(cfi); } public: WriteToTransactionEnvironment(std::string const& dataDir) : dataDir(dataDir), node(makeReference(dataDir)) { platform::eraseDirectoryRecursive(dataDir); setup(); } Future set(Optional configClass, int64_t value, KeyRef knobName = "test_long"_sr) { return set(this, configClass, value, knobName); } Future clear(Optional configClass) { return clear(this, configClass); } Future compact() { return cfi.compact.getReply(ConfigFollowerCompactRequest{ lastWrittenVersion }); } void restartNode() { cfiServer.cancel(); ctiServer.cancel(); node = makeReference(dataDir); setup(); } ConfigTransactionInterface getTransactionInterface() const { return cti; } ConfigFollowerInterface getFollowerInterface() const { return cfi; } Future getError() const { return cfiServer || ctiServer; } }; class ReadFromLocalConfigEnvironment { UID id; std::string dataDir; LocalConfiguration localConfiguration; Reference const> cbi; Future consumer; ACTOR static Future checkEventually(LocalConfiguration const* localConfiguration, Optional expected) { state double lastMismatchTime = now(); loop { if (localConfiguration->getTestKnobs().TEST_LONG == expected.orDefault(0)) { return Void(); } if (now() > lastMismatchTime + 1.0) { TraceEvent(SevWarn, "CheckEventuallyStillChecking") .detail("Expected", expected.present() ? expected.get() : 0) .detail("TestLong", localConfiguration->getTestKnobs().TEST_LONG); lastMismatchTime = now(); } wait(delayJittered(0.1)); } } ACTOR static Future setup(ReadFromLocalConfigEnvironment* self) { wait(self->localConfiguration.initialize()); if (self->cbi) { // LocalConfiguration runs in a loop waiting for messages from the // broadcaster. These unit tests use the same // ConfigBroadcastInterface across restarts, so when "killing" the // old LocalConfiguration, it's necessary to make sure it is // completely stopped before starting the second config. This // prevents two actors trying to listen for the same message on the // same interface, causing lots of issues! self->consumer.cancel(); self->consumer = self->localConfiguration.consume(self->cbi->get()); } return Void(); } public: ReadFromLocalConfigEnvironment(std::string const& dataDir, std::string const& configPath, std::map const& manualKnobOverrides) : dataDir(dataDir), localConfiguration(dataDir, configPath, manualKnobOverrides, IsTest::True), consumer(Never()) {} Future setup() { return setup(this); } Future restartLocalConfig(std::string const& newConfigPath) { localConfiguration = LocalConfiguration(dataDir, newConfigPath, {}, IsTest::True); return setup(); } void connectToBroadcaster(Reference const> const& cbi) { this->cbi = cbi; consumer = localConfiguration.consume(cbi->get()); } void checkImmediate(Optional expected) const { if (expected.present()) { ASSERT_EQ(localConfiguration.getTestKnobs().TEST_LONG, expected.get()); } else { ASSERT_EQ(localConfiguration.getTestKnobs().TEST_LONG, 0); } } Future checkEventually(Optional expected) const { return checkEventually(&localConfiguration, expected); } LocalConfiguration& getMutableLocalConfiguration() { return localConfiguration; } Future getError() const { return consumer; } Version lastSeenVersion() { return localConfiguration.lastSeenVersion(); } ConfigClassSet configClassSet() { return localConfiguration.configClassSet(); } }; class LocalConfigEnvironment { ReadFromLocalConfigEnvironment readFrom; Version lastWrittenVersion{ 0 }; Future addMutation(Optional configClass, Optional value) { Standalone> versionedMutations; appendVersionedMutation(versionedMutations, ++lastWrittenVersion, configClass, "test_long"_sr, value); return readFrom.getMutableLocalConfiguration().addChanges(versionedMutations, lastWrittenVersion); } public: LocalConfigEnvironment(std::string const& dataDir, std::string const& configPath, std::map const& manualKnobOverrides = {}) : readFrom(dataDir, configPath, manualKnobOverrides) {} Future setup(ConfigClassSet const& configClassSet) { return readFrom.setup(); } Future restartLocalConfig(std::string const& newConfigPath) { return readFrom.restartLocalConfig(newConfigPath); } Future getError() const { return Never(); } Future clear(Optional configClass) { return addMutation(configClass, {}); } Future set(Optional configClass, int64_t value) { auto knobValue = KnobValueRef::create(value); return addMutation(configClass, knobValue.contents()); } void check(Optional value) const { return readFrom.checkImmediate(value); } }; class BroadcasterToLocalConfigEnvironment { ReadFromLocalConfigEnvironment readFrom; Reference> cbi; ConfigBroadcaster broadcaster; Version lastWrittenVersion{ 0 }; Future broadcastServer; ACTOR static Future setup(BroadcasterToLocalConfigEnvironment* self, ConfigClassSet configClassSet) { wait(self->readFrom.setup()); self->cbi = makeReference>(); self->readFrom.connectToBroadcaster(self->cbi); self->broadcastServer = self->broadcaster.registerWorker(0, configClassSet, Never(), self->cbi->get()); return Void(); } void addMutation(Optional configClass, KnobValueRef value) { Standalone> versionedMutations; appendVersionedMutation(versionedMutations, ++lastWrittenVersion, configClass, "test_long"_sr, value); broadcaster.applyChanges(versionedMutations, lastWrittenVersion, {}); } public: BroadcasterToLocalConfigEnvironment(std::string const& dataDir, std::string const& configPath) : readFrom(dataDir, configPath, {}), cbi(makeReference>()), broadcaster(ConfigFollowerInterface{}) {} Future setup(ConfigClassSet const& configClassSet) { return setup(this, configClassSet); } void set(Optional configClass, int64_t value) { auto knobValue = KnobValueRef::create(value); addMutation(configClass, knobValue.contents()); } void clear(Optional configClass) { addMutation(configClass, {}); } Future check(Optional value) const { return readFrom.checkEventually(value); } void changeBroadcaster() { broadcastServer.cancel(); cbi->set(ConfigBroadcastInterface{}); readFrom.connectToBroadcaster(cbi); broadcastServer = broadcaster.registerWorker(readFrom.lastSeenVersion(), readFrom.configClassSet(), Never(), cbi->get()); } Future restartLocalConfig(std::string const& newConfigPath) { return readFrom.restartLocalConfig(newConfigPath); } void compact() { broadcaster.compact(lastWrittenVersion); } Future getError() const { return readFrom.getError() || broadcaster.getError(); } }; class TransactionEnvironment { WriteToTransactionEnvironment writeTo; ACTOR static Future check(TransactionEnvironment* self, Optional configClass, Optional expected) { state Reference tr = IConfigTransaction::createTestSimple(self->writeTo.getTransactionInterface()); state Key configKey = encodeConfigKey(configClass, "test_long"_sr); state Optional value = wait(tr->get(configKey)); if (expected.present()) { ASSERT_EQ(BinaryReader::fromStringRef(value.get(), Unversioned()), expected.get()); } else { ASSERT(!value.present()); } return Void(); } ACTOR static Future>> getConfigClasses(TransactionEnvironment* self) { state Reference tr = IConfigTransaction::createTestSimple(self->writeTo.getTransactionInterface()); state KeySelector begin = firstGreaterOrEqual(configClassKeys.begin); state KeySelector end = firstGreaterOrEqual(configClassKeys.end); RangeResult range = wait(tr->getRange(begin, end, 1000)); Standalone> result; for (const auto& kv : range) { result.push_back_deep(result.arena(), kv.key); ASSERT(kv.value == ""_sr); } return result; } ACTOR static Future>> getKnobNames(TransactionEnvironment* self, Optional configClass) { state Reference tr = IConfigTransaction::createTestSimple(self->writeTo.getTransactionInterface()); state KeyRange keys = globalConfigKnobKeys; if (configClass.present()) { keys = singleKeyRange(configClass.get().withPrefix(configKnobKeys.begin)); } KeySelector begin = firstGreaterOrEqual(keys.begin); KeySelector end = firstGreaterOrEqual(keys.end); RangeResult range = wait(tr->getRange(begin, end, 1000)); Standalone> result; for (const auto& kv : range) { result.push_back_deep(result.arena(), kv.key); ASSERT(kv.value == ""_sr); } return result; } ACTOR static Future badRangeRead(TransactionEnvironment* self) { state Reference tr = IConfigTransaction::createTestSimple(self->writeTo.getTransactionInterface()); KeySelector begin = firstGreaterOrEqual(normalKeys.begin); KeySelector end = firstGreaterOrEqual(normalKeys.end); wait(success(tr->getRange(begin, end, 1000))); return Void(); } public: TransactionEnvironment(std::string const& dataDir) : writeTo(dataDir) {} Future setup() { return Void(); } void restartNode() { writeTo.restartNode(); } template Future set(Optional configClass, T value, KeyRef knobName = "test_long"_sr) { return writeTo.set(configClass, value, knobName); } Future clear(Optional configClass) { return writeTo.clear(configClass); } Future check(Optional configClass, Optional expected) { return check(this, configClass, expected); } Future badRangeRead() { return badRangeRead(this); } Future>> getConfigClasses() { return getConfigClasses(this); } Future>> getKnobNames(Optional configClass) { return getKnobNames(this, configClass); } Future compact() { return writeTo.compact(); } Future getError() const { return writeTo.getError(); } }; class TransactionToLocalConfigEnvironment { WriteToTransactionEnvironment writeTo; ReadFromLocalConfigEnvironment readFrom; Reference> cbi; ConfigBroadcaster broadcaster; Future broadcastServer; ACTOR static Future setup(TransactionToLocalConfigEnvironment* self, ConfigClassSet configClassSet) { wait(self->readFrom.setup()); self->cbi = makeReference>(); self->readFrom.connectToBroadcaster(self->cbi); self->broadcastServer = self->broadcaster.registerWorker(0, configClassSet, Never(), self->cbi->get()); return Void(); } public: TransactionToLocalConfigEnvironment(std::string const& dataDir, std::string const& configPath) : writeTo(dataDir), readFrom(dataDir, configPath, {}), cbi(makeReference>()), broadcaster(writeTo.getFollowerInterface()) {} Future setup(ConfigClassSet const& configClassSet) { return setup(this, configClassSet); } void restartNode() { writeTo.restartNode(); } void changeBroadcaster() { broadcastServer.cancel(); cbi->set(ConfigBroadcastInterface{}); readFrom.connectToBroadcaster(cbi); broadcastServer = broadcaster.registerWorker(readFrom.lastSeenVersion(), readFrom.configClassSet(), Never(), cbi->get()); } Future restartLocalConfig(std::string const& newConfigPath) { return readFrom.restartLocalConfig(newConfigPath); } Future compact() { return writeTo.compact(); } template Future set(Optional configClass, T const& value) { return writeTo.set(configClass, value); } Future clear(Optional configClass) { return writeTo.clear(configClass); } Future check(Optional value) const { return readFrom.checkEventually(value); } Future getError() const { return writeTo.getError() || readFrom.getError() || broadcaster.getError(); } }; // These functions give a common interface to all environments, to improve code reuse template Future set(Env& env, Args&&... args) { return waitOrError(env.set(std::forward(args)...), env.getError()); } template Future set(BroadcasterToLocalConfigEnvironment& env, Args&&... args) { env.set(std::forward(args)...); return Void(); } template Future clear(Env& env, Args&&... args) { return waitOrError(env.clear(std::forward(args)...), env.getError()); } template Future clear(BroadcasterToLocalConfigEnvironment& env, Args&&... args) { env.clear(std::forward(args)...); return Void(); } template Future check(Env& env, Args&&... args) { return waitOrError(env.check(std::forward(args)...), env.getError()); } template Future check(LocalConfigEnvironment& env, Args&&... args) { env.check(std::forward(args)...); return Void(); } template Future compact(Env& env) { return waitOrError(env.compact(), env.getError()); } Future compact(BroadcasterToLocalConfigEnvironment& env) { env.compact(); return Void(); } ACTOR template Future testRestartLocalConfig(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(check(env, int64_t{ 1 })); wait(env.restartLocalConfig("class-A")); wait(check(env, int64_t{ 1 })); wait(set(env, "class-A"_sr, 2)); wait(check(env, int64_t{ 2 })); return Void(); } ACTOR template Future testRestartLocalConfigAndChangeClass(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr, "class-B"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(check(env, int64_t{ 1 })); wait(env.restartLocalConfig("class-B")); wait(check(env, int64_t{ 0 })); wait(set(env, "class-B"_sr, int64_t{ 2 })); wait(check(env, int64_t{ 2 })); return Void(); } ACTOR template Future testNewLocalConfigAfterCompaction(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(check(env, int64_t{ 1 })); wait(compact(env)); // Erase the data dir to simulate a new worker joining the system after // compaction. platform::eraseDirectoryRecursive(params.getDataDir()); platform::createDirectory(params.getDataDir()); wait(env.restartLocalConfig("class-A")); // Reregister worker with broadcaster. env.changeBroadcaster(); wait(check(env, int64_t{ 1 })); wait(set(env, "class-A"_sr, 2)); wait(check(env, int64_t{ 2 })); return Void(); } ACTOR template Future testSet(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(check(env, int64_t{ 1 })); return Void(); } ACTOR template Future testClear(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(clear(env, "class-A"_sr)); wait(check(env, Optional{})); return Void(); } ACTOR template Future testGlobalSet(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, Optional{}, int64_t{ 1 })); wait(check(env, int64_t{ 1 })); wait(set(env, "class-A"_sr, int64_t{ 10 })); wait(check(env, int64_t{ 10 })); return Void(); } ACTOR template Future testIgnore(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr, "class-B"_sr }))); wait(set(env, "class-B"_sr, int64_t{ 1 })); choose { when(wait(delay(5))) {} when(wait(check(env, int64_t{ 1 }))) { ASSERT(false); } } return Void(); } // TODO: ./bin/fdbserver -r simulation -f ../foundationdb/tests/SpecificUnitTest.txt --seed 2118116838 // Rare timing bug where compaction runs before the consumer learns of the update ACTOR template Future testCompact(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(compact(env)); wait(check(env, 1)); wait(set(env, "class-A"_sr, int64_t{ 2 })); wait(check(env, 2)); return Void(); } ACTOR template Future testChangeBroadcaster(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(check(env, int64_t{ 1 })); env.changeBroadcaster(); wait(set(env, "class-A"_sr, int64_t{ 2 })); wait(check(env, int64_t{ 2 })); return Void(); } bool matches(Standalone> const& vec, std::set const& compareTo) { std::set s; for (const auto& value : vec) { s.insert(value); } return (s == compareTo); } ACTOR Future testGetConfigClasses(UnitTestParameters params, bool doCompact) { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(set(env, "class-B"_sr, int64_t{ 1 })); if (doCompact) { wait(compact(env)); } Standalone> configClasses = wait(env.getConfigClasses()); ASSERT(matches(configClasses, { "class-A"_sr, "class-B"_sr })); return Void(); } ACTOR Future testGetKnobs(UnitTestParameters params, bool global, bool doCompact) { state TransactionEnvironment env(params.getDataDir()); state Optional configClass; if (!global) { configClass = "class-A"_sr; } wait(set(env, configClass.castTo(), int64_t{ 1 }, "test_long"_sr)); wait(set(env, configClass.castTo(), int{ 2 }, "test_int"_sr)); wait(set(env, "class-B"_sr, double{ 3.0 }, "test_double"_sr)); // ignored if (doCompact) { wait(compact(env)); } Standalone> knobNames = wait(waitOrError(env.getKnobNames(configClass.castTo()), env.getError())); ASSERT(matches(knobNames, { "test_long"_sr, "test_int"_sr })); return Void(); } } // namespace TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Set") { wait(testSet(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Restart") { wait(testRestartLocalConfig(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/RestartFresh") { wait(testRestartLocalConfigAndChangeClass(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Clear") { wait(testClear(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/GlobalSet") { wait(testGlobalSet(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ConflictingOverrides") { state LocalConfigEnvironment env(params.getDataDir(), "class-A/class-B", {}); wait(env.setup(ConfigClassSet({ "class-A"_sr, "class-B"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(set(env, "class-B"_sr, int64_t{ 10 })); env.check(10); return Void(); } TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Manual") { state LocalConfigEnvironment env(params.getDataDir(), "class-A", { { "test_long", "1000" } }); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); env.check(1000); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Set") { wait(testSet(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Clear") { wait(testClear(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Ignore") { wait(testIgnore(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/GlobalSet") { wait(testGlobalSet(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/ChangeBroadcaster") { wait(testChangeBroadcaster(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/RestartLocalConfig") { wait(testRestartLocalConfig(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/RestartLocalConfigAndChangeClass") { wait(testRestartLocalConfigAndChangeClass(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Compact") { wait(testCompact(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/RestartLocalConfigurationAfterCompaction") { wait(testNewLocalConfigAfterCompaction(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/Set") { wait(testSet(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/Clear") { wait(testClear(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/GlobalSet") { wait(testGlobalSet(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/RestartNode") { state TransactionToLocalConfigEnvironment env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); wait(set(env, "class-A"_sr, int64_t{ 1 })); env.restartNode(); wait(check(env, int64_t{ 1 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/ChangeBroadcaster") { wait(testChangeBroadcaster(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/RestartLocalConfigAndChangeClass") { wait(testRestartLocalConfigAndChangeClass(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/CompactNode") { wait(testCompact(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/RestartLocalConfigurationAfterCompaction") { wait(testNewLocalConfigAfterCompaction(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Set") { state TransactionEnvironment env(params.getDataDir()); wait(env.setup()); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(check(env, "class-A"_sr, int64_t{ 1 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Clear") { state TransactionEnvironment env(params.getDataDir()); wait(env.setup()); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(clear(env, "class-A"_sr)); wait(check(env, "class-A"_sr, Optional{})); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Restart") { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, int64_t{ 1 })); env.restartNode(); wait(check(env, "class-A"_sr, int64_t{ 1 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/CompactNode") { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, int64_t{ 1 })); wait(compact(env)); wait(check(env, "class-A"_sr, int64_t{ 1 })); wait(set(env, "class-A"_sr, int64_t{ 2 })); wait(check(env, "class-A"_sr, int64_t{ 2 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/GetConfigClasses") { wait(testGetConfigClasses(params, false)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/CompactThenGetConfigClasses") { wait(testGetConfigClasses(params, true)); return Void(); } // TODO: The below tests seem to always segfault TEST_CASE("/fdbserver/ConfigDB/Transaction/GetKnobs") { wait(testGetKnobs(params, false, false)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/CompactThenGetKnobs") { wait(testGetKnobs(params, false, true)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/GetGlobalKnobs") { wait(testGetKnobs(params, true, false)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/CompactThenGetGlobalKnobs") { wait(testGetKnobs(params, true, true)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/BadRangeRead") { state TransactionEnvironment env(params.getDataDir()); try { wait(env.badRangeRead() || env.getError()); ASSERT(false); } catch (Error& e) { ASSERT_EQ(e.code(), error_code_invalid_config_db_range_read); } return Void(); } // TODO: Test worker failure detection on ConfigBroadcaster