Add unit tests for LocalConfiguration

This commit is contained in:
sfc-gh-tclinkenbeard 2021-05-12 11:56:45 -07:00
parent ba196b81aa
commit 16d670d3bb
9 changed files with 100 additions and 71 deletions

View File

@ -37,13 +37,13 @@ class ConfigBroadcasterImpl {
Counter compactRequest;
Counter successfulChangeRequest;
Counter failedChangeRequest;
Counter fullDBRequest;
Counter snapshotRequest;
Future<Void> logger;
ConfigBroadcasterImpl()
: lastCompactedVersion(0), mostRecentVersion(0), cc("ConfigBroadcaster"), compactRequest("CompactRequest", cc),
successfulChangeRequest("SuccessfulChangeRequest", cc), failedChangeRequest("FailedChangeRequest", cc),
fullDBRequest("FullDBRequest", cc) {
snapshotRequest("SnapshotRequest", cc) {
logger = traceCounters(
"ConfigBroadcasterMetrics", UID{}, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "ConfigBroadcasterMetrics");
}
@ -56,10 +56,10 @@ class ConfigBroadcasterImpl {
when(ConfigFollowerGetVersionRequest req = waitNext(cfi.getVersion.getFuture())) {
req.reply.send(impl->mostRecentVersion);
}
when(ConfigFollowerGetFullDatabaseRequest req = waitNext(cfi.getFullDatabase.getFuture())) {
++impl->fullDBRequest;
ConfigFollowerGetFullDatabaseReply reply;
reply.database = impl->snapshot;
when(ConfigFollowerGetSnapshotRequest req = waitNext(cfi.getSnapshot.getFuture())) {
++impl->snapshotRequest;
ConfigFollowerGetSnapshotReply reply;
reply.snapshot = impl->snapshot;
for (const auto& versionedMutation : impl->versionedMutations) {
const auto& version = versionedMutation.version;
const auto& mutation = versionedMutation.mutation;
@ -67,16 +67,16 @@ class ConfigBroadcasterImpl {
break;
}
if (req.configClassSet.contains(mutation.getConfigClass())) {
TraceEvent(SevDebug, "BroadcasterAppendingMutationToFullDBOutput")
TraceEvent(SevDebug, "BroadcasterAppendingMutationToSnapshotOutput")
.detail("ReqVersion", req.version)
.detail("MutationVersion", version)
.detail("ConfigClass", mutation.getConfigClass())
.detail("KnobName", mutation.getKnobName())
.detail("KnobValue", mutation.getValue());
if (mutation.isSet()) {
reply.database[mutation.getKey()] = mutation.getValue();
reply.snapshot[mutation.getKey()] = mutation.getValue();
} else {
reply.database.erase(mutation.getKey());
reply.snapshot.erase(mutation.getKey());
}
}
}

View File

@ -24,7 +24,7 @@
void ConfigFollowerInterface::setupWellKnownEndpoints() {
getVersion.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_GETVERSION, TaskPriority::Coordination);
getFullDatabase.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_GETFULLDB, TaskPriority::Coordination);
getSnapshot.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_GETSNAPSHOT, TaskPriority::Coordination);
getChanges.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_GETCHANGES, TaskPriority::Coordination);
compact.makeWellKnownEndpoint(WLTOKEN_CONFIGFOLLOWER_COMPACT, TaskPriority::Coordination);
}
@ -33,7 +33,7 @@ ConfigFollowerInterface::ConfigFollowerInterface() : _id(deterministicRandom()->
ConfigFollowerInterface::ConfigFollowerInterface(NetworkAddress const& remote)
: _id(deterministicRandom()->randomUniqueID()), getVersion(Endpoint({ remote }, WLTOKEN_CONFIGFOLLOWER_GETVERSION)),
getFullDatabase(Endpoint({ remote }, WLTOKEN_CONFIGFOLLOWER_GETFULLDB)),
getSnapshot(Endpoint({ remote }, WLTOKEN_CONFIGFOLLOWER_GETSNAPSHOT)),
getChanges(Endpoint({ remote }, WLTOKEN_CONFIGFOLLOWER_GETCHANGES)),
compact(Endpoint({ remote }, WLTOKEN_CONFIGFOLLOWER_COMPACT)) {}

View File

@ -66,29 +66,29 @@ struct ConfigFollowerGetVersionRequest {
}
};
struct ConfigFollowerGetFullDatabaseReply {
struct ConfigFollowerGetSnapshotReply {
static constexpr FileIdentifier file_identifier = 1734095;
std::map<ConfigKey, Value> database;
std::map<ConfigKey, Value> snapshot;
ConfigFollowerGetFullDatabaseReply() = default;
explicit ConfigFollowerGetFullDatabaseReply(std::map<ConfigKey, Value> const& database) : database(database) {
ConfigFollowerGetSnapshotReply() = default;
explicit ConfigFollowerGetSnapshotReply(std::map<ConfigKey, Value> const& snapshot) : snapshot(snapshot) {
// TODO: Support move constructor as well
}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, database);
serializer(ar, snapshot);
}
};
struct ConfigFollowerGetFullDatabaseRequest {
struct ConfigFollowerGetSnapshotRequest {
static constexpr FileIdentifier file_identifier = 294811;
Version version;
ConfigClassSet configClassSet;
ReplyPromise<ConfigFollowerGetFullDatabaseReply> reply;
ReplyPromise<ConfigFollowerGetSnapshotReply> reply;
ConfigFollowerGetFullDatabaseRequest() : version(-1) {}
explicit ConfigFollowerGetFullDatabaseRequest(Version version, ConfigClassSet const& configClassSet)
ConfigFollowerGetSnapshotRequest() : version(-1) {}
explicit ConfigFollowerGetSnapshotRequest(Version version, ConfigClassSet const& configClassSet)
: version(version), configClassSet(configClassSet) {}
template <class Ar>
@ -164,7 +164,7 @@ class ConfigFollowerInterface {
public:
static constexpr FileIdentifier file_identifier = 7721102;
RequestStream<ConfigFollowerGetVersionRequest> getVersion;
RequestStream<ConfigFollowerGetFullDatabaseRequest> getFullDatabase;
RequestStream<ConfigFollowerGetSnapshotRequest> getSnapshot;
RequestStream<ConfigFollowerGetChangesRequest> getChanges;
RequestStream<ConfigFollowerCompactRequest> compact;
@ -177,6 +177,6 @@ public:
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, _id, getVersion, getFullDatabase, getChanges);
serializer(ar, _id, getVersion, getSnapshot, getChanges);
}
};

View File

@ -33,7 +33,7 @@ constexpr UID WLTOKEN_GENERATIONREG_READ(-1, 8);
constexpr UID WLTOKEN_GENERATIONREG_WRITE(-1, 9);
constexpr UID WLTOKEN_CONFIGFOLLOWER_GETVERSION(-1, 15);
constexpr UID WLTOKEN_CONFIGFOLLOWER_GETFULLDB(-1, 16);
constexpr UID WLTOKEN_CONFIGFOLLOWER_GETSNAPSHOT(-1, 16);
constexpr UID WLTOKEN_CONFIGFOLLOWER_GETCHANGES(-1, 17);
constexpr UID WLTOKEN_CONFIGFOLLOWER_COMPACT(-1, 18);

View File

@ -23,6 +23,8 @@
#include "fdbserver/IKeyValueStore.h"
#include "fdbserver/LocalConfiguration.h"
#include "flow/Knobs.h"
#include "flow/UnitTest.h"
#include "flow/actorcompiler.h" // This must be the last #include.
namespace {
@ -50,19 +52,13 @@ class ConfigKnobOverrides {
public:
ConfigKnobOverrides() = default;
ConfigKnobOverrides(std::string const& paramString) {
explicit ConfigKnobOverrides(std::string const& paramString) {
// TODO: Validate string
// FIXME: Fix this implementation
/*
auto b = paramString.begin();
while (b != paramString.end()) {
auto e = std::find(b, paramString.end(), '/');
configPath.emplace_back_deep(configPath.arena(), reinterpret_cast<uint8_t const *>(&(*b)), e-b);
if (e != paramString.end()) {
b = e+1;
}
StringRef s(reinterpret_cast<uint8_t const*>(paramString.c_str()), paramString.size());
while (s.size()) {
configPath.push_back_deep(configPath.arena(), s.eat("/"_sr));
configClassToKnobToValue[configPath.back()] = {};
}
*/
}
ConfigClassSet getConfigClassSet() const { return ConfigClassSet(configPath); }
void set(KeyRef configClass, KeyRef knobName, ValueRef value) {
@ -74,11 +70,10 @@ public:
void update(KS&... knobCollections) const {
for (const auto& configClass : configPath) {
const auto& knobToValue = configClassToKnobToValue.find(configClass);
if (knobToValue != configClassToKnobToValue.end()) {
for (const auto& [knobName, knobValue] : knobToValue->second) {
// Assert here because we should be validating on the client
ASSERT(updateSingleKnob(knobName, knobValue, knobCollections...));
}
ASSERT(knobToValue != configClassToKnobToValue.end());
for (const auto& [knobName, knobValue] : knobToValue->second) {
// Assert here because we should be validating on the client
ASSERT(updateSingleKnob(knobName, knobValue, knobCollections...));
}
}
}
@ -95,7 +90,7 @@ class ManualKnobOverrides {
std::map<Key, Value> overrides;
public:
ManualKnobOverrides(std::map<Key, Value>&& overrides) : overrides(std::move(overrides)) {}
explicit ManualKnobOverrides(std::map<Key, Value>&& overrides) : overrides(std::move(overrides)) {}
template <class... KS>
void update(KS&... knobCollections) const {
@ -110,6 +105,36 @@ public:
} // namespace
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/updateSingleKnob") {
TestKnobs knobs;
updateSingleKnob("test"_sr, "5"_sr, knobs);
ASSERT(knobs.TEST == 5);
return Void();
}
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ManualKnobOverrides") {
TestKnobs knobs;
// TODO: Add more test knobs
std::map<Key, Value> m;
m["test"_sr] = "5"_sr;
ManualKnobOverrides manualKnobOverrides(std::move(m));
manualKnobOverrides.update(knobs);
ASSERT(knobs.TEST == 5);
return Void();
}
TEST_CASE("/fdbserver/ConfigDB/LocalConfiguration/ConfigKnobOverrides") {
TestKnobs knobs;
ConfigKnobOverrides configKnobOverrides("class-A/class-B");
configKnobOverrides.update(knobs);
ASSERT(knobs.TEST == 0);
configKnobOverrides.set("class-A"_sr, "test"_sr, "5"_sr);
ASSERT(knobs.TEST == 0);
configKnobOverrides.update(knobs);
ASSERT(knobs.TEST == 5);
return Void();
}
class LocalConfigurationImpl {
IKeyValueStore* kvStore; // FIXME: fix leaks?
Version lastSeenVersion { 0 };
@ -174,17 +199,17 @@ class LocalConfigurationImpl {
return Void();
}
void initializeKnobs() {
flowKnobs.initialize();
clientKnobs.initialize();
serverKnobs.initialize();
void initializeKnobs(bool randomize = false, bool isSimulated = false) {
flowKnobs.initialize(randomize, isSimulated);
clientKnobs.initialize(randomize);
serverKnobs.initialize(randomize, &clientKnobs, isSimulated);
testKnobs.initialize();
}
void resetKnobs() {
flowKnobs.reset();
clientKnobs.reset();
serverKnobs.reset();
serverKnobs.reset(&clientKnobs);
testKnobs.reset();
}
@ -196,9 +221,9 @@ class LocalConfigurationImpl {
initializeKnobs();
}
ACTOR static Future<Void> applyKnobUpdates(LocalConfigurationImpl *self, ConfigFollowerGetFullDatabaseReply reply) {
ACTOR static Future<Void> applyKnobUpdates(LocalConfigurationImpl* self, ConfigFollowerGetSnapshotReply reply) {
self->kvStore->clear(knobOverrideKeys);
for (const auto& [configKey, knobValue] : reply.database) {
for (const auto& [configKey, knobValue] : reply.snapshot) {
self->configKnobOverrides.set(configKey.configClass, configKey.knobName, knobValue);
}
self->kvStore->set(KeyValueRef(lastSeenVersionKey, BinaryWriter::toValue(self->lastSeenVersion, IncludeVersion())));
@ -237,11 +262,11 @@ class LocalConfigurationImpl {
if (e.code() == error_code_version_already_compacted) {
ConfigFollowerGetVersionReply versionReply = wait(broadcaster.getVersion.getReply(ConfigFollowerGetVersionRequest{}));
self->lastSeenVersion = versionReply.version;
ConfigFollowerGetFullDatabaseReply fullDBReply =
wait(broadcaster.getFullDatabase.getReply(ConfigFollowerGetFullDatabaseRequest{
ConfigFollowerGetSnapshotReply snapshotReply =
wait(broadcaster.getSnapshot.getReply(ConfigFollowerGetSnapshotRequest{
self->lastSeenVersion, self->configKnobOverrides.getConfigClassSet() }));
// TODO: Avoid applying if there are no updates
wait(applyKnobUpdates(self, fullDBReply));
wait(applyKnobUpdates(self, snapshotReply));
} else {
throw e;
}
@ -350,6 +375,10 @@ Future<Void> LocalConfiguration::consume(Reference<AsyncVar<ServerDBInfo> const>
#define init(knob, value) initKnob(knob, value, #knob)
TestKnobs::TestKnobs() {
initialize();
}
void TestKnobs::initialize() {
init(TEST, 0);
}

View File

@ -30,6 +30,7 @@
class TestKnobs : public Knobs {
public:
TestKnobs();
int64_t TEST;
void initialize();
void reset();

View File

@ -30,7 +30,7 @@ class SimpleConfigConsumerImpl {
Counter compactRequest;
Counter successfulChangeRequest;
Counter failedChangeRequest;
Counter fullDBRequest;
Counter snapshotRequest;
Future<Void> logger;
static const double POLLING_INTERVAL; // TODO: Make knob?
@ -68,12 +68,12 @@ class SimpleConfigConsumerImpl {
wait(self->cfi.getVersion.getReply(ConfigFollowerGetVersionRequest{}));
ASSERT(versionReply.version > self->mostRecentVersion);
self->mostRecentVersion = versionReply.version;
ConfigFollowerGetFullDatabaseReply dbReply = wait(self->cfi.getFullDatabase.getReply(
ConfigFollowerGetFullDatabaseRequest{ self->mostRecentVersion, {} }));
ConfigFollowerGetSnapshotReply dbReply = wait(self->cfi.getSnapshot.getReply(
ConfigFollowerGetSnapshotRequest{ self->mostRecentVersion, {} }));
// TODO: Remove unnecessary copy
auto snapshot = dbReply.database;
auto snapshot = dbReply.snapshot;
broadcaster->setSnapshot(std::move(snapshot), self->mostRecentVersion);
++self->fullDBRequest;
++self->snapshotRequest;
} else {
throw e;
}
@ -85,11 +85,11 @@ class SimpleConfigConsumerImpl {
ConfigFollowerGetVersionReply versionReply =
wait(self->cfi.getVersion.getReply(ConfigFollowerGetVersionRequest{}));
self->mostRecentVersion = versionReply.version;
ConfigFollowerGetFullDatabaseReply reply = wait(
self->cfi.getFullDatabase.getReply(ConfigFollowerGetFullDatabaseRequest{ self->mostRecentVersion, {} }));
ConfigFollowerGetSnapshotReply reply =
wait(self->cfi.getSnapshot.getReply(ConfigFollowerGetSnapshotRequest{ self->mostRecentVersion, {} }));
TraceEvent(SevDebug, "ConfigGotInitialSnapshot").detail("Version", self->mostRecentVersion);
// TODO: Remove unnecessary copy
auto snapshot = reply.database;
auto snapshot = reply.snapshot;
broadcaster->setSnapshot(std::move(snapshot), self->mostRecentVersion);
return Void();
}
@ -97,7 +97,7 @@ class SimpleConfigConsumerImpl {
SimpleConfigConsumerImpl()
: mostRecentVersion(0), cc("ConfigConsumer"), compactRequest("CompactRequest", cc),
successfulChangeRequest("SuccessfulChangeRequest", cc), failedChangeRequest("FailedChangeRequest", cc),
fullDBRequest("FullDBRequest", cc) {
snapshotRequest("SnapshotRequest", cc) {
logger = traceCounters(
"ConfigConsumerMetrics", UID{}, SERVER_KNOBS->WORKER_LOGGING_INTERVAL, &cc, "ConfigConsumerMetrics");
}

View File

@ -54,7 +54,7 @@ Version getVersionFromVersionedMutationKey(KeyRef versionedMutationKey) {
} //namespace
TEST_CASE("/fdbserver/SimpleConfigDatabaseNode/versionedMutationKeys") {
TEST_CASE("/fdbserver/ConfigDB/SimpleConfigDatabaseNode/versionedMutationKeys") {
std::vector<Key> keys;
for (Version version = 0; version < 1000; ++version) {
for (int index = 0; index < 5; ++index) {
@ -67,7 +67,7 @@ TEST_CASE("/fdbserver/SimpleConfigDatabaseNode/versionedMutationKeys") {
return Void();
}
TEST_CASE("/fdbserver/SimpleConfigDatabaseNode/versionedMutationKeyOrdering") {
TEST_CASE("/fdbserver/ConfigDB/SimpleConfigDatabaseNode/versionedMutationKeyOrdering") {
Standalone<VectorRef<KeyRef>> keys;
for (Version version = 0; version < 1000; ++version) {
for (auto index = 0; index < 5; ++index) {
@ -318,19 +318,18 @@ class SimpleConfigDatabaseNodeImpl {
}
}
ACTOR static Future<Void> getFullDatabase(SimpleConfigDatabaseNodeImpl* self,
ConfigFollowerGetFullDatabaseRequest req) {
ACTOR static Future<Void> getSnapshot(SimpleConfigDatabaseNodeImpl* self, ConfigFollowerGetSnapshotRequest req) {
wait(self->globalLock.take());
state FlowLock::Releaser releaser(self->globalLock);
state ConfigFollowerGetFullDatabaseReply reply;
state ConfigFollowerGetSnapshotReply reply;
Standalone<RangeResultRef> data = wait(self->kvStore->readRange(kvKeys));
for (const auto& kv : data) {
reply
.database[BinaryReader::fromStringRef<ConfigKey>(kv.key.removePrefix(kvKeys.begin), IncludeVersion())] =
.snapshot[BinaryReader::fromStringRef<ConfigKey>(kv.key.removePrefix(kvKeys.begin), IncludeVersion())] =
kv.value;
}
state Version lastCompactedVersion = wait(getLastCompactedVersion(self));
TraceEvent(SevDebug, "ConfigDatabaseNodeGettingFullDB")
TraceEvent(SevDebug, "ConfigDatabaseNodeGettingSnapshot")
.detail("ReqVersion", req.version)
.detail("LastCompactedVersion", lastCompactedVersion);
if (lastCompactedVersion > req.version) {
@ -341,9 +340,9 @@ class SimpleConfigDatabaseNodeImpl {
for (const auto& versionedMutation : versionedMutations) {
const auto& mutation = versionedMutation.mutation;
if (mutation.isSet()) {
reply.database[mutation.getKey()] = mutation.getValue();
reply.snapshot[mutation.getKey()] = mutation.getValue();
} else {
reply.database.erase(mutation.getKey());
reply.snapshot.erase(mutation.getKey());
}
}
req.reply.send(reply);
@ -398,8 +397,8 @@ class SimpleConfigDatabaseNodeImpl {
when(ConfigFollowerGetVersionRequest req = waitNext(cfi->getVersion.getFuture())) {
self->actors.add(getCommittedVersion(self, req));
}
when(ConfigFollowerGetFullDatabaseRequest req = waitNext(cfi->getFullDatabase.getFuture())) {
self->actors.add(getFullDatabase(self, req));
when(ConfigFollowerGetSnapshotRequest req = waitNext(cfi->getSnapshot.getFuture())) {
self->actors.add(getSnapshot(self, req));
}
when(ConfigFollowerGetChangesRequest req = waitNext(cfi->getChanges.getFuture())) {
self->actors.add(getChanges(self, req));

View File

@ -4,4 +4,4 @@ useDB=false
testName=UnitTests
maxTestCases=10
testsMatching=/fdbserver/SimpleConfigDatabaseNode/
testsMatching=/fdbserver/ConfigDB/