McrouterRouteHandlePtr makeHashRoute( const folly::dynamic& json, std::vector<McrouterRouteHandlePtr> rh, size_t threadId) { std::string salt; folly::StringPiece funcType = Ch3HashFunc::type(); if (json.isObject()) { if (auto jsalt = json.get_ptr("salt")) { checkLogic(jsalt->isString(), "HashRoute: salt is not a string"); salt = jsalt->getString(); } if (auto jhashFunc = json.get_ptr("hash_func")) { checkLogic(jhashFunc->isString(), "HashRoute: hash_func is not a string"); funcType = jhashFunc->stringPiece(); } } if (funcType == Ch3HashFunc::type()) { return makeHashRouteCh3(std::move(rh), std::move(salt)); } else if (funcType == Crc32HashFunc::type()) { return makeHashRouteCrc32(std::move(rh), std::move(salt)); } else if (funcType == WeightedCh3HashFunc::type()) { WeightedCh3HashFunc func{json, rh.size()}; return makeHashRouteWeightedCh3(std::move(rh), std::move(salt), std::move(func)); } else if (funcType == ConstShardHashFunc::type()) { return makeHashRouteConstShard(std::move(rh), std::move(salt)); } else if (funcType == "Latest") { return makeLatestRoute(json, std::move(rh), threadId); } throwLogic("Unknown hash function: {}", funcType); }
void TestClient::sendGet(std::string key, mc_res_t expectedResult, uint32_t timeoutMs) { inflight_++; fm_.addTask([key, expectedResult, this, timeoutMs]() { auto msg = createMcMsgRef(key.c_str()); msg->op = mc_op_get; McRequestWithMcOp<mc_op_get> req{std::move(msg)}; try { auto reply = client_->sendSync(req, std::chrono::milliseconds(timeoutMs)); if (reply.result() == mc_res_found) { auto value = getRange(reply.value()); if (req.fullKey() == "empty") { checkLogic(reply.hasValue(), "Reply has no value"); checkLogic(value.empty(), "Expected empty value, got {}", value); } else { checkLogic(value == req.fullKey(), "Expected {}, got {}", req.fullKey(), value); } } checkLogic(expectedResult == reply.result(), "Expected {}, got {}", mc_res_to_string(expectedResult), mc_res_to_string(reply.result())); } catch (const std::exception& e) { CHECK(false) << "Failed: " << e.what(); } inflight_--; }); }
McrouterRouteHandlePtr makeFailoverWithExptimeRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { checkLogic(json.isObject(), "FailoverWithExptimeRoute is not an object"); auto jnormal = json.get_ptr("normal"); checkLogic(jnormal, "FailoverWithExptimeRoute: normal not found"); auto normal = factory.create(*jnormal); int32_t failoverExptime = 60; if (auto jexptime = json.get_ptr("failover_exptime")) { checkLogic(jexptime->isInt(), "FailoverWithExptimeRoute: " "failover_exptime is not an integer"); failoverExptime = jexptime->getInt(); } std::vector<McrouterRouteHandlePtr> failover; if (auto jfailover = json.get_ptr("failover")) { failover = factory.createList(*jfailover); } auto children = getFailoverChildren(std::move(normal), std::move(failover), failoverExptime); return makeFailoverRoute(json, std::move(children)); }
FailoverWithExptimeRoute::FailoverWithExptimeRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) : failoverExptime_(60) { checkLogic(json.isObject(), "FailoverWithExptimeRoute is not object"); std::vector<McrouterRouteHandlePtr> failoverTargets; if (json.count("failover")) { failoverTargets = factory.createList(json["failover"]); } failover_ = FailoverRoute<McrouterRouteHandleIf>(std::move(failoverTargets)); if (json.count("normal")) { normal_ = factory.create(json["normal"]); } if (json.count("failover_exptime")) { checkLogic(json["failover_exptime"].isInt(), "failover_exptime is not integer"); failoverExptime_ = json["failover_exptime"].asInt(); } if (json.count("settings")) { settings_ = FailoverWithExptimeSettings(json["settings"]); } }
std::vector<double> WeightedChHashFuncBase::parseWeights( const folly::dynamic& json, size_t n) { std::vector<double> weights; checkLogic( json.isObject() && json.count("weights"), "WeightedChHashFunc: not an object or no weights"); checkLogic( json["weights"].isArray(), "WeightedChHashFunc: weights is not array"); const auto& jWeights = json["weights"]; LOG_IF(ERROR, jWeights.size() < n) << "WeightedChHashFunc: CONFIG IS BROKEN!!! number of weights (" << jWeights.size() << ") is smaller than number of servers (" << n << "). Missing weights are set to 0.5"; for (size_t i = 0; i < std::min(n, jWeights.size()); ++i) { const auto& weight = jWeights[i]; checkLogic(weight.isNumber(), "WeightedChHashFunc: weight is not number"); const auto weightNum = weight.asDouble(); checkLogic( 0 <= weightNum && weightNum <= 1.0, "WeightedChHashFunc: weight must be in range [0, 1.0]"); weights.push_back(weightNum); } weights.resize(n, 0.5); return weights; }
PoolFactory::PoolJson PoolFactory::parseNamedPool(folly::StringPiece name) { auto existingIt = pools_.find(name); if (existingIt == pools_.end()) { // get the pool from ConfigApi std::string jsonStr; checkLogic(configApi_.get(ConfigType::Pool, name.str(), jsonStr), "Can not read pool: {}", name); existingIt = pools_.emplace( name, std::make_pair(parseJsonString(jsonStr), PoolState::PARSED) ).first; return PoolJson(existingIt->first, existingIt->second.first); } name = existingIt->first; auto& json = existingIt->second.first; auto& state = existingIt->second.second; switch (state) { case PoolState::PARSED: return PoolJson(name, json); case PoolState::PARSING: throwLogic("Cycle in pool inheritance"); case PoolState::NEW: state = PoolState::PARSING; break; } if (auto jInherit = json.get_ptr("inherit")) { checkLogic(jInherit->isString(), "Pool {}: inherit is not a string", name); auto& newJson = parseNamedPool(jInherit->stringPiece()).json; json.update_missing(newJson); json.erase("inherit"); } state = PoolState::PARSED; return PoolJson(name, json); }
McrouterRouteHandlePtr makeFailoverWithExptimeRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { checkLogic(json.isObject(), "FailoverWithExptimeRoute is not an object"); McrouterRouteHandlePtr normal; if (auto jnormal = json.get_ptr("normal")) { normal = factory.create(*jnormal); } std::vector<McrouterRouteHandlePtr> failoverTargets; if (auto jfailover = json.get_ptr("failover")) { failoverTargets = factory.createList(*jfailover); } int32_t failoverExptime = 60; if (auto jexptime = json.get_ptr("failover_exptime")) { checkLogic(jexptime->isInt(), "FailoverWithExptimeRoute: " "failover_exptime is not an integer"); failoverExptime = jexptime->getInt(); } // Check if only one format is being used checkLogic(!(json.count("settings") && // old (json.count("failover_errors") || json.count("failover_tag"))), // new "Use either 'settings' (old format) or 'failover_errors' / 'failover_tag'" ); // new format FailoverErrorsSettings failoverErrors; bool failoverTagging = false; if (auto jfailoverTag = json.get_ptr("failover_tag")) { checkLogic(jfailoverTag->isBool(), "FailoverWithExptime: failover_tag is not bool"); failoverTagging = jfailoverTag->getBool(); } if (auto jfailoverErrors = json.get_ptr("failover_errors")) { failoverErrors = FailoverErrorsSettings(*jfailoverErrors); } // old format if (auto jsettings = json.get_ptr("settings")) { VLOG(1) << "FailoverWithExptime: This config format is deprecated. " "Use 'failover_errors' instead of 'settings'."; auto oldSettings = FailoverWithExptimeSettings(*jsettings); failoverTagging = oldSettings.failoverTagging; failoverErrors = oldSettings.getFailoverErrors(); } return makeFailoverWithExptimeRoute( std::move(normal), std::move(failoverTargets), failoverExptime, std::move(failoverErrors), failoverTagging); }
PoolFactory::PoolJson PoolFactory::parsePool(const folly::dynamic& json) { checkLogic(json.isString() || json.isObject(), "Pool should be a string (name of pool) or an object"); if (json.isString()) { return parseNamedPool(json.stringPiece()); } auto jname = json.get_ptr("name"); checkLogic(jname && jname->isString(), "Pool should have string 'name'"); pools_.emplace(jname->stringPiece(), std::make_pair(json, PoolState::NEW)); return parseNamedPool(jname->stringPiece()); }
McrouterRouteHandlePtr makeRateLimitRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { checkLogic(json.isObject(), "RateLimitRoute is not an object"); auto jtarget = json.get_ptr("target"); checkLogic(jtarget, "RateLimitRoute: target not found"); auto target = factory.create(*jtarget); auto jrates = json.get_ptr("rates"); checkLogic(jrates, "RateLimitRoute: rates not found"); return makeRateLimitRoute(std::move(target), RateLimiter(*jrates)); }
std::shared_ptr<ClientPool> PoolFactory::parsePool(const folly::dynamic& json) { checkLogic(json.isString() || json.isObject(), "Pool should be a string (name of pool) or an object"); if (json.isString()) { return parsePool(json.stringPiece().str(), json); } else { auto name = json.get_ptr("name"); checkLogic(name && name->isString(), "Pool should have string 'name'"); return parsePool(name->stringPiece().str(), json); } }
FailoverErrorsSettings::List::List(const folly::dynamic& json) { checkLogic(json.isArray(), "List of failover errors is not an array."); std::vector<std::string> errors; errors.reserve(json.size()); for (const auto& elem : json) { checkLogic(elem.isString(), "Failover error {} is not a string", elem); errors.push_back(elem.getString()); } init(std::move(errors)); }
PoolFactory::PoolFactory(const folly::dynamic& config, ConfigApiIf& configApi) : configApi_(configApi) { checkLogic(config.isObject(), "config is not an object"); if (auto jpools = config.get_ptr("pools")) { checkLogic(jpools->isObject(), "config: 'pools' is not an object"); for (const auto& it : jpools->items()) { pools_.emplace( it.first.stringPiece(), std::make_pair(it.second, PoolState::NEW)); } } }
PoolFactory::PoolFactory(const folly::dynamic& config, ConfigApi& configApi, const McrouterOptions& opts) : configApi_(configApi), opts_(opts) { checkLogic(config.isObject(), "config is not an object"); if (auto jpools = config.get_ptr("pools")) { checkLogic(jpools->isObject(), "config: 'pools' is not an object"); for (const auto& it : jpools->items()) { parsePool(it.first.stringPiece().str(), it.second); } } }
std::vector<McrouterRouteHandlePtr> makeShadowRoutes( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json, proxy_t& proxy, ExtraRouteHandleProviderIf& extraProvider) { checkLogic(json.isObject(), "ShadowRoute should be an object"); const auto jchildren = json.get_ptr("children"); checkLogic(jchildren, "ShadowRoute: children not found"); auto children = factory.createList(*jchildren); if (json.count("shadows")) { children = makeShadowRoutes( factory, json, std::move(children), proxy, extraProvider); } return children; }
McrouterRouteHandlePtr makeErrorRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { checkLogic(json.isObject() || json.isString() || json.isNull(), "ErrorRoute: should be string or object"); std::string response; if (json.isString()) { response = json.stringPiece().str(); } else if (json.isObject()) { if (auto jResponse = json.get_ptr("response")) { checkLogic(jResponse->isString(), "ErrorRoute: response is not a string"); response = jResponse->stringPiece().str(); } } return makeErrorRoute(std::move(response)); }
void ShadowSettings::setKeyRange(double start, double end) { checkLogic(0 <= start && start <= end && end <= 1, "invalid key_fraction_range [{}, {}]", start, end); uint64_t keyStart = start * std::numeric_limits<uint32_t>::max(); uint64_t keyEnd = end * std::numeric_limits<uint32_t>::max(); keyRange_ = (keyStart << 32UL) | keyEnd; }
size_t WeightedCh3HashFunc::operator()(folly::StringPiece key) const { auto n = weights_.size(); checkLogic(n && n <= furc_maximum_pool_size(), "Invalid pool size: {}", n); size_t salt = 0; size_t index = 0; std::string saltedKey; auto originalKey = key; for (size_t i = 0; i < kNumTries; ++i) { index = furc_hash(key.data(), key.size(), n); /* Use 32-bit hash, but store in 64-bit ints so that we don't have to deal with overflows */ uint64_t p = folly::hash::SpookyHashV2::Hash32(key.data(), key.size(), kHashSeed); assert(0 <= weights_[index] && weights_[index] <= 1.0); uint64_t w = weights_[index] * std::numeric_limits<uint32_t>::max(); /* Rehash only if p is out of range */ if (LIKELY(p < w)) { return index; } /* Change the key to rehash */ auto s = salt++; saltedKey = originalKey.str(); do { saltedKey.push_back(char(s % 10) + '0'); s /= 10; } while (s > 0); key = saltedKey; } return index; }
WeightedCh3HashFunc::WeightedCh3HashFunc(const folly::dynamic& json, size_t n) { checkLogic(json.isObject() && json.count("weights"), "WeightedCh3HashFunc: not an object or no weights"); checkLogic(json["weights"].isArray(), "WeightedCh3HashFunc: weights is not array"); const auto& jWeights = json["weights"]; LOG_IF(ERROR, jWeights.size() < n) << "WeightedCh3HashFunc: CONFIG IS BROKEN!!! number of weights (" << jWeights.size() << ") is smaller than number of servers (" << n << "). Missing weights are set to 0.5"; for (size_t i = 0; i < std::min(n, jWeights.size()); ++i) { const auto& weight = jWeights[i]; checkLogic(weight.isNumber(), "WeightedCh3HashFunc: weight is not number"); weights_.push_back(weight.asDouble()); } weights_.resize(n, 0.5); }
void DestinationClient::initializeAsyncMcClient() { FBI_ASSERT(proxy_->eventBase); auto pdstn = pdstn_.lock(); assert(pdstn != nullptr); ConnectionOptions options(pdstn->accessPoint); options.noNetwork = proxy_->opts.no_network; options.tcpKeepAliveCount = proxy_->opts.keepalive_cnt; options.tcpKeepAliveIdle = proxy_->opts.keepalive_idle_s; options.tcpKeepAliveInterval = proxy_->opts.keepalive_interval_s; options.timeout = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::seconds(pdstn->server_timeout.tv_sec) + std::chrono::microseconds(pdstn->server_timeout.tv_usec)); if (proxy_->opts.enable_qos) { options.enableQoS = true; options.qos = pdstn->qos; } if (pdstn->use_ssl) { auto& opts = proxy_->opts; checkLogic(!opts.pem_cert_path.empty() && !opts.pem_key_path.empty() && !opts.pem_ca_path.empty(), "Some of ssl key paths are not set!"); options.sslContextProvider = [&opts] { return getSSLContext(opts.pem_cert_path, opts.pem_key_path, opts.pem_ca_path); }; } asyncMcClient_ = folly::make_unique<AsyncMcClient>(*proxy_->eventBase, std::move(options)); auto pdstnWeakPtr = pdstn_; asyncMcClient_->setStatusCallbacks( [pdstnWeakPtr] () { auto pdstnPtr = pdstnWeakPtr.lock(); if (!pdstnPtr) { return; } pdstnPtr->on_up(); }, [pdstnWeakPtr] (const apache::thrift::transport::TTransportException&) { auto pdstnPtr = pdstnWeakPtr.lock(); if (!pdstnPtr) { return; } pdstnPtr->on_down(); }); if (proxy_->opts.target_max_inflight_requests > 0) { asyncMcClient_->setThrottle(proxy_->opts.target_max_inflight_requests, proxy_->opts.target_max_pending_requests); } }
void ShadowSettings::registerOnUpdateCallback(McrouterInstance& router) { handle_ = router.rtVarsData().subscribeAndCall( [this](std::shared_ptr<const RuntimeVarsData> oldVars, std::shared_ptr<const RuntimeVarsData> newVars) { if (!newVars || keyFractionRangeRv_.empty()) { return; } auto val = newVars->getVariableByName(keyFractionRangeRv_); if (val != nullptr) { checkLogic(val.isArray(), "runtime vars: {} is not an array", keyFractionRangeRv_); checkLogic(val.size() == 2, "runtime vars: size of {} is not 2", keyFractionRangeRv_); checkLogic(val[0].isNumber(), "runtime vars: {}#0 is not a number", keyFractionRangeRv_); checkLogic(val[1].isNumber(), "runtime vars: {}#1 is not a number", keyFractionRangeRv_); setKeyRange(val[0].asDouble(), val[1].asDouble()); } }); }
/** * @return target and asynclogName * Caller may call makeAsynclogRoute afterwards. */ std::pair<McrouterRouteHandlePtr, std::string> parseAsynclogRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { std::string asynclogName; McrouterRouteHandlePtr target; checkLogic(json.isObject() || json.isString(), "AsynclogRoute should be object or string"); if (json.isString()) { asynclogName = json.stringPiece().str(); target = factory.create(json); } else { // object auto jname = json.get_ptr("name"); checkLogic(jname && jname->isString(), "AsynclogRoute: required string name"); auto jtarget = json.get_ptr("target"); checkLogic(jtarget, "AsynclogRoute: target not found"); asynclogName = jname->stringPiece().str(); target = factory.create(*jtarget); } return { std::move(target), std::move(asynclogName) }; }
McrouterRouteHandlePtr makeOperationSelectorRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { if (!json.isObject()) { return factory.create(json); } McrouterRouteHandlePtr defaultPolicy; if (json.count("default_policy")) { defaultPolicy = factory.create(json["default_policy"]); } std::vector<McrouterRouteHandlePtr> operationPolicies{mc_nops}; if (auto jOpPolicies = json.get_ptr("operation_policies")) { checkLogic(jOpPolicies->isObject(), "OperationSelectorRoute: operation_policies is not an object"); std::map<std::string, const folly::dynamic*> orderedPolicies; for (auto& it : jOpPolicies->items()) { checkLogic(it.first.isString(), "OperationSelectorRoute: operation_policies' " "key is not a string"); auto key = it.first.stringPiece().str(); orderedPolicies.emplace(std::move(key), &it.second); } // order is important here: named handles may not be resolved if we parse // policies in random order for (const auto& it : orderedPolicies) { auto opId = mc_op_from_string(it.first.data()); checkLogic(opId != mc_op_unknown, "Unknown mc operation: {}", it.first); operationPolicies[opId] = factory.create(*it.second); } return makeOperationSelectorRoute(std::move(operationPolicies), std::move(defaultPolicy)); } return defaultPolicy; }
RouteHandleMap::RouteHandleMap( const RouteSelectorMap& routeSelectors, const RoutingPrefix& defaultRoute, bool sendInvalidRouteToDefault) : defaultRoute_(defaultRoute), sendInvalidRouteToDefault_(sendInvalidRouteToDefault) { checkLogic(routeSelectors.find(defaultRoute_) != routeSelectors.end(), "invalid default route: {}", defaultRoute_.str()); RouteSelectorVector allRoutes; std::unordered_map<std::string, RouteSelectorVector> byRegion; std::unordered_map<std::string, RouteSelectorVector> byRoute; // add defaults first for (const auto& it : routeSelectors) { RoutingPrefix prefix(it.first); if (prefix.str() == defaultRoute_.str()) { allRoutes.push_back(it.second); } if (prefix.getRegion() == defaultRoute_.getRegion()) { byRegion[prefix.getRegion().str()].push_back(it.second); } } // then add rest for (const auto& it : routeSelectors) { RoutingPrefix prefix(it.first); if (prefix.str() != defaultRoute_.str()) { allRoutes.push_back(it.second); } if (prefix.getRegion() != defaultRoute_.getRegion()) { byRegion[prefix.getRegion().str()].push_back(it.second); } byRoute[it.first].push_back(it.second); } // create corresponding RoutePolicyMaps UniqueVectorMap uniqueVectors; allRoutes_ = makePolicyMap(uniqueVectors, allRoutes); for (const auto& it : byRegion) { byRegion_.emplace(it.first, makePolicyMap(uniqueVectors, it.second)); } for (const auto& it : byRoute) { byRoute_.emplace(it.first, makePolicyMap(uniqueVectors, it.second)); } assert(byRoute_.find(defaultRoute_) != byRoute_.end()); defaultRouteMap_ = byRoute_[defaultRoute_]; }
std::pair<std::shared_ptr<ClientPool>, std::vector<McrouterRouteHandlePtr>> McRouteHandleProvider::makePool(const folly::dynamic& json) { checkLogic(json.isString() || json.isObject(), "Pool should be a string (name of pool) or an object"); auto pool = poolFactory_.parsePool(json); std::vector<McrouterRouteHandlePtr> destinations; for (const auto& client : pool->getClients()) { auto pdstn = destinationMap_.fetch(*client); auto route = makeDestinationRoute(client, std::move(pdstn)); destinations.push_back(std::move(route)); } return std::make_pair(std::move(pool), std::move(destinations)); }
OperationSelectorRoute::OperationSelectorRoute( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json) { if (!json.isObject()) { defaultPolicy_ = factory.create(json); return; } if (json.count("default_policy")) { defaultPolicy_ = factory.create(json["default_policy"]); } operationPolicies_.resize(mc_nops); if (json.count("operation_policies")) { const auto& policies = json["operation_policies"]; checkLogic(policies.isObject(), "OperationSelectorRoute: operation_policies is not object"); std::map<std::string, folly::dynamic> orderedPolicies; for (const auto& it : policies.items()) { checkLogic(it.first.isString(), "OperationSelectorRoute: operation_policies " "key is not a string"); auto key = it.first.asString().toStdString(); orderedPolicies.insert({ key, it.second }); } // order is important here: named handles may not be resolved if we parse // policies in random order for (const auto& it : orderedPolicies) { auto opId = mc_op_from_string(it.first.data()); checkLogic(opId != mc_op_unknown, "Unknown mc operation: {}", it.first); operationPolicies_[opId] = factory.create(it.second); } } }
ProxyConfig::ProxyConfig(proxy_t* proxy, const folly::dynamic& json, std::string configMd5Digest, std::shared_ptr<PoolFactory> poolFactory) : poolFactory_(std::move(poolFactory)), configMd5Digest_(std::move(configMd5Digest)) { McRouteHandleProvider provider(proxy, *proxy->destinationMap, *poolFactory_); RouteHandleFactory<McrouterRouteHandleIf> factory(provider); checkLogic(json.isObject(), "Config is not an object"); if (json.count("named_handles")) { checkLogic(json["named_handles"].isArray(), "named_handles is not array"); for (const auto& it : json["named_handles"]) { factory.create(it); } } checkLogic(!json.count("route") || !json.count("routes"), "Config ambiguous, has both route and routes"); RouteSelectorMap routeSelectors; if (json.count("route")) { addRouteSelector({ proxy->opts.default_route.str() }, json["route"], factory, routeSelectors); } else if (json.count("routes")) { checkLogic(json["routes"].isArray(), "Config: routes is not array"); for (const auto& it : json["routes"]) { checkLogic(it.isObject(), "RoutePolicy is not object"); checkLogic(it.count("route"), "RoutePolicy has no route"); checkLogic(it.count("aliases"), "RoutePolicy has no aliases"); const auto& aliases = it["aliases"]; checkLogic(aliases.isArray(), "RoutePolicy aliases is not array"); addRouteSelector(aliases, it["route"], factory, routeSelectors); } } else { throw std::logic_error("No route/routes in config"); } asyncLogRoutes_ = provider.releaseAsyncLogRoutes(); proxyRoute_ = std::make_shared<ProxyRoute>(proxy, routeSelectors); serviceInfo_ = std::make_shared<ServiceInfo>(proxy, *this); }
McrouterRouteHandlePtr makeLatestRoute( const folly::dynamic& json, std::vector<McrouterRouteHandlePtr> targets) { size_t failoverCount = 5; FailoverErrorsSettings failoverErrors; if (json.isObject()) { if (auto jfailoverCount = json.get_ptr("failover_count")) { checkLogic(jfailoverCount->isInt(), "LatestRoute: failover_count is not an integer"); failoverCount = jfailoverCount->getInt(); } if (auto jFailoverErrors = json.get_ptr("failover_errors")) { failoverErrors = FailoverErrorsSettings(*jFailoverErrors); } } return makeLatestRoute(std::move(targets), failoverCount, std::move(failoverErrors)); }
std::shared_ptr<ShadowSettings> ShadowSettings::create(const folly::dynamic& json, McrouterInstance& router) { auto result = std::shared_ptr<ShadowSettings>(new ShadowSettings()); try { checkLogic(json.isObject(), "json is not an object"); if (auto jKeyFractionRange = json.get_ptr("key_fraction_range")) { checkLogic(jKeyFractionRange->isArray(), "key_fraction_range is not an array"); auto ar = folly::convertTo<std::vector<double>>(*jKeyFractionRange); checkLogic(ar.size() == 2, "key_fraction_range size is not 2"); result->setKeyRange(ar[0], ar[1]); } if (auto jIndexRange = json.get_ptr("index_range")) { checkLogic(jIndexRange->isArray(), "index_range is not an array"); auto ar = folly::convertTo<std::vector<size_t>>(*jIndexRange); checkLogic(ar.size() == 2, "index_range size is not 2"); checkLogic(ar[0] <= ar[1], "index_range start > end"); result->startIndex_ = ar[0]; result->endIndex_ = ar[1]; } if (auto jKeyFractionRangeRv = json.get_ptr("key_fraction_range_rv")) { checkLogic(jKeyFractionRangeRv->isString(), "key_fraction_range_rv is not a string"); result->keyFractionRangeRv_ = jKeyFractionRangeRv->stringPiece().str(); } if (auto jValidateReplies = json.get_ptr("validate_replies")) { checkLogic(jValidateReplies->isBool(), "validate_replies is not a bool"); result->validateReplies_ = jValidateReplies->getBool(); } } catch (const std::logic_error& e) { MC_LOG_FAILURE(router.opts(), failure::Category::kInvalidConfig, "ShadowSettings: {}", e.what()); return nullptr; } result->registerOnUpdateCallback(router); return result; }
void FailoverErrorsSettings::List::init(std::vector<std::string> errors) { failover_ = folly::make_unique<std::array<bool, mc_nres>>(); for (const auto& error : errors) { int i; for (i = 0; i < mc_nres; ++i) { mc_res_t errorType = static_cast<mc_res_t>(i); folly::StringPiece errorName(mc_res_to_string(errorType)); errorName.removePrefix("mc_res_"); if (mc_res_is_err(errorType) && error == errorName) { (*failover_)[i] = true; break; } } checkLogic(i < mc_nres, "Failover error '{}' is not a valid error type.", error); } }
FailoverErrorsSettings::FailoverErrorsSettings(const folly::dynamic& json) { checkLogic(json.isObject() || json.isArray(), "Failover errors must be either an array or an object."); if (json.isObject()) { if (auto jsonGets = json.get_ptr("gets")) { gets_ = FailoverErrorsSettings::List(*jsonGets); } if (auto jsonUpdates = json.get_ptr("updates")) { updates_ = FailoverErrorsSettings::List(*jsonUpdates); } if (auto jsonDeletes = json.get_ptr("deletes")) { deletes_ = FailoverErrorsSettings::List(*jsonDeletes); } } else if (json.isArray()) { gets_ = FailoverErrorsSettings::List(json); updates_ = FailoverErrorsSettings::List(json); deletes_ = FailoverErrorsSettings::List(json); } }