mcrouter/lib/network/McServerRequestContext.h (221 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 <string>
#include <utility>
#include <folly/Optional.h>
#include <folly/io/IOBuf.h>
#include <folly/io/async/AsyncTransport.h>
#include <folly/io/async/EventBase.h>
#include <thrift/lib/cpp2/server/Cpp2ConnContext.h>
#include "mcrouter/lib/Operation.h"
#include "mcrouter/lib/carbon/RequestReplyUtil.h"
#include "mcrouter/lib/carbon/RoutingGroups.h"
#include "mcrouter/lib/network/CarbonMessageList.h"
#include "mcrouter/lib/network/ServerLoad.h"
namespace facebook {
namespace memcache {
template <class OnRequest, class RequestList>
class McServerOnRequestWrapper;
class McServerSession;
class MultiOpParent;
/**
* API for users of McServer to send back a reply for a request.
*
* Each onRequest callback is provided a context object,
* which must eventually be surrendered back via a reply() call.
*/
class McServerRequestContext {
public:
using DestructorFunc = void (*)(void*);
template <class Reply>
static void
reply(McServerRequestContext&& ctx, Reply&& reply, bool flush = false);
template <class Reply>
static void reply(
McServerRequestContext&& ctx,
Reply&& reply,
DestructorFunc destructor,
void* toDestruct);
~McServerRequestContext();
McServerRequestContext(McServerRequestContext&& other) noexcept;
McServerRequestContext& operator=(McServerRequestContext&& other);
/**
* Get the associated McServerSession
*/
McServerSession& session();
ServerLoad getServerLoad() const noexcept;
folly::Optional<struct sockaddr_storage> getPeerSocketAddress();
folly::Optional<std::string> getPeerSocketAddressStr();
folly::EventBase& getSessionEventBase() const noexcept;
const apache::thrift::Cpp2RequestContext* getThriftRequestContext()
const noexcept;
const folly::AsyncTransportWrapper* getTransport() const noexcept;
void markAsTraced();
void* getConnectionUserData();
private:
McServerSession* session_;
/* Pack these together, operation + flags takes one word */
bool isEndContext_{false}; // Used to mark end of ASCII multi-get request
bool noReply_;
bool replied_{false};
bool isTraced_{false};
uint64_t reqid_;
struct AsciiState {
std::shared_ptr<MultiOpParent> parent_;
folly::Optional<folly::IOBuf> key_;
};
std::unique_ptr<AsciiState> asciiState_;
template <class Reply>
bool noReply(const Reply& r) const;
bool noReply(const McLeaseGetReply& r) const;
template <class Reply, class... Args>
static typename std::enable_if<carbon::GetLike<
RequestFromReplyType<Reply, RequestReplyPairs>>::value>::type
replyImpl(McServerRequestContext&& ctx, Reply&& reply, Args&&... args);
template <class Reply, class... Args>
static typename std::enable_if<carbon::OtherThan<
RequestFromReplyType<Reply, RequestReplyPairs>,
carbon::GetLike<>>::value>::type
replyImpl(McServerRequestContext&& ctx, Reply&& reply, Args&&... args);
template <class Reply, class SessionType = McServerSession>
static void replyImpl2(
McServerRequestContext&& ctx,
Reply&& reply,
DestructorFunc destructor = nullptr,
void* toDestruct = nullptr,
bool flush = false);
folly::Optional<folly::IOBuf>& asciiKey() {
if (!asciiState_) {
asciiState_ = std::make_unique<AsciiState>();
}
return asciiState_->key_;
}
bool hasParent() const {
return asciiState_ && asciiState_->parent_;
}
MultiOpParent& parent() const {
assert(hasParent());
return *asciiState_->parent_;
}
bool isParentError() const;
// Whether or not *this is used to mark the end of a multi-get request
bool isEndContext() const {
return isEndContext_;
}
/**
* If reply is error, multi-op parent may inform this context that it will
* assume responsibility for reporting the error. If so, this context should
* not call McServerSession::reply. Returns true iff parent assumes
* responsibility for reporting error. If true is returned, errorMessage is
* moved to parent.
*/
bool moveReplyToParent(
carbon::Result result,
uint32_t errorCode,
std::string&& errorMessage) const;
McServerRequestContext(const McServerRequestContext&) = delete;
const McServerRequestContext& operator=(const McServerRequestContext&) =
delete;
/* Only McServerSession can create these */
friend class McServerSession;
friend class MultiOpParent;
friend class WriteBuffer;
McServerRequestContext(
McServerSession& s,
uint64_t r,
bool nr = false,
std::shared_ptr<MultiOpParent> parent = nullptr,
bool isEndContext = false);
};
void markContextAsTraced(McServerRequestContext& ctx);
static_assert(
#if defined(__i386__)
sizeof(McServerRequestContext) == 20,
#elif defined(__ARM_ARCH) && !defined(__aarch64__)
sizeof(McServerRequestContext) == 24,
#else
sizeof(McServerRequestContext) == 32,
#endif
"Think twice before adding more fields to McServerRequestContext,"
" doing so WILL have perf implications");
/**
* McServerOnRequest is a polymorphic base class used as a callback
* by AsyncMcServerWorker and McAsciiParser to hand off a request
* to McrouterClient.
*
* The complexity in the implementation below is due to the fact that we
* effectively need templated virtual member functions (which do not really
* exist in C++).
*/
template <class RequestList>
class McServerOnRequestIf;
/**
* OnRequest callback interface. This is an implementation detail.
*/
template <class Request>
class McServerOnRequestIf<List<Request>> {
public:
virtual void caretRequestReady(
const CaretMessageInfo& headerInfo,
const folly::IOBuf& reqBody,
McServerRequestContext&& ctx) = 0;
virtual void requestReady(McServerRequestContext&&, Request&&) {
LOG(ERROR) << "requestReady() not implemented for request type "
<< Request::name;
}
virtual ~McServerOnRequestIf() = default;
};
template <class Request, class... Requests>
class McServerOnRequestIf<List<Request, Requests...>>
: public McServerOnRequestIf<List<Requests...>> {
public:
using McServerOnRequestIf<List<Requests...>>::requestReady;
virtual void requestReady(McServerRequestContext&&, Request&&) {
LOG(ERROR) << "requestReady() not implemented for request type "
<< Request::name;
}
~McServerOnRequestIf() override = default;
};
class McServerOnRequest : public McServerOnRequestIf<McRequestList> {
public:
explicit McServerOnRequest(std::string requestHandlerName)
: requestHandlerName_(std::move(requestHandlerName)) {}
/**
* Return the name of the request handler being used in this
* instance of McServerOnRequest.
*/
const std::string& name() const {
return requestHandlerName_;
}
private:
std::string requestHandlerName_;
};
/**
* Helper class to wrap user-defined callbacks in a correct virtual interface.
* This is needed since we're mixing templates and virtual functions.
*/
template <class OnRequest, class RequestList = McRequestList>
class McServerOnRequestWrapper;
template <class OnRequest>
class McServerOnRequestWrapper<OnRequest, List<>> : public McServerOnRequest {
public:
using McServerOnRequest::requestReady;
template <class... Args>
explicit McServerOnRequestWrapper(Args&&... args)
: McServerOnRequest(OnRequest::name),
onRequest_(std::forward<Args>(args)...) {}
void caretRequestReady(
const CaretMessageInfo& headerInfo,
const folly::IOBuf& reqBody,
McServerRequestContext&& ctx) final;
void dispatchTypedRequestIfDefined(
const CaretMessageInfo& headerInfo,
const folly::IOBuf& reqBody,
McServerRequestContext&& ctx,
std::true_type) {
if (!onRequest_.dispatchTypedRequest(headerInfo, reqBody, std::move(ctx))) {
throw std::runtime_error("dispatchTypedRequestIfDefined got bad request");
}
}
void dispatchTypedRequestIfDefined(
const CaretMessageInfo&,
const folly::IOBuf& /* reqBody */,
McServerRequestContext&&,
std::false_type) {
throw std::runtime_error("dispatchTypedRequestIfDefined got bad request");
}
template <class Request>
void requestReadyImpl(
McServerRequestContext&& ctx,
Request&& req,
std::true_type) {
onRequest_.onRequest(std::move(ctx), std::move(req));
}
template <class Request>
void
requestReadyImpl(McServerRequestContext&& ctx, Request&&, std::false_type) {
McServerRequestContext::reply(
std::move(ctx), ReplyT<Request>(carbon::Result::LOCAL_ERROR));
}
protected:
OnRequest onRequest_;
};
template <class OnRequest, class Request, class... Requests>
class McServerOnRequestWrapper<OnRequest, List<Request, Requests...>>
: public McServerOnRequestWrapper<OnRequest, List<Requests...>> {
public:
using McServerOnRequestWrapper<OnRequest, List<Requests...>>::requestReady;
template <class... Args>
explicit McServerOnRequestWrapper(Args&&... args)
: McServerOnRequestWrapper<OnRequest, List<Requests...>>(
std::forward<Args>(args)...) {}
void requestReady(McServerRequestContext&& ctx, Request&& req) final {
this->requestReadyImpl(
std::move(ctx),
std::move(req),
carbon::detail::CanHandleRequest::value<Request, OnRequest>());
}
};
} // namespace memcache
} // namespace facebook
#include "McServerRequestContext-inl.h"