Refactor ConfigDatabaseUnitTests

This commit is contained in:
sfc-gh-tclinkenbeard 2021-05-16 14:36:52 -07:00
parent e86532d40a
commit be2e9e1b5a
1 changed files with 217 additions and 158 deletions

View File

@ -54,7 +54,7 @@ Value longToValue(int64_t v) {
return StringRef(reinterpret_cast<uint8_t const*>(s.c_str()), s.size()); return StringRef(reinterpret_cast<uint8_t const*>(s.c_str()), s.size());
} }
Future<Void> addTestClearMutations(SimpleConfigTransaction& tr) { Future<Void> addTestClearMutations(SimpleConfigTransaction& tr, Version version /* TODO: shouldn't need this */) {
tr.fullReset(); tr.fullReset();
auto configKeyA = encodeConfigKey("class-A"_sr, "test_long"_sr); auto configKeyA = encodeConfigKey("class-A"_sr, "test_long"_sr);
auto configKeyB = encodeConfigKey("class-B"_sr, "test_long"_sr); auto configKeyB = encodeConfigKey("class-B"_sr, "test_long"_sr);
@ -104,7 +104,9 @@ Future<Void> addTestSetMutations(WriteTo& writeTo, int64_t value) {
return writeTo.addVersionedMutations(versionedMutations, version); return writeTo.addVersionedMutations(versionedMutations, version);
} }
ACTOR Future<Void> readConfigState(SimpleConfigTransaction* tr, Optional<int64_t> expected) { ACTOR Future<Void> readConfigState(SimpleConfigTransaction* tr,
Optional<int64_t> expected,
bool immediate /* TODO : remove this */) {
tr->fullReset(); tr->fullReset();
state Key configKeyA = encodeConfigKey("class-A"_sr, "test_long"_sr); state Key configKeyA = encodeConfigKey("class-A"_sr, "test_long"_sr);
state Key configKeyB = encodeConfigKey("class-B"_sr, "test_long"_sr); state Key configKeyB = encodeConfigKey("class-B"_sr, "test_long"_sr);
@ -120,14 +122,28 @@ ACTOR Future<Void> readConfigState(SimpleConfigTransaction* tr, Optional<int64_t
return Void(); return Void();
} }
template <bool immediate> ACTOR template <class F>
Future<Void> readConfigState(LocalConfiguration const* localConfiguration, int64_t expected) { Future<Void> waitUntil(F isReady) {
if constexpr (immediate) { loop {
ASSERT_EQ(localConfiguration->getTestKnobs().TEST_LONG, expected); if (isReady()) {
return Void();
}
wait(delayJittered(0.1));
}
}
Future<Void> readConfigState(LocalConfiguration const* localConfiguration, Optional<int64_t> expected, bool immediate) {
if (immediate) {
if (expected.present()) {
ASSERT_EQ(localConfiguration->getTestKnobs().TEST_LONG, expected.get());
} else {
ASSERT_EQ(localConfiguration->getTestKnobs().TEST_LONG, 0);
}
return Void(); return Void();
} else { } else {
return waitUntil( return waitUntil([localConfiguration, expected] {
[localConfiguration, expected] { return localConfiguration->getTestKnobs().TEST_LONG == expected; }); return localConfiguration->getTestKnobs().TEST_LONG == (expected.present() ? expected.get() : 0);
});
} }
} }
@ -139,6 +155,22 @@ Future<Void> addTestGlobalSetMutation(ConfigStore& configStore, Version& lastWri
return configStore.addVersionedMutations(versionedMutations, lastWrittenVersion); return configStore.addVersionedMutations(versionedMutations, lastWrittenVersion);
} }
class LocalConfigEnvironment {
LocalConfiguration localConfiguration;
public:
LocalConfigEnvironment(std::string const& configPath, std::map<Key, Value> const& manualKnobOverrides)
: localConfiguration(configPath, manualKnobOverrides) {}
Future<Void> setup() { return localConfiguration.initialize("./", deterministicRandom()->randomUniqueID()); }
template <class TestType, class... Args>
Future<Void> run(Args&&... args) {
return TestType::run(localConfiguration, localConfiguration, std::forward<Args>(args)...);
}
};
class BroadcasterToLocalConfigEnvironment {
class DummyConfigSource { class DummyConfigSource {
ConfigFollowerInterface cfi; ConfigFollowerInterface cfi;
ACTOR static Future<Void> serve(DummyConfigSource* self) { ACTOR static Future<Void> serve(DummyConfigSource* self) {
@ -157,182 +189,209 @@ class DummyConfigSource {
public: public:
Future<Void> serve() { return serve(this); } Future<Void> serve() { return serve(this); }
ConfigFollowerInterface const& getInterface() { return cfi; } ConfigFollowerInterface const& getInterface() { return cfi; }
} dummyConfigSource;
ConfigBroadcaster broadcaster;
Reference<AsyncVar<ConfigFollowerInterface>> cfi;
LocalConfiguration localConfiguration;
ActorCollection actors{ false };
ACTOR static Future<Void> setup(BroadcasterToLocalConfigEnvironment* self) {
wait(self->localConfiguration.initialize("./", deterministicRandom()->randomUniqueID()));
self->actors.add(self->dummyConfigSource.serve());
self->actors.add(self->broadcaster.serve(self->cfi->get()));
self->actors.add(
self->localConfiguration.consume(IDependentAsyncVar<ConfigFollowerInterface>::create(self->cfi)));
return Void();
}
public:
BroadcasterToLocalConfigEnvironment()
: broadcaster(dummyConfigSource.getInterface(), deterministicRandom()->randomUniqueID()),
cfi(makeReference<AsyncVar<ConfigFollowerInterface>>()), localConfiguration("class-A", {}) {}
Future<Void> setup() { return setup(this); }
template <class TestType, class... Args>
Future<Void> run(Args&&... args) {
return waitOrError(TestType::run(broadcaster, localConfiguration, std::forward<Args>(args)...),
actors.getResult());
}
}; };
ACTOR template <class F> class TransactionEnvironment {
Future<Void> runLocalConfigEnvironment(std::string configPath, std::map<Key, Value> manualKnobOverrides, F f) { ConfigTransactionInterface cti;
state LocalConfiguration localConfiguration(configPath, manualKnobOverrides); SimpleConfigTransaction tr;
state Version lastWrittenVersion = 0; SimpleConfigDatabaseNode node;
wait(localConfiguration.initialize("./", deterministicRandom()->randomUniqueID())); ActorCollection actors{ false };
wait(f(localConfiguration));
ACTOR static Future<Void> setup(TransactionEnvironment* self) {
wait(self->node.initialize("./", deterministicRandom()->randomUniqueID()));
self->actors.add(self->node.serve(self->cti));
return Void(); return Void();
} }
ACTOR template <class F> public:
Future<Void> runBroadcasterToLocalConfigEnvironment(F f) { TransactionEnvironment() : tr(cti) {}
state DummyConfigSource dummyConfigSource;
state ConfigBroadcaster broadcaster(dummyConfigSource.getInterface(), deterministicRandom()->randomUniqueID()); Future<Void> setup() { return setup(this); }
state Reference<AsyncVar<ConfigFollowerInterface>> cfi = makeReference<AsyncVar<ConfigFollowerInterface>>();
state LocalConfiguration localConfigurationA("class-A", {}); template <class TestType, class... Args>
state LocalConfiguration localConfigurationB("class-B", {}); Future<Void> run(Args&&... args) {
state ActorCollection actors(false); return waitOrError(TestType::run(tr, tr, std::forward<Args>(args)...), actors.getResult());
wait(localConfigurationA.initialize("./", deterministicRandom()->randomUniqueID())); }
wait(localConfigurationB.initialize("./", deterministicRandom()->randomUniqueID())); };
actors.add(dummyConfigSource.serve());
actors.add(broadcaster.serve(cfi->get())); class TransactionToLocalConfigEnvironment {
actors.add(localConfigurationA.consume(IDependentAsyncVar<ConfigFollowerInterface>::create(cfi))); ConfigTransactionInterface cti;
actors.add(localConfigurationB.consume(IDependentAsyncVar<ConfigFollowerInterface>::create(cfi))); Reference<AsyncVar<ConfigFollowerInterface>> cfi;
wait(waitOrError(f(broadcaster, localConfigurationA, localConfigurationB), actors.getResult())); SimpleConfigTransaction tr;
SimpleConfigDatabaseNode node;
LocalConfiguration localConfiguration;
ActorCollection actors{ false };
ACTOR Future<Void> setup(TransactionToLocalConfigEnvironment* self) {
wait(self->localConfiguration.initialize("./", deterministicRandom()->randomUniqueID()));
wait(self->node.initialize("./", deterministicRandom()->randomUniqueID()));
self->actors.add(self->node.serve(self->cti));
self->actors.add(self->node.serve(self->cfi->get()));
self->actors.add(
self->localConfiguration.consume(IDependentAsyncVar<ConfigFollowerInterface>::create(self->cfi)));
return Void(); return Void();
} }
ACTOR template <class F> public:
Future<Void> runTransactionToLocalConfigEnvironment(F f) { TransactionToLocalConfigEnvironment()
state ConfigTransactionInterface cti; : cfi(makeReference<AsyncVar<ConfigFollowerInterface>>()), tr(cti), localConfiguration("class-A", {}) {}
state Reference<AsyncVar<ConfigFollowerInterface>> cfi = makeReference<AsyncVar<ConfigFollowerInterface>>();
state SimpleConfigTransaction tr(cti);
state SimpleConfigDatabaseNode node;
state ActorCollection actors(false);
state LocalConfiguration localConfigurationA("class-A", {});
state LocalConfiguration localConfigurationB("class-B", {});
wait(localConfigurationA.initialize("./", deterministicRandom()->randomUniqueID()));
wait(localConfigurationB.initialize("./", deterministicRandom()->randomUniqueID()));
wait(node.initialize("./", deterministicRandom()->randomUniqueID()));
actors.add(node.serve(cti));
actors.add(node.serve(cfi->get()));
actors.add(localConfigurationA.consume(IDependentAsyncVar<ConfigFollowerInterface>::create(cfi)));
actors.add(localConfigurationB.consume(IDependentAsyncVar<ConfigFollowerInterface>::create(cfi)));
wait(waitOrError(f(tr, localConfigurationA, localConfigurationB), actors.getResult()));
return Void();
}
ACTOR template <class F> Future<Void> setup() { return setup(this); }
Future<Void> runTransactionEnvironment(F f) {
state ConfigTransactionInterface cti;
state SimpleConfigTransaction tr(cti);
state SimpleConfigDatabaseNode node;
state ActorCollection actors(false);
wait(node.initialize("./", deterministicRandom()->randomUniqueID()));
actors.add(node.serve(cti));
wait(waitOrError(f(tr), actors.getResult()));
return Void();
}
ACTOR template <class F> template <class TestType, class... Args>
Future<Void> waitUntil(F isReady) { Future<Void> run(Args&&... args) {
loop { return waitOrError(TestType::run(tr, localConfiguration, std::forward<Args>(args)...), actors.getResult());
if (isReady()) {
return Void();
} }
wait(delayJittered(0.1)); };
class TestSet {
public:
template <class WriteTo, class ReadFrom>
static Future<Void> run(WriteTo& writeTo, ReadFrom& readFrom, int64_t expected, bool immediate) {
std::function<Future<Void>(Void const&)> check = [&readFrom, expected, immediate](Void const&) {
return readConfigState(&readFrom, expected, immediate);
};
return addTestSetMutations(writeTo, 1) >>= check;
} }
};
class TestClear {
public:
template <class WriteTo, class ReadFrom>
static Future<Void> run(WriteTo& writeTo, ReadFrom& readFrom, bool immediate) {
std::function<Future<Void>(Void const&)> clear = [&writeTo](Void const&) {
return addTestClearMutations(writeTo, 2);
};
std::function<Future<Void>(Void const&)> check = [&readFrom, immediate](Void const&) {
return readConfigState(&readFrom, {}, immediate);
};
return (addTestSetMutations(writeTo, 1) >>= clear) >>= check;
} }
};
class TestGlobal {
public:
template <class WriteTo>
static Future<Void> run(WriteTo& writeTo, LocalConfiguration const& readFrom, bool immediate) {
std::function<Future<Void>(Void const&)> globalWrite = [&writeTo](Void const&) {
return addTestGlobalSetMutation(writeTo, 2);
};
std::function<Future<Void>(Void const&)> check = [&readFrom, immediate](Void const&) {
return readConfigState(&readFrom, 2, immediate);
};
return (addTestSetMutations(writeTo, 1) >>= globalWrite) >>= check;
}
};
} // namespace } // namespace
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ManualOverride") { TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Set") {
wait(runLocalConfigEnvironment("class-A", { { "test_long"_sr, "1000"_sr } }, [](auto& conf) { state LocalConfigEnvironment environment("class-A", std::map<Key, Value>{});
std::function<Future<Void>(Void const&)> check = [&conf](Void const&) { wait(environment.setup());
return readConfigState<true>(&conf, 1000); wait(environment.run<TestSet>(1, true));
};
return addTestSetMutations(conf, 1) >>= check;
}));
return Void();
}
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ConflictingOverride") {
wait(runLocalConfigEnvironment("class-A/class-B", {}, [](auto& conf) {
std::function<Future<Void>(Void const&)> check = [&conf](Void const&) {
return readConfigState<true>(&conf, 10);
};
return addTestSetMutations(conf, 1) >>= check;
}));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Clear") { TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Clear") {
wait(runLocalConfigEnvironment("class-A/class-B", {}, [](auto& conf) { state LocalConfigEnvironment environment("class-A", std::map<Key, Value>{});
std::function<Future<Void>(Void const&)> clear = [&conf](Void const&) { wait(environment.setup());
return addTestClearMutations(conf, 2); wait(environment.run<TestClear>(true));
}; return Void();
std::function<Future<Void>(Void const&)> check = [&conf](Void const&) { }
return readConfigState<true>(&conf, 0);
}; TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ManualOverride") {
return (addTestSetMutations(conf, 1) >>= clear) >>= check; state LocalConfigEnvironment environment("class-A", std::map<Key, Value>{ { "test_long"_sr, "1000"_sr } });
})); wait(environment.setup());
wait(environment.run<TestSet>(1000, true));
return Void();
}
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ConflictingOverride") {
state LocalConfigEnvironment environment("class-A/class-B", std::map<Key, Value>{});
wait(environment.setup());
wait(environment.run<TestSet>(10, true));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Global") { TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/Global") {
wait(runLocalConfigEnvironment("class-A", {}, [](auto& conf) { state LocalConfigEnvironment environment("class-A", std::map<Key, Value>{});
std::function<Future<Void>(Void const&)> setGlobal = [&conf](Void const&) { wait(environment.setup());
return addTestGlobalSetMutation(conf, 2); wait(environment.run<TestGlobal>(true));
};
std::function<Future<Void>(Void const&)> check = [&conf](Void const&) {
return readConfigState<true>(&conf, 2);
};
return (addTestSetMutations(conf, 1) >>= setGlobal) >>= check;
}));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Set") { TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Set") {
wait(runBroadcasterToLocalConfigEnvironment([](auto& broadcaster, auto& confA, auto& confB) { state BroadcasterToLocalConfigEnvironment environment;
std::function<Future<Void>(Void const&)> check = [&confA, &confB](Void const&) { wait(environment.setup());
return readConfigState<false>(&confA, 1) && readConfigState<false>(&confB, 10); wait(environment.run<TestSet>(1, false));
};
return addTestSetMutations(broadcaster, 1) >>= check;
}));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Clear") { TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Clear") {
wait(runBroadcasterToLocalConfigEnvironment([](auto& broadcaster, auto& confA, auto& confB) { state BroadcasterToLocalConfigEnvironment environment;
std::function<Future<Void>(Void const&)> clear = [&broadcaster](Void const&) { wait(environment.setup());
return addTestClearMutations(broadcaster, 2); wait(environment.run<TestClear>(false));
}; return Void();
std::function<Future<Void>(Void const&)> check = [&confA, &confB](Void const&) { }
return readConfigState<false>(&confA, 0) && readConfigState<false>(&confB, 0);
}; TEST_CASE("/fdbserver/ConfigDB/BroadcasterToLocalConfig/Global") {
return (addTestSetMutations(broadcaster, 1) >>= clear) >>= check; state BroadcasterToLocalConfigEnvironment environment;
})); wait(environment.setup());
wait(environment.run<TestGlobal>(false));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/Transaction/Set") { TEST_CASE("/fdbserver/ConfigDB/Transaction/Set") {
wait(runTransactionEnvironment([](auto& tr) { state TransactionEnvironment environment;
std::function<Future<Void>(Void const&)> check = [&tr](Void const&) { return readConfigState(&tr, 1); }; wait(environment.setup());
return addTestSetMutations(tr, 1) >>= check; wait(environment.run<TestSet>(1, true));
}));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/Transaction/Clear") { TEST_CASE("/fdbserver/ConfigDB/Transaction/Clear") {
wait(runTransactionEnvironment([](auto& tr) { state TransactionEnvironment environment;
std::function<Future<Void>(Void const&)> clear = [&tr](Void const&) { return addTestClearMutations(tr); }; wait(environment.setup());
std::function<Future<Void>(Void const&)> check = [&tr](Void const&) { return readConfigState(&tr, {}); }; wait(environment.run<TestClear>(true));
return (addTestSetMutations(tr, 1) >>= clear) >>= check;
}));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/Set") { TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/Set") {
wait(runTransactionToLocalConfigEnvironment([](auto& tr, auto const& confA, auto const& confB) { state TransactionToLocalConfigEnvironment environment;
std::function<Future<Void>(Void const&)> check = [&confA, &confB](Void const&) { wait(environment.setup());
return readConfigState<false>(&confA, 1) && readConfigState<false>(&confB, 10); wait(environment.run<TestSet>(1, false));
};
return addTestSetMutations(tr, 1) >>= check;
}));
return Void(); return Void();
} }
TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/Clear") { TEST_CASE("/fdbserver/ConfigDB/TransactionToLocalConfig/Clear") {
wait(runTransactionToLocalConfigEnvironment([](auto& tr, auto const& confA, auto const& confB) { state TransactionToLocalConfigEnvironment environment;
std::function<Future<Void>(Void const&)> clear = [&tr](Void const&) { return addTestClearMutations(tr); }; wait(environment.setup());
std::function<Future<Void>(Void const&)> check = [&confA, &confB](Void const&) { wait(environment.run<TestClear>(false));
return readConfigState<false>(&confA, 0) && readConfigState<false>(&confB, 0);
};
return (addTestSetMutations(tr, 1) >>= clear) >>= check;
}));
return Void(); return Void();
} }