void PoolFactory::parseQos(std::string parentName, const folly::dynamic& jQos, uint64_t& qosClass, uint64_t& qosPath) { if (!jQos.isObject()) { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "{}: qos must be an object.", parentName); return; } uint64_t prevClass = qosClass; if (auto jClass = jQos.get_ptr("class")) { if (jClass->isInt() && isQosClassValid(jClass->getInt())) { qosClass = jClass->getInt(); } else { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "{}: qos.class must be an integer in the range [0, 4]", parentName); } } if (auto jPath = jQos.get_ptr("path")) { if (jPath->isInt() && isQosPathValid(jPath->getInt())) { qosPath = jPath->getInt(); } else { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "{}: qos.path must be an integer in the range [0, 3]", parentName); qosClass = prevClass; } } }
bool McrouterLogger::start() { if (running_ || router_.opts().stats_logging_interval == 0) { return false; } if (!ensureDirExistsAndWritable(router_.opts().stats_root)) { LOG(ERROR) << "Can't create or chmod " << router_.opts().stats_root << ", disabling stats logging"; return false; } auto path = stats_file_path(router_.opts(), kStatsStartupOptionsSfx); if (std::find(touchStatsFilepaths_.begin(), touchStatsFilepaths_.end(), path) == touchStatsFilepaths_.end()) { touchStatsFilepaths_.push_back(std::move(path)); } running_ = true; const std::string threadName = "mcrtr-stats-logger"; try { loggerThread_ = std::thread( std::bind(&McrouterLogger::loggerThreadRun, this)); folly::setThreadName(loggerThread_.native_handle(), threadName); } catch (const std::system_error& e) { running_ = false; MC_LOG_FAILURE(router_.opts(), memcache::failure::Category::kSystemError, "Can not start McrouterLogger thread {}: {}", threadName, e.what()); } return running_; }
void ProxyDestinationMap::scheduleTimer(bool initialAttempt) { if (!resetTimer_->scheduleTimeout(inactivityTimeout_)) { MC_LOG_FAILURE( proxy_->router().opts(), memcache::failure::Category::kSystemError, "failed to {}schedule inactivity timer", initialAttempt ? "" : "re-"); } }
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; }
/** Adds an asynchronous request to the event log. */ void asynclog_delete(proxy_t* proxy, const ProxyClientCommon& pclient, folly::StringPiece key, folly::StringPiece poolName) { dynamic json = {}; const auto& host = pclient.ap->getHost(); const auto& port = proxy->router().opts().asynclog_port_override == 0 ? pclient.ap->getPort() : proxy->router().opts().asynclog_port_override; if (proxy->router().opts().use_asynclog_version2) { json = dynamic::object; json["f"] = proxy->router().opts().flavor_name; json["h"] = folly::sformat("[{}]:{}", host, port); json["p"] = poolName.str(); json["k"] = key.str(); } else { /* ["host", port, escaped_command] */ json.push_back(host); json.push_back(port); json.push_back(folly::sformat("delete {}\r\n", key)); } auto fd = asynclog_open(proxy); if (!fd) { MC_LOG_FAILURE(proxy->router().opts(), memcache::failure::Category::kSystemError, "asynclog_open() failed (key {}, pool {})", key, poolName); return; } // ["AS1.0", 1289416829.836, "C", ["10.0.0.1", 11302, "delete foo\r\n"]] // OR ["AS2.0", 1289416829.836, "C", {"f":"flavor","h":"[10.0.0.1]:11302", // "p":"pool_name","k":"foo\r\n"}] dynamic jsonOut = {}; if (proxy->router().opts().use_asynclog_version2) { jsonOut.push_back(ASYNCLOG_MAGIC2); } else { jsonOut.push_back(ASYNCLOG_MAGIC); } struct timeval timestamp; CHECK(gettimeofday(×tamp, nullptr) == 0); auto timestamp_ms = facebook::memcache::to<std::chrono::milliseconds>(timestamp).count(); jsonOut.push_back(1e-3 * timestamp_ms); jsonOut.push_back(std::string("C")); jsonOut.push_back(json); auto jstr = folly::toJson(jsonOut) + "\n"; ssize_t size = folly::writeFull(fd->fd(), jstr.data(), jstr.size()); if (size == -1 || size_t(size) < jstr.size()) { MC_LOG_FAILURE(proxy->router().opts(), memcache::failure::Category::kSystemError, "Error fully writing asynclog request (key {}, pool {})", key, poolName); } }
std::shared_ptr<ClientPool> PoolFactory::parsePool(const std::string& name, const folly::dynamic& json) { auto seenPoolIt = pools_.find(name); if (seenPoolIt != pools_.end()) { return seenPoolIt->second; } if (json.isString()) { // get the pool from ConfigApi std::string jsonStr; checkLogic(configApi_.get(ConfigType::Pool, name, jsonStr), "Can not read pool: {}", name); return parsePool(name, parseJsonString(jsonStr)); } else { // one day we may add inheriting from local pool if (auto jinherit = json.get_ptr("inherit")) { checkLogic(jinherit->isString(), "Pool {}: inherit is not a string", name); auto path = jinherit->stringPiece().str(); std::string jsonStr; checkLogic(configApi_.get(ConfigType::Pool, path, jsonStr), "Can not read pool from: {}", path); auto newJson = parseJsonString(jsonStr); for (auto& it : json.items()) { newJson.insert(it.first, it.second); } newJson.erase("inherit"); return parsePool(name, newJson); } } // pool_locality std::chrono::milliseconds timeout{opts_.server_timeout_ms}; if (auto jlocality = json.get_ptr("pool_locality")) { if (!jlocality->isString()) { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "Pool {}: pool_locality is not a string", name); } else { auto str = jlocality->stringPiece(); if (str == "cluster") { if (opts_.cluster_pools_timeout_ms != 0) { timeout = std::chrono::milliseconds(opts_.cluster_pools_timeout_ms); } } else if (str == "region") { if (opts_.regional_pools_timeout_ms != 0) { timeout = std::chrono::milliseconds(opts_.regional_pools_timeout_ms); } } else { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "Pool {}: '{}' pool locality is not supported", name, str); } } } // region & cluster std::string region, cluster; if (auto jregion = json.get_ptr("region")) { if (!jregion->isString()) { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "Pool {}: pool_region is not a string", name); } else { region = jregion->stringPiece().str(); } } if (auto jcluster = json.get_ptr("cluster")) { if (!jcluster->isString()) { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "Pool {}: pool_cluster is not a string", name); } else { cluster = jcluster->stringPiece().str(); } } if (auto jtimeout = json.get_ptr("server_timeout")) { if (!jtimeout->isInt()) { MC_LOG_FAILURE(opts_, memcache::failure::Category::kInvalidConfig, "Pool {}: server_timeout is not an int", name); } else { timeout = std::chrono::milliseconds(jtimeout->getInt()); } } if (!region.empty() && !cluster.empty()) { auto& route = opts_.default_route; if (region == route.getRegion() && cluster == route.getCluster()) { if (opts_.within_cluster_timeout_ms != 0) { timeout = std::chrono::milliseconds(opts_.within_cluster_timeout_ms); } } else if (region == route.getRegion()) { if (opts_.cross_cluster_timeout_ms != 0) { timeout = std::chrono::milliseconds(opts_.cross_cluster_timeout_ms); } } else { if (opts_.cross_region_timeout_ms != 0) { timeout = std::chrono::milliseconds(opts_.cross_region_timeout_ms); } } } auto protocol = parseProtocol(json, mc_ascii_protocol); bool keep_routing_prefix = false; if (auto jkeep_routing_prefix = json.get_ptr("keep_routing_prefix")) { checkLogic(jkeep_routing_prefix->isBool(), "Pool {}: keep_routing_prefix is not a bool"); keep_routing_prefix = jkeep_routing_prefix->getBool(); } uint64_t qosClass = opts_.default_qos_class; uint64_t qosPath = opts_.default_qos_path; if (auto jqos = json.get_ptr("qos")) { parseQos(folly::sformat("Pool {}", name), *jqos, qosClass, qosPath); } bool useSsl = false; if (auto juseSsl = json.get_ptr("use_ssl")) { checkLogic(juseSsl->isBool(), "Pool {}: use_ssl is not a bool", name); useSsl = juseSsl->getBool(); } // servers auto jservers = json.get_ptr("servers"); checkLogic(jservers, "Pool {}: servers not found", name); checkLogic(jservers->isArray(), "Pool {}: servers is not an array", name); auto clientPool = std::make_shared<ClientPool>(name); for (size_t i = 0; i < jservers->size(); ++i) { const auto& server = jservers->at(i); std::shared_ptr<AccessPoint> ap; bool serverUseSsl = useSsl; uint64_t serverQosClass = qosClass; uint64_t serverQosPath = qosPath; checkLogic(server.isString() || server.isObject(), "Pool {}: server #{} is not a string/object", name, i); if (server.isString()) { // we support both host:port and host:port:protocol ap = AccessPoint::create(server.stringPiece(), protocol); checkLogic(ap != nullptr, "Pool {}: invalid server {}", name, server.stringPiece()); } else { // object auto jhostname = server.get_ptr("hostname"); checkLogic(jhostname, "Pool {}: hostname not found for server #{}", name, i); checkLogic(jhostname->isString(), "Pool {}: hostname is not a string for server #{}", name, i); if (auto jqos = server.get_ptr("qos")) { parseQos(folly::sformat("Pool {}, server #{}", name, i), *jqos, qosClass, qosPath); } if (auto juseSsl = server.get_ptr("use_ssl")) { checkLogic(juseSsl->isBool(), "Pool {}: use_ssl is not a bool for server #{}", name, i); serverUseSsl = juseSsl->getBool(); } ap = AccessPoint::create(jhostname->stringPiece(), parseProtocol(server, protocol)); checkLogic(ap != nullptr, "Pool {}: invalid server #{}", name, i); } auto client = clientPool->emplaceClient( timeout, std::move(ap), keep_routing_prefix, serverUseSsl, serverQosClass, serverQosPath); clients_.push_back(std::move(client)); } // servers // weights if (auto jweights = json.get_ptr("weights")) { clientPool->setWeights(*jweights); } pools_.emplace(name, clientPool); return clientPool; }
std::vector<McrouterRouteHandlePtr> makeShadowRoutes( RouteHandleFactory<McrouterRouteHandleIf>& factory, const folly::dynamic& json, std::vector<McrouterRouteHandlePtr> children, proxy_t& proxy, ExtraRouteHandleProviderIf& extraProvider) { folly::StringPiece shadowPolicy = "default"; if (auto jshadow_policy = json.get_ptr("shadow_policy")) { checkLogic(jshadow_policy->isString(), "ShadowRoute: shadow_policy is not a string"); shadowPolicy = jshadow_policy->stringPiece(); } auto jshadows = json.get_ptr("shadows"); checkLogic(jshadows, "ShadowRoute: route doesn't contain shadows field"); if (!jshadows->isArray()) { MC_LOG_FAILURE(proxy.router().opts(), failure::Category::kInvalidConfig, "ShadowRoute: shadows specified in route is not an array"); return children; } McrouterShadowData data; for (auto& shadow : *jshadows) { if (!shadow.isObject()) { MC_LOG_FAILURE(proxy.router().opts(), failure::Category::kInvalidConfig, "ShadowRoute: shadow is not an object"); continue; } auto jtarget = shadow.get_ptr("target"); if (!jtarget) { MC_LOG_FAILURE(proxy.router().opts(), failure::Category::kInvalidConfig, "ShadowRoute shadows: no target for shadow"); continue; } try { auto s = ShadowSettings::create(shadow, proxy.router()); if (s) { data.emplace_back(factory.create(*jtarget), std::move(s)); } } catch (const std::exception& e) { MC_LOG_FAILURE(proxy.router().opts(), failure::Category::kInvalidConfig, "Can not create shadow for ShadowRoute: {}", e.what()); } } for (size_t i = 0; i < children.size(); ++i) { McrouterShadowData childrenShadows; for (const auto& shadowData : data) { if (shadowData.second->startIndex() <= i && i < shadowData.second->endIndex()) { childrenShadows.push_back(shadowData); } } if (!childrenShadows.empty()) { childrenShadows.shrink_to_fit(); children[i] = extraProvider.makeShadow(proxy, std::move(children[i]), std::move(childrenShadows), shadowPolicy); } } return children; }