diff --git a/mcrouter/lib/mc/msg.h b/mcrouter/lib/mc/msg.h index 790276944..caf4d3535 100644 --- a/mcrouter/lib/mc/msg.h +++ b/mcrouter/lib/mc/msg.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -48,6 +49,7 @@ typedef enum mc_op_e { mc_op_touch, mc_op_gat, mc_op_gats, + mc_op_meta_commands_get, mc_nops // placeholder } mc_op_t; @@ -114,6 +116,8 @@ static inline const char* mc_op_to_string(const mc_op_t op) { return "gat"; case mc_op_gats: return "gats"; + case mc_op_meta_commands_get: + return "mg"; case mc_nops: return "unknown"; } @@ -402,6 +406,7 @@ static inline int mc_op_has_key(mc_op_t op) { case mc_op_gets: case mc_op_gat: case mc_op_gats: + case mc_op_meta_commands_get: return 1; default: @@ -440,3 +445,19 @@ const char* mc_req_err_to_string(const mc_req_err_t err); * @return mc_res_t code */ mc_res_t mc_res_from_string(const char* result); + +constexpr uint64_t MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY = 1; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN = 1ull << 1; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS = 1ull << 2; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_WAS_HIT = 1ull << 3; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_KEY = 1ull << 4; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_TIME_SINCE_LAST_ACCESS = 1ull << 5; +constexpr uint64_t MC_META_COMMANDS_FLAG_USE_NOREPLY_SEMANTICS = 1ull << 6; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_ITEM_SIZE = 1ull << 7; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_ITEM_TTL = 1ull << 8; +constexpr uint64_t MC_META_COMMANDS_FLAG_DO_NOT_BUMP_LRU = 1ull << 9; +constexpr uint64_t MC_META_COMMANDS_FLAG_RETURN_VALUE = 1ull << 10; +constexpr uint64_t MC_META_COMMANDS_FLAG_WON_RECACHE = 1ull << 11; +constexpr uint64_t MC_META_COMMANDS_FLAG_ITEM_IS_STALE = 1ull << 12; +constexpr uint64_t MC_META_COMMANDS_FLAG_LOST_RECACHE = 1ull << 13; +constexpr uint64_t MC_META_COMMANDS_FLAG_INVALIDATE_IF_OLDER_CAS = 1ull << 14; \ No newline at end of file diff --git a/mcrouter/lib/network/AsciiSerialized.cpp b/mcrouter/lib/network/AsciiSerialized.cpp index 416c91b25..5d3528730 100644 --- a/mcrouter/lib/network/AsciiSerialized.cpp +++ b/mcrouter/lib/network/AsciiSerialized.cpp @@ -9,6 +9,9 @@ #include "mcrouter/lib/IOBufUtil.h" #include "mcrouter/lib/McResUtil.h" +#include "mcrouter/lib/mc/msg.h" +#include +#include namespace facebook { namespace memcache { @@ -144,6 +147,81 @@ void AsciiSerializedRequest::prepareImpl(const McGatsRequest& request) { "\r\n"); } +void serializeBooleanMetaCommandFlags(char** pos, char** end, uint64_t flags) { + struct FlagBit { + uint64_t bit; + char ch; + }; + static constexpr FlagBit booleanFlags[] = { + {MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY, 'b'}, + {MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN, 'c'}, + {MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS, 'f'}, + {MC_META_COMMANDS_FLAG_RETURN_WAS_HIT, 'h'}, + {MC_META_COMMANDS_FLAG_RETURN_KEY, 'k'}, + {MC_META_COMMANDS_FLAG_RETURN_TIME_SINCE_LAST_ACCESS, 'l'}, + {MC_META_COMMANDS_FLAG_USE_NOREPLY_SEMANTICS, 'q'}, + {MC_META_COMMANDS_FLAG_RETURN_ITEM_SIZE, 's'}, + {MC_META_COMMANDS_FLAG_RETURN_ITEM_TTL, 't'}, + {MC_META_COMMANDS_FLAG_DO_NOT_BUMP_LRU, 'u'}, + {MC_META_COMMANDS_FLAG_RETURN_VALUE, 'v'}, + {MC_META_COMMANDS_FLAG_WON_RECACHE, 'W'}, + {MC_META_COMMANDS_FLAG_ITEM_IS_STALE, 'X'}, + {MC_META_COMMANDS_FLAG_LOST_RECACHE, 'Z'}, + {MC_META_COMMANDS_FLAG_INVALIDATE_IF_OLDER_CAS, 'I'}, + }; + + for (const auto& f : booleanFlags) { + if (flags & f.bit) { + auto n = snprintf(*pos, *end - *pos, " %c", f.ch); + *pos += n; + } + } +} + +// Meta commands get. +void AsciiSerializedRequest::prepareImpl( + const McMetaCommandsGetRequest& request) { + // Build flags string into printBuffer_ + char* pos = printBuffer_; + char* end = printBuffer_ + kMaxBufferLength; + uint64_t flags = *request.flags_ref(); + + serializeBooleanMetaCommandFlags(&pos, &end, flags); + + if (request.casToken_ref().has_value()) { + auto n = snprintf(pos, end - pos, " C%lu", *request.casToken_ref()); + pos += n; + } + if (request.newCasToken_ref().has_value()) { + auto n = snprintf(pos, end - pos, " E%lu", *request.newCasToken_ref()); + pos += n; + } + if (request.vivifyOnMissTTL_ref().has_value()) { + auto n = + snprintf(pos, end - pos, " N%d", *request.vivifyOnMissTTL_ref()); + pos += n; + } + if (request.refreshIfTTLLessThan_ref().has_value()) { + auto n = snprintf( + pos, end - pos, " R%d", *request.refreshIfTTLLessThan_ref()); + pos += n; + } + if (request.newTTL_ref().has_value()) { + auto n = snprintf(pos, end - pos, " T%d", *request.newTTL_ref()); + pos += n; + } + + assert(pos < end); + addStrings( + "mg ", + request.key_ref()->fullKey(), + folly::StringPiece(printBuffer_, pos)); + if (request.opaqueToken_ref().has_value()) { + addStrings(" O", *request.opaqueToken_ref()); + } + addString("\r\n"); +} + // Update-like ops. void AsciiSerializedRequest::prepareImpl(const McSetRequest& request) { keyValueRequestCommon("set ", request); @@ -269,6 +347,7 @@ void AsciiSerializedRequest::prepareImpl(const McFlushAllRequest& request) { void AsciiSerializedReply::clear() { iovsCount_ = 0; iobuf_.reset(); + iobuf2_.reset(); auxString_.reset(); } @@ -856,6 +935,83 @@ void AsciiSerializedReply::prepareImpl(McExecReply&& reply) { } } +// Meta commands get +void AsciiSerializedReply::prepareImpl( + McMetaCommandsGetReply&& reply, + folly::StringPiece /* key */) { + if (isHitResult(*reply.result_ref())) { + char* pos = printBuffer_; + char* end = printBuffer_ + kMaxBufferLength; + + folly::StringPiece valueStr; + if (reply.value_ref().has_value() && !reply.value_ref()->empty()) { + valueStr = coalesceAndGetRange(reply.value_ref()); + pos += snprintf( + pos, + end - pos, + " %zu", + valueStr.size()); + } + + serializeBooleanMetaCommandFlags(&pos, &end, *reply.flags_ref()); + + if (reply.casToken_ref().has_value()) { + pos += snprintf(pos, end - pos, " c%lu", *reply.casToken_ref()); + } + + if (reply.clientFlags_ref().has_value()) { + pos += snprintf(pos, end - pos, " f%lu", *reply.clientFlags_ref()); + } + + if (reply.itemSize_ref().has_value()) { + pos += snprintf(pos, end - pos, " s%lu", *reply.itemSize_ref()); + } + + if (reply.remainingTTL_ref().has_value()) { + pos += snprintf(pos, end - pos, " t%d", *reply.remainingTTL_ref()); + } + + if (reply.wasHitBefore_ref().has_value()) { + pos += snprintf(pos, end - pos, " h%d", *reply.wasHitBefore_ref()); + } + + if (reply.lastAccessTime_ref().has_value()) { + pos += snprintf(pos, end - pos, " l%d", *reply.lastAccessTime_ref()); + } + + assert(pos < end); + addStrings( + valueStr.empty() ? "HD" : "VA", + folly::StringPiece(printBuffer_, pos)); + + if (reply.opaqueToken_ref().has_value() && !reply.opaqueToken_ref()->empty()) { + auxString_ = std::move(*reply.opaqueToken_ref()); + addStrings(" O", *auxString_); + } + + if (reply.key_ref().has_value() && !reply.key_ref()->empty()) { + const auto keyStr = reply.key_ref()->fullKey(); + assert(!iobuf_.has_value()); + iobuf_ = std::move(reply.key_ref().value().raw()); + addStrings(" k", keyStr); + } + + addString("\r\n"); + + if (!valueStr.empty()) { + assert(!iobuf2_.has_value()); + iobuf2_ = std::move(reply.value_ref().value()); + addStrings(valueStr, "\r\n"); + } + } else if (isMissResult(*reply.result_ref())) { + addString("EN\r\n"); + } else if (isErrorResult(*reply.result_ref())) { + handleError(*reply.result_ref(), 0, std::string()); + } else { + handleUnexpected(*reply.result_ref(), "mg"); + } +} + // Shutdown void AsciiSerializedReply::prepareImpl(McShutdownReply&& reply) { if (*reply.result_ref() == carbon::Result::OK) { diff --git a/mcrouter/lib/network/AsciiSerialized.h b/mcrouter/lib/network/AsciiSerialized.h index bce5f655d..1e7cac691 100644 --- a/mcrouter/lib/network/AsciiSerialized.h +++ b/mcrouter/lib/network/AsciiSerialized.h @@ -52,9 +52,9 @@ class AsciiSerializedRequest { // We need at most 5 iovecs (lease-set): // command + key + printBuffer + value + "\r\n" static constexpr size_t kMaxIovs = 8; - // The longest print buffer we need is for lease-set/cas operations. - // It requires 2 uint64, 2 uint32 + 4 spaces + "\r\n" + '\0' = 67 chars. - static constexpr size_t kMaxBufferLength = 80; + // The longest print buffer we need is for OSS mg. + // It requires 2 uint64, 3 uint32, 11 char + 16 spaces + "\r\n" + '\0' = 105 chars. + static constexpr size_t kMaxBufferLength = 105; struct iovec iovs_[kMaxIovs]; size_t iovsCount_{0}; @@ -98,6 +98,8 @@ class AsciiSerializedRequest { void prepareImpl(const McVersionRequest& request); // FlushAll op. void prepareImpl(const McFlushAllRequest& request); + // Meta commands. + void prepareImpl(const McMetaCommandsGetRequest& request); // Everything else is false. template @@ -160,18 +162,19 @@ class AsciiSerializedReply { private: // See comment in prepareImpl for McMetagetReply for explanation - static constexpr size_t kMaxBufferLength = 100; + static constexpr size_t kMaxBufferLength = 113; static const size_t kMaxIovs = 16; struct iovec iovs_[kMaxIovs]; size_t iovsCount_{0}; char printBuffer_[kMaxBufferLength]; - // Used to keep alive the reply's IOBuf field (value, stats, etc.). For now, - // replies have at most one IOBuf, so we only need one here. Note that one of - // the iovs_ will point into the data managed by this IOBuf. A serialized - // reply should not set iobuf_ more than once. + // Used to keep alive the reply's IOBuf fields (value, stats, etc.). For now, + // replies have at most two IOBufs. Note that one of + // the iovs_ will point into the data managed by these IOBufs. A serialized + // reply should not set a given iobuf_ more than once. // We also keep an auxiliary string for a similar purpose. folly::Optional iobuf_; + folly::Optional iobuf2_; folly::Optional auxString_; void addString(folly::ByteRange range); @@ -225,6 +228,8 @@ class AsciiSerializedReply { void prepareImpl(McExecReply&&); void prepareImpl(McFlushReReply&&); void prepareImpl(McFlushAllReply&&); + // Meta commands + void prepareImpl(McMetaCommandsGetReply&& reply, folly::StringPiece key); // Server and client error helper void handleError(carbon::Result result, uint16_t errorCode, std::string&& message); diff --git a/mcrouter/lib/network/CarbonMessageList.h b/mcrouter/lib/network/CarbonMessageList.h index 15d9ff037..469f3ad1a 100644 --- a/mcrouter/lib/network/CarbonMessageList.h +++ b/mcrouter/lib/network/CarbonMessageList.h @@ -39,7 +39,8 @@ using RequestReplyPairs = List< Pair, Pair, Pair, - Pair>; + Pair, + Pair>; using McRequestList = PairListFirstT; @@ -73,7 +74,8 @@ using RequestOpMapping = List< KV, KV, KV, - KV>; + KV, + KV>; using ReplyOpMapping = List< KV, @@ -99,7 +101,8 @@ using ReplyOpMapping = List< KV, KV, KV, - KV>; + KV, + KV>; /** * Given a Request Type T and a Mapping of mc_op_t to Request Type, diff --git a/mcrouter/lib/network/McAsciiParser-inl.h b/mcrouter/lib/network/McAsciiParser-inl.h index 7f2a2cc71..8c456afda 100644 --- a/mcrouter/lib/network/McAsciiParser-inl.h +++ b/mcrouter/lib/network/McAsciiParser-inl.h @@ -134,6 +134,9 @@ void McClientAsciiParser::initializeReplyParser(); template <> void McClientAsciiParser::initializeReplyParser(); +template <> +void McClientAsciiParser::initializeReplyParser(); + template void McClientAsciiParser::initializeReplyParser() { throwLogic( diff --git a/mcrouter/lib/network/McAsciiParser.h b/mcrouter/lib/network/McAsciiParser.h index f421768f3..51a4695ea 100644 --- a/mcrouter/lib/network/McAsciiParser.h +++ b/mcrouter/lib/network/McAsciiParser.h @@ -94,6 +94,9 @@ class McAsciiParserBase { State state_{State::UNINIT}; bool negative_{false}; + const char* keyPieceStart_{nullptr}; + folly::IOBuf currentKey_; + // Variables used by ragel. int savedCs_; int errorCs_; @@ -232,6 +235,9 @@ class McServerAsciiParser : public McAsciiParserBase { void consumeStats(folly::IOBuf& buffer); void consumeExec(folly::IOBuf& buffer); + // Meta commands. + void consumeMetaCommandsGet(folly::IOBuf& buffer); + // Flush. void consumeFlushRe(folly::IOBuf& buffer); void consumeFlushAll(folly::IOBuf& buffer); @@ -240,8 +246,6 @@ class McServerAsciiParser : public McAsciiParserBase { std::unique_ptr> callback_; - const char* keyPieceStart_{nullptr}; - folly::IOBuf currentKey_; bool noreply_{false}; using RequestVariant = carbon::makeVariantFromList; diff --git a/mcrouter/lib/network/McAsciiParser.rl b/mcrouter/lib/network/McAsciiParser.rl index 35e76acf4..27abc2a08 100644 --- a/mcrouter/lib/network/McAsciiParser.rl +++ b/mcrouter/lib/network/McAsciiParser.rl @@ -473,6 +473,73 @@ void McClientAsciiParser::consumeMessage( }%% } +// McMetaCommandsGet reply. +%%{ +machine mc_ascii_meta_commands_get_reply; +include mc_ascii_common; + +# Response flag tokens (no value) +mg_resp_flag_b = 'b' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY; }; +mg_resp_flag_W = 'W' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_WON_RECACHE; }; +mg_resp_flag_X = 'X' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_ITEM_IS_STALE; }; +mg_resp_flag_Z = 'Z' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_LOST_RECACHE; }; + +# Response flag tokens (with value) +mg_resp_flag_c = 'c' uint %{ message.casToken_ref() = currentUInt_; }; +mg_resp_flag_f = 'f' uint %{ message.clientFlags_ref() = currentUInt_; }; +mg_resp_flag_s = 's' uint %{ message.itemSize_ref() = currentUInt_; }; +mg_resp_flag_l = 'l' uint %{ message.lastAccessTime_ref() = static_cast(currentUInt_); }; +mg_resp_flag_h = 'h' uint %{ message.wasHitBefore_ref() = static_cast(currentUInt_); }; +mg_resp_flag_t = 't' negative? uint %{ + auto value = static_cast(currentUInt_); + message.remainingTTL_ref() = negative_ ? -value : value; + negative_ = false; +}; +mg_resp_flag_k = 'k' key; +mg_resp_flag_O = 'O' (any {1,32} -- (cntrl | space)) >key_start %key_end %{ + currentKey_.coalesce(); + message.opaqueToken_ref() = std::string( + reinterpret_cast(currentKey_.data()), currentKey_.length()); +}; +# Skip unknown response flags +mg_resp_unknown = (any - cntrl - space - [bcWXZ]) (any - cntrl - space)*; + +mg_resp_flag = mg_resp_flag_b | mg_resp_flag_W | mg_resp_flag_X | + mg_resp_flag_Z | mg_resp_flag_c | mg_resp_flag_O | + mg_resp_flag_f | mg_resp_flag_s | mg_resp_flag_l | + mg_resp_flag_h | mg_resp_flag_t | mg_resp_flag_k | + mg_resp_unknown; + +mg_resp_flags = (' '+ mg_resp_flag)*; + +# VA *\r\n\r\n +va_hit = 'VA' %{ message.result_ref() = carbon::Result::FOUND; } + ' '+ value_bytes mg_resp_flags new_line @reply_value_data; + +# HD *\r\n +hd_hit = 'HD' %{ message.result_ref() = carbon::Result::FOUND; } + mg_resp_flags; + +# EN\r\n +en_miss = 'EN' %{ message.result_ref() = carbon::Result::NOTFOUND; }; + +mg_reply = va_hit | hd_hit | en_miss; +mg_reply_entry := (mg_reply | error) msg_end; + +write data; +}%% + +template <> +void McClientAsciiParser::consumeMessage( + folly::IOBuf& buffer) { + auto& message = currentMessage_.get(); + %%{ + machine mc_ascii_meta_commands_get_reply; + write init nocs; + write exec; + }%% +} + // McFlushAll reply. %%{ machine mc_ascii_flushall_reply; @@ -612,6 +679,14 @@ void McClientAsciiParser::initializeReplyParser() { consumer_ = &McClientAsciiParser::consumeMessage; } +template <> +void McClientAsciiParser::initializeReplyParser() { + initializeCommon(); + savedCs_ = mc_ascii_meta_commands_get_reply_en_mg_reply_entry; + errorCs_ = mc_ascii_meta_commands_get_reply_error; + consumer_ = &McClientAsciiParser::consumeMessage; +} + template <> void McClientAsciiParser::initializeReplyParser() { initializeCommon(); @@ -646,6 +721,8 @@ void McClientAsciiParser::initializeCommon() { remainingIOBufLength_ = 0; state_ = State::PARTIAL; + currentKey_.clear(); + currentMessage_.emplace(); } @@ -1016,6 +1093,69 @@ void McServerAsciiParser::consumeFlushAll(folly::IOBuf&) { }%% } +// Meta commands get (mg) request. + +%%{ +machine mc_ascii_mg_req_body; +include mc_ascii_common; + +# Request flag tokens (no value) - set bitfield +mg_req_flag_b = 'b' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY; }; +mg_req_flag_c = 'c' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN; }; +mg_req_flag_f = 'f' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS; }; +mg_req_flag_h = 'h' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_WAS_HIT; }; +mg_req_flag_k = 'k' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_KEY; }; +mg_req_flag_l = 'l' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_TIME_SINCE_LAST_ACCESS; }; +mg_req_flag_q = 'q' %{ + message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_USE_NOREPLY_SEMANTICS; + noreply_ = true; +}; +mg_req_flag_s = 's' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_ITEM_SIZE; }; +mg_req_flag_t = 't' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_ITEM_TTL; }; +mg_req_flag_u = 'u' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_DO_NOT_BUMP_LRU; }; +mg_req_flag_v = 'v' %{ message.flags_ref() = *message.flags_ref() | MC_META_COMMANDS_FLAG_RETURN_VALUE; }; + +# Request flag tokens (with value) +mg_req_flag_C = 'C' uint %{ message.casToken_ref() = currentUInt_; }; +mg_req_flag_E = 'E' uint %{ message.newCasToken_ref() = currentUInt_; }; +mg_req_flag_N = 'N' uint %{ message.vivifyOnMissTTL_ref() = static_cast(currentUInt_); }; +mg_req_flag_R = 'R' uint %{ message.refreshIfTTLLessThan_ref() = static_cast(currentUInt_); }; +mg_req_flag_T = 'T' uint %{ message.newTTL_ref() = static_cast(currentUInt_); }; +mg_req_flag_O = 'O' (any {1,32} -- (cntrl | space)) >key_start %key_end %{ + currentKey_.coalesce(); + message.opaqueToken_ref() = std::string( + reinterpret_cast(currentKey_.data()), currentKey_.length()); +}; + +# Skip unknown flags +mg_req_unknown = (any - cntrl - space - [bcfhklqstuvCNORT]) (any - cntrl - space)*; + +mg_req_flag = mg_req_flag_b | mg_req_flag_c | mg_req_flag_f | + mg_req_flag_h | mg_req_flag_k | mg_req_flag_l | + mg_req_flag_q | mg_req_flag_s | mg_req_flag_t | + mg_req_flag_u | mg_req_flag_v | + mg_req_flag_C | mg_req_flag_E |mg_req_flag_N | mg_req_flag_R | + mg_req_flag_T | mg_req_flag_O | + mg_req_unknown; + +req_body := ' '* key (' '+ mg_req_flag)* ' '* new_line @{ + callback_->onRequest(std::move(message), noreply_); + finishReq(); + fbreak; + }; + +write data; +}%% + +void McServerAsciiParser::consumeMetaCommandsGet(folly::IOBuf& buffer) { + auto& message = currentMessage_.get(); + %%{ + machine mc_ascii_mg_req_body; + write init nocs; + write exec; + }%% +} + // Operation keyword parser. %%{ @@ -1048,6 +1188,15 @@ metaget = 'metaget ' @{ fbreak; }; +mg = 'mg ' @{ + savedCs_ = mc_ascii_mg_req_body_en_req_body; + errorCs_ = mc_ascii_mg_req_body_error; + state_ = State::PARTIAL; + currentMessage_.emplace(); + consumer_ = &McServerAsciiParser::consumeMetaCommandsGet; + fbreak; +}; + gat = 'gat ' @{ initGatLike(); fbreak; @@ -1187,7 +1336,7 @@ flush_all = 'flush_all' @{ fbreak; }; -command := get | gets | lease_get | metaget | set | add | replace | append | +command := get | gets | lease_get | metaget | mg | set | add | replace | append | prepend | cas | lease_set | delete | shutdown | incr | decr | version | quit | stats | exec | flush_re | flush_all | touch | gat | gats; diff --git a/mcrouter/lib/network/McServerRequestContext-inl.h b/mcrouter/lib/network/McServerRequestContext-inl.h index 46f5fe1a9..0e5f8bdc3 100644 --- a/mcrouter/lib/network/McServerRequestContext-inl.h +++ b/mcrouter/lib/network/McServerRequestContext-inl.h @@ -46,7 +46,7 @@ void McServerRequestContext::replyImpl( RequestFromReplyType, void>) { if constexpr (carbon::GetLike< - RequestFromReplyType>::value) { + RequestFromReplyType>::value && !std::is_same_v, McMetaCommandsGetRequest>) { // On error, multi-get parent may assume responsiblity of replying if (ctx.moveReplyToParent( *reply.result_ref(), diff --git a/mcrouter/lib/network/McServerSession-inl.h b/mcrouter/lib/network/McServerSession-inl.h index 9cfaf88b9..72b44dd72 100644 --- a/mcrouter/lib/network/McServerSession-inl.h +++ b/mcrouter/lib/network/McServerSession-inl.h @@ -32,9 +32,12 @@ void McServerSession::asciiRequestReady( return; } - if (carbon::GetLike::value && !currentMultiop_) { - currentMultiop_ = std::make_shared(*this, tailReqid_++); + if constexpr (carbon::GetLike::value && !std::is_same_v) { + if (!currentMultiop_) { + currentMultiop_ = std::make_shared(*this, tailReqid_++); + } } + uint64_t reqid; reqid = tailReqid_++; diff --git a/mcrouter/lib/network/gen/Memcache.thrift b/mcrouter/lib/network/gen/Memcache.thrift index 6466f2d86..5d4a7de96 100644 --- a/mcrouter/lib/network/gen/Memcache.thrift +++ b/mcrouter/lib/network/gen/Memcache.thrift @@ -519,5 +519,45 @@ struct McGatsReply { 6: i16 appSpecificErrorCode } +@thrift.DeprecatedUnvalidatedAnnotations{ + items = { + "cpp.virtual": "1" + }, +} +struct McMetaCommandsGetRequest { + @thrift.Mixin + -1: MemcacheRequestCommon memcacheRequestCommon + 1: carbon.IOBufKey key + 2: carbon.ui64 flags + 3: optional carbon.ui64 casToken + 4: optional string opaqueToken + 5: optional i32 vivifyOnMissTTL + 6: optional i32 refreshIfTTLLessThan + 7: optional i32 newTTL + 8: optional carbon.ui64 newCasToken +} +@thrift.DeprecatedUnvalidatedAnnotations{ + items = { + "cpp.virtual": "1" + }, +} +struct McMetaCommandsGetResponse { + @thrift.Mixin + -1: MemcacheReplyCommon memcacheReplyCommon + 1: carbon_result.Result result + 2: optional carbon.ui64 casToken + 3: optional carbon.IOBuf value + 4: carbon.ui64 flags + 5: optional carbon.IOBufKey key + 6: optional string opaqueToken + 7: optional carbon.ui64 clientFlags + 8: optional i32 remainingTTL + 9: optional carbon.ui64 itemSize + 10: optional i32 lastAccessTime + 11: optional i32 wasHitBefore + 12: string message + 13: i16 appSpecificErrorCode +} + @cpp.Type{name = "std::unordered_map"} -typedef map string_to_carbon_ui64_std_unordered_map \ No newline at end of file +typedef map string_to_carbon_ui64_std_unordered_map diff --git a/mcrouter/lib/network/gen/MemcacheMessages-inl.h b/mcrouter/lib/network/gen/MemcacheMessages-inl.h index e9a885b82..17ad2dfd7 100644 --- a/mcrouter/lib/network/gen/MemcacheMessages-inl.h +++ b/mcrouter/lib/network/gen/MemcacheMessages-inl.h @@ -2146,6 +2146,210 @@ void visitFields(const McGatsReply& self, V&& v) { return; } } + +template +void serialize(const McMetaCommandsGetRequest& self, Writer&& writer) { + writer.writeStructBegin(); + writer.writeField(-1 /* field id */, self.memcacheRequestCommon_ref()); + writer.writeField(1 /* field id */, self.key_ref()); + writer.writeField(2 /* field id */, self.flags_ref()); + writer.writeField(3 /* field id */, self.casToken_ref()); + writer.writeField(4 /* field id */, self.opaqueToken_ref()); + writer.writeField(5 /* field id */, self.vivifyOnMissTTL_ref()); + writer.writeField(6 /* field id */, self.refreshIfTTLLessThan_ref()); + writer.writeField(7 /* field id */, self.newTTL_ref()); + writer.writeField(8 /* field id */, self.newCasToken_ref()); + writer.writeFieldStop(); + writer.writeStructEnd(); +} + +template +void visitFields(McMetaCommandsGetRequest& self, V&& v) { + if (v.enterMixin(1, "MemcacheRequestCommon", *self.memcacheRequestCommon_ref())) { + visitFields(*self.memcacheRequestCommon_ref(), std::forward(v)); + } + if (!v.leaveMixin()) { + return; + } + if (!v.visitField(1, "key", *self.key_ref())) { + return; + } + if (!v.visitField(2, "flags", *self.flags_ref())) { + return; + } + if (!v.visitField(3, "casToken", *self.casToken_ref())) { + return; + } + if (!v.visitField(4, "opaqueToken", *self.opaqueToken_ref())) { + return; + } + if (!v.visitField(5, "vivifyOnMissTTL", *self.vivifyOnMissTTL_ref())) { + return; + } + if (!v.visitField(6, "refreshIfTTLLessThan", *self.refreshIfTTLLessThan_ref())) { + return; + } + if (!v.visitField(7, "newTTL", *self.newTTL_ref())) { + return; + } + if (!v.visitField(8, "newCasToken", *self.newCasToken_ref())) { + return; + } +} + +template +void visitFields(const McMetaCommandsGetRequest& self, V&& v) { + if (v.enterMixin(1, "MemcacheRequestCommon", *self.memcacheRequestCommon_ref())) { + visitFields(*self.memcacheRequestCommon_ref(), std::forward(v)); + } + if (!v.leaveMixin()) { + return; + } + if (!v.visitField(1, "key", *self.key_ref())) { + return; + } + if (!v.visitField(2, "flags", *self.flags_ref())) { + return; + } + if (!v.visitField(3, "casToken", *self.casToken_ref())) { + return; + } + if (!v.visitField(4, "opaqueToken", *self.opaqueToken_ref())) { + return; + } + if (!v.visitField(5, "vivifyOnMissTTL", *self.vivifyOnMissTTL_ref())) { + return; + } + if (!v.visitField(6, "refreshIfTTLLessThan", *self.refreshIfTTLLessThan_ref())) { + return; + } + if (!v.visitField(7, "newTTL", *self.newTTL_ref())) { + return; + } + if (!v.visitField(8, "newCasToken", *self.newCasToken_ref())) { + return; + } +} + +template +void serialize(const McMetaCommandsGetResponse& self, Writer&& writer) { + writer.writeStructBegin(); + writer.writeField(-1 /* field id */, self.memcacheReplyCommon_ref()); + writer.writeField(1 /* field id */, self.result_ref()); + writer.writeField(2 /* field id */, self.casToken_ref()); + writer.writeField(3 /* field id */, self.value_ref()); + writer.writeField(4 /* field id */, self.flags_ref()); + writer.writeField(5 /* field id */, self.key_ref()); + writer.writeField(6 /* field id */, self.opaqueToken_ref()); + writer.writeField(7 /* field id */, self.clientFlags_ref()); + writer.writeField(8 /* field id */, self.remainingTTL_ref()); + writer.writeField(9 /* field id */, self.itemSize_ref()); + writer.writeField(10 /* field id */, self.lastAccessTime_ref()); + writer.writeField(11 /* field id */, self.wasHitBefore_ref()); + writer.writeField(12 /* field id */, self.message_ref()); + writer.writeField(13 /* field id */, self.appSpecificErrorCode_ref()); + writer.writeFieldStop(); + writer.writeStructEnd(); +} + +template +void visitFields(McMetaCommandsGetResponse& self, V&& v) { + if (v.enterMixin(1, "MemcacheReplyCommon", *self.memcacheReplyCommon_ref())) { + visitFields(*self.memcacheReplyCommon_ref(), std::forward(v)); + } + if (!v.leaveMixin()) { + return; + } + if (!v.visitField(1, "result", *self.result_ref())) { + return; + } + if (!v.visitField(2, "casToken", *self.casToken_ref())) { + return; + } + if (!v.visitField(3, "value", self.value_ref())) { + return; + } + if (!v.visitField(4, "flags", *self.flags_ref())) { + return; + } + if (!v.visitField(5, "key", *self.key_ref())) { + return; + } + if (!v.visitField(6, "opaqueToken", *self.opaqueToken_ref())) { + return; + } + if (!v.visitField(7, "clientFlags", *self.clientFlags_ref())) { + return; + } + if (!v.visitField(8, "remainingTTL", *self.remainingTTL_ref())) { + return; + } + if (!v.visitField(9, "itemSize", *self.itemSize_ref())) { + return; + } + if (!v.visitField(10, "lastAccessTime", *self.lastAccessTime_ref())) { + return; + } + if (!v.visitField(11, "wasHitBefore", *self.wasHitBefore_ref())) { + return; + } + if (!v.visitField(12, "message", *self.message_ref())) { + return; + } + if (!v.visitField(13, "appSpecificErrorCode", *self.appSpecificErrorCode_ref())) { + return; + } +} + +template +void visitFields(const McMetaCommandsGetResponse& self, V&& v) { + if (v.enterMixin(1, "MemcacheReplyCommon", *self.memcacheReplyCommon_ref())) { + visitFields(*self.memcacheReplyCommon_ref(), std::forward(v)); + } + if (!v.leaveMixin()) { + return; + } + if (!v.visitField(1, "result", *self.result_ref())) { + return; + } + if (!v.visitField(2, "casToken", *self.casToken_ref())) { + return; + } + if (!v.visitField(3, "value", self.value_ref())) { + return; + } + if (!v.visitField(4, "flags", *self.flags_ref())) { + return; + } + if (!v.visitField(5, "key", *self.key_ref())) { + return; + } + if (!v.visitField(6, "opaqueToken", *self.opaqueToken_ref())) { + return; + } + if (!v.visitField(7, "clientFlags", *self.clientFlags_ref())) { + return; + } + if (!v.visitField(8, "remainingTTL", *self.remainingTTL_ref())) { + return; + } + if (!v.visitField(9, "itemSize", *self.itemSize_ref())) { + return; + } + if (!v.visitField(10, "lastAccessTime", *self.lastAccessTime_ref())) { + return; + } + if (!v.visitField(11, "wasHitBefore", *self.wasHitBefore_ref())) { + return; + } + if (!v.visitField(12, "message", *self.message_ref())) { + return; + } + if (!v.visitField(13, "appSpecificErrorCode", *self.appSpecificErrorCode_ref())) { + return; + } +} + } // namespace thrift } // namespace memcache } // namespace facebook @@ -3151,5 +3355,55 @@ class Cpp2Ops { return value->serializedSizeZC(prot); } }; + +template <> +class Cpp2Ops { + public: + typedef facebook::memcache::McMetaCommandsGetRequest Type; + static constexpr protocol::TType thriftType() { + return protocol::T_STRUCT; + } + template + static uint32_t write(Protocol* prot, const Type* value) { + return value->write(prot); + } + template + static void read(Protocol* prot, Type* value) { + value->read(prot); + } + template + static uint32_t serializedSize(Protocol* prot, const Type* value) { + return value->serializedSize(prot); + } + template + static uint32_t serializedSizeZC(Protocol* prot, const Type* value) { + return value->serializedSizeZC(prot); + } +}; + +template <> +class Cpp2Ops { + public: + typedef facebook::memcache::McMetaCommandsGetReply Type; + static constexpr protocol::TType thriftType() { + return protocol::T_STRUCT; + } + template + static uint32_t write(Protocol* prot, const Type* value) { + return value->write(prot); + } + template + static void read(Protocol* prot, Type* value) { + value->read(prot); + } + template + static uint32_t serializedSize(Protocol* prot, const Type* value) { + return value->serializedSize(prot); + } + template + static uint32_t serializedSizeZC(Protocol* prot, const Type* value) { + return value->serializedSizeZC(prot); + } +}; } // namespace thrift } // namespace apache diff --git a/mcrouter/lib/network/gen/MemcacheMessages.h b/mcrouter/lib/network/gen/MemcacheMessages.h index c63a560e4..4a48546d3 100644 --- a/mcrouter/lib/network/gen/MemcacheMessages.h +++ b/mcrouter/lib/network/gen/MemcacheMessages.h @@ -877,6 +877,50 @@ class McGatsReply : public carbon::ReplyCommon, public facebook::memcache::thrif }; +class McMetaCommandsGetReply; + +class McMetaCommandsGetRequest : public carbon::RequestCommon, public facebook::memcache::thrift::McMetaCommandsGetRequest { + public: + using reply_type = McMetaCommandsGetReply; + + static constexpr size_t typeId = 49; + static constexpr const char* name = "mg"; + static constexpr const char* idlName = "Memcache"; + + McMetaCommandsGetRequest () = default; + McMetaCommandsGetRequest (const McMetaCommandsGetRequest &) = default; + McMetaCommandsGetRequest & operator=(const McMetaCommandsGetRequest &) = default; + McMetaCommandsGetRequest (McMetaCommandsGetRequest &&) = default; + McMetaCommandsGetRequest & operator=(McMetaCommandsGetRequest &&) = default; + explicit McMetaCommandsGetRequest (folly::StringPiece sp) { + key_ref() = sp; + } + explicit McMetaCommandsGetRequest (folly::IOBuf&& carbonKey) { + key_ref() = std::move(carbonKey); + } + + friend class apache::thrift::Cpp2Ops; + +}; + +class McMetaCommandsGetReply : public carbon::ReplyCommon, public facebook::memcache::thrift::McMetaCommandsGetResponse { + public: + + static constexpr size_t typeId = 50; + + McMetaCommandsGetReply() = default; + McMetaCommandsGetReply(const McMetaCommandsGetReply&) = default; + McMetaCommandsGetReply& operator=(const McMetaCommandsGetReply&) = default; + McMetaCommandsGetReply(McMetaCommandsGetReply&&) = default; + McMetaCommandsGetReply& operator=(McMetaCommandsGetReply&&) = default; + explicit McMetaCommandsGetReply(carbon::Result carbonResult) { + result_ref() = carbonResult; + } + + friend class apache::thrift::Cpp2Ops; + +}; + } // namespace memcache } // namespace facebook @@ -1000,6 +1044,12 @@ struct IsCarbonStruct<::facebook::memcache::McGatsRequest> : std::true_type {}; template <> struct IsCarbonStruct<::facebook::memcache::McGatsReply> : std::true_type {}; + +template <> +struct IsCarbonStruct<::facebook::memcache::McMetaCommandsGetRequest> : std::true_type {}; + +template <> +struct IsCarbonStruct<::facebook::memcache::McMetaCommandsGetReply> : std::true_type {}; } // namespace carbon namespace facebook { @@ -1445,6 +1495,28 @@ void visitFields(McGatsReply& self, V&& v); template void visitFields(const McGatsReply& self, V&& v); + +template +void serialize(const McMetaCommandsGetRequest& self, Writer&& writer); + +void deserialize(McMetaCommandsGetRequest& self, carbon::CarbonProtocolReader& reader); + +template +void visitFields(McMetaCommandsGetRequest& self, V&& v); + +template +void visitFields(const McMetaCommandsGetRequest& self, V&& v); + +template +void serialize(const McMetaCommandsGetResponse& self, Writer&& writer); + +void deserialize(McMetaCommandsGetResponse& self, carbon::CarbonProtocolReader& reader); + +template +void visitFields(McMetaCommandsGetResponse& self, V&& v); + +template +void visitFields(const McMetaCommandsGetResponse& self, V&& v); } // namespace thrift } // namespace memcache } // namespace facebook diff --git a/mcrouter/lib/network/gen/MemcacheMessagesThrift.cpp b/mcrouter/lib/network/gen/MemcacheMessagesThrift.cpp index 6357d66cd..69abac58e 100644 --- a/mcrouter/lib/network/gen/MemcacheMessagesThrift.cpp +++ b/mcrouter/lib/network/gen/MemcacheMessagesThrift.cpp @@ -1603,6 +1603,141 @@ void deserialize(McGatsReply& self, carbon::CarbonProtocolReader& reader) { } reader.readStructEnd(); } + +void deserialize(McMetaCommandsGetRequest& self, carbon::CarbonProtocolReader& reader) { + reader.readStructBegin(); + while (true) { + const auto pr = reader.readFieldHeader(); + const auto fieldType = pr.first; + const auto fieldId = pr.second; + + if (fieldType == carbon::FieldType::Stop) { + break; + } + + switch (fieldId) { + case -1: { + reader.readField(*self.memcacheRequestCommon_ref(), fieldType); + break; + } + case 1: { + reader.readField(self.key_ref(), fieldType); + break; + } + case 2: { + reader.readField(self.flags_ref(), fieldType); + break; + } + case 3: { + reader.readField(self.casToken_ref(), fieldType); + break; + } + case 4: { + reader.readField(self.opaqueToken_ref(), fieldType); + break; + } + case 5: { + reader.readField(self.vivifyOnMissTTL_ref(), fieldType); + break; + } + case 6: { + reader.readField(self.refreshIfTTLLessThan_ref(), fieldType); + break; + } + case 7: { + reader.readField(self.newTTL_ref(), fieldType); + break; + } + case 8: { + reader.readField(self.newCasToken_ref(), fieldType); + break; + } + default: { + reader.skip(fieldType); + break; + } + } + } + reader.readStructEnd(); +} + +void deserialize(McMetaCommandsGetResponse& self, carbon::CarbonProtocolReader& reader) { + reader.readStructBegin(); + while (true) { + const auto pr = reader.readFieldHeader(); + const auto fieldType = pr.first; + const auto fieldId = pr.second; + + if (fieldType == carbon::FieldType::Stop) { + break; + } + + switch (fieldId) { + case -1: { + reader.readField(*self.memcacheReplyCommon_ref(), fieldType); + break; + } + case 1: { + reader.readField(self.result_ref(), fieldType); + break; + } + case 2: { + reader.readField(self.casToken_ref(), fieldType); + break; + } + case 3: { + reader.readField(self.value_ref(), fieldType); + break; + } + case 4: { + reader.readField(self.flags_ref(), fieldType); + break; + } + case 5: { + reader.readField(self.key_ref(), fieldType); + break; + } + case 6: { + reader.readField(self.opaqueToken_ref(), fieldType); + break; + } + case 7: { + reader.readField(self.clientFlags_ref(), fieldType); + break; + } + case 8: { + reader.readField(self.remainingTTL_ref(), fieldType); + break; + } + case 9: { + reader.readField(self.itemSize_ref(), fieldType); + break; + } + case 10: { + reader.readField(self.lastAccessTime_ref(), fieldType); + break; + } + case 11: { + reader.readField(self.wasHitBefore_ref(), fieldType); + break; + } + case 12: { + reader.readField(self.message_ref(), fieldType); + break; + } + case 13: { + reader.readField(self.appSpecificErrorCode_ref(), fieldType); + break; + } + default: { + reader.skip(fieldType); + break; + } + } + } + reader.readStructEnd(); +} + } // namespace thrift } // namespace memcache } // namespace facebook diff --git a/mcrouter/lib/network/gen/MemcacheRouteHandleIf.h b/mcrouter/lib/network/gen/MemcacheRouteHandleIf.h index 08d0de004..27e4b6a40 100644 --- a/mcrouter/lib/network/gen/MemcacheRouteHandleIf.h +++ b/mcrouter/lib/network/gen/MemcacheRouteHandleIf.h @@ -58,6 +58,7 @@ class MemcacheRouteHandleIf { virtual McReplaceReply route(const McReplaceRequest&) = 0; virtual McSetReply route(const McSetRequest&) = 0; virtual McTouchReply route(const McTouchRequest&) = 0; + virtual McMetaCommandsGetReply route(const McMetaCommandsGetRequest&) = 0; virtual bool traverse( const McAddRequest&, @@ -116,6 +117,9 @@ virtual bool traverse( virtual bool traverse( const McTouchRequest&, RouteHandleTraverser&) const = 0; +virtual bool traverse( + const McMetaCommandsGetRequest&, + RouteHandleTraverser&) const = 0; }; template @@ -186,6 +190,10 @@ class MemcacheRouteHandle : public MemcacheRouteHandleIf { return route_.route(request); } + McMetaCommandsGetReply route(const McMetaCommandsGetRequest& request) override final { + return route_.route(request); + } + bool traverse( const McAddRequest& request, RouteHandleTraverser& traverser) @@ -300,6 +308,12 @@ bool traverse( const override final { return route_.traverse(request, traverser); } +bool traverse( + const McMetaCommandsGetRequest& request, + RouteHandleTraverser& traverser) + const override final { + return route_.traverse(request, traverser); +} private: Route route_; diff --git a/mcrouter/lib/network/gen/MemcacheRouterInfo.h b/mcrouter/lib/network/gen/MemcacheRouterInfo.h index 9ca946a09..3de175579 100644 --- a/mcrouter/lib/network/gen/MemcacheRouterInfo.h +++ b/mcrouter/lib/network/gen/MemcacheRouterInfo.h @@ -24,6 +24,7 @@ #include +#include "mcrouter/lib/network/gen/MemcacheMessages.h" #include "mcrouter/lib/network/gen/MemcacheRouteHandleIf.h" #include "mcrouter/lib/network/gen/MemcacheRouterStats.h" #include "mcrouter/lib/network/gen/MemcacheRoutingGroups.h" diff --git a/mcrouter/lib/network/gen/MemcacheRouterInfoFwd.h b/mcrouter/lib/network/gen/MemcacheRouterInfoFwd.h index 3f4eccff0..fd5b64863 100644 --- a/mcrouter/lib/network/gen/MemcacheRouterInfoFwd.h +++ b/mcrouter/lib/network/gen/MemcacheRouterInfoFwd.h @@ -14,6 +14,7 @@ */ #pragma once +#include "mcrouter/lib/network/gen/MemcacheMessages.h" #include namespace facebook::memcache { @@ -65,6 +66,8 @@ class McTouchReply; class McTouchRequest; class McVersionReply; class McVersionRequest; +class McMetaCommandsGetReply; +class McMetaCommandsGetRequest; } // namespace facebook::memcache namespace facebook { @@ -91,7 +94,8 @@ using MemcacheRoutableRequests = carbon::List< McPrependRequest, McReplaceRequest, McSetRequest, - McTouchRequest>; + McTouchRequest, + McMetaCommandsGetRequest>; using MemcacheRoutableReplies = carbon::List< McAddReply, McAppendReply, @@ -111,7 +115,8 @@ using MemcacheRoutableReplies = carbon::List< McPrependReply, McReplaceReply, McSetReply, - McTouchReply>; + McTouchReply, + McMetaCommandsGetReply>; using MemcacheAllRequests = carbon::List< McAddRequest, McAppendRequest, @@ -136,7 +141,8 @@ using MemcacheAllRequests = carbon::List< McShutdownRequest, McStatsRequest, McTouchRequest, - McVersionRequest>; + McVersionRequest, + McMetaCommandsGetRequest>; } // namespace detail } // namespace memcache -} // namespace facebook \ No newline at end of file +} // namespace facebook diff --git a/mcrouter/lib/network/gen/MemcacheRouterStats.h b/mcrouter/lib/network/gen/MemcacheRouterStats.h index 45a7e4ccd..f8c10a127 100644 --- a/mcrouter/lib/network/gen/MemcacheRouterStats.h +++ b/mcrouter/lib/network/gen/MemcacheRouterStats.h @@ -23,7 +23,7 @@ namespace facebook { namespace memcache { struct MemcacheRouterStatsConfig { - static constexpr size_t kNumRequestGroups = 19; + static constexpr size_t kNumRequestGroups = 20; static constexpr std::array sumStatNames{{folly::StringPiece("cmd_add_count"), folly::StringPiece("cmd_append_count"), @@ -43,7 +43,8 @@ struct MemcacheRouterStatsConfig { folly::StringPiece("cmd_prepend_count"), folly::StringPiece("cmd_replace_count"), folly::StringPiece("cmd_set_count"), - folly::StringPiece("cmd_touch_count")}}; + folly::StringPiece("cmd_touch_count"), + folly::StringPiece("cmd_mg_count")}}; static constexpr std::array rateStatNames{{folly::StringPiece("cmd_add"), folly::StringPiece("cmd_append"), @@ -64,6 +65,7 @@ struct MemcacheRouterStatsConfig { folly::StringPiece("cmd_replace"), folly::StringPiece("cmd_set"), folly::StringPiece("cmd_touch"), + folly::StringPiece("cmd_mg"), folly::StringPiece("cmd_add_out"), folly::StringPiece("cmd_append_out"), folly::StringPiece("cmd_cas_out"), @@ -83,6 +85,7 @@ struct MemcacheRouterStatsConfig { folly::StringPiece("cmd_replace_out"), folly::StringPiece("cmd_set_out"), folly::StringPiece("cmd_touch_out"), + folly::StringPiece("cmd_mg_out"), folly::StringPiece("cmd_add_out_all"), folly::StringPiece("cmd_append_out_all"), folly::StringPiece("cmd_cas_out_all"), @@ -101,7 +104,8 @@ struct MemcacheRouterStatsConfig { folly::StringPiece("cmd_prepend_out_all"), folly::StringPiece("cmd_replace_out_all"), folly::StringPiece("cmd_set_out_all"), - folly::StringPiece("cmd_touch_out_all")}}; + folly::StringPiece("cmd_touch_out_all"), + folly::StringPiece("cmd_mg_out_all")}}; template static constexpr size_t getStatGroup(); @@ -220,5 +224,11 @@ inline constexpr size_t MemcacheRouterStatsConfig::getStatGroup() { return 18; // stat group 'touch' } + +template <> +inline constexpr size_t +MemcacheRouterStatsConfig::getStatGroup() { + return 19; // stat group 'mg' +} } // namespace memcache } // namespace facebook diff --git a/mcrouter/lib/network/gen/MemcacheRoutingGroups.h b/mcrouter/lib/network/gen/MemcacheRoutingGroups.h index 31d56eaf0..3dc4e617e 100644 --- a/mcrouter/lib/network/gen/MemcacheRoutingGroups.h +++ b/mcrouter/lib/network/gen/MemcacheRoutingGroups.h @@ -98,6 +98,11 @@ struct ArithmeticLike { static const bool value = false; }; +template <> +struct ArithmeticLike { + static const bool value = false; +}; + template <> struct ArithmeticLike { static const bool value = false; @@ -220,6 +225,11 @@ struct DeleteLike { static const bool value = false; }; +template <> +struct DeleteLike { + static const bool value = false; +}; + template <> struct DeleteLike { static const bool value = false; @@ -346,6 +356,12 @@ struct GetLike { static const bool value = false; }; +template <> +struct GetLike { + static const bool value = true; + typedef void* Type; +}; + template <> struct GetLike { static const bool value = true; @@ -472,6 +488,11 @@ struct UpdateLike { typedef void* Type; }; +template <> +struct UpdateLike { + static const bool value = false; +}; + template <> struct UpdateLike { static const bool value = false; diff --git a/mcrouter/lib/network/gen/MemcacheService.thrift b/mcrouter/lib/network/gen/MemcacheService.thrift index 6d391197f..0a6920cae 100644 --- a/mcrouter/lib/network/gen/MemcacheService.thrift +++ b/mcrouter/lib/network/gen/MemcacheService.thrift @@ -62,6 +62,8 @@ service Memcache { Memcache_McTouchReply mcTouch(1: Memcache_McTouchRequest request) throws (1: Common.CarbonResultBusy carbonResultBusy, 2: Common.CarbonResultRemoteError carbonResultRemoteError) @cpp.ProcessInEbThreadUnsafe Common_McVersionReply mcVersion(1: Common_McVersionRequest request) + @cpp.ProcessInEbThreadUnsafe + Memcache_McMetaCommandsGetReply mcMetaCommandsGet(1: Memcache_McMetaCommandsGetRequest request) throws (1: Common.CarbonResultBusy carbonResultBusy, 2: Common.CarbonResultRemoteError carbonResultRemoteError) } @cpp.Type{name = "facebook::memcache::McAddReply"} @@ -140,7 +142,11 @@ typedef Memcache.McSetRequest Memcache_McSetRequest typedef Memcache.McTouchReply Memcache_McTouchReply @cpp.Type{name = "facebook::memcache::McTouchRequest"} typedef Memcache.McTouchRequest Memcache_McTouchRequest +@cpp.Type{name = "facebook::memcache::McMetaCommandsGetReply"} +typedef Memcache.McMetaCommandsGetResponse Memcache_McMetaCommandsGetReply +@cpp.Type{name = "facebook::memcache::McMetaCommandsGetRequest"} +typedef Memcache.McMetaCommandsGetRequest Memcache_McMetaCommandsGetRequest @cpp.Type{name = "facebook::memcache::McVersionReply"} typedef Common.McVersionReply Common_McVersionReply @cpp.Type{name = "facebook::memcache::McVersionRequest"} -typedef Common.McVersionRequest Common_McVersionRequest \ No newline at end of file +typedef Common.McVersionRequest Common_McVersionRequest diff --git a/mcrouter/lib/network/gen/MemcacheThriftTransport.h b/mcrouter/lib/network/gen/MemcacheThriftTransport.h index 890f6215f..0d3c19a86 100644 --- a/mcrouter/lib/network/gen/MemcacheThriftTransport.h +++ b/mcrouter/lib/network/gen/MemcacheThriftTransport.h @@ -26,6 +26,7 @@ #include #include +#include "mcrouter/lib/network/gen/MemcacheMessages.h" #include "mcrouter/lib/network/gen/gen-cpp2/MemcacheAsyncClient.h" namespace facebook { @@ -837,6 +838,46 @@ folly::Try> sendSyncHelper( return reply; } +folly::Try> sendSyncHelper( + typename MemcacheRouterInfo::RouteHandleAsyncClient* thriftClient, + const McMetaCommandsGetRequest& request, + apache::thrift::RpcOptions& rpcOptions, + RpcStatsContext* rpcStatsContext = nullptr) { + bool needServerLoad = mcrouter::fiber_local::getThriftServerLoadEnabled(); + if (FOLLY_UNLIKELY(needServerLoad)) { + rpcOptions.setWriteHeader(kLoadHeader, kDefaultLoadCounter); + } + if (FOLLY_UNLIKELY(request.getCryptoAuthToken().has_value())) { + rpcOptions.setWriteHeader( + std::string{carbon::MessageCommon::kCryptoAuthTokenHeader}, request.getCryptoAuthToken().value()); + } + if (FOLLY_UNLIKELY(request.getClientIdentifier().has_value())) { + rpcOptions.setWriteHeader( + std::string{carbon::MessageCommon::kClientIdentifierHeader}, request.getClientIdentifier().value()); + } + rpcOptions.setContextPropMask(0); + +#ifndef LIBMC_FBTRACE_DISABLE + traceRequest(request, rpcOptions); +#endif + auto reply = thriftClient->sync_complete_mcMetaCommandsGet( + std::move(rpcOptions), request); + if (rpcStatsContext && reply.hasValue()) { + auto& stats = reply->responseContext.rpcTransportStats; + rpcStatsContext->requestBodySize = stats.requestSerializedSizeBytes; + rpcStatsContext->replySizeBeforeCompression = stats.responseSerializedSizeBytes; + rpcStatsContext->replySizeAfterCompression = stats.responseWireSizeBytes; + if (FOLLY_UNLIKELY(needServerLoad && reply->responseContext.serverLoad)) { + rpcStatsContext->serverLoad = ServerLoad( + static_cast(*reply->responseContext.serverLoad)); + } + } +#ifndef LIBMC_FBTRACE_DISABLE + traceResponse(request, reply); +#endif + return reply; +} + protected: std::optional> thriftClient_; }; @@ -1233,6 +1274,25 @@ class ThriftTransport : public ThriftTransportMethods>( + folly::make_exception_wrapper( + apache::thrift::transport::TTransportException::NOT_OPEN, + "Error creating thrift client.")); + } + }); + } + private: apache::thrift::Client* getThriftClient() { if (FOLLY_UNLIKELY(!thriftClient_)) { diff --git a/mcrouter/lib/network/test/AsciiSerializedTest.cpp b/mcrouter/lib/network/test/AsciiSerializedTest.cpp new file mode 100644 index 000000000..4269869dd --- /dev/null +++ b/mcrouter/lib/network/test/AsciiSerializedTest.cpp @@ -0,0 +1,104 @@ +/* + * 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. + */ +#include +#include +#include +#include + +#include +#include + +#include + +#include "mcrouter/lib/network/AsciiSerialized.h" +#include "mcrouter/lib/carbon/Keys.h" + +using namespace facebook::memcache; + +namespace { + +template std::string getSerialized(const Request &&req) { + AsciiSerializedRequest sreq; + std::ostringstream os; + const struct iovec *ioVecs; + size_t nIoVecs; + + sreq.prepare(std::move(req), ioVecs, nIoVecs); + + for (size_t i = 0; i < nIoVecs; i++) { + os << std::string_view{static_cast(ioVecs[i].iov_base), + ioVecs[i].iov_len}; + } + + return os.str(); +} + +template +std::string getSerialized(Reply &&res, folly::Optional &&key) { + AsciiSerializedReply sres; + std::ostringstream os; + const struct iovec *ioVecs; + size_t nIoVecs; + + sres.prepare(std::move(res), key, ioVecs, nIoVecs); + + for (size_t i = 0; i < nIoVecs; i++) { + os << std::string_view{static_cast(ioVecs[i].iov_base), + ioVecs[i].iov_len}; + } + + return os.str(); +} + +TEST(AsciiSerializedRequest, largestPossibleMetaCommandsGetRequest) { + McMetaCommandsGetRequest req("mykey"); + uint64_t allBooleanFlags = + MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY | + MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN | + MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS | + MC_META_COMMANDS_FLAG_RETURN_WAS_HIT | MC_META_COMMANDS_FLAG_RETURN_KEY | + MC_META_COMMANDS_FLAG_RETURN_TIME_SINCE_LAST_ACCESS | + MC_META_COMMANDS_FLAG_USE_NOREPLY_SEMANTICS | + MC_META_COMMANDS_FLAG_RETURN_ITEM_SIZE | + MC_META_COMMANDS_FLAG_RETURN_ITEM_TTL | + MC_META_COMMANDS_FLAG_DO_NOT_BUMP_LRU | + MC_META_COMMANDS_FLAG_RETURN_VALUE; + req.flags_ref() = allBooleanFlags; + req.casToken_ref() = std::numeric_limits::max(); + req.newCasToken_ref() = std::numeric_limits::max(); + req.vivifyOnMissTTL_ref() = std::numeric_limits::max(); + req.refreshIfTTLLessThan_ref() = std::numeric_limits::max(); + req.newTTL_ref() = std::numeric_limits::max(); + req.opaqueToken_ref() = "401b30e3b8b5d629635a5c613cdb79"; + + EXPECT_EQ(getSerialized(std::move(req)), + "mg mykey b c f h k l q s t u v C18446744073709551615 " + "E18446744073709551615 N2147483647 R2147483647 T2147483647 " + "O401b30e3b8b5d629635a5c613cdb79\r\n"); +} + +TEST(AsciiSerializedReply, largestPossibleMetaCommandsGetReply) { + McMetaCommandsGetReply res(carbon::Result::FOUND); + res.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + res.flags_ref() = MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY | + MC_META_COMMANDS_FLAG_WON_RECACHE | + MC_META_COMMANDS_FLAG_ITEM_IS_STALE; + res.casToken_ref() = std::numeric_limits::max(); + res.opaqueToken_ref() = "401b30e3b8b5d629635a5c613cdb79"; + res.clientFlags_ref() = std::numeric_limits::max(); + res.remainingTTL_ref() = std::numeric_limits::max(); + res.lastAccessTime_ref() = std::numeric_limits::max(); + res.wasHitBefore_ref() = 1; + res.itemSize_ref() = std::numeric_limits::max(); + res.key_ref() = carbon::Keys(folly::IOBuf(folly::IOBuf::COPY_BUFFER, "mykey")); + + EXPECT_EQ(getSerialized(std::move(res), {}), + "VA 5 b W X c18446744073709551615 f18446744073709551615 " + "s18446744073709551615 t2147483647 h1 l2147483647 " + "O401b30e3b8b5d629635a5c613cdb79 kmykey\r\nhello\r\n"); +} +} // namespace diff --git a/mcrouter/lib/network/test/McAsciiParserTest.cpp b/mcrouter/lib/network/test/McAsciiParserTest.cpp index 05dec1fa5..2ccc15ae4 100644 --- a/mcrouter/lib/network/test/McAsciiParserTest.cpp +++ b/mcrouter/lib/network/test/McAsciiParserTest.cpp @@ -706,6 +706,104 @@ TYPED_TEST(McAsciiParserTestMetaget, MetagetHit_Unknown_NegativeOne_notrans) { h.runTest(1); } +/** + * Test meta commands get (mg) + */ +TEST(McAsciiParserTestMetaCommandsGet, MgMiss) { + McAsciiParserHarness h("EN\r\n"); + h.expectNext( + McMetaCommandsGetReply(carbon::Result::NOTFOUND)); + h.runTest(0); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHitValueOnly) { + McAsciiParserHarness h("VA 5\r\nhello\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHitWithCas) { + McAsciiParserHarness h("VA 5 c12345\r\nhello\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + expected.casToken_ref() = 12345; + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHitWithFlags) { + McAsciiParserHarness h("VA 5 W X\r\nhello\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + expected.flags_ref() = + MC_META_COMMANDS_FLAG_WON_RECACHE | MC_META_COMMANDS_FLAG_ITEM_IS_STALE; + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHitWithAllFlags) { + McAsciiParserHarness h("VA 5 b W s750 X l63 Otest f456 t892 c99\r\nhello\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + expected.flags_ref() = + MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY | + MC_META_COMMANDS_FLAG_WON_RECACHE | + MC_META_COMMANDS_FLAG_ITEM_IS_STALE; + expected.casToken_ref() = 99; + expected.opaqueToken_ref() = "test"; + expected.clientFlags_ref() = 456; + expected.remainingTTL_ref() = 892; + expected.lastAccessTime_ref() = 63; + expected.wasHitBefore_ref() = 1; + expected.itemSize_ref() = 750; + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHdNoValue) { + McAsciiParserHarness h("HD\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + h.expectNext(std::move(expected)); + h.runTest(0); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHdWithFlags) { + McAsciiParserHarness h("HD W c42\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.flags_ref() = MC_META_COMMANDS_FLAG_WON_RECACHE; + expected.casToken_ref() = 42; + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgHitWithKey) { + // The 'k' flag is skipped by the client parser (client already knows the key) + McAsciiParserHarness h("VA 5 kMyKey\r\nhello\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgSkipsUnknownFlags) { + McAsciiParserHarness h("VA 5 s42 t100 W\r\nhello\r\n"); + McMetaCommandsGetReply expected(carbon::Result::FOUND); + expected.value_ref() = folly::IOBuf(folly::IOBuf::COPY_BUFFER, "hello"); + expected.flags_ref() = MC_META_COMMANDS_FLAG_WON_RECACHE; + h.expectNext(std::move(expected)); + h.runTest(1); +} + +TEST(McAsciiParserTestMetaCommandsGet, MgServerError) { + McAsciiParserHarness h("SERVER_ERROR what\r\n"); + h.expectNext( + replyWithMessage( + carbon::Result::REMOTE_ERROR, "what")); + h.runTest(3); +} + /** * Test flush_all */ diff --git a/mcrouter/lib/network/test/McServerAsciiParserTest.cpp b/mcrouter/lib/network/test/McServerAsciiParserTest.cpp index 7378a9c32..26ef55994 100644 --- a/mcrouter/lib/network/test/McServerAsciiParserTest.cpp +++ b/mcrouter/lib/network/test/McServerAsciiParserTest.cpp @@ -636,6 +636,95 @@ TEST(McServerAsciiParserHarness, flush_all) { .run("flush_all 123456789 \r\n"); } +TEST(McServerAsciiParserHarness, mg_basic) { + // Basic mg with key only, no flags. + McMetaCommandsGetRequest r("test:key:1"); + TestRunner() + .expectNext(r) + .run("mg test:key:1\r\n") + .run("mg test:key:1\r\n") + .run("mg test:key:1 \r\n") + .run("mg test:key:1\n"); +} + +TEST(McServerAsciiParserHarness, mg_single_flags) { + // mg with single boolean flags. + McMetaCommandsGetRequest r("test:key:1"); + r.flags_ref() = + MC_META_COMMANDS_FLAG_RETURN_VALUE | + MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN | + MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS; + TestRunner() + .expectNext(r) + .run("mg test:key:1 v c f\r\n") + .run("mg test:key:1 v c f\r\n") + .run("mg test:key:1 v c f \r\n"); +} + +TEST(McServerAsciiParserHarness, mg_all_boolean_flags) { + McMetaCommandsGetRequest r("mykey"); + r.flags_ref() = + MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY | + MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN | + MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS | + MC_META_COMMANDS_FLAG_RETURN_WAS_HIT | + MC_META_COMMANDS_FLAG_RETURN_KEY | + MC_META_COMMANDS_FLAG_RETURN_TIME_SINCE_LAST_ACCESS | + MC_META_COMMANDS_FLAG_USE_NOREPLY_SEMANTICS | + MC_META_COMMANDS_FLAG_RETURN_ITEM_SIZE | + MC_META_COMMANDS_FLAG_RETURN_ITEM_TTL | + MC_META_COMMANDS_FLAG_DO_NOT_BUMP_LRU | + MC_META_COMMANDS_FLAG_RETURN_VALUE; + TestRunner() + .expectNext(r, true /* noreply because q flag is set */) + .run("mg mykey b c f h k l q s t u v\r\n"); +} + +TEST(McServerAsciiParserHarness, mg_valued_flags) { + McMetaCommandsGetRequest r("test:key"); + r.flags_ref() = MC_META_COMMANDS_FLAG_RETURN_VALUE; + r.casToken_ref() = 12345; + r.vivifyOnMissTTL_ref() = 30; + r.refreshIfTTLLessThan_ref() = 10; + r.newTTL_ref() = 60; + r.opaqueToken_ref() = "abc123"; + TestRunner() + .expectNext(r) + .run("mg test:key v C12345 N30 R10 T60 Oabc123\r\n") + .run("mg test:key v C12345 N30 R10 T60 Oabc123 \r\n"); +} + +TEST(McServerAsciiParserHarness, mg_all_flags) { + McMetaCommandsGetRequest r("mykey"); + uint64_t allBooleanFlags = MC_META_COMMANDS_FLAG_BASE64_ENCODED_KEY | + MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN | + MC_META_COMMANDS_FLAG_RETURN_CLIENT_FLAGS | + MC_META_COMMANDS_FLAG_RETURN_WAS_HIT | + MC_META_COMMANDS_FLAG_RETURN_KEY | + MC_META_COMMANDS_FLAG_RETURN_TIME_SINCE_LAST_ACCESS | + MC_META_COMMANDS_FLAG_USE_NOREPLY_SEMANTICS | + MC_META_COMMANDS_FLAG_RETURN_ITEM_SIZE | + MC_META_COMMANDS_FLAG_RETURN_ITEM_TTL | + MC_META_COMMANDS_FLAG_DO_NOT_BUMP_LRU | + MC_META_COMMANDS_FLAG_RETURN_VALUE; + r.flags_ref() = allBooleanFlags; + r.casToken_ref() = std::numeric_limits::max(); + r.newCasToken_ref() = std::numeric_limits::max(); + r.vivifyOnMissTTL_ref() = std::numeric_limits::max(); + r.refreshIfTTLLessThan_ref() = std::numeric_limits::max(); + r.newTTL_ref() = std::numeric_limits::max(); + r.opaqueToken_ref() = "401b30e3b8b5d629635a5c613cdb79"; + + TestRunner() + .expectNext(r, true /* noreply */) + .run("mg mykey b c f h k l q s t u v C18446744073709551615 E18446744073709551615 N2147483647 R2147483647 T2147483647 O401b30e3b8b5d629635a5c613cdb79\r\n"); +} + +TEST(McServerAsciiParserHarness, mg_error) { + // Missing key. + TestRunner().expectError().run("mg\r\n").run("mg \r\n"); +} + TEST(McServerAsciiParserHarness, flush_regex) { // Flush_regex expects a key. TestRunner().expectError().run("flush_regex\r\n").run("flush_regex \r\n"); @@ -702,6 +791,11 @@ TEST(McServerAsciiParserHarness, allOps) { McDeleteRequest deleteRequest("test:stepan:13"); deleteRequest.exptime_ref() = 2345234; + McMetaCommandsGetRequest mgRequest("test:stepan:14"); + mgRequest.flags_ref() = + MC_META_COMMANDS_FLAG_RETURN_VALUE | MC_META_COMMANDS_FLAG_RETURN_CAS_TOKEN; + mgRequest.newTTL_ref() = 300; + TestRunner() .expectNext(McGetRequest("test:stepan:1")) .expectMultiOpEnd() @@ -724,6 +818,7 @@ TEST(McServerAsciiParserHarness, allOps) { .expectNext(casRequest) .expectNext(leaseSetRequest) .expectNext(deleteRequest) + .expectNext(mgRequest) .expectNext(McStatsRequest("test stats")) .expectNext(McExecRequest("reboot server")) .expectNext(McQuitRequest(), true) @@ -746,6 +841,7 @@ TEST(McServerAsciiParserHarness, allOps) { "cas test:stepan:11 765 -1 8 893\r\nFacebook\r\n" "lease-set test:stepan:12 846 294 563 4\r\nhAcK\r\n" "delete test:stepan:13 2345234\r\n" + "mg test:stepan:14 v c T300\r\n" "stats test stats\r\n" "exec reboot server\r\n" "quit\r\n" diff --git a/mcrouter/routes/BigValueRoute-inl.h b/mcrouter/routes/BigValueRoute-inl.h index d16440e6f..dd4af26fc 100644 --- a/mcrouter/routes/BigValueRoute-inl.h +++ b/mcrouter/routes/BigValueRoute-inl.h @@ -22,6 +22,7 @@ #include "mcrouter/lib/IOBufUtil.h" #include "mcrouter/lib/McResUtil.h" #include "mcrouter/lib/Reply.h" +#include "mcrouter/lib/network/gen/MemcacheMessages.h" #include "mcrouter/routes/McRouteHandleBuilder.h" namespace facebook { @@ -270,6 +271,13 @@ McMetagetReply BigValueRoute::route( return ch_->route(req); } +template +McMetaCommandsGetReply BigValueRoute::route( + const McMetaCommandsGetRequest& req) const { + // TODO: Implement. + return ch_->route(req); +} + template McLeaseGetReply BigValueRoute::route( const McLeaseGetRequest& req) const { diff --git a/mcrouter/routes/BigValueRoute.h b/mcrouter/routes/BigValueRoute.h index 159461990..f9d37e5cd 100644 --- a/mcrouter/routes/BigValueRoute.h +++ b/mcrouter/routes/BigValueRoute.h @@ -84,6 +84,7 @@ class BigValueRoute { route(const Request& req) const; McMetagetReply route(const McMetagetRequest& req) const; + McMetaCommandsGetReply route(const McMetaCommandsGetRequest& req) const; McLeaseGetReply route(const McLeaseGetRequest& req) const; template