/* * ConfigDatabaseUnitTests.actor.cpp * * This source file is part of the FoundationDB open source project * * Copyright 2013-2022 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("%lld", v); return StringRef(reinterpret_cast(s.c_str()), s.size()); } ACTOR static Future set(WriteToTransactionEnvironment* self, Optional configClass, KeyRef knobName, int64_t value) { 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, KeyRef knobName) { state Reference tr = IConfigTransaction::createTestSimple(self->cti); auto configKey = encodeConfigKey(configClass, knobName); 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, KeyRef knobName, int64_t value) { return set(this, configClass, knobName, value); } Future clear(Optional configClass, KeyRef knobName) { return clear(this, configClass, knobName); } Future compact() { return cfi.compact.getReply(ConfigFollowerCompactRequest{ lastWrittenVersion }); } Future rollforward(Optional rollback, Version lastKnownCommitted, Version target, Standalone> mutations, Standalone> annotations) { return cfi.rollforward.getReply( ConfigFollowerRollforwardRequest{ rollback, lastKnownCommitted, target, mutations, annotations }); } void restartNode() { cfiServer.cancel(); ctiServer.cancel(); node = makeReference(dataDir); setup(); } ConfigTransactionInterface getTransactionInterface() const { return cti; } ConfigFollowerInterface getFollowerInterface() const { return cfi; } void close() const { node->close(); } Future onClosed() const { return node->onClosed(); } Future getError() const { return cfiServer || ctiServer; } }; class ReadFromLocalConfigEnvironment { UID id; std::string dataDir; Reference localConfiguration; Reference const> cbi; Future consumer; ACTOR template static Future checkEventually(Reference localConfiguration, V T::*member, Optional expected) { state double lastMismatchTime = now(); loop { if (localConfiguration->getTestKnobs().*member == expected.orDefault(0)) { return Void(); } if (now() > lastMismatchTime + 1.0) { TraceEvent(SevWarn, "CheckEventuallyStillChecking") .detail("Expected", expected.present() ? expected.get() : 0) .detail("TestMember", localConfiguration->getTestKnobs().*member); 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(makeReference(dataDir, configPath, manualKnobOverrides, IsTest::True)), consumer(Never()) {} Future setup() { return setup(this); } Future restartLocalConfig(std::string const& newConfigPath) { std::map manualKnobOverrides = {}; localConfiguration = makeReference(dataDir, newConfigPath, manualKnobOverrides, IsTest::True); return setup(); } void connectToBroadcaster(Reference const> const& cbi) { this->cbi = cbi; consumer = localConfiguration->consume(cbi->get()); } template void checkImmediate(V T::*member, Optional expected) const { if (expected.present()) { ASSERT_EQ(localConfiguration->getTestKnobs().*member, expected.get()); } else { ASSERT_EQ(localConfiguration->getTestKnobs().*member, 0); } } template Future checkEventually(V T::*member, Optional expected) const { return checkEventually(localConfiguration, member, expected); } LocalConfiguration& getMutableLocalConfiguration() { return *localConfiguration; } void close() const { localConfiguration->close(); } Future onClosed() const { return localConfiguration->onClosed(); } 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, KeyRef knobName, Optional value) { Standalone> versionedMutations; appendVersionedMutation(versionedMutations, ++lastWrittenVersion, configClass, knobName, 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, KeyRef knobName) { return addMutation(configClass, knobName, {}); } Future set(Optional configClass, KeyRef knobName, int64_t value) { auto knobValue = KnobValueRef::create(value); return addMutation(configClass, knobName, knobValue.contents()); } template void check(V T::*member, Optional value) const { return readFrom.checkImmediate(member, value); } }; class BroadcasterToLocalConfigEnvironment { ReadFromLocalConfigEnvironment readFrom; Reference> cbi; ConfigBroadcaster broadcaster; Version lastWrittenVersion{ 0 }; Future broadcastServer; Promise workerFailure; Future workerFailed_; ACTOR static Future setup(BroadcasterToLocalConfigEnvironment* self, ConfigClassSet configClassSet) { wait(self->readFrom.setup()); self->cbi = makeReference>(); self->readFrom.connectToBroadcaster(self->cbi); self->broadcastServer = self->broadcaster.registerNode( WorkerInterface(), 0, configClassSet, self->workerFailure.getFuture(), self->cbi->get()); return Void(); } void addMutation(Optional configClass, KeyRef knobName, KnobValueRef value) { Standalone> versionedMutations; appendVersionedMutation(versionedMutations, ++lastWrittenVersion, configClass, knobName, 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, KeyRef knobName, int64_t value) { auto knobValue = KnobValueRef::create(value); addMutation(configClass, knobName, knobValue.contents()); } void clear(Optional configClass, KeyRef knobName) { addMutation(configClass, knobName, {}); } template Future check(V T::*member, Optional value) const { return readFrom.checkEventually(member, value); } void changeBroadcaster() { broadcastServer.cancel(); cbi->set(ConfigBroadcastInterface{}); readFrom.connectToBroadcaster(cbi); broadcastServer = broadcaster.registerNode(WorkerInterface(), readFrom.lastSeenVersion(), readFrom.configClassSet(), workerFailure.getFuture(), cbi->get()); } Future restartLocalConfig(std::string const& newConfigPath) { return readFrom.restartLocalConfig(newConfigPath); } void killLocalConfig() { workerFailed_ = broadcaster.getClientFailure(cbi->get().id()); workerFailure.send(Void()); } Future workerFailed() { ASSERT(workerFailed_.isValid()); return workerFailed_; } void compact() { broadcaster.compact(lastWrittenVersion); } void close() const { readFrom.close(); } Future onClosed() const { return readFrom.onClosed(); } Future getError() const { return readFrom.getError() || broadcaster.getError(); } }; class TransactionEnvironment { WriteToTransactionEnvironment writeTo; ACTOR static Future check(TransactionEnvironment* self, Optional configClass, KeyRef knobName, Optional expected) { state Reference tr = IConfigTransaction::createTestSimple(self->writeTo.getTransactionInterface()); state Key configKey = encodeConfigKey(configClass, knobName); 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, KeyRef knobName, T value) { return writeTo.set(configClass, knobName, value); } Future clear(Optional configClass, KeyRef knobName) { return writeTo.clear(configClass, knobName); } Future check(Optional configClass, KeyRef knobName, Optional expected) { return check(this, configClass, knobName, 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 rollforward(Optional rollback, Version lastKnownCommitted, Version target, Standalone> mutations, Standalone> annotations) { return writeTo.rollforward(rollback, lastKnownCommitted, target, mutations, annotations); } Future getError() const { return writeTo.getError(); } }; class TransactionToLocalConfigEnvironment { WriteToTransactionEnvironment writeTo; ReadFromLocalConfigEnvironment readFrom; Reference> cbi; ConfigBroadcaster broadcaster; Future broadcastServer; Promise workerFailure; Future workerFailed_; ACTOR static Future setup(TransactionToLocalConfigEnvironment* self, ConfigClassSet configClassSet) { wait(self->readFrom.setup()); self->cbi = makeReference>(); self->readFrom.connectToBroadcaster(self->cbi); self->broadcastServer = self->broadcaster.registerNode( WorkerInterface(), 0, configClassSet, self->workerFailure.getFuture(), 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.registerNode(WorkerInterface(), readFrom.lastSeenVersion(), readFrom.configClassSet(), workerFailure.getFuture(), cbi->get()); } Future restartLocalConfig(std::string const& newConfigPath) { return readFrom.restartLocalConfig(newConfigPath); } void killLocalConfig() { workerFailed_ = broadcaster.getClientFailure(cbi->get().id()); workerFailure.send(Void()); } Future workerFailed() { ASSERT(workerFailed_.isValid()); return workerFailed_; } Future compact() { return writeTo.compact(); } template Future set(Optional configClass, KeyRef knobName, T const& value) { return writeTo.set(configClass, knobName, value); } Future clear(Optional configClass, KeyRef knobName) { return writeTo.clear(configClass, knobName); } template Future check(V T::*member, Optional value) const { return readFrom.checkEventually(member, value); } void close() const { writeTo.close(); readFrom.close(); } Future onClosed() const { return writeTo.onClosed() && readFrom.onClosed(); } 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(); } template Future rollforward(Env& env, Args&&... args) { return waitOrError(env.rollforward(std::forward(args)...), env.getError()); } 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, "test_long"_sr, int64_t{ 1 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); wait(env.restartLocalConfig("class-A")); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); wait(set(env, "class-A"_sr, "test_long"_sr, 2)); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); wait(env.restartLocalConfig("class-B")); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 0 })); wait(set(env, "class-B"_sr, "test_long"_sr, int64_t{ 2 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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, &TestKnobs::TEST_LONG, Optional{ 1 })); wait(set(env, "class-A"_sr, "test_long"_sr, 2)); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 2 })); return Void(); } ACTOR template Future testKillWorker(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); env.killLocalConfig(); // Make sure broadcaster detects worker death in a timely manner. wait(timeoutError(env.workerFailed(), 3)); Future closed = env.onClosed(); env.close(); wait(closed); 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, "test_long"_sr, int64_t{ 1 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); return Void(); } ACTOR template Future testAtomicSet(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); state bool restarted = false; try { wait(set(env, "class-A"_sr, "test_atomic_long"_sr, int64_t{ 1 })); } catch (Error& e) { ASSERT(e.code() == error_code_local_config_changed); restarted = true; } ASSERT(restarted); wait(env.restartLocalConfig("class-A")); wait(check(env, &TestKnobs::TEST_ATOMIC_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); wait(clear(env, "class-A"_sr, "test_long"_sr)); wait(check(env, &TestKnobs::TEST_LONG, Optional{})); return Void(); } ACTOR template Future testAtomicClear(UnitTestParameters params) { state Env env(params.getDataDir(), "class-A"); wait(env.setup(ConfigClassSet({ "class-A"_sr }))); state bool restarted = false; try { wait(set(env, "class-A"_sr, "test_atomic_long"_sr, int64_t{ 1 })); } catch (Error& e) { ASSERT(e.code() == error_code_local_config_changed); restarted = true; } ASSERT(restarted); restarted = false; try { wait(clear(env, "class-A"_sr, "test_atomic_long"_sr)); } catch (Error& e) { ASSERT(e.code() == error_code_local_config_changed); restarted = true; } ASSERT(restarted); wait(check(env, &TestKnobs::TEST_ATOMIC_LONG, 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{}, "test_long"_sr, int64_t{ 1 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 10 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); choose { when(wait(delay(5))) {} when(wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 }))) { ASSERT(false); } } return Void(); } 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, "test_long"_sr, int64_t{ 1 })); wait(compact(env)); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 2 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 1 })); env.changeBroadcaster(); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 2 })); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); wait(set(env, "class-B"_sr, "test_long"_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(), "test_long"_sr, int64_t{ 1 })); wait(set(env, configClass.castTo(), "test_int"_sr, int{ 2 })); wait(set(env, "class-B"_sr, "test_double"_sr, double{ 3.0 })); // 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/AtomicSet") { wait(testAtomicSet(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/AtomicClear") { wait(testAtomicClear(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, "test_long"_sr, int64_t{ 1 })); wait(set(env, "class-B"_sr, "test_long"_sr, int64_t{ 10 })); env.check(&TestKnobs::TEST_LONG, Optional{ 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, "test_long"_sr, int64_t{ 1 })); env.check(&TestKnobs::TEST_LONG, Optional{ 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/BroadcasterToLocalConfig/KillWorker") { wait(testKillWorker(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, "test_long"_sr, int64_t{ 1 })); env.restartNode(); wait(check(env, &TestKnobs::TEST_LONG, Optional{ 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/TransactionToLocalConfig/KillWorker") { wait(testKillWorker(params)); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Set") { state TransactionEnvironment env(params.getDataDir()); wait(env.setup()); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 1 })); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{ 1 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Clear") { state TransactionEnvironment env(params.getDataDir()); wait(env.setup()); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 1 })); wait(clear(env, "class-A"_sr, "test_long"_sr)); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{})); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Restart") { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 1 })); env.restartNode(); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{ 1 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/CompactNode") { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 1 })); wait(compact(env)); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{ 1 })); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 2 })); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{ 2 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/Rollforward") { state TransactionEnvironment env(params.getDataDir()); Standalone> mutations; appendVersionedMutation( mutations, 1, "class-A"_sr, "test_long_v1"_sr, KnobValueRef::create(int64_t{ 1 }).contents()); appendVersionedMutation( mutations, 2, "class-B"_sr, "test_long_v2"_sr, KnobValueRef::create(int64_t{ 2 }).contents()); Standalone> annotations; annotations.emplace_back_deep(annotations.arena(), 1, ConfigCommitAnnotationRef{ "unit_test"_sr, now() }); annotations.emplace_back_deep(annotations.arena(), 2, ConfigCommitAnnotationRef{ "unit_test"_sr, now() }); wait(rollforward(env, Optional{}, 0, 2, mutations, annotations)); wait(check(env, "class-A"_sr, "test_long_v1"_sr, Optional{ 1 })); wait(check(env, "class-B"_sr, "test_long_v2"_sr, Optional{ 2 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/RollforwardWithExistingMutation") { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 1 })); Standalone> mutations; appendVersionedMutation( mutations, 2, "class-A"_sr, "test_long_v2"_sr, KnobValueRef::create(int64_t{ 2 }).contents()); appendVersionedMutation( mutations, 3, "class-A"_sr, "test_long_v3"_sr, KnobValueRef::create(int64_t{ 3 }).contents()); Standalone> annotations; annotations.emplace_back_deep(annotations.arena(), 2, ConfigCommitAnnotationRef{ "unit_test"_sr, now() }); annotations.emplace_back_deep(annotations.arena(), 3, ConfigCommitAnnotationRef{ "unit_test"_sr, now() }); wait(rollforward(env, Optional{}, 1, 3, mutations, annotations)); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{ 1 })); wait(check(env, "class-A"_sr, "test_long_v2"_sr, Optional{ 2 })); wait(check(env, "class-A"_sr, "test_long_v3"_sr, Optional{ 3 })); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/RollforwardWithInvalidMutation") { state TransactionEnvironment env(params.getDataDir()); Standalone> mutations; appendVersionedMutation( mutations, 1, "class-A"_sr, "test_long_v1"_sr, KnobValueRef::create(int64_t{ 1 }).contents()); appendVersionedMutation( mutations, 10, "class-A"_sr, "test_long_v10"_sr, KnobValueRef::create(int64_t{ 2 }).contents()); Standalone> annotations; annotations.emplace_back_deep(annotations.arena(), 1, ConfigCommitAnnotationRef{ "unit_test"_sr, now() }); wait(rollforward(env, Optional{}, 0, 5, mutations, annotations)); wait(check(env, "class-A"_sr, "test_long_v1"_sr, Optional{ 1 })); wait(check(env, "class-A"_sr, "test_long_v10"_sr, Optional{})); return Void(); } TEST_CASE("/fdbserver/ConfigDB/Transaction/RollbackThenRollforward") { state TransactionEnvironment env(params.getDataDir()); wait(set(env, "class-A"_sr, "test_long"_sr, int64_t{ 1 })); Standalone> mutations; appendVersionedMutation( mutations, 1, "class-B"_sr, "test_long_v1"_sr, KnobValueRef::create(int64_t{ 2 }).contents()); Standalone> annotations; annotations.emplace_back_deep(annotations.arena(), 1, ConfigCommitAnnotationRef{ "unit_test"_sr, now() }); wait(rollforward(env, 0, 1, 1, mutations, annotations)); wait(check(env, "class-A"_sr, "test_long"_sr, Optional{})); wait(check(env, "class-B"_sr, "test_long_v1"_sr, Optional{ 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(); } 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(); }