Store global config data on heap

This commit is contained in:
Lukas Joswiak 2021-03-19 13:28:03 -07:00
parent 1c60653c2a
commit 7ba7257cd2
5 changed files with 52 additions and 37 deletions

View File

@ -55,16 +55,16 @@ Key GlobalConfig::prefixedKey(KeyRef key) {
return key.withPrefix(SpecialKeySpace::getModuleRange(SpecialKeySpace::MODULE::GLOBALCONFIG).begin);
}
const ConfigValue GlobalConfig::get(KeyRef name) {
const Reference<ConfigValue> GlobalConfig::get(KeyRef name) {
auto it = data.find(name);
if (it == data.end()) {
return ConfigValue{ Arena(), std::any{} };
return Reference<ConfigValue>();
}
return it->second;
}
const std::map<KeyRef, ConfigValue> GlobalConfig::get(KeyRangeRef range) {
std::map<KeyRef, ConfigValue> results;
const std::map<KeyRef, Reference<ConfigValue>> GlobalConfig::get(KeyRangeRef range) {
std::map<KeyRef, Reference<ConfigValue>> results;
for (const auto& [key, value] : data) {
if (range.contains(key)) {
results[key] = value;
@ -78,21 +78,25 @@ Future<Void> GlobalConfig::onInitialized() {
}
void GlobalConfig::insert(KeyRef key, ValueRef value) {
data.erase(key);
Arena arena(key.expectedSize() + value.expectedSize());
KeyRef stableKey = KeyRef(arena, key);
try {
std::any any;
Tuple t = Tuple::unpack(value);
if (t.getType(0) == Tuple::ElementType::UTF8) {
data[stableKey] = ConfigValue{ arena, StringRef(arena, t.getString(0).contents()) };
any = StringRef(arena, t.getString(0).contents());
} else if (t.getType(0) == Tuple::ElementType::INT) {
data[stableKey] = ConfigValue{ arena, t.getInt(0) };
any = t.getInt(0);
} else if (t.getType(0) == Tuple::ElementType::FLOAT) {
data[stableKey] = ConfigValue{ arena, t.getFloat(0) };
any = t.getFloat(0);
} else if (t.getType(0) == Tuple::ElementType::DOUBLE) {
data[stableKey] = ConfigValue{ arena, t.getDouble(0) };
any = t.getDouble(0);
} else {
ASSERT(false);
}
data[stableKey] = makeReference<ConfigValue>(std::move(arena), std::move(any));
} catch (Error& e) {
TraceEvent("GlobalConfigTupleError").detail("What", e.what());
}
@ -135,12 +139,12 @@ ACTOR Future<Void> GlobalConfig::updater(GlobalConfig* self, Reference<AsyncVar<
try {
wait(dbInfo->onChange());
if (dbInfo->get().id.second() != 123456789) {
auto& history = dbInfo->get().history;
if (history.size() == 0) {
continue;
}
auto& history = dbInfo->get().history;
if (history.size() == 0 || (self->lastUpdate < history[0].version && self->lastUpdate != 0)) {
if (self->lastUpdate < history[0].version) {
// This process missed too many global configuration
// history updates or the protocol version changed, so it
// must re-read the entire configuration range.

View File

@ -48,9 +48,12 @@ extern const KeyRef fdbClientInfoTxnSizeLimit;
extern const KeyRef transactionTagSampleRate;
extern const KeyRef transactionTagSampleCost;
struct ConfigValue {
struct ConfigValue : ReferenceCounted<ConfigValue> {
Arena arena;
std::any value;
ConfigValue() {}
ConfigValue(Arena&& a, std::any&& v) : arena(a), value(v) {}
};
class GlobalConfig {
@ -67,13 +70,12 @@ public:
// For example, given "config/a", returns "\xff\xff/global_config/config/a".
static Key prefixedKey(KeyRef key);
// Get a value from the framework. Values are returned in a ConfigValue
// struct which also contains a reference to the arena containing the
// memory for the object. As long as the caller keeps a reference to the
// returned ConfigValue, the value is guaranteed to be readable (if it
// exists).
const ConfigValue get(KeyRef name);
const std::map<KeyRef, ConfigValue> get(KeyRangeRef range);
// Get a value from the framework. Values are returned as a ConfigValue
// reference which also contains the arena holding the object. As long as
// the caller keeps the ConfigValue reference, the value is guaranteed to
// be readable. An empty reference is returned if the value does not exist.
const Reference<ConfigValue> get(KeyRef name);
const std::map<KeyRef, Reference<ConfigValue>> get(KeyRangeRef range);
// For arithmetic value types, returns a copy of the value for the given
// key, or the supplied default value if the framework does not know about
@ -81,9 +83,11 @@ public:
template <typename T, typename std::enable_if<std::is_arithmetic<T>{}, bool>::type = true>
const T get(KeyRef name, T defaultVal) {
try {
auto any = get(name).value;
if (any.has_value()) {
return std::any_cast<T>(any);
auto configValue = get(name);
if (configValue.isValid()) {
if (configValue->value.has_value()) {
return std::any_cast<T>(configValue->value);
}
}
return defaultVal;
@ -114,7 +118,7 @@ private:
Database cx;
Future<Void> _updater;
Promise<Void> initialized;
std::unordered_map<StringRef, ConfigValue> data;
std::unordered_map<StringRef, Reference<ConfigValue>> data;
Version lastUpdate;
};

View File

@ -28,6 +28,9 @@
struct VersionHistory {
constexpr static FileIdentifier file_identifier = 5863456;
VersionHistory() {}
VersionHistory(Version v) : version(v) {}
Version version;
Standalone<VectorRef<MutationRef>> mutations;

View File

@ -1380,22 +1380,22 @@ Future<Standalone<RangeResultRef>> GlobalConfigImpl::getRange(ReadYourWritesTran
auto& globalConfig = GlobalConfig::globalConfig();
KeyRangeRef modified =
KeyRangeRef(kr.begin.removePrefix(getKeyRange().begin), kr.end.removePrefix(getKeyRange().begin));
std::map<KeyRef, ConfigValue> values = globalConfig.get(modified);
std::map<KeyRef, Reference<ConfigValue>> values = globalConfig.get(modified);
for (const auto& [key, config] : values) {
Key prefixedKey = key.withPrefix(getKeyRange().begin);
if (config.value.has_value()) {
if (config.value.type() == typeid(StringRef)) {
if (config.isValid() && config->value.has_value()) {
if (config->value.type() == typeid(StringRef)) {
result.push_back_deep(result.arena(),
KeyValueRef(prefixedKey, std::any_cast<StringRef>(config.value).toString()));
} else if (config.value.type() == typeid(int64_t)) {
KeyValueRef(prefixedKey, std::any_cast<StringRef>(config->value).toString()));
} else if (config->value.type() == typeid(int64_t)) {
result.push_back_deep(result.arena(),
KeyValueRef(prefixedKey, std::to_string(std::any_cast<int64_t>(config.value))));
} else if (config.value.type() == typeid(float)) {
KeyValueRef(prefixedKey, std::to_string(std::any_cast<int64_t>(config->value))));
} else if (config->value.type() == typeid(float)) {
result.push_back_deep(result.arena(),
KeyValueRef(prefixedKey, std::to_string(std::any_cast<float>(config.value))));
} else if (config.value.type() == typeid(double)) {
KeyValueRef(prefixedKey, std::to_string(std::any_cast<float>(config->value))));
} else if (config->value.type() == typeid(double)) {
result.push_back_deep(result.arena(),
KeyValueRef(prefixedKey, std::to_string(std::any_cast<double>(config.value))));
KeyValueRef(prefixedKey, std::to_string(std::any_cast<double>(config->value))));
} else {
ASSERT(false);
}

View File

@ -3212,17 +3212,21 @@ ACTOR Future<Void> monitorGlobalConfig(ClusterControllerData::DBInfo* db) {
clientInfo.history.clear();
for (const auto& kv : globalConfigHistory) {
VersionHistory vh;
ObjectReader reader(kv.value.begin(), IncludeVersion());
if (reader.protocolVersion() != g_network->protocolVersion()) {
// If the protocol version has changed, the
// GlobalConfig actor should refresh its view by
// reading the entire global configuration key
// range. An empty mutation list will signal the
// actor to refresh.
// range. Setting the version to the max int64_t
// will always cause the global configuration
// updater to refresh its view of the configuration
// keyspace.
clientInfo.history.clear();
clientInfo.history.emplace_back(std::numeric_limits<Version>::max());
break;
}
VersionHistory vh;
reader.deserialize(vh);
// Read commit version out of versionstamp at end of key.
@ -3236,7 +3240,7 @@ ACTOR Future<Void> monitorGlobalConfig(ClusterControllerData::DBInfo* db) {
clientInfo.history.push_back(std::move(vh));
}
clientInfo.id = UID(deterministicRandom()->randomUniqueID().first(), 123456789);
clientInfo.id = deterministicRandom()->randomUniqueID();
db->clientInfo->set(clientInfo);
}