mcrouter/routes/ShadowRoute.h (147 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <algorithm> #include <functional> #include <memory> #include <utility> #include <vector> #include <folly/Function.h> #include <folly/Optional.h> #include "mcrouter/McrouterFiberContext.h" #include "mcrouter/McrouterLogFailure.h" #include "mcrouter/ProxyBase.h" #include "mcrouter/lib/Reply.h" #include "mcrouter/lib/RouteHandleTraverser.h" #include "mcrouter/routes/DefaultShadowPolicy.h" #include "mcrouter/routes/McRouteHandleBuilder.h" #include "mcrouter/routes/ShadowRouteIf.h" #include "mcrouter/routes/ShadowSettings.h" namespace folly { struct dynamic; } namespace facebook { namespace memcache { template <class RouteHandleIf> class RouteHandleFactory; namespace mcrouter { /** * Shadowing using dynamic settings. * * Always sends the request to normalRoute. * In addition, asynchronously sends the same request to shadowRoutes if key * hash is within settings range * Key range might be updated at runtime. * We can shadow to multiple shadow destinations for a given normal route. */ template <class RouterInfo, class ShadowPolicy> class ShadowRoute { private: using RouteHandleIf = typename RouterInfo::RouteHandleIf; public: static std::string routeName() { return "shadow"; } ShadowRoute( std::shared_ptr<RouteHandleIf> normalRoute, ShadowData<RouterInfo> shadowData, ShadowPolicy shadowPolicy) : normal_(std::move(normalRoute)), shadowData_(std::move(shadowData)), shadowPolicy_(std::move(shadowPolicy)) {} template <class Request> bool traverse( const Request& req, const RouteHandleTraverser<RouteHandleIf>& t) const { if (t(*normal_, req)) { return true; } return fiber_local<RouterInfo>::runWithLocals([this, &req, &t]() mutable { fiber_local<RouterInfo>::addRequestClass(RequestClass::kShadow); for (auto& shadowData : shadowData_) { if (t(*shadowData.first, req)) { return true; } } return false; }); } template <class Request> ReplyT<Request> route(const Request& req) const { std::shared_ptr<const Request> adjustedNormalReq; folly::Optional<ReplyT<Request>> normalReply; for (const auto& iter : shadowData_) { if (shouldShadow(req, iter.second.get())) { auto shadow = iter.first; if (!shadow) { if (auto& reqCtx = fiber_local<RouterInfo>::getSharedCtx()) { MC_LOG_FAILURE( reqCtx->proxy().router().opts(), failure::Category::kInvalidConfig, "ShadowRoute: ShadowData has unexpected nullptr route handle"); } continue; } if (!adjustedNormalReq) { adjustedNormalReq = shadowPolicy_.makeAdjustedNormalRequest(req); assert(adjustedNormalReq); } if (!normalReply && shadowPolicy_.template shouldDelayShadow<Request>()) { normalReply = normal_->route(*adjustedNormalReq); } dispatchShadowRequest( std::move(shadow), shadowPolicy_.makeShadowRequest(adjustedNormalReq), normalReply ? shadowPolicy_.makePostShadowReplyFn(*normalReply) : nullptr); } } return normalReply ? std::move(*normalReply) : normal_->route(adjustedNormalReq ? *adjustedNormalReq : req); } private: const std::shared_ptr<RouteHandleIf> normal_; const ShadowData<RouterInfo> shadowData_; ShadowPolicy shadowPolicy_; template <class Request> bool shouldShadow(const Request& req, ShadowSettings* settings) const { auto& ctx = fiber_local<RouterInfo>::getSharedCtx(); if (!settings) { if (ctx) { MC_LOG_FAILURE( ctx->proxy().router().opts(), failure::Category::kInvalidConfig, "ShadowRoute: ShadowSettings is nullptr"); } return false; } if (!ctx) { LOG_FAILURE( "mcrouter", failure::Category::kInvalidConfig, "ShadowRoute: ProxyRequestContext is nullptr. Ignoring randomness."); return settings->shouldShadowKey(req); } return settings->shouldShadow(req, ctx->proxy().randomGenerator()); } template <class Request> void dispatchShadowRequest( std::shared_ptr<RouteHandleIf> shadow, std::shared_ptr<Request> adjustedReq, folly::Function<void(const ReplyT<Request>&)> postShadowReplyFn) const; }; template <class RouterInfo> std::shared_ptr<typename RouterInfo::RouteHandleIf> makeShadowRouteDefault( std::shared_ptr<typename RouterInfo::RouteHandleIf> normalRoute, ShadowData<RouterInfo> shadowData, DefaultShadowPolicy shadowPolicy); template <class RouterInfo> std::vector<std::shared_ptr<typename RouterInfo::RouteHandleIf>> makeShadowRoutes( RouteHandleFactory<typename RouterInfo::RouteHandleIf>& factory, const folly::dynamic& json, std::vector<std::shared_ptr<typename RouterInfo::RouteHandleIf>> children, ProxyBase& proxy, ExtraRouteHandleProviderIf<RouterInfo>& extraProvider); template <class RouterInfo> std::vector<std::shared_ptr<typename RouterInfo::RouteHandleIf>> makeShadowRoutes( RouteHandleFactory<typename RouterInfo::RouteHandleIf>& factory, const folly::dynamic& json, ProxyBase& proxy, ExtraRouteHandleProviderIf<RouterInfo>& extraProvider); } // namespace mcrouter } // namespace memcache } // namespace facebook #include "ShadowRoute-inl.h"