1
0
Fork 0
mirror of https://github.com/dragonflydb/dragonfly.git synced 2024-12-14 11:58:02 +00:00

refactor: conn_context and reply_builder refactoring (#2251)

* refactor: conn_context and reply_builder refactoring
This commit is contained in:
Borys 2023-12-06 08:23:32 +02:00 committed by GitHub
parent 1900e499ba
commit 33d0879416
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1424 additions and 1314 deletions

View file

@ -36,11 +36,6 @@ class ConnectionContext {
return protocol_; return protocol_;
} }
// A convenient proxy for redis interface.
// Use with caution -- should only be used only
// in execution paths that are Redis *only*
RedisReplyBuilder* operator->();
SinkReplyBuilder* reply_builder() { SinkReplyBuilder* reply_builder() {
return rbuilder_.get(); return rbuilder_.get();
} }
@ -56,8 +51,28 @@ class ConnectionContext {
rbuilder_->SendError(str, type); rbuilder_->SendError(str, type);
} }
void SendError(ErrorReply&& error) { void SendError(ErrorReply error) {
rbuilder_->SendError(std::move(error)); rbuilder_->SendError(error);
}
void SendError(OpStatus status) {
rbuilder_->SendError(status);
}
void SendStored() {
rbuilder_->SendStored();
}
void SendSetSkipped() {
rbuilder_->SendSetSkipped();
}
void SendMGetResponse(SinkReplyBuilder::MGetResponse resp) {
rbuilder_->SendMGetResponse(std::move(resp));
}
void SendLong(long val) {
rbuilder_->SendLong(val);
} }
void SendSimpleString(std::string_view str) { void SendSimpleString(std::string_view str) {
@ -68,6 +83,10 @@ class ConnectionContext {
rbuilder_->SendOk(); rbuilder_->SendOk();
} }
void SendProtocolError(std::string_view str) {
rbuilder_->SendProtocolError(str);
}
virtual size_t UsedMemory() const { virtual size_t UsedMemory() const {
return dfly::HeapSize(rbuilder_); return dfly::HeapSize(rbuilder_);
} }

View file

@ -138,12 +138,6 @@ ConnectionContext::ConnectionContext(::io::Sink* stream, Connection* owner) : ow
subscriptions = 0; subscriptions = 0;
} }
RedisReplyBuilder* ConnectionContext::operator->() {
CHECK(Protocol::REDIS == protocol());
return static_cast<RedisReplyBuilder*>(rbuilder_.get());
}
CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key,
int8_t last_key, uint32_t acl_categories) int8_t last_key, uint32_t acl_categories)
: name_(name), : name_(name),

View file

@ -24,7 +24,7 @@ thread_local ConnectionStats tl_stats;
class OkService : public ServiceInterface { class OkService : public ServiceInterface {
public: public:
void DispatchCommand(CmdArgList args, ConnectionContext* cntx) final { void DispatchCommand(CmdArgList args, ConnectionContext* cntx) final {
(*cntx)->SendOk(); cntx->SendOk();
} }
size_t DispatchManyCommands(absl::Span<CmdArgList> args_lists, ConnectionContext* cntx) final { size_t DispatchManyCommands(absl::Span<CmdArgList> args_lists, ConnectionContext* cntx) final {

View file

@ -50,13 +50,14 @@ AclFamily::AclFamily(UserRegistry* registry, util::ProactorPool* pool)
} }
void AclFamily::Acl(CmdArgList args, ConnectionContext* cntx) { void AclFamily::Acl(CmdArgList args, ConnectionContext* cntx) {
(*cntx)->SendError("Wrong number of arguments for acl command"); cntx->SendError("Wrong number of arguments for acl command");
} }
void AclFamily::List(CmdArgList args, ConnectionContext* cntx) { void AclFamily::List(CmdArgList args, ConnectionContext* cntx) {
const auto registry_with_lock = registry_->GetRegistryWithLock(); const auto registry_with_lock = registry_->GetRegistryWithLock();
const auto& registry = registry_with_lock.registry; const auto& registry = registry_with_lock.registry;
(*cntx)->StartArray(registry.size()); auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(registry.size());
for (const auto& [username, user] : registry) { for (const auto& [username, user] : registry) {
std::string buffer = "user "; std::string buffer = "user ";
@ -71,7 +72,7 @@ void AclFamily::List(CmdArgList args, ConnectionContext* cntx) {
absl::StrAppend(&buffer, username, " ", user.IsActive() ? "on "sv : "off "sv, password, " ", absl::StrAppend(&buffer, username, " ", user.IsActive() ? "on "sv : "off "sv, password, " ",
acl_cat, maybe_space, acl_commands); acl_cat, maybe_space, acl_commands);
(*cntx)->SendSimpleString(buffer); cntx->SendSimpleString(buffer);
} }
} }
@ -97,7 +98,7 @@ using facade::ErrorReply;
void AclFamily::SetUser(CmdArgList args, ConnectionContext* cntx) { void AclFamily::SetUser(CmdArgList args, ConnectionContext* cntx) {
std::string_view username = facade::ToSV(args[0]); std::string_view username = facade::ToSV(args[0]);
auto req = ParseAclSetUser(args.subspan(1), *cmd_registry_); auto req = ParseAclSetUser(args.subspan(1), *cmd_registry_);
auto error_case = [cntx](ErrorReply&& error) { (*cntx)->SendError(error); }; auto error_case = [cntx](ErrorReply&& error) { cntx->SendError(std::move(error)); };
auto update_case = [username, cntx, this](User::UpdateRequest&& req) { auto update_case = [username, cntx, this](User::UpdateRequest&& req) {
auto user_with_lock = registry_->MaybeAddAndUpdateWithLock(username, std::move(req)); auto user_with_lock = registry_->MaybeAddAndUpdateWithLock(username, std::move(req));
if (user_with_lock.exists) { if (user_with_lock.exists) {
@ -313,7 +314,7 @@ void AclFamily::Load(CmdArgList args, ConnectionContext* cntx) {
void AclFamily::Log(CmdArgList args, ConnectionContext* cntx) { void AclFamily::Log(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
(*cntx)->SendError(facade::OpStatus::OUT_OF_RANGE); cntx->SendError(facade::OpStatus::OUT_OF_RANGE);
} }
size_t max_output = 10; size_t max_output = 10;
@ -322,12 +323,12 @@ void AclFamily::Log(CmdArgList args, ConnectionContext* cntx) {
if (absl::EqualsIgnoreCase(option, "RESET")) { if (absl::EqualsIgnoreCase(option, "RESET")) {
pool_->AwaitFiberOnAll( pool_->AwaitFiberOnAll(
[](auto index, auto* context) { ServerState::tlocal()->acl_log.Reset(); }); [](auto index, auto* context) { ServerState::tlocal()->acl_log.Reset(); });
(*cntx)->SendOk(); cntx->SendOk();
return; return;
} }
if (!absl::SimpleAtoi(facade::ToSV(args[0]), &max_output)) { if (!absl::SimpleAtoi(facade::ToSV(args[0]), &max_output)) {
(*cntx)->SendError("Invalid count"); cntx->SendError("Invalid count");
return; return;
} }
} }
@ -342,32 +343,33 @@ void AclFamily::Log(CmdArgList args, ConnectionContext* cntx) {
total_entries += log.size(); total_entries += log.size();
} }
auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
if (total_entries == 0) { if (total_entries == 0) {
(*cntx)->SendEmptyArray(); rb->SendEmptyArray();
return; return;
} }
(*cntx)->StartArray(total_entries); rb->StartArray(total_entries);
auto print_element = [cntx](const auto& entry) { auto print_element = [rb](const auto& entry) {
(*cntx)->StartArray(12); rb->StartArray(12);
(*cntx)->SendSimpleString("reason"); rb->SendSimpleString("reason");
using Reason = AclLog::Reason; using Reason = AclLog::Reason;
std::string_view reason = entry.reason == Reason::COMMAND ? "COMMAND" : "AUTH"; std::string_view reason = entry.reason == Reason::COMMAND ? "COMMAND" : "AUTH";
(*cntx)->SendSimpleString(reason); rb->SendSimpleString(reason);
(*cntx)->SendSimpleString("object"); rb->SendSimpleString("object");
(*cntx)->SendSimpleString(entry.object); rb->SendSimpleString(entry.object);
(*cntx)->SendSimpleString("username"); rb->SendSimpleString("username");
(*cntx)->SendSimpleString(entry.username); rb->SendSimpleString(entry.username);
(*cntx)->SendSimpleString("age-seconds"); rb->SendSimpleString("age-seconds");
auto now_diff = std::chrono::system_clock::now() - entry.entry_creation; auto now_diff = std::chrono::system_clock::now() - entry.entry_creation;
auto secs = std::chrono::duration_cast<std::chrono::seconds>(now_diff); auto secs = std::chrono::duration_cast<std::chrono::seconds>(now_diff);
auto left_over = now_diff - std::chrono::duration_cast<std::chrono::microseconds>(secs); auto left_over = now_diff - std::chrono::duration_cast<std::chrono::microseconds>(secs);
auto age = absl::StrCat(secs.count(), ".", left_over.count()); auto age = absl::StrCat(secs.count(), ".", left_over.count());
(*cntx)->SendSimpleString(absl::StrCat(age)); rb->SendSimpleString(absl::StrCat(age));
(*cntx)->SendSimpleString("client-info"); rb->SendSimpleString("client-info");
(*cntx)->SendSimpleString(entry.client_info); rb->SendSimpleString(entry.client_info);
(*cntx)->SendSimpleString("timestamp-created"); rb->SendSimpleString("timestamp-created");
(*cntx)->SendLong(entry.entry_creation.time_since_epoch().count()); rb->SendLong(entry.entry_creation.time_since_epoch().count());
}; };
auto n_way_minimum = [](const auto& logs) { auto n_way_minimum = [](const auto& logs) {
@ -394,15 +396,16 @@ void AclFamily::Log(CmdArgList args, ConnectionContext* cntx) {
void AclFamily::Users(CmdArgList args, ConnectionContext* cntx) { void AclFamily::Users(CmdArgList args, ConnectionContext* cntx) {
const auto registry_with_lock = registry_->GetRegistryWithLock(); const auto registry_with_lock = registry_->GetRegistryWithLock();
const auto& registry = registry_with_lock.registry; const auto& registry = registry_with_lock.registry;
(*cntx)->StartArray(registry.size()); auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(registry.size());
for (const auto& [username, _] : registry) { for (const auto& [username, _] : registry) {
(*cntx)->SendSimpleString(username); rb->SendSimpleString(username);
} }
} }
void AclFamily::Cat(CmdArgList args, ConnectionContext* cntx) { void AclFamily::Cat(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
(*cntx)->SendError(facade::OpStatus::SYNTAX_ERR); cntx->SendError(facade::OpStatus::SYNTAX_ERR);
return; return;
} }
@ -411,7 +414,7 @@ void AclFamily::Cat(CmdArgList args, ConnectionContext* cntx) {
std::string_view category = facade::ToSV(args[0]); std::string_view category = facade::ToSV(args[0]);
if (!CATEGORY_INDEX_TABLE.contains(category)) { if (!CATEGORY_INDEX_TABLE.contains(category)) {
auto error = absl::StrCat("Unkown category: ", category); auto error = absl::StrCat("Unkown category: ", category);
(*cntx)->SendError(error); cntx->SendError(error);
return; return;
} }
@ -423,10 +426,11 @@ void AclFamily::Cat(CmdArgList args, ConnectionContext* cntx) {
} }
}; };
auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
cmd_registry_->Traverse(cb); cmd_registry_->Traverse(cb);
(*cntx)->StartArray(results.size()); rb->StartArray(results.size());
for (const auto& command : results) { for (const auto& command : results) {
(*cntx)->SendSimpleString(command); rb->SendSimpleString(command);
} }
return; return;
@ -439,10 +443,11 @@ void AclFamily::Cat(CmdArgList args, ConnectionContext* cntx) {
} }
} }
(*cntx)->StartArray(total_categories); auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(total_categories);
for (auto& elem : REVERSE_CATEGORY_INDEX_TABLE) { for (auto& elem : REVERSE_CATEGORY_INDEX_TABLE) {
if (elem != "_RESERVED") { if (elem != "_RESERVED") {
(*cntx)->SendSimpleString(elem); rb->SendSimpleString(elem);
} }
} }
} }
@ -453,39 +458,40 @@ void AclFamily::GetUser(CmdArgList args, ConnectionContext* cntx) {
const auto& registry = registry_with_lock.registry; const auto& registry = registry_with_lock.registry;
if (!registry.contains(username)) { if (!registry.contains(username)) {
auto error = absl::StrCat("User: ", username, " does not exists!"); auto error = absl::StrCat("User: ", username, " does not exists!");
(*cntx)->SendError(error); cntx->SendError(error);
return; return;
} }
auto& user = registry.find(username)->second; auto& user = registry.find(username)->second;
std::string status = user.IsActive() ? "on" : "off"; std::string status = user.IsActive() ? "on" : "off";
auto pass = user.Password(); auto pass = user.Password();
(*cntx)->StartArray(6); auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(6);
(*cntx)->SendSimpleString("flags"); rb->SendSimpleString("flags");
const size_t total_elements = (pass != "nopass") ? 1 : 2; const size_t total_elements = (pass != "nopass") ? 1 : 2;
(*cntx)->StartArray(total_elements); rb->StartArray(total_elements);
(*cntx)->SendSimpleString(status); rb->SendSimpleString(status);
if (total_elements == 2) { if (total_elements == 2) {
(*cntx)->SendSimpleString(pass); rb->SendSimpleString(pass);
} }
(*cntx)->SendSimpleString("passwords"); rb->SendSimpleString("passwords");
if (pass != "nopass") { if (pass != "nopass") {
(*cntx)->SendSimpleString(pass); rb->SendSimpleString(pass);
} else { } else {
(*cntx)->SendEmptyArray(); rb->SendEmptyArray();
} }
(*cntx)->SendSimpleString("commands"); rb->SendSimpleString("commands");
std::string acl = absl::StrCat(AclCatToString(user.AclCategory()), " ", std::string acl = absl::StrCat(AclCatToString(user.AclCategory()), " ",
AclCommandToString(user.AclCommandsRef())); AclCommandToString(user.AclCommandsRef()));
(*cntx)->SendSimpleString(acl); rb->SendSimpleString(acl);
} }
void AclFamily::GenPass(CmdArgList args, ConnectionContext* cntx) { void AclFamily::GenPass(CmdArgList args, ConnectionContext* cntx) {
if (args.length() > 1) { if (args.length() > 1) {
(*cntx)->SendError(facade::UnknownSubCmd("GENPASS", "ACL")); cntx->SendError(facade::UnknownSubCmd("GENPASS", "ACL"));
return; return;
} }
uint32_t random_bits = 256; uint32_t random_bits = 256;
@ -493,7 +499,7 @@ void AclFamily::GenPass(CmdArgList args, ConnectionContext* cntx) {
auto requested_bits = facade::ArgS(args, 0); auto requested_bits = facade::ArgS(args, 0);
if (!absl::SimpleAtoi(requested_bits, &random_bits) || random_bits == 0 || random_bits > 4096) { if (!absl::SimpleAtoi(requested_bits, &random_bits) || random_bits == 0 || random_bits > 4096) {
return (*cntx)->SendError( return cntx->SendError(
"ACL GENPASS argument must be the number of bits for the output password, a positive " "ACL GENPASS argument must be the number of bits for the output password, a positive "
"number up to 4096"); "number up to 4096");
} }
@ -508,7 +514,7 @@ void AclFamily::GenPass(CmdArgList args, ConnectionContext* cntx) {
response.resize(result_length); response.resize(result_length);
(*cntx)->SendSimpleString(response); cntx->SendSimpleString(response);
} }
void AclFamily::DryRun(CmdArgList args, ConnectionContext* cntx) { void AclFamily::DryRun(CmdArgList args, ConnectionContext* cntx) {
@ -517,7 +523,7 @@ void AclFamily::DryRun(CmdArgList args, ConnectionContext* cntx) {
const auto& registry = registry_with_lock.registry; const auto& registry = registry_with_lock.registry;
if (!registry.contains(username)) { if (!registry.contains(username)) {
auto error = absl::StrCat("User: ", username, " does not exists!"); auto error = absl::StrCat("User: ", username, " does not exists!");
(*cntx)->SendError(error); cntx->SendError(error);
return; return;
} }
@ -526,18 +532,18 @@ void AclFamily::DryRun(CmdArgList args, ConnectionContext* cntx) {
auto* cid = cmd_registry_->Find(command); auto* cid = cmd_registry_->Find(command);
if (!cid) { if (!cid) {
auto error = absl::StrCat("Command: ", command, " does not exists!"); auto error = absl::StrCat("Command: ", command, " does not exists!");
(*cntx)->SendError(error); cntx->SendError(error);
return; return;
} }
const auto& user = registry.find(username)->second; const auto& user = registry.find(username)->second;
if (IsUserAllowedToInvokeCommandGeneric(user.AclCategory(), user.AclCommandsRef(), *cid)) { if (IsUserAllowedToInvokeCommandGeneric(user.AclCategory(), user.AclCommandsRef(), *cid)) {
(*cntx)->SendOk(); cntx->SendOk();
return; return;
} }
auto error = absl::StrCat("User: ", username, " is not allowed to execute command: ", command); auto error = absl::StrCat("User: ", username, " is not allowed to execute command: ", command);
(*cntx)->SendError(error); cntx->SendError(error);
} }
using MemberFunc = void (AclFamily::*)(CmdArgList args, ConnectionContext* cntx); using MemberFunc = void (AclFamily::*)(CmdArgList args, ConnectionContext* cntx);

View file

@ -500,17 +500,17 @@ template <typename T> void HandleOpValueResult(const OpResult<T>& result, Connec
"we are only handling types that are integral types in the return types from " "we are only handling types that are integral types in the return types from "
"here"); "here");
if (result) { if (result) {
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} else { } else {
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
(*cntx)->SendError(kWrongTypeErr); cntx->SendError(kWrongTypeErr);
break; break;
case OpStatus::OUT_OF_MEMORY: case OpStatus::OUT_OF_MEMORY:
(*cntx)->SendError(kOutOfMemory); cntx->SendError(kOutOfMemory);
break; break;
default: default:
(*cntx)->SendLong(0); // in case we don't have the value we should just send 0 cntx->SendLong(0); // in case we don't have the value we should just send 0
break; break;
} }
} }
@ -523,7 +523,7 @@ void BitPos(CmdArgList args, ConnectionContext* cntx) {
// See details at https://redis.io/commands/bitpos/ // See details at https://redis.io/commands/bitpos/
if (args.size() < 1 || args.size() > 5) { if (args.size() < 1 || args.size() > 5) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
std::string_view key = ArgS(args, 0); std::string_view key = ArgS(args, 0);
@ -534,21 +534,21 @@ void BitPos(CmdArgList args, ConnectionContext* cntx) {
bool as_bit = false; bool as_bit = false;
if (!absl::SimpleAtoi(ArgS(args, 1), &value)) { if (!absl::SimpleAtoi(ArgS(args, 1), &value)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (args.size() >= 3) { if (args.size() >= 3) {
if (!absl::SimpleAtoi(ArgS(args, 2), &start)) { if (!absl::SimpleAtoi(ArgS(args, 2), &start)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (args.size() >= 4) { if (args.size() >= 4) {
if (!absl::SimpleAtoi(ArgS(args, 3), &end)) { if (!absl::SimpleAtoi(ArgS(args, 3), &end)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (args.size() >= 5) { if (args.size() >= 5) {
if (!ToUpperAndGetAsBit(args, 4, &as_bit)) { if (!ToUpperAndGetAsBit(args, 4, &as_bit)) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
} }
} }
@ -568,7 +568,7 @@ void BitCount(CmdArgList args, ConnectionContext* cntx) {
// Please note that if the key don't exists, it would return 0 // Please note that if the key don't exists, it would return 0
if (args.size() == 2 || args.size() > 4) { if (args.size() == 2 || args.size() > 4) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
std::string_view key = ArgS(args, 0); std::string_view key = ArgS(args, 0);
@ -578,11 +578,11 @@ void BitCount(CmdArgList args, ConnectionContext* cntx) {
if (args.size() >= 3) { if (args.size() >= 3) {
if (absl::SimpleAtoi(ArgS(args, 1), &start) == 0 || if (absl::SimpleAtoi(ArgS(args, 1), &start) == 0 ||
absl::SimpleAtoi(ArgS(args, 2), &end) == 0) { absl::SimpleAtoi(ArgS(args, 2), &end) == 0) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (args.size() == 4) { if (args.size() == 4) {
if (!ToUpperAndGetAsBit(args, 3, &as_bit)) { if (!ToUpperAndGetAsBit(args, 3, &as_bit)) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
} }
} }
@ -1068,26 +1068,28 @@ nonstd::expected<CommandList, std::string> ParseToCommandList(CmdArgList args, b
} }
void SendResults(const std::vector<ResultType>& results, ConnectionContext* cntx) { void SendResults(const std::vector<ResultType>& results, ConnectionContext* cntx) {
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
const size_t total = results.size(); const size_t total = results.size();
if (total == 0) { if (total == 0) {
(*cntx)->SendNullArray(); rb->SendNullArray();
return; return;
} }
(*cntx)->StartArray(total); rb->StartArray(total);
for (const auto& elem : results) { for (const auto& elem : results) {
if (elem) { if (elem) {
(*cntx)->SendLong(*elem); rb->SendLong(*elem);
continue; continue;
} }
(*cntx)->SendNull(); rb->SendNull();
} }
} }
void BitFieldGeneric(CmdArgList args, bool read_only, ConnectionContext* cntx) { void BitFieldGeneric(CmdArgList args, bool read_only, ConnectionContext* cntx) {
if (args.size() == 1) { if (args.size() == 1) {
(*cntx)->SendNullArray(); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendNullArray();
return; return;
} }
auto key = ArgS(args, 0); auto key = ArgS(args, 0);
@ -1136,7 +1138,7 @@ void BitOp(CmdArgList args, ConnectionContext* cntx) {
[&op](auto val) { return op == val; }); [&op](auto val) { return op == val; });
if (illegal || (op == NOT_OP_NAME && args.size() > 3)) { if (illegal || (op == NOT_OP_NAME && args.size() > 3)) {
return (*cntx)->SendError(kSyntaxErr); // too many arguments return cntx->SendError(kSyntaxErr); // too many arguments
} }
// Multi shard access - read only // Multi shard access - read only
@ -1166,7 +1168,7 @@ void BitOp(CmdArgList args, ConnectionContext* cntx) {
// Second phase - save to targe key if successful // Second phase - save to targe key if successful
if (!joined_results) { if (!joined_results) {
cntx->transaction->Conclude(); cntx->transaction->Conclude();
(*cntx)->SendError(joined_results.status()); cntx->SendError(joined_results.status());
return; return;
} else { } else {
auto op_result = joined_results.value(); auto op_result = joined_results.value();
@ -1187,7 +1189,7 @@ void BitOp(CmdArgList args, ConnectionContext* cntx) {
}; };
cntx->transaction->Execute(std::move(store_cb), true); cntx->transaction->Execute(std::move(store_cb), true);
(*cntx)->SendLong(op_result.size()); cntx->SendLong(op_result.size());
} }
} }
@ -1199,7 +1201,7 @@ void GetBit(CmdArgList args, ConnectionContext* cntx) {
std::string_view key = ArgS(args, 0); std::string_view key = ArgS(args, 0);
if (!absl::SimpleAtoi(ArgS(args, 1), &offset)) { if (!absl::SimpleAtoi(ArgS(args, 1), &offset)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {
return ReadValueBitsetAt(t->GetOpArgs(shard), key, offset); return ReadValueBitsetAt(t->GetOpArgs(shard), key, offset);
@ -1218,7 +1220,7 @@ void SetBit(CmdArgList args, ConnectionContext* cntx) {
std::string_view key = ArgS(args, 0); std::string_view key = ArgS(args, 0);
if (!absl::SimpleAtoi(ArgS(args, 1), &offset) || !absl::SimpleAtoi(ArgS(args, 2), &value)) { if (!absl::SimpleAtoi(ArgS(args, 1), &offset) || !absl::SimpleAtoi(ArgS(args, 2), &value)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {

View file

@ -107,46 +107,48 @@ void ClusterFamily::ClusterHelp(ConnectionContext* cntx) {
"HELP", "HELP",
" Prints this help.", " Prints this help.",
}; };
return (*cntx)->SendSimpleStrArr(help_arr); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return rb->SendSimpleStrArr(help_arr);
} }
namespace { namespace {
void ClusterShardsImpl(const ClusterShards& config, ConnectionContext* cntx) { void ClusterShardsImpl(const ClusterShards& config, ConnectionContext* cntx) {
// For more details https://redis.io/commands/cluster-shards/ // For more details https://redis.io/commands/cluster-shards/
constexpr unsigned int kEntrySize = 4; constexpr unsigned int kEntrySize = 4;
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
auto WriteNode = [&](const Node& node, string_view role) { auto WriteNode = [&](const Node& node, string_view role) {
constexpr unsigned int kNodeSize = 14; constexpr unsigned int kNodeSize = 14;
(*cntx)->StartArray(kNodeSize); rb->StartArray(kNodeSize);
(*cntx)->SendBulkString("id"); rb->SendBulkString("id");
(*cntx)->SendBulkString(node.id); rb->SendBulkString(node.id);
(*cntx)->SendBulkString("endpoint"); rb->SendBulkString("endpoint");
(*cntx)->SendBulkString(node.ip); rb->SendBulkString(node.ip);
(*cntx)->SendBulkString("ip"); rb->SendBulkString("ip");
(*cntx)->SendBulkString(node.ip); rb->SendBulkString(node.ip);
(*cntx)->SendBulkString("port"); rb->SendBulkString("port");
(*cntx)->SendLong(node.port); rb->SendLong(node.port);
(*cntx)->SendBulkString("role"); rb->SendBulkString("role");
(*cntx)->SendBulkString(role); rb->SendBulkString(role);
(*cntx)->SendBulkString("replication-offset"); rb->SendBulkString("replication-offset");
(*cntx)->SendLong(0); rb->SendLong(0);
(*cntx)->SendBulkString("health"); rb->SendBulkString("health");
(*cntx)->SendBulkString("online"); rb->SendBulkString("online");
}; };
(*cntx)->StartArray(config.size()); rb->StartArray(config.size());
for (const auto& shard : config) { for (const auto& shard : config) {
(*cntx)->StartArray(kEntrySize); rb->StartArray(kEntrySize);
(*cntx)->SendBulkString("slots"); rb->SendBulkString("slots");
(*cntx)->StartArray(shard.slot_ranges.size() * 2); rb->StartArray(shard.slot_ranges.size() * 2);
for (const auto& slot_range : shard.slot_ranges) { for (const auto& slot_range : shard.slot_ranges) {
(*cntx)->SendLong(slot_range.start); rb->SendLong(slot_range.start);
(*cntx)->SendLong(slot_range.end); rb->SendLong(slot_range.end);
} }
(*cntx)->SendBulkString("nodes"); rb->SendBulkString("nodes");
(*cntx)->StartArray(1 + shard.replicas.size()); rb->StartArray(1 + shard.replicas.size());
WriteNode(shard.master, "master"); WriteNode(shard.master, "master");
for (const auto& replica : shard.replicas) { for (const auto& replica : shard.replicas) {
WriteNode(replica, "replica"); WriteNode(replica, "replica");
@ -161,19 +163,20 @@ void ClusterFamily::ClusterShards(ConnectionContext* cntx) {
} else if (tl_cluster_config != nullptr) { } else if (tl_cluster_config != nullptr) {
return ClusterShardsImpl(tl_cluster_config->GetConfig(), cntx); return ClusterShardsImpl(tl_cluster_config->GetConfig(), cntx);
} else { } else {
return (*cntx)->SendError(kClusterNotConfigured); return cntx->SendError(kClusterNotConfigured);
} }
} }
namespace { namespace {
void ClusterSlotsImpl(const ClusterShards& config, ConnectionContext* cntx) { void ClusterSlotsImpl(const ClusterShards& config, ConnectionContext* cntx) {
// For more details https://redis.io/commands/cluster-slots/ // For more details https://redis.io/commands/cluster-slots/
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
auto WriteNode = [&](const Node& node) { auto WriteNode = [&](const Node& node) {
constexpr unsigned int kNodeSize = 3; constexpr unsigned int kNodeSize = 3;
(*cntx)->StartArray(kNodeSize); rb->StartArray(kNodeSize);
(*cntx)->SendBulkString(node.ip); rb->SendBulkString(node.ip);
(*cntx)->SendLong(node.port); rb->SendLong(node.port);
(*cntx)->SendBulkString(node.id); rb->SendBulkString(node.id);
}; };
unsigned int slot_ranges = 0; unsigned int slot_ranges = 0;
@ -181,14 +184,14 @@ void ClusterSlotsImpl(const ClusterShards& config, ConnectionContext* cntx) {
slot_ranges += shard.slot_ranges.size(); slot_ranges += shard.slot_ranges.size();
} }
(*cntx)->StartArray(slot_ranges); rb->StartArray(slot_ranges);
for (const auto& shard : config) { for (const auto& shard : config) {
for (const auto& slot_range : shard.slot_ranges) { for (const auto& slot_range : shard.slot_ranges) {
const unsigned int array_size = const unsigned int array_size =
/* slot-start, slot-end */ 2 + /* master */ 1 + /* replicas */ shard.replicas.size(); /* slot-start, slot-end */ 2 + /* master */ 1 + /* replicas */ shard.replicas.size();
(*cntx)->StartArray(array_size); rb->StartArray(array_size);
(*cntx)->SendLong(slot_range.start); rb->SendLong(slot_range.start);
(*cntx)->SendLong(slot_range.end); rb->SendLong(slot_range.end);
WriteNode(shard.master); WriteNode(shard.master);
for (const auto& replica : shard.replicas) { for (const auto& replica : shard.replicas) {
WriteNode(replica); WriteNode(replica);
@ -204,7 +207,7 @@ void ClusterFamily::ClusterSlots(ConnectionContext* cntx) {
} else if (tl_cluster_config != nullptr) { } else if (tl_cluster_config != nullptr) {
return ClusterSlotsImpl(tl_cluster_config->GetConfig(), cntx); return ClusterSlotsImpl(tl_cluster_config->GetConfig(), cntx);
} else { } else {
return (*cntx)->SendError(kClusterNotConfigured); return cntx->SendError(kClusterNotConfigured);
} }
} }
@ -247,7 +250,8 @@ void ClusterNodesImpl(const ClusterShards& config, string_view my_id, Connection
} }
} }
return (*cntx)->SendBulkString(result); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return rb->SendBulkString(result);
} }
} // namespace } // namespace
@ -257,7 +261,7 @@ void ClusterFamily::ClusterNodes(ConnectionContext* cntx) {
} else if (tl_cluster_config != nullptr) { } else if (tl_cluster_config != nullptr) {
return ClusterNodesImpl(tl_cluster_config->GetConfig(), server_family_->master_id(), cntx); return ClusterNodesImpl(tl_cluster_config->GetConfig(), server_family_->master_id(), cntx);
} else { } else {
return (*cntx)->SendError(kClusterNotConfigured); return cntx->SendError(kClusterNotConfigured);
} }
} }
@ -309,7 +313,8 @@ void ClusterInfoImpl(const ClusterShards& config, ConnectionContext* cntx) {
append("cluster_stats_messages_pong_received", 1); append("cluster_stats_messages_pong_received", 1);
append("cluster_stats_messages_meet_received", 0); append("cluster_stats_messages_meet_received", 0);
append("cluster_stats_messages_received", 1); append("cluster_stats_messages_received", 1);
(*cntx)->SendBulkString(msg); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendBulkString(msg);
} }
} // namespace } // namespace
@ -325,11 +330,11 @@ void ClusterFamily::ClusterInfo(ConnectionContext* cntx) {
void ClusterFamily::KeySlot(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::KeySlot(CmdArgList args, ConnectionContext* cntx) {
if (args.size() != 2) { if (args.size() != 2) {
return (*cntx)->SendError(WrongNumArgsError("CLUSTER KEYSLOT")); return cntx->SendError(WrongNumArgsError("CLUSTER KEYSLOT"));
} }
SlotId id = ClusterConfig::KeySlot(ArgS(args, 1)); SlotId id = ClusterConfig::KeySlot(ArgS(args, 1));
return (*cntx)->SendLong(id); return cntx->SendLong(id);
} }
void ClusterFamily::Cluster(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::Cluster(CmdArgList args, ConnectionContext* cntx) {
@ -340,7 +345,7 @@ void ClusterFamily::Cluster(CmdArgList args, ConnectionContext* cntx) {
string_view sub_cmd = ArgS(args, 0); string_view sub_cmd = ArgS(args, 0);
if (!ClusterConfig::IsEnabledOrEmulated()) { if (!ClusterConfig::IsEnabledOrEmulated()) {
return (*cntx)->SendError(kClusterDisabled); return cntx->SendError(kClusterDisabled);
} }
if (sub_cmd == "HELP") { if (sub_cmd == "HELP") {
@ -356,31 +361,31 @@ void ClusterFamily::Cluster(CmdArgList args, ConnectionContext* cntx) {
} else if (sub_cmd == "KEYSLOT") { } else if (sub_cmd == "KEYSLOT") {
return KeySlot(args, cntx); return KeySlot(args, cntx);
} else { } else {
return (*cntx)->SendError(facade::UnknownSubCmd(sub_cmd, "CLUSTER"), facade::kSyntaxErrType); return cntx->SendError(facade::UnknownSubCmd(sub_cmd, "CLUSTER"), facade::kSyntaxErrType);
} }
} }
void ClusterFamily::ReadOnly(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::ReadOnly(CmdArgList args, ConnectionContext* cntx) {
if (!ClusterConfig::IsEmulated()) { if (!ClusterConfig::IsEmulated()) {
return (*cntx)->SendError(kClusterDisabled); return cntx->SendError(kClusterDisabled);
} }
(*cntx)->SendOk(); cntx->SendOk();
} }
void ClusterFamily::ReadWrite(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::ReadWrite(CmdArgList args, ConnectionContext* cntx) {
if (!ClusterConfig::IsEmulated()) { if (!ClusterConfig::IsEmulated()) {
return (*cntx)->SendError(kClusterDisabled); return cntx->SendError(kClusterDisabled);
} }
(*cntx)->SendOk(); cntx->SendOk();
} }
void ClusterFamily::DflyCluster(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflyCluster(CmdArgList args, ConnectionContext* cntx) {
if (!ClusterConfig::IsEnabledOrEmulated()) { if (!ClusterConfig::IsEnabledOrEmulated()) {
return (*cntx)->SendError(kClusterDisabled); return cntx->SendError(kClusterDisabled);
} }
if (cntx->conn() && !cntx->conn()->IsPrivileged()) { if (cntx->conn() && !cntx->conn()->IsPrivileged()) {
return (*cntx)->SendError(kDflyClusterCmdPort); return cntx->SendError(kDflyClusterCmdPort);
} }
ToUpper(&args[0]); ToUpper(&args[0]);
@ -400,14 +405,15 @@ void ClusterFamily::DflyCluster(CmdArgList args, ConnectionContext* cntx) {
return DflySlotMigrationStatus(args, cntx); return DflySlotMigrationStatus(args, cntx);
} }
return (*cntx)->SendError(UnknownSubCmd(sub_cmd, "DFLYCLUSTER"), kSyntaxErrType); return cntx->SendError(UnknownSubCmd(sub_cmd, "DFLYCLUSTER"), kSyntaxErrType);
} }
void ClusterFamily::DflyClusterMyId(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflyClusterMyId(CmdArgList args, ConnectionContext* cntx) {
if (!args.empty()) { if (!args.empty()) {
return (*cntx)->SendError(WrongNumArgsError("DFLYCLUSTER MYID")); return cntx->SendError(WrongNumArgsError("DFLYCLUSTER MYID"));
} }
(*cntx)->SendBulkString(server_family_->master_id()); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendBulkString(server_family_->master_id());
} }
namespace { namespace {
@ -533,17 +539,18 @@ void ClusterFamily::DflyClusterConfig(CmdArgList args, ConnectionContext* cntx)
void ClusterFamily::DflyClusterGetSlotInfo(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflyClusterGetSlotInfo(CmdArgList args, ConnectionContext* cntx) {
CmdArgParser parser(args); CmdArgParser parser(args);
parser.ExpectTag("SLOTS"); parser.ExpectTag("SLOTS");
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
vector<std::pair<SlotId, SlotStats>> slots_stats; vector<std::pair<SlotId, SlotStats>> slots_stats;
do { do {
auto sid = parser.Next<uint32_t>(); auto sid = parser.Next<uint32_t>();
if (sid > ClusterConfig::kMaxSlotNum) if (sid > ClusterConfig::kMaxSlotNum)
return (*cntx)->SendError("Invalid slot id"); return rb->SendError("Invalid slot id");
slots_stats.emplace_back(sid, SlotStats{}); slots_stats.emplace_back(sid, SlotStats{});
} while (parser.HasNext()); } while (parser.HasNext());
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return rb->SendError(err->MakeReply());
Mutex mu; Mutex mu;
@ -560,36 +567,34 @@ void ClusterFamily::DflyClusterGetSlotInfo(CmdArgList args, ConnectionContext* c
shard_set->pool()->AwaitFiberOnAll(std::move(cb)); shard_set->pool()->AwaitFiberOnAll(std::move(cb));
(*cntx)->StartArray(slots_stats.size()); rb->StartArray(slots_stats.size());
for (const auto& slot_data : slots_stats) { for (const auto& slot_data : slots_stats) {
(*cntx)->StartArray(7); rb->StartArray(7);
(*cntx)->SendLong(slot_data.first); rb->SendLong(slot_data.first);
(*cntx)->SendBulkString("key_count"); rb->SendBulkString("key_count");
(*cntx)->SendLong(static_cast<long>(slot_data.second.key_count)); rb->SendLong(static_cast<long>(slot_data.second.key_count));
(*cntx)->SendBulkString("total_reads"); rb->SendBulkString("total_reads");
(*cntx)->SendLong(static_cast<long>(slot_data.second.total_reads)); rb->SendLong(static_cast<long>(slot_data.second.total_reads));
(*cntx)->SendBulkString("total_writes"); rb->SendBulkString("total_writes");
(*cntx)->SendLong(static_cast<long>(slot_data.second.total_writes)); rb->SendLong(static_cast<long>(slot_data.second.total_writes));
} }
} }
void ClusterFamily::DflyClusterFlushSlots(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflyClusterFlushSlots(CmdArgList args, ConnectionContext* cntx) {
SinkReplyBuilder* rb = cntx->reply_builder();
SlotSet slots; SlotSet slots;
slots.reserve(args.size()); slots.reserve(args.size());
for (size_t i = 0; i < args.size(); ++i) { for (size_t i = 0; i < args.size(); ++i) {
unsigned slot; unsigned slot;
if (!absl::SimpleAtoi(ArgS(args, i), &slot) || (slot > ClusterConfig::kMaxSlotNum)) { if (!absl::SimpleAtoi(ArgS(args, i), &slot) || (slot > ClusterConfig::kMaxSlotNum)) {
return rb->SendError(kSyntaxErrType); return cntx->SendError(kSyntaxErrType);
} }
slots.insert(static_cast<SlotId>(slot)); slots.insert(static_cast<SlotId>(slot));
} }
DeleteSlots(slots); DeleteSlots(slots);
return rb->SendOk(); return cntx->SendOk();
} }
void ClusterFamily::DflyClusterStartSlotMigration(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflyClusterStartSlotMigration(CmdArgList args, ConnectionContext* cntx) {
@ -601,26 +606,24 @@ void ClusterFamily::DflyClusterStartSlotMigration(CmdArgList args, ConnectionCon
slots.emplace_back(SlotRange{slot_start, slot_end}); slots.emplace_back(SlotRange{slot_start, slot_end});
} while (parser.HasNext()); } while (parser.HasNext());
SinkReplyBuilder* rb = cntx->reply_builder();
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return rb->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
auto* node = AddMigration(std::string(host_ip), port, std::move(slots)); auto* node = AddMigration(std::string(host_ip), port, std::move(slots));
if (!node) { if (!node) {
return rb->SendError("Can't start the migration, another one is in progress"); return cntx->SendError("Can't start the migration, another one is in progress");
} }
node->Start(cntx); node->Start(cntx);
return rb->SendOk(); return cntx->SendOk();
} }
void ClusterFamily::DflySlotMigrationStatus(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflySlotMigrationStatus(CmdArgList args, ConnectionContext* cntx) {
CmdArgParser parser(args); CmdArgParser parser(args);
auto [host_ip, port] = parser.Next<std::string_view, uint16_t>(); auto [host_ip, port] = parser.Next<std::string_view, uint16_t>();
SinkReplyBuilder* rb = cntx->reply_builder();
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return rb->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
auto state = [&] { auto state = [&] {
lock_guard lk(migrations_jobs_mu_); lock_guard lk(migrations_jobs_mu_);
@ -647,7 +650,7 @@ void ClusterFamily::DflySlotMigrationStatus(CmdArgList args, ConnectionContext*
return "UNDEFINED_STATE"sv; return "UNDEFINED_STATE"sv;
}(); }();
return rb->SendSimpleString(state_str); return cntx->SendSimpleString(state_str);
} }
void ClusterFamily::DflyMigrate(CmdArgList args, ConnectionContext* cntx) { void ClusterFamily::DflyMigrate(CmdArgList args, ConnectionContext* cntx) {
@ -657,7 +660,7 @@ void ClusterFamily::DflyMigrate(CmdArgList args, ConnectionContext* cntx) {
if (sub_cmd == "CONF") { if (sub_cmd == "CONF") {
MigrationConf(args, cntx); MigrationConf(args, cntx);
} else { } else {
(*cntx)->SendError(facade::UnknownSubCmd(sub_cmd, "DFLYMIGRATE"), facade::kSyntaxErrType); cntx->SendError(facade::UnknownSubCmd(sub_cmd, "DFLYMIGRATE"), facade::kSyntaxErrType);
} }
} }
@ -687,10 +690,10 @@ void ClusterFamily::MigrationConf(CmdArgList args, ConnectionContext* cntx) {
} while (parser.HasNext()); } while (parser.HasNext());
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
if (!tl_cluster_config) { if (!tl_cluster_config) {
(*cntx)->SendError(kClusterNotConfigured); cntx->SendError(kClusterNotConfigured);
return; return;
} }
@ -699,7 +702,7 @@ void ClusterFamily::MigrationConf(CmdArgList args, ConnectionContext* cntx) {
if (!tl_cluster_config->IsMySlot(i)) { if (!tl_cluster_config->IsMySlot(i)) {
VLOG(1) << "Invalid migration slot " << i << " in range " << migration_range.start << ':' VLOG(1) << "Invalid migration slot " << i << " in range " << migration_range.start << ':'
<< migration_range.end; << migration_range.end;
(*cntx)->SendError("Invalid slots range"); cntx->SendError("Invalid slots range");
return; return;
} }
} }
@ -707,7 +710,7 @@ void ClusterFamily::MigrationConf(CmdArgList args, ConnectionContext* cntx) {
cntx->conn()->SetName("slot_migration_ctrl"); cntx->conn()->SetName("slot_migration_ctrl");
(*cntx)->SendLong(shard_set->size()); cntx->SendLong(shard_set->size());
return; return;
} }

View file

@ -33,7 +33,7 @@ error_code ClusterSlotMigration::Start(ConnectionContext* cntx) {
auto check_connection_error = [this, &cntx](error_code ec, const char* msg) -> error_code { auto check_connection_error = [this, &cntx](error_code ec, const char* msg) -> error_code {
if (ec) { if (ec) {
(*cntx)->SendError(absl::StrCat(msg, ec.message())); cntx->SendError(absl::StrCat(msg, ec.message()));
} }
return ec; return ec;
}; };

View file

@ -177,10 +177,11 @@ void ConnectionContext::ChangeSubscription(bool to_add, bool to_reply, CmdArgLis
if (to_reply) { if (to_reply) {
for (size_t i = 0; i < result.size(); ++i) { for (size_t i = 0; i < result.size(); ++i) {
const char* action[2] = {"unsubscribe", "subscribe"}; const char* action[2] = {"unsubscribe", "subscribe"};
(*this)->StartCollection(3, RedisReplyBuilder::CollectionType::PUSH); auto rb = static_cast<RedisReplyBuilder*>(reply_builder());
(*this)->SendBulkString(action[to_add]); rb->StartCollection(3, RedisReplyBuilder::CollectionType::PUSH);
(*this)->SendBulkString(ArgS(args, i)); rb->SendBulkString(action[to_add]);
(*this)->SendLong(result[i]); rb->SendBulkString(ArgS(args, i));
rb->SendLong(result[i]);
} }
} }
} }
@ -224,13 +225,14 @@ void ConnectionContext::PUnsubscribeAll(bool to_reply) {
void ConnectionContext::SendSubscriptionChangedResponse(string_view action, void ConnectionContext::SendSubscriptionChangedResponse(string_view action,
std::optional<string_view> topic, std::optional<string_view> topic,
unsigned count) { unsigned count) {
(*this)->StartCollection(3, RedisReplyBuilder::CollectionType::PUSH); auto rb = static_cast<RedisReplyBuilder*>(reply_builder());
(*this)->SendBulkString(action); rb->StartCollection(3, RedisReplyBuilder::CollectionType::PUSH);
rb->SendBulkString(action);
if (topic.has_value()) if (topic.has_value())
(*this)->SendBulkString(topic.value()); rb->SendBulkString(topic.value());
else else
(*this)->SendNull(); rb->SendNull();
(*this)->SendLong(count); rb->SendLong(count);
} }
size_t ConnectionContext::UsedMemory() const { size_t ConnectionContext::UsedMemory() const {

View file

@ -251,7 +251,8 @@ void DebugCmd::Run(CmdArgList args) {
"HELP", "HELP",
" Prints this help.", " Prints this help.",
}; };
return (*cntx_)->SendSimpleStrArr(help_arr); auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
return rb->SendSimpleStrArr(help_arr);
} }
VLOG(1) << "subcmd " << subcmd; VLOG(1) << "subcmd " << subcmd;
@ -298,7 +299,7 @@ void DebugCmd::Run(CmdArgList args) {
} }
string reply = UnknownSubCmd(subcmd, "DEBUG"); string reply = UnknownSubCmd(subcmd, "DEBUG");
return (*cntx_)->SendError(reply, kSyntaxErrType); return cntx_->SendError(reply, kSyntaxErrType);
} }
void DebugCmd::Reload(CmdArgList args) { void DebugCmd::Reload(CmdArgList args) {
@ -312,7 +313,7 @@ void DebugCmd::Reload(CmdArgList args) {
if (opt == "NOSAVE") { if (opt == "NOSAVE") {
save = false; save = false;
} else { } else {
return (*cntx_)->SendError("DEBUG RELOAD only supports the NOSAVE options."); return cntx_->SendError("DEBUG RELOAD only supports the NOSAVE options.");
} }
} }
@ -322,7 +323,7 @@ void DebugCmd::Reload(CmdArgList args) {
GenericError ec = sf_.DoSave(); GenericError ec = sf_.DoSave();
if (ec) { if (ec) {
return (*cntx_)->SendError(ec.Format()); return cntx_->SendError(ec.Format());
} }
} }
@ -335,24 +336,25 @@ void DebugCmd::Replica(CmdArgList args) {
ToUpper(&args[0]); ToUpper(&args[0]);
string_view opt = ArgS(args, 0); string_view opt = ArgS(args, 0);
auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
if (opt == "PAUSE" || opt == "RESUME") { if (opt == "PAUSE" || opt == "RESUME") {
sf_.PauseReplication(opt == "PAUSE"); sf_.PauseReplication(opt == "PAUSE");
return (*cntx_)->SendOk(); return rb->SendOk();
} else if (opt == "OFFSET") { } else if (opt == "OFFSET") {
const auto offset_info = sf_.GetReplicaOffsetInfo(); const auto offset_info = sf_.GetReplicaOffsetInfo();
if (offset_info) { if (offset_info) {
(*cntx_)->StartArray(2); rb->StartArray(2);
(*cntx_)->SendBulkString(offset_info.value().sync_id); rb->SendBulkString(offset_info.value().sync_id);
(*cntx_)->StartArray(offset_info.value().flow_offsets.size()); rb->StartArray(offset_info.value().flow_offsets.size());
for (uint64_t offset : offset_info.value().flow_offsets) { for (uint64_t offset : offset_info.value().flow_offsets) {
(*cntx_)->SendLong(offset); rb->SendLong(offset);
} }
return; return;
} else { } else {
return (*cntx_)->SendError("I am master"); return rb->SendError("I am master");
} }
} }
return (*cntx_)->SendError(UnknownSubCmd("replica", "DEBUG")); return rb->SendError(UnknownSubCmd("replica", "DEBUG"));
} }
void DebugCmd::Load(string_view filename) { void DebugCmd::Load(string_view filename) {
@ -389,22 +391,22 @@ void DebugCmd::Load(string_view filename) {
ec = fut_ec.get(); ec = fut_ec.get();
if (ec) { if (ec) {
LOG(INFO) << "Could not load file " << ec.message(); LOG(INFO) << "Could not load file " << ec.message();
return (*cntx_)->SendError(ec.message()); return cntx_->SendError(ec.message());
} }
} }
(*cntx_)->SendOk(); cntx_->SendOk();
} }
optional<DebugCmd::PopulateOptions> DebugCmd::ParsePopulateArgs(CmdArgList args) { optional<DebugCmd::PopulateOptions> DebugCmd::ParsePopulateArgs(CmdArgList args) {
if (args.size() < 2 || args.size() > 8) { if (args.size() < 2 || args.size() > 8) {
(*cntx_)->SendError(UnknownSubCmd("populate", "DEBUG")); cntx_->SendError(UnknownSubCmd("populate", "DEBUG"));
return nullopt; return nullopt;
} }
PopulateOptions options; PopulateOptions options;
if (!absl::SimpleAtoi(ArgS(args, 1), &options.total_count)) { if (!absl::SimpleAtoi(ArgS(args, 1), &options.total_count)) {
(*cntx_)->SendError(kUintErr); cntx_->SendError(kUintErr);
return nullopt; return nullopt;
} }
@ -414,7 +416,7 @@ optional<DebugCmd::PopulateOptions> DebugCmd::ParsePopulateArgs(CmdArgList args)
if (args.size() > 3) { if (args.size() > 3) {
if (!absl::SimpleAtoi(ArgS(args, 3), &options.val_size)) { if (!absl::SimpleAtoi(ArgS(args, 3), &options.val_size)) {
(*cntx_)->SendError(kUintErr); cntx_->SendError(kUintErr);
return nullopt; return nullopt;
} }
} }
@ -426,7 +428,7 @@ optional<DebugCmd::PopulateOptions> DebugCmd::ParsePopulateArgs(CmdArgList args)
options.populate_random_values = true; options.populate_random_values = true;
} else if (str == "SLOTS") { } else if (str == "SLOTS") {
if (args.size() < index + 3) { if (args.size() < index + 3) {
(*cntx_)->SendError(kSyntaxErr); cntx_->SendError(kSyntaxErr);
return nullopt; return nullopt;
} }
@ -443,19 +445,19 @@ optional<DebugCmd::PopulateOptions> DebugCmd::ParsePopulateArgs(CmdArgList args)
auto start = parse_slot(ArgS(args, ++index)); auto start = parse_slot(ArgS(args, ++index));
if (start.status() != facade::OpStatus::OK) { if (start.status() != facade::OpStatus::OK) {
(*cntx_)->SendError(start.status()); cntx_->SendError(start.status());
return nullopt; return nullopt;
} }
auto end = parse_slot(ArgS(args, ++index)); auto end = parse_slot(ArgS(args, ++index));
if (end.status() != facade::OpStatus::OK) { if (end.status() != facade::OpStatus::OK) {
(*cntx_)->SendError(end.status()); cntx_->SendError(end.status());
return nullopt; return nullopt;
} }
options.slot_range = ClusterConfig::SlotRange{.start = static_cast<SlotId>(start.value()), options.slot_range = ClusterConfig::SlotRange{.start = static_cast<SlotId>(start.value()),
.end = static_cast<SlotId>(end.value())}; .end = static_cast<SlotId>(end.value())};
} else { } else {
(*cntx_)->SendError(kSyntaxErr); cntx_->SendError(kSyntaxErr);
return nullopt; return nullopt;
} }
} }
@ -491,7 +493,7 @@ void DebugCmd::Populate(CmdArgList args) {
for (auto& fb : fb_arr) for (auto& fb : fb_arr)
fb.Join(); fb.Join();
(*cntx_)->SendOk(); cntx_->SendOk();
} }
void DebugCmd::PopulateRangeFiber(uint64_t from, uint64_t num_of_keys, void DebugCmd::PopulateRangeFiber(uint64_t from, uint64_t num_of_keys,
@ -607,7 +609,7 @@ void DebugCmd::Inspect(string_view key) {
string resp; string resp;
if (!res.found) { if (!res.found) {
(*cntx_)->SendError(kKeyNotFoundErr); cntx_->SendError(kKeyNotFoundErr);
return; return;
} }
@ -625,7 +627,7 @@ void DebugCmd::Inspect(string_view key) {
if (res.lock_status != ObjInfo::NONE) { if (res.lock_status != ObjInfo::NONE) {
StrAppend(&resp, " lock:", res.lock_status == ObjInfo::X ? "x" : "s"); StrAppend(&resp, " lock:", res.lock_status == ObjInfo::X ? "x" : "s");
} }
(*cntx_)->SendSimpleString(resp); cntx_->SendSimpleString(resp);
} }
void DebugCmd::Watched() { void DebugCmd::Watched() {
@ -647,12 +649,13 @@ void DebugCmd::Watched() {
} }
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
shard_set->RunBlockingInParallel(cb); shard_set->RunBlockingInParallel(cb);
(*cntx_)->StartArray(4); rb->StartArray(4);
(*cntx_)->SendBulkString("awaked"); rb->SendBulkString("awaked");
(*cntx_)->SendStringArr(awaked_trans); rb->SendStringArr(awaked_trans);
(*cntx_)->SendBulkString("watched"); rb->SendBulkString("watched");
(*cntx_)->SendStringArr(watched_keys); rb->SendStringArr(watched_keys);
} }
void DebugCmd::TxAnalysis() { void DebugCmd::TxAnalysis() {
@ -708,8 +711,8 @@ void DebugCmd::TxAnalysis() {
shard_set->RunBriefInParallel(cb); shard_set->RunBriefInParallel(cb);
(*cntx_)->SendSimpleString(absl::StrCat("queue_len:", queue_len.load(), cntx_->SendSimpleString(absl::StrCat("queue_len:", queue_len.load(), "armed: ", armed_cnt.load(),
"armed: ", armed_cnt.load(), " free:", free_cnt.load())); " free:", free_cnt.load()));
} }
void DebugCmd::ObjHist() { void DebugCmd::ObjHist() {
@ -735,7 +738,8 @@ void DebugCmd::ObjHist() {
} }
absl::StrAppend(&result, "___end object histogram___\n"); absl::StrAppend(&result, "___end object histogram___\n");
(*cntx_)->SendBulkString(result); auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
rb->SendBulkString(result);
} }
void DebugCmd::Stacktrace() { void DebugCmd::Stacktrace() {
@ -744,7 +748,7 @@ void DebugCmd::Stacktrace() {
std::unique_lock lk(m); std::unique_lock lk(m);
fb2::detail::FiberInterface::PrintAllFiberStackTraces(); fb2::detail::FiberInterface::PrintAllFiberStackTraces();
}); });
(*cntx_)->SendOk(); cntx_->SendOk();
} }
void DebugCmd::Shards() { void DebugCmd::Shards() {
@ -798,8 +802,8 @@ void DebugCmd::Shards() {
#undef ADD_STAT #undef ADD_STAT
#undef MAXMIN_STAT #undef MAXMIN_STAT
auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
(*cntx_)->SendBulkString(out); rb->SendBulkString(out);
} }
} // namespace dfly } // namespace dfly

View file

@ -105,8 +105,6 @@ DflyCmd::DflyCmd(ServerFamily* server_family) : sf_(server_family) {
} }
void DflyCmd::Run(CmdArgList args, ConnectionContext* cntx) { void DflyCmd::Run(CmdArgList args, ConnectionContext* cntx) {
RedisReplyBuilder* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
DCHECK_GE(args.size(), 1u); DCHECK_GE(args.size(), 1u);
ToUpper(&args[0]); ToUpper(&args[0]);
string_view sub_cmd = ArgS(args, 0); string_view sub_cmd = ArgS(args, 0);
@ -143,7 +141,7 @@ void DflyCmd::Run(CmdArgList args, ConnectionContext* cntx) {
return ReplicaOffset(args, cntx); return ReplicaOffset(args, cntx);
} }
rb->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
} }
#if 0 #if 0
@ -395,7 +393,7 @@ void DflyCmd::TakeOver(CmdArgList args, ConnectionContext* cntx) {
parser.Next(); parser.Next();
float timeout = parser.Next<float>(); float timeout = parser.Next<float>();
if (timeout < 0) { if (timeout < 0) {
return (*cntx)->SendError("timeout is negative"); return cntx->SendError("timeout is negative");
} }
bool save_flag = static_cast<bool>(parser.Check("SAVE").IgnoreCase()); bool save_flag = static_cast<bool>(parser.Check("SAVE").IgnoreCase());
@ -403,7 +401,7 @@ void DflyCmd::TakeOver(CmdArgList args, ConnectionContext* cntx) {
string_view sync_id_str = parser.Next<std::string_view>(); string_view sync_id_str = parser.Next<std::string_view>();
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
VLOG(1) << "Got DFLY TAKEOVER " << sync_id_str << " time out:" << timeout; VLOG(1) << "Got DFLY TAKEOVER " << sync_id_str << " time out:" << timeout;
@ -479,7 +477,7 @@ void DflyCmd::TakeOver(CmdArgList args, ConnectionContext* cntx) {
sf_->service().SwitchState(GlobalState::TAKEN_OVER, GlobalState::ACTIVE); sf_->service().SwitchState(GlobalState::TAKEN_OVER, GlobalState::ACTIVE);
return rb->SendError("Takeover failed!"); return rb->SendError("Takeover failed!");
} }
(*cntx)->SendOk(); cntx->SendOk();
if (save_flag) { if (save_flag) {
VLOG(1) << "Save snapshot after Takeover."; VLOG(1) << "Save snapshot after Takeover.";

View file

@ -687,24 +687,25 @@ void GenericFamily::Del(CmdArgList args, ConnectionContext* cntx) {
mc_builder->SendSimpleString("DELETED"); mc_builder->SendSimpleString("DELETED");
} }
} else { } else {
(*cntx)->SendLong(del_cnt); cntx->SendLong(del_cnt);
} }
} }
void GenericFamily::Ping(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Ping(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
return (*cntx)->SendError(facade::WrongNumArgsError("ping"), kSyntaxErrType); return cntx->SendError(facade::WrongNumArgsError("ping"), kSyntaxErrType);
} }
// We synchronously block here until the engine sends us the payload and notifies that // We synchronously block here until the engine sends us the payload and notifies that
// the I/O operation has been processed. // the I/O operation has been processed.
if (args.size() == 0) { if (args.size() == 0) {
return (*cntx)->SendSimpleString("PONG"); return cntx->SendSimpleString("PONG");
} else { } else {
string_view arg = ArgS(args, 0); string_view arg = ArgS(args, 0);
DVLOG(2) << "Ping " << arg; DVLOG(2) << "Ping " << arg;
return (*cntx)->SendBulkString(arg); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return rb->SendBulkString(arg);
} }
} }
@ -725,7 +726,7 @@ void GenericFamily::Exists(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = transaction->ScheduleSingleHop(std::move(cb));
CHECK_EQ(OpStatus::OK, status); CHECK_EQ(OpStatus::OK, status);
return (*cntx)->SendLong(result.load(memory_order_acquire)); return cntx->SendLong(result.load(memory_order_acquire));
} }
void GenericFamily::Persist(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Persist(CmdArgList args, ConnectionContext* cntx) {
@ -735,9 +736,9 @@ void GenericFamily::Persist(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(move(cb));
if (status == OpStatus::OK) if (status == OpStatus::OK)
(*cntx)->SendLong(1); cntx->SendLong(1);
else else
(*cntx)->SendLong(0); cntx->SendLong(0);
} }
std::optional<int32_t> ParseExpireOptionsOrReply(const CmdArgList args, ConnectionContext* cntx) { std::optional<int32_t> ParseExpireOptionsOrReply(const CmdArgList args, ConnectionContext* cntx) {
@ -754,16 +755,16 @@ std::optional<int32_t> ParseExpireOptionsOrReply(const CmdArgList args, Connecti
} else if (arg_sv == "LT") { } else if (arg_sv == "LT") {
flags |= ExpireFlags::EXPIRE_LT; flags |= ExpireFlags::EXPIRE_LT;
} else { } else {
(*cntx)->SendError(absl::StrCat("Unsupported option: ", arg_sv)); cntx->SendError(absl::StrCat("Unsupported option: ", arg_sv));
return nullopt; return nullopt;
} }
} }
if ((flags & ExpireFlags::EXPIRE_NX) && (flags & ~ExpireFlags::EXPIRE_NX)) { if ((flags & ExpireFlags::EXPIRE_NX) && (flags & ~ExpireFlags::EXPIRE_NX)) {
(*cntx)->SendError("NX and XX, GT or LT options at the same time are not compatible"); cntx->SendError("NX and XX, GT or LT options at the same time are not compatible");
return nullopt; return nullopt;
} }
if ((flags & ExpireFlags::EXPIRE_GT) && (flags & ExpireFlags::EXPIRE_LT)) { if ((flags & ExpireFlags::EXPIRE_GT) && (flags & ExpireFlags::EXPIRE_LT)) {
(*cntx)->SendError("GT and LT options at the same time are not compatible"); cntx->SendError("GT and LT options at the same time are not compatible");
return nullopt; return nullopt;
} }
return flags; return flags;
@ -775,11 +776,11 @@ void GenericFamily::Expire(CmdArgList args, ConnectionContext* cntx) {
int64_t int_arg; int64_t int_arg;
if (!absl::SimpleAtoi(sec, &int_arg)) { if (!absl::SimpleAtoi(sec, &int_arg)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (int_arg > kMaxExpireDeadlineSec || int_arg < -kMaxExpireDeadlineSec) { if (int_arg > kMaxExpireDeadlineSec || int_arg < -kMaxExpireDeadlineSec) {
return (*cntx)->SendError(InvalidExpireTime(cntx->cid->name())); return cntx->SendError(InvalidExpireTime(cntx->cid->name()));
} }
int_arg = std::max<int64_t>(int_arg, -1); int_arg = std::max<int64_t>(int_arg, -1);
@ -794,7 +795,7 @@ void GenericFamily::Expire(CmdArgList args, ConnectionContext* cntx) {
}; };
OpStatus status = cntx->transaction->ScheduleSingleHop(move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(move(cb));
(*cntx)->SendLong(status == OpStatus::OK); cntx->SendLong(status == OpStatus::OK);
} }
void GenericFamily::ExpireAt(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::ExpireAt(CmdArgList args, ConnectionContext* cntx) {
@ -803,7 +804,7 @@ void GenericFamily::ExpireAt(CmdArgList args, ConnectionContext* cntx) {
int64_t int_arg; int64_t int_arg;
if (!absl::SimpleAtoi(sec, &int_arg)) { if (!absl::SimpleAtoi(sec, &int_arg)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
int_arg = std::max<int64_t>(int_arg, 0L); int_arg = std::max<int64_t>(int_arg, 0L);
@ -820,9 +821,9 @@ void GenericFamily::ExpireAt(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (status == OpStatus::OUT_OF_RANGE) { if (status == OpStatus::OUT_OF_RANGE) {
return (*cntx)->SendError(kExpiryOutOfRange); return cntx->SendError(kExpiryOutOfRange);
} else { } else {
(*cntx)->SendLong(status == OpStatus::OK); cntx->SendLong(status == OpStatus::OK);
} }
} }
@ -841,9 +842,10 @@ void GenericFamily::Keys(CmdArgList args, ConnectionContext* cntx) {
cursor = ScanGeneric(cursor, scan_opts, &keys, cntx); cursor = ScanGeneric(cursor, scan_opts, &keys, cntx);
} while (cursor != 0 && keys.size() < output_limit); } while (cursor != 0 && keys.size() < output_limit);
(*cntx)->StartArray(keys.size()); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(keys.size());
for (const auto& k : keys) { for (const auto& k : keys) {
(*cntx)->SendBulkString(k); rb->SendBulkString(k);
} }
} }
@ -853,7 +855,7 @@ void GenericFamily::PexpireAt(CmdArgList args, ConnectionContext* cntx) {
int64_t int_arg; int64_t int_arg;
if (!absl::SimpleAtoi(msec, &int_arg)) { if (!absl::SimpleAtoi(msec, &int_arg)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
int_arg = std::max<int64_t>(int_arg, 0L); int_arg = std::max<int64_t>(int_arg, 0L);
auto expire_options = ParseExpireOptionsOrReply(args.subspan(2), cntx); auto expire_options = ParseExpireOptionsOrReply(args.subspan(2), cntx);
@ -871,9 +873,9 @@ void GenericFamily::PexpireAt(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (status == OpStatus::OUT_OF_RANGE) { if (status == OpStatus::OUT_OF_RANGE) {
return (*cntx)->SendError(kExpiryOutOfRange); return cntx->SendError(kExpiryOutOfRange);
} else { } else {
(*cntx)->SendLong(status == OpStatus::OK); cntx->SendLong(status == OpStatus::OK);
} }
} }
@ -883,7 +885,7 @@ void GenericFamily::Pexpire(CmdArgList args, ConnectionContext* cntx) {
int64_t int_arg; int64_t int_arg;
if (!absl::SimpleAtoi(msec, &int_arg)) { if (!absl::SimpleAtoi(msec, &int_arg)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
int_arg = std::max<int64_t>(int_arg, 0L); int_arg = std::max<int64_t>(int_arg, 0L);
auto expire_options = ParseExpireOptionsOrReply(args.subspan(2), cntx); auto expire_options = ParseExpireOptionsOrReply(args.subspan(2), cntx);
@ -899,9 +901,9 @@ void GenericFamily::Pexpire(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (status == OpStatus::OUT_OF_RANGE) { if (status == OpStatus::OUT_OF_RANGE) {
return (*cntx)->SendError(kExpiryOutOfRange); return cntx->SendError(kExpiryOutOfRange);
} else { } else {
(*cntx)->SendLong(status == OpStatus::OK); cntx->SendLong(status == OpStatus::OK);
} }
} }
@ -925,7 +927,7 @@ void GenericFamily::Stick(CmdArgList args, ConnectionContext* cntx) {
DVLOG(2) << "Stick ts " << transaction->txid(); DVLOG(2) << "Stick ts " << transaction->txid();
uint32_t match_cnt = result.load(memory_order_relaxed); uint32_t match_cnt = result.load(memory_order_relaxed);
(*cntx)->SendLong(match_cnt); cntx->SendLong(match_cnt);
} }
// Used to conditionally store double score // Used to conditionally store double score
@ -1045,11 +1047,11 @@ void GenericFamily::Sort(CmdArgList args, ConnectionContext* cntx) {
} else if (arg == "LIMIT") { } else if (arg == "LIMIT") {
int offset, limit; int offset, limit;
if (i + 2 >= args.size()) { if (i + 2 >= args.size()) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
if (!absl::SimpleAtoi(ArgS(args, i + 1), &offset) || if (!absl::SimpleAtoi(ArgS(args, i + 1), &offset) ||
!absl::SimpleAtoi(ArgS(args, i + 2), &limit)) { !absl::SimpleAtoi(ArgS(args, i + 2), &limit)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
bounds = {offset, limit}; bounds = {offset, limit};
i += 2; i += 2;
@ -1062,10 +1064,11 @@ void GenericFamily::Sort(CmdArgList args, ConnectionContext* cntx) {
}); });
if (fetch_result.status() == OpStatus::WRONG_TYPE) if (fetch_result.status() == OpStatus::WRONG_TYPE)
return (*cntx)->SendError("One or more scores can't be converted into double"); return cntx->SendError("One or more scores can't be converted into double");
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (!fetch_result.ok()) if (!fetch_result.ok())
return (*cntx)->SendEmptyArray(); return rb->SendEmptyArray();
auto result_type = fetch_result.type(); auto result_type = fetch_result.type();
auto sort_call = [cntx, bounds, reversed, result_type](auto& entries) { auto sort_call = [cntx, bounds, reversed, result_type](auto& entries) {
@ -1089,11 +1092,12 @@ void GenericFamily::Sort(CmdArgList args, ConnectionContext* cntx) {
} }
bool is_set = (result_type == OBJ_SET || result_type == OBJ_ZSET); bool is_set = (result_type == OBJ_SET || result_type == OBJ_ZSET);
(*cntx)->StartCollection(std::distance(start_it, end_it), auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
is_set ? RedisReplyBuilder::SET : RedisReplyBuilder::ARRAY); rb->StartCollection(std::distance(start_it, end_it),
is_set ? RedisReplyBuilder::SET : RedisReplyBuilder::ARRAY);
for (auto it = start_it; it != end_it; ++it) { for (auto it = start_it; it != end_it; ++it) {
(*cntx)->SendBulkString(it->key); rb->SendBulkString(it->key);
} }
}; };
std::visit(std::move(sort_call), fetch_result.value()); std::visit(std::move(sort_call), fetch_result.value());
@ -1104,15 +1108,15 @@ void GenericFamily::Restore(CmdArgList args, ConnectionContext* cntx) {
std::string_view serialized_value = ArgS(args, 2); std::string_view serialized_value = ArgS(args, 2);
int rdb_version = 0; int rdb_version = 0;
if (!VerifyFooter(serialized_value, &rdb_version)) { if (!VerifyFooter(serialized_value, &rdb_version)) {
return (*cntx)->SendError("ERR DUMP payload version or checksum are wrong"); return cntx->SendError("ERR DUMP payload version or checksum are wrong");
} }
OpResult<RestoreArgs> restore_args = RestoreArgs::TryFrom(args); OpResult<RestoreArgs> restore_args = RestoreArgs::TryFrom(args);
if (!restore_args) { if (!restore_args) {
if (restore_args.status() == OpStatus::OUT_OF_RANGE) { if (restore_args.status() == OpStatus::OUT_OF_RANGE) {
return (*cntx)->SendError("Invalid IDLETIME value, must be >= 0"); return cntx->SendError("Invalid IDLETIME value, must be >= 0");
} else { } else {
return (*cntx)->SendError(restore_args.status()); return cntx->SendError(restore_args.status());
} }
} }
@ -1124,18 +1128,18 @@ void GenericFamily::Restore(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
if (result.value()) { if (result.value()) {
return (*cntx)->SendOk(); return cntx->SendOk();
} else { } else {
return (*cntx)->SendError("Bad data format"); return cntx->SendError("Bad data format");
} }
} else { } else {
switch (result.status()) { switch (result.status()) {
case OpStatus::KEY_EXISTS: case OpStatus::KEY_EXISTS:
return (*cntx)->SendError("BUSYKEY: key name already exists."); return cntx->SendError("BUSYKEY: key name already exists.");
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
return (*cntx)->SendError("Bad data format"); return cntx->SendError("Bad data format");
default: default:
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
} }
} }
} }
@ -1151,11 +1155,11 @@ void GenericFamily::FieldTtl(CmdArgList args, ConnectionContext* cntx) {
OpResult<long> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<long> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
return; return;
} }
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
void GenericFamily::Move(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Move(CmdArgList args, ConnectionContext* cntx) {
@ -1164,15 +1168,15 @@ void GenericFamily::Move(CmdArgList args, ConnectionContext* cntx) {
int64_t target_db; int64_t target_db;
if (!absl::SimpleAtoi(target_db_sv, &target_db)) { if (!absl::SimpleAtoi(target_db_sv, &target_db)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (target_db < 0 || target_db >= absl::GetFlag(FLAGS_dbnum)) { if (target_db < 0 || target_db >= absl::GetFlag(FLAGS_dbnum)) {
return (*cntx)->SendError(kDbIndOutOfRangeErr); return cntx->SendError(kDbIndOutOfRangeErr);
} }
if (target_db == cntx->db_index()) { if (target_db == cntx->db_index()) {
return (*cntx)->SendError("source and destination objects are the same"); return cntx->SendError("source and destination objects are the same");
} }
OpStatus res = OpStatus::SKIPPED; OpStatus res = OpStatus::SKIPPED;
@ -1194,23 +1198,23 @@ void GenericFamily::Move(CmdArgList args, ConnectionContext* cntx) {
cntx->transaction->ScheduleSingleHop(std::move(cb)); cntx->transaction->ScheduleSingleHop(std::move(cb));
// Exactly one shard will call OpMove. // Exactly one shard will call OpMove.
DCHECK(res != OpStatus::SKIPPED); DCHECK(res != OpStatus::SKIPPED);
(*cntx)->SendLong(res == OpStatus::OK); cntx->SendLong(res == OpStatus::OK);
} }
void GenericFamily::Rename(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Rename(CmdArgList args, ConnectionContext* cntx) {
OpResult<void> st = RenameGeneric(args, false, cntx); OpResult<void> st = RenameGeneric(args, false, cntx);
(*cntx)->SendError(st.status()); cntx->SendError(st.status());
} }
void GenericFamily::RenameNx(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::RenameNx(CmdArgList args, ConnectionContext* cntx) {
OpResult<void> st = RenameGeneric(args, true, cntx); OpResult<void> st = RenameGeneric(args, true, cntx);
OpStatus status = st.status(); OpStatus status = st.status();
if (status == OpStatus::OK) { if (status == OpStatus::OK) {
(*cntx)->SendLong(1); cntx->SendLong(1);
} else if (status == OpStatus::KEY_EXISTS) { } else if (status == OpStatus::KEY_EXISTS) {
(*cntx)->SendLong(0); cntx->SendLong(0);
} else { } else {
(*cntx)->SendError(status); cntx->SendError(status);
} }
} }
@ -1230,18 +1234,18 @@ void GenericFamily::TtlGeneric(CmdArgList args, ConnectionContext* cntx, TimeUni
if (result) { if (result) {
long ttl = (unit == TimeUnit::SEC) ? (result.value() + 500) / 1000 : result.value(); long ttl = (unit == TimeUnit::SEC) ? (result.value() + 500) / 1000 : result.value();
(*cntx)->SendLong(ttl); cntx->SendLong(ttl);
return; return;
} }
switch (result.status()) { switch (result.status()) {
case OpStatus::KEY_NOTFOUND: case OpStatus::KEY_NOTFOUND:
(*cntx)->SendLong(-2); cntx->SendLong(-2);
break; break;
default: default:
LOG_IF(ERROR, result.status() != OpStatus::SKIPPED) LOG_IF(ERROR, result.status() != OpStatus::SKIPPED)
<< "Unexpected status " << result.status(); << "Unexpected status " << result.status();
(*cntx)->SendLong(-1); cntx->SendLong(-1);
break; break;
} }
} }
@ -1250,13 +1254,13 @@ void GenericFamily::Select(CmdArgList args, ConnectionContext* cntx) {
string_view key = ArgS(args, 0); string_view key = ArgS(args, 0);
int64_t index; int64_t index;
if (!absl::SimpleAtoi(key, &index)) { if (!absl::SimpleAtoi(key, &index)) {
return (*cntx)->SendError(kInvalidDbIndErr); return cntx->SendError(kInvalidDbIndErr);
} }
if (ClusterConfig::IsEnabled() && index != 0) { if (ClusterConfig::IsEnabled() && index != 0) {
return (*cntx)->SendError("SELECT is not allowed in cluster mode"); return cntx->SendError("SELECT is not allowed in cluster mode");
} }
if (index < 0 || index >= absl::GetFlag(FLAGS_dbnum)) { if (index < 0 || index >= absl::GetFlag(FLAGS_dbnum)) {
return (*cntx)->SendError(kDbIndOutOfRangeErr); return cntx->SendError(kDbIndOutOfRangeErr);
} }
cntx->conn_state.db_index = index; cntx->conn_state.db_index = index;
auto cb = [index](EngineShard* shard) { auto cb = [index](EngineShard* shard) {
@ -1265,7 +1269,7 @@ void GenericFamily::Select(CmdArgList args, ConnectionContext* cntx) {
}; };
shard_set->RunBriefInParallel(std::move(cb)); shard_set->RunBriefInParallel(std::move(cb));
return (*cntx)->SendOk(); return cntx->SendOk();
} }
void GenericFamily::Dump(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Dump(CmdArgList args, ConnectionContext* cntx) {
@ -1275,12 +1279,13 @@ void GenericFamily::Dump(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
DVLOG(1) << "Dump " << trans->DebugId() << ": " << key << ", dump size " DVLOG(1) << "Dump " << trans->DebugId() << ": " << key << ", dump size "
<< result.value().size(); << result.value().size();
(*cntx)->SendBulkString(*result); rb->SendBulkString(*result);
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
} }
@ -1297,9 +1302,9 @@ void GenericFamily::Type(CmdArgList args, ConnectionContext* cntx) {
}; };
OpResult<int> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<int> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (!result) { if (!result) {
(*cntx)->SendSimpleString("none"); cntx->SendSimpleString("none");
} else { } else {
(*cntx)->SendSimpleString(ObjTypeName(result.value())); cntx->SendSimpleString(ObjTypeName(result.value()));
} }
} }
@ -1311,9 +1316,10 @@ void GenericFamily::Time(CmdArgList args, ConnectionContext* cntx) {
now_usec = absl::GetCurrentTimeNanos() / 1000; now_usec = absl::GetCurrentTimeNanos() / 1000;
} }
(*cntx)->StartArray(2); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendLong(now_usec / 1000000); rb->StartArray(2);
(*cntx)->SendLong(now_usec % 1000000); rb->SendLong(now_usec / 1000000);
rb->SendLong(now_usec % 1000000);
} }
OpResult<void> GenericFamily::RenameGeneric(CmdArgList args, bool skip_exist_dest, OpResult<void> GenericFamily::RenameGeneric(CmdArgList args, bool skip_exist_dest,
@ -1349,7 +1355,8 @@ OpResult<void> GenericFamily::RenameGeneric(CmdArgList args, bool skip_exist_des
void GenericFamily::Echo(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Echo(CmdArgList args, ConnectionContext* cntx) {
string_view key = ArgS(args, 0); string_view key = ArgS(args, 0);
return (*cntx)->SendBulkString(key); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return rb->SendBulkString(key);
} }
void GenericFamily::Scan(CmdArgList args, ConnectionContext* cntx) { void GenericFamily::Scan(CmdArgList args, ConnectionContext* cntx) {
@ -1357,13 +1364,13 @@ void GenericFamily::Scan(CmdArgList args, ConnectionContext* cntx) {
uint64_t cursor = 0; uint64_t cursor = 0;
if (!absl::SimpleAtoi(token, &cursor)) { if (!absl::SimpleAtoi(token, &cursor)) {
return (*cntx)->SendError("invalid cursor"); return cntx->SendError("invalid cursor");
} }
OpResult<ScanOpts> ops = ScanOpts::TryFrom(args.subspan(1)); OpResult<ScanOpts> ops = ScanOpts::TryFrom(args.subspan(1));
if (!ops) { if (!ops) {
DVLOG(1) << "Scan invalid args - return " << ops << " to the user"; DVLOG(1) << "Scan invalid args - return " << ops << " to the user";
return (*cntx)->SendError(ops.status()); return cntx->SendError(ops.status());
} }
ScanOpts scan_op = ops.value(); ScanOpts scan_op = ops.value();
@ -1371,11 +1378,12 @@ void GenericFamily::Scan(CmdArgList args, ConnectionContext* cntx) {
StringVec keys; StringVec keys;
cursor = ScanGeneric(cursor, scan_op, &keys, cntx); cursor = ScanGeneric(cursor, scan_op, &keys, cntx);
(*cntx)->StartArray(2); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendBulkString(absl::StrCat(cursor)); rb->StartArray(2);
(*cntx)->StartArray(keys.size()); rb->SendBulkString(absl::StrCat(cursor));
rb->StartArray(keys.size());
for (const auto& k : keys) { for (const auto& k : keys) {
(*cntx)->SendBulkString(k); rb->SendBulkString(k);
} }
} }

View file

@ -32,20 +32,20 @@ template <typename T> void HandleOpValueResult(const OpResult<T>& result, Connec
"we are only handling types that are integral types in the return types from " "we are only handling types that are integral types in the return types from "
"here"); "here");
if (result) { if (result) {
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} else { } else {
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
(*cntx)->SendError(kWrongTypeErr); cntx->SendError(kWrongTypeErr);
break; break;
case OpStatus::OUT_OF_MEMORY: case OpStatus::OUT_OF_MEMORY:
(*cntx)->SendError(kOutOfMemory); cntx->SendError(kOutOfMemory);
break; break;
case OpStatus::INVALID_VALUE: case OpStatus::INVALID_VALUE:
(*cntx)->SendError(HllFamily::kInvalidHllErr); cntx->SendError(HllFamily::kInvalidHllErr);
break; break;
default: default:
(*cntx)->SendLong(0); // in case we don't have the value we should just send 0 cntx->SendLong(0); // in case we don't have the value we should just send 0
break; break;
} }
} }
@ -275,9 +275,9 @@ void PFMerge(CmdArgList args, ConnectionContext* cntx) {
OpResult<int> result = PFMergeInternal(args, cntx); OpResult<int> result = PFMergeInternal(args, cntx);
if (result.ok()) { if (result.ok()) {
if (result.value() == 0) { if (result.value() == 0) {
(*cntx)->SendOk(); cntx->SendOk();
} else { } else {
(*cntx)->SendError(HllFamily::kInvalidHllErr); cntx->SendError(HllFamily::kInvalidHllErr);
} }
} else { } else {
HandleOpValueResult(result, cntx); HandleOpValueResult(result, cntx);

View file

@ -709,19 +709,20 @@ void HGetGeneric(CmdArgList args, ConnectionContext* cntx, uint8_t getall_mask)
OpResult<vector<string>> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<vector<string>> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
bool is_map = (getall_mask == (VALUES | FIELDS)); bool is_map = (getall_mask == (VALUES | FIELDS));
(*cntx)->SendStringArr(absl::Span<const string>{*result}, rb->SendStringArr(absl::Span<const string>{*result},
is_map ? RedisReplyBuilder::MAP : RedisReplyBuilder::ARRAY); is_map ? RedisReplyBuilder::MAP : RedisReplyBuilder::ARRAY);
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
// HSETEX key tll_sec field value field value ... // HSETEX key tll_sec field value field value ...
void HSetEx(CmdArgList args, ConnectionContext* cntx) { void HSetEx(CmdArgList args, ConnectionContext* cntx) {
if (args.size() % 2 != 0) { if (args.size() % 2 != 0) {
return (*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()), kSyntaxErrType); return cntx->SendError(facade::WrongNumArgsError(cntx->cid->name()), kSyntaxErrType);
} }
string_view key = ArgS(args, 0); string_view key = ArgS(args, 0);
@ -730,7 +731,7 @@ void HSetEx(CmdArgList args, ConnectionContext* cntx) {
constexpr uint32_t kMaxTtl = (1UL << 26); constexpr uint32_t kMaxTtl = (1UL << 26);
if (!absl::SimpleAtoi(ttl_str, &ttl_sec) || ttl_sec == 0 || ttl_sec > kMaxTtl) { if (!absl::SimpleAtoi(ttl_str, &ttl_sec) || ttl_sec == 0 || ttl_sec > kMaxTtl) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
args.remove_prefix(2); args.remove_prefix(2);
@ -743,9 +744,9 @@ void HSetEx(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -761,9 +762,9 @@ void HSetFamily::HDel(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result || result.status() == OpStatus::KEY_NOTFOUND) { if (result || result.status() == OpStatus::KEY_NOTFOUND) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -774,9 +775,9 @@ void HSetFamily::HLen(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -790,9 +791,9 @@ void HSetFamily::HExists(CmdArgList args, ConnectionContext* cntx) {
OpResult<int> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<int> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -806,26 +807,27 @@ void HSetFamily::HMGet(CmdArgList args, ConnectionContext* cntx) {
OpResult<vector<OptStr>> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<vector<OptStr>> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder()); SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder());
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
for (const auto& val : *result) { for (const auto& val : *result) {
if (val) { if (val) {
(*cntx)->SendBulkString(*val); rb->SendBulkString(*val);
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
} }
} else if (result.status() == OpStatus::KEY_NOTFOUND) { } else if (result.status() == OpStatus::KEY_NOTFOUND) {
SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder()); SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder());
(*cntx)->StartArray(args.size()); rb->StartArray(args.size());
for (unsigned i = 0; i < args.size(); ++i) { for (unsigned i = 0; i < args.size(); ++i) {
(*cntx)->SendNull(); rb->SendNull();
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -837,14 +839,15 @@ void HSetFamily::HGet(CmdArgList args, ConnectionContext* cntx) {
return OpGet(t->GetOpArgs(shard), key, field); return OpGet(t->GetOpArgs(shard), key, field);
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendBulkString(*result); rb->SendBulkString(*result);
} else { } else {
if (result.status() == OpStatus::KEY_NOTFOUND) { if (result.status() == OpStatus::KEY_NOTFOUND) {
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
} }
@ -856,7 +859,7 @@ void HSetFamily::HIncrBy(CmdArgList args, ConnectionContext* cntx) {
int64_t ival = 0; int64_t ival = 0;
if (!absl::SimpleAtoi(incrs, &ival)) { if (!absl::SimpleAtoi(incrs, &ival)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
IncrByParam param{ival}; IncrByParam param{ival};
@ -868,17 +871,17 @@ void HSetFamily::HIncrBy(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (status == OpStatus::OK) { if (status == OpStatus::OK) {
(*cntx)->SendLong(get<int64_t>(param)); cntx->SendLong(get<int64_t>(param));
} else { } else {
switch (status) { switch (status) {
case OpStatus::INVALID_VALUE: case OpStatus::INVALID_VALUE:
(*cntx)->SendError("hash value is not an integer"); cntx->SendError("hash value is not an integer");
break; break;
case OpStatus::OUT_OF_RANGE: case OpStatus::OUT_OF_RANGE:
(*cntx)->SendError(kIncrOverflow); cntx->SendError(kIncrOverflow);
break; break;
default: default:
(*cntx)->SendError(status); cntx->SendError(status);
break; break;
} }
} }
@ -891,7 +894,7 @@ void HSetFamily::HIncrByFloat(CmdArgList args, ConnectionContext* cntx) {
double dval = 0; double dval = 0;
if (!absl::SimpleAtod(incrs, &dval)) { if (!absl::SimpleAtod(incrs, &dval)) {
return (*cntx)->SendError(kInvalidFloatErr); return cntx->SendError(kInvalidFloatErr);
} }
IncrByParam param{dval}; IncrByParam param{dval};
@ -903,14 +906,15 @@ void HSetFamily::HIncrByFloat(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (status == OpStatus::OK) { if (status == OpStatus::OK) {
(*cntx)->SendDouble(get<double>(param)); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendDouble(get<double>(param));
} else { } else {
switch (status) { switch (status) {
case OpStatus::INVALID_VALUE: case OpStatus::INVALID_VALUE:
(*cntx)->SendError("hash value is not a float"); cntx->SendError("hash value is not a float");
break; break;
default: default:
(*cntx)->SendError(status); cntx->SendError(status);
break; break;
} }
} }
@ -935,19 +939,19 @@ void HSetFamily::HScan(CmdArgList args, ConnectionContext* cntx) {
uint64_t cursor = 0; uint64_t cursor = 0;
if (!absl::SimpleAtoi(token, &cursor)) { if (!absl::SimpleAtoi(token, &cursor)) {
return (*cntx)->SendError("invalid cursor"); return cntx->SendError("invalid cursor");
} }
// HSCAN key cursor [MATCH pattern] [COUNT count] // HSCAN key cursor [MATCH pattern] [COUNT count]
if (args.size() > 6) { if (args.size() > 6) {
DVLOG(1) << "got " << args.size() << " this is more than it should be"; DVLOG(1) << "got " << args.size() << " this is more than it should be";
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
OpResult<ScanOpts> ops = ScanOpts::TryFrom(args.subspan(2)); OpResult<ScanOpts> ops = ScanOpts::TryFrom(args.subspan(2));
if (!ops) { if (!ops) {
DVLOG(1) << "HScan invalid args - return " << ops << " to the user"; DVLOG(1) << "HScan invalid args - return " << ops << " to the user";
return (*cntx)->SendError(ops.status()); return cntx->SendError(ops.status());
} }
ScanOpts scan_op = ops.value(); ScanOpts scan_op = ops.value();
@ -956,16 +960,17 @@ void HSetFamily::HScan(CmdArgList args, ConnectionContext* cntx) {
return OpScan(t->GetOpArgs(shard), key, &cursor, scan_op); return OpScan(t->GetOpArgs(shard), key, &cursor, scan_op);
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result.status() != OpStatus::WRONG_TYPE) { if (result.status() != OpStatus::WRONG_TYPE) {
(*cntx)->StartArray(2); rb->StartArray(2);
(*cntx)->SendBulkString(absl::StrCat(cursor)); rb->SendBulkString(absl::StrCat(cursor));
(*cntx)->StartArray(result->size()); // Within scan the page type is array rb->StartArray(result->size()); // Within scan the page type is array
for (const auto& k : *result) { for (const auto& k : *result) {
(*cntx)->SendBulkString(k); rb->SendBulkString(k);
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -975,7 +980,7 @@ void HSetFamily::HSet(CmdArgList args, ConnectionContext* cntx) {
string_view cmd{cntx->cid->name()}; string_view cmd{cntx->cid->name()};
if (args.size() % 2 != 1) { if (args.size() % 2 != 1) {
return (*cntx)->SendError(facade::WrongNumArgsError(cmd), kSyntaxErrType); return cntx->SendError(facade::WrongNumArgsError(cmd), kSyntaxErrType);
} }
args.remove_prefix(1); args.remove_prefix(1);
@ -986,9 +991,9 @@ void HSetFamily::HSet(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result && cmd == "HSET") { if (result && cmd == "HSET") {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1002,9 +1007,9 @@ void HSetFamily::HSetNx(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1018,9 +1023,9 @@ void HSetFamily::HStrLen(CmdArgList args, ConnectionContext* cntx) {
OpResult<size_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<size_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1035,7 +1040,7 @@ void StrVecEmplaceBack(StringVec& str_vec, const listpackEntry& lp) {
void HSetFamily::HRandField(CmdArgList args, ConnectionContext* cntx) { void HSetFamily::HRandField(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 3) { if (args.size() > 3) {
DVLOG(1) << "Wrong number of command arguments: " << args.size(); DVLOG(1) << "Wrong number of command arguments: " << args.size();
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
string_view key = ArgS(args, 0); string_view key = ArgS(args, 0);
@ -1043,13 +1048,13 @@ void HSetFamily::HRandField(CmdArgList args, ConnectionContext* cntx) {
bool with_values = false; bool with_values = false;
if ((args.size() > 1) && (!SimpleAtoi(ArgS(args, 1), &count))) { if ((args.size() > 1) && (!SimpleAtoi(ArgS(args, 1), &count))) {
return (*cntx)->SendError("count value is not an integer", kSyntaxErrType); return cntx->SendError("count value is not an integer", kSyntaxErrType);
} }
if (args.size() == 3) { if (args.size() == 3) {
ToUpper(&args[2]); ToUpper(&args[2]);
if (ArgS(args, 2) != "WITHVALUES") if (ArgS(args, 2) != "WITHVALUES")
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
else else
with_values = true; with_values = true;
} }
@ -1133,13 +1138,14 @@ void HSetFamily::HRandField(CmdArgList args, ConnectionContext* cntx) {
return str_vec; return str_vec;
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendStringArr(*result); rb->SendStringArr(*result);
} else if (result.status() == OpStatus::KEY_NOTFOUND) { } else if (result.status() == OpStatus::KEY_NOTFOUND) {
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }

View file

@ -98,20 +98,21 @@ JsonExpression ParseJsonPath(string_view path, error_code* ec) {
template <typename T> template <typename T>
void PrintOptVec(ConnectionContext* cntx, const OpResult<vector<optional<T>>>& result) { void PrintOptVec(ConnectionContext* cntx, const OpResult<vector<optional<T>>>& result) {
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result->empty()) { if (result->empty()) {
(*cntx)->SendNullArray(); rb->SendNullArray();
} else { } else {
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
for (auto& it : *result) { for (auto& it : *result) {
if (it.has_value()) { if (it.has_value()) {
if constexpr (is_floating_point_v<T>) { if constexpr (is_floating_point_v<T>) {
(*cntx)->SendDouble(*it); rb->SendDouble(*it);
} else { } else {
static_assert(is_integral_v<T>, "Integral required."); static_assert(is_integral_v<T>, "Integral required.");
(*cntx)->SendLong(*it); rb->SendLong(*it);
} }
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
} }
} }
@ -362,30 +363,30 @@ size_t CountJsonFields(const JsonType& j) {
return res; return res;
} }
void SendJsonValue(ConnectionContext* cntx, const JsonType& j) { void SendJsonValue(RedisReplyBuilder* rb, const JsonType& j) {
if (j.is_double()) { if (j.is_double()) {
(*cntx)->SendDouble(j.as_double()); rb->SendDouble(j.as_double());
} else if (j.is_number()) { } else if (j.is_number()) {
(*cntx)->SendLong(j.as_integer<long>()); rb->SendLong(j.as_integer<long>());
} else if (j.is_bool()) { } else if (j.is_bool()) {
(*cntx)->SendSimpleString(j.as_bool() ? "true" : "false"); rb->SendSimpleString(j.as_bool() ? "true" : "false");
} else if (j.is_null()) { } else if (j.is_null()) {
(*cntx)->SendNull(); rb->SendNull();
} else if (j.is_string()) { } else if (j.is_string()) {
(*cntx)->SendSimpleString(j.as_string_view()); rb->SendSimpleString(j.as_string_view());
} else if (j.is_object()) { } else if (j.is_object()) {
(*cntx)->StartArray(j.size() + 1); rb->StartArray(j.size() + 1);
(*cntx)->SendSimpleString("{"); rb->SendSimpleString("{");
for (const auto& item : j.object_range()) { for (const auto& item : j.object_range()) {
(*cntx)->StartArray(2); rb->StartArray(2);
(*cntx)->SendSimpleString(item.key()); rb->SendSimpleString(item.key());
SendJsonValue(cntx, item.value()); SendJsonValue(rb, item.value());
} }
} else if (j.is_array()) { } else if (j.is_array()) {
(*cntx)->StartArray(j.size() + 1); rb->StartArray(j.size() + 1);
(*cntx)->SendSimpleString("["); rb->SendSimpleString("[");
for (const auto& item : j.array_range()) { for (const auto& item : j.array_range()) {
SendJsonValue(cntx, item); SendJsonValue(rb, item);
} }
} }
} }
@ -1146,7 +1147,7 @@ void JsonFamily::Set(CmdArgList args, ConnectionContext* cntx) {
} else if (absl::EqualsIgnoreCase(operation_opts, "XX")) { } else if (absl::EqualsIgnoreCase(operation_opts, "XX")) {
is_xx_condition = true; is_xx_condition = true;
} else { } else {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
} }
@ -1157,15 +1158,15 @@ void JsonFamily::Set(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<bool> result = trans->ScheduleSingleHopT(move(cb)); OpResult<bool> result = trans->ScheduleSingleHopT(move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
if (*result) { if (*result) {
(*cntx)->SendSimpleString("OK"); rb->SendSimpleString("OK");
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -1181,7 +1182,7 @@ void JsonFamily::Resp(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1192,13 +1193,14 @@ void JsonFamily::Resp(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<vector<JsonType>> result = trans->ScheduleSingleHopT(move(cb)); OpResult<vector<JsonType>> result = trans->ScheduleSingleHopT(move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
for (const auto& it : *result) { for (const auto& it : *result) {
SendJsonValue(cntx, it); SendJsonValue(rb, it);
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -1208,22 +1210,23 @@ void JsonFamily::Debug(CmdArgList args, ConnectionContext* cntx) {
// The 'MEMORY' sub-command is not supported yet, calling to operation function should be added // The 'MEMORY' sub-command is not supported yet, calling to operation function should be added
// here. // here.
if (absl::EqualsIgnoreCase(command, "help")) { if (absl::EqualsIgnoreCase(command, "help")) {
(*cntx)->StartArray(2); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendSimpleString( rb->StartArray(2);
rb->SendSimpleString(
"JSON.DEBUG FIELDS <key> <path> - report number of fields in the JSON element."); "JSON.DEBUG FIELDS <key> <path> - report number of fields in the JSON element.");
(*cntx)->SendSimpleString("JSON.DEBUG HELP - print help message."); rb->SendSimpleString("JSON.DEBUG HELP - print help message.");
return; return;
} else if (absl::EqualsIgnoreCase(command, "fields")) { } else if (absl::EqualsIgnoreCase(command, "fields")) {
func = &OpFields; func = &OpFields;
} else { } else {
(*cntx)->SendError(facade::UnknownSubCmd(command, "JSON.DEBUG"), facade::kSyntaxErrType); cntx->SendError(facade::UnknownSubCmd(command, "JSON.DEBUG"), facade::kSyntaxErrType);
return; return;
} }
if (args.size() < 3) { if (args.size() < 3) {
(*cntx)->SendError(facade::WrongNumArgsError(cntx->cid->name()), facade::kSyntaxErrType); cntx->SendError(facade::WrongNumArgsError(cntx->cid->name()), facade::kSyntaxErrType);
return; return;
} }
@ -1234,7 +1237,7 @@ void JsonFamily::Debug(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1248,7 +1251,7 @@ void JsonFamily::Debug(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1261,7 +1264,7 @@ void JsonFamily::MGet(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1298,12 +1301,13 @@ void JsonFamily::MGet(CmdArgList args, ConnectionContext* cntx) {
} }
} }
(*cntx)->StartArray(results.size()); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(results.size());
for (auto& it : results) { for (auto& it : results) {
if (!it) { if (!it) {
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
(*cntx)->SendBulkString(*it); rb->SendBulkString(*it);
} }
} }
} }
@ -1317,18 +1321,18 @@ void JsonFamily::ArrIndex(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
optional<JsonType> search_value = JsonFromString(ArgS(args, 2)); optional<JsonType> search_value = JsonFromString(ArgS(args, 2));
if (!search_value) { if (!search_value) {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
if (search_value->is_object() || search_value->is_array()) { if (search_value->is_object() || search_value->is_array()) {
(*cntx)->SendError(kWrongTypeErr); cntx->SendError(kWrongTypeErr);
return; return;
} }
@ -1336,7 +1340,7 @@ void JsonFamily::ArrIndex(CmdArgList args, ConnectionContext* cntx) {
if (args.size() >= 4) { if (args.size() >= 4) {
if (!absl::SimpleAtoi(ArgS(args, 3), &start_index)) { if (!absl::SimpleAtoi(ArgS(args, 3), &start_index)) {
VLOG(1) << "Failed to convert the start index to numeric" << ArgS(args, 3); VLOG(1) << "Failed to convert the start index to numeric" << ArgS(args, 3);
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
} }
@ -1345,7 +1349,7 @@ void JsonFamily::ArrIndex(CmdArgList args, ConnectionContext* cntx) {
if (args.size() >= 5) { if (args.size() >= 5) {
if (!absl::SimpleAtoi(ArgS(args, 4), &end_index)) { if (!absl::SimpleAtoi(ArgS(args, 4), &end_index)) {
VLOG(1) << "Failed to convert the stop index to numeric" << ArgS(args, 4); VLOG(1) << "Failed to convert the stop index to numeric" << ArgS(args, 4);
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
} }
@ -1361,7 +1365,7 @@ void JsonFamily::ArrIndex(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1372,7 +1376,7 @@ void JsonFamily::ArrInsert(CmdArgList args, ConnectionContext* cntx) {
if (!absl::SimpleAtoi(ArgS(args, 2), &index)) { if (!absl::SimpleAtoi(ArgS(args, 2), &index)) {
VLOG(1) << "Failed to convert the following value to numeric: " << ArgS(args, 2); VLOG(1) << "Failed to convert the following value to numeric: " << ArgS(args, 2);
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1380,7 +1384,7 @@ void JsonFamily::ArrInsert(CmdArgList args, ConnectionContext* cntx) {
for (size_t i = 3; i < args.size(); i++) { for (size_t i = 3; i < args.size(); i++) {
optional<JsonType> val = JsonFromString(ArgS(args, i)); optional<JsonType> val = JsonFromString(ArgS(args, i));
if (!val) { if (!val) {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1396,7 +1400,7 @@ void JsonFamily::ArrInsert(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1407,7 +1411,7 @@ void JsonFamily::ArrAppend(CmdArgList args, ConnectionContext* cntx) {
for (size_t i = 2; i < args.size(); ++i) { for (size_t i = 2; i < args.size(); ++i) {
optional<JsonType> converted_val = JsonFromString(ArgS(args, i)); optional<JsonType> converted_val = JsonFromString(ArgS(args, i));
if (!converted_val) { if (!converted_val) {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
append_values.emplace_back(converted_val); append_values.emplace_back(converted_val);
@ -1422,7 +1426,7 @@ void JsonFamily::ArrAppend(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1434,18 +1438,18 @@ void JsonFamily::ArrTrim(CmdArgList args, ConnectionContext* cntx) {
if (!absl::SimpleAtoi(ArgS(args, 2), &start_index)) { if (!absl::SimpleAtoi(ArgS(args, 2), &start_index)) {
VLOG(1) << "Failed to parse array start index"; VLOG(1) << "Failed to parse array start index";
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
if (!absl::SimpleAtoi(ArgS(args, 3), &stop_index)) { if (!absl::SimpleAtoi(ArgS(args, 3), &stop_index)) {
VLOG(1) << "Failed to parse array stop index"; VLOG(1) << "Failed to parse array stop index";
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
if (stop_index < 0) { if (stop_index < 0) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1458,7 +1462,7 @@ void JsonFamily::ArrTrim(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1479,7 +1483,7 @@ void JsonFamily::ArrPop(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1489,17 +1493,18 @@ void JsonFamily::ArrPop(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<vector<OptString>> result = trans->ScheduleSingleHopT(move(cb)); OpResult<vector<OptString>> result = trans->ScheduleSingleHopT(move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
for (auto& it : *result) { for (auto& it : *result) {
if (!it) { if (!it) {
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
(*cntx)->SendSimpleString(*it); rb->SendSimpleString(*it);
} }
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -1515,9 +1520,9 @@ void JsonFamily::Clear(CmdArgList args, ConnectionContext* cntx) {
OpResult<long> result = trans->ScheduleSingleHopT(move(cb)); OpResult<long> result = trans->ScheduleSingleHopT(move(cb));
if (result) { if (result) {
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1540,7 +1545,7 @@ void JsonFamily::StrAppend(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1553,7 +1558,7 @@ void JsonFamily::ObjKeys(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1563,18 +1568,18 @@ void JsonFamily::ObjKeys(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<vector<StringVec>> result = trans->ScheduleSingleHopT(move(cb)); OpResult<vector<StringVec>> result = trans->ScheduleSingleHopT(move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
for (auto& it : *result) { for (auto& it : *result) {
if (it.empty()) { if (it.empty()) {
(*cntx)->SendNullArray(); rb->SendNullArray();
} else { } else {
(*cntx)->SendStringArr(it); rb->SendStringArr(it);
} }
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -1591,7 +1596,7 @@ void JsonFamily::Del(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<long> result = trans->ScheduleSingleHopT(move(cb)); OpResult<long> result = trans->ScheduleSingleHopT(move(cb));
(*cntx)->SendLong(*result); cntx->SendLong(*result);
} }
void JsonFamily::NumIncrBy(CmdArgList args, ConnectionContext* cntx) { void JsonFamily::NumIncrBy(CmdArgList args, ConnectionContext* cntx) {
@ -1601,7 +1606,7 @@ void JsonFamily::NumIncrBy(CmdArgList args, ConnectionContext* cntx) {
double dnum; double dnum;
if (!ParseDouble(num, &dnum)) { if (!ParseDouble(num, &dnum)) {
(*cntx)->SendError(kWrongTypeErr); cntx->SendError(kWrongTypeErr);
return; return;
} }
@ -1613,9 +1618,9 @@ void JsonFamily::NumIncrBy(CmdArgList args, ConnectionContext* cntx) {
OpResult<string> result = trans->ScheduleSingleHopT(move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(move(cb));
if (result) { if (result) {
(*cntx)->SendSimpleString(*result); cntx->SendSimpleString(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1626,7 +1631,7 @@ void JsonFamily::NumMultBy(CmdArgList args, ConnectionContext* cntx) {
double dnum; double dnum;
if (!ParseDouble(num, &dnum)) { if (!ParseDouble(num, &dnum)) {
(*cntx)->SendError(kWrongTypeErr); cntx->SendError(kWrongTypeErr);
return; return;
} }
@ -1638,9 +1643,9 @@ void JsonFamily::NumMultBy(CmdArgList args, ConnectionContext* cntx) {
OpResult<string> result = trans->ScheduleSingleHopT(move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(move(cb));
if (result) { if (result) {
(*cntx)->SendSimpleString(*result); cntx->SendSimpleString(*result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1658,7 +1663,7 @@ void JsonFamily::Toggle(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1671,7 +1676,7 @@ void JsonFamily::Type(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1681,19 +1686,19 @@ void JsonFamily::Type(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<vector<string>> result = trans->ScheduleSingleHopT(move(cb)); OpResult<vector<string>> result = trans->ScheduleSingleHopT(move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
if (result->empty()) { if (result->empty()) {
// When vector is empty, the path doesn't exist in the corresponding json. // When vector is empty, the path doesn't exist in the corresponding json.
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
(*cntx)->SendStringArr(*result); rb->SendStringArr(*result);
} }
} else { } else {
if (result.status() == OpStatus::KEY_NOTFOUND) { if (result.status() == OpStatus::KEY_NOTFOUND) {
(*cntx)->SendNullArray(); rb->SendNullArray();
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
} }
@ -1707,7 +1712,7 @@ void JsonFamily::ArrLen(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1721,7 +1726,7 @@ void JsonFamily::ArrLen(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1734,7 +1739,7 @@ void JsonFamily::ObjLen(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1748,7 +1753,7 @@ void JsonFamily::ObjLen(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1761,7 +1766,7 @@ void JsonFamily::StrLen(CmdArgList args, ConnectionContext* cntx) {
if (ec) { if (ec) {
VLOG(1) << "Invalid JSONPath syntax: " << ec.message(); VLOG(1) << "Invalid JSONPath syntax: " << ec.message();
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -1775,7 +1780,7 @@ void JsonFamily::StrLen(CmdArgList args, ConnectionContext* cntx) {
if (result) { if (result) {
PrintOptVec(cntx, result); PrintOptVec(cntx, result);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1812,7 +1817,7 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
expr = ParseJsonPath(expr_str, &ec); expr = ParseJsonPath(expr_str, &ec);
if (ec) { if (ec) {
LOG(WARNING) << "path '" << expr_str << "': Invalid JSONPath syntax: " << ec.message(); LOG(WARNING) << "path '" << expr_str << "': Invalid JSONPath syntax: " << ec.message();
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
} }
@ -1820,7 +1825,7 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
} }
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
bool should_format = (indent || new_line || space); bool should_format = (indent || new_line || space);
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {
@ -1829,14 +1834,14 @@ void JsonFamily::Get(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<string> result = trans->ScheduleSingleHopT(move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
(*cntx)->SendBulkString(*result); rb->SendBulkString(*result);
} else { } else {
if (result == facade::OpStatus::KEY_NOTFOUND) { if (result == facade::OpStatus::KEY_NOTFOUND) {
(*cntx)->SendNull(); // Match Redis rb->SendNull(); // Match Redis
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
} }

View file

@ -754,17 +754,18 @@ void MoveGeneric(ConnectionContext* cntx, string_view src, string_view dest, Lis
result = MoveTwoShards(cntx->transaction, src, dest, src_dir, dest_dir, true); result = MoveTwoShards(cntx->transaction, src, dest, src_dir, dest_dir, true);
} }
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
return (*cntx)->SendBulkString(*result); return rb->SendBulkString(*result);
} }
switch (result.status()) { switch (result.status()) {
case OpStatus::KEY_NOTFOUND: case OpStatus::KEY_NOTFOUND:
(*cntx)->SendNull(); rb->SendNull();
break; break;
default: default:
(*cntx)->SendError(result.status()); rb->SendError(result.status());
break; break;
} }
} }
@ -783,27 +784,28 @@ void BRPopLPush(CmdArgList args, ConnectionContext* cntx) {
float timeout; float timeout;
if (!absl::SimpleAtof(timeout_str, &timeout)) { if (!absl::SimpleAtof(timeout_str, &timeout)) {
return (*cntx)->SendError("timeout is not a float or out of range"); return cntx->SendError("timeout is not a float or out of range");
} }
if (timeout < 0) { if (timeout < 0) {
return (*cntx)->SendError("timeout is negative"); return cntx->SendError("timeout is negative");
} }
BPopPusher bpop_pusher(src, dest, ListDir::RIGHT, ListDir::LEFT); BPopPusher bpop_pusher(src, dest, ListDir::RIGHT, ListDir::LEFT);
OpResult<string> op_res = bpop_pusher.Run(cntx->transaction, unsigned(timeout * 1000)); OpResult<string> op_res = bpop_pusher.Run(cntx->transaction, unsigned(timeout * 1000));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (op_res) { if (op_res) {
return (*cntx)->SendBulkString(*op_res); return rb->SendBulkString(*op_res);
} }
switch (op_res.status()) { switch (op_res.status()) {
case OpStatus::TIMED_OUT: case OpStatus::TIMED_OUT:
return (*cntx)->SendNull(); return rb->SendNull();
break; break;
default: default:
return (*cntx)->SendError(op_res.status()); return rb->SendError(op_res.status());
break; break;
} }
} }
@ -815,11 +817,11 @@ void BLMove(CmdArgList args, ConnectionContext* cntx) {
float timeout; float timeout;
if (!absl::SimpleAtof(timeout_str, &timeout)) { if (!absl::SimpleAtof(timeout_str, &timeout)) {
return (*cntx)->SendError("timeout is not a float or out of range"); return cntx->SendError("timeout is not a float or out of range");
} }
if (timeout < 0) { if (timeout < 0) {
return (*cntx)->SendError("timeout is negative"); return cntx->SendError("timeout is negative");
} }
ToUpper(&args[2]); ToUpper(&args[2]);
@ -828,23 +830,24 @@ void BLMove(CmdArgList args, ConnectionContext* cntx) {
optional<ListDir> src_dir = ParseDir(ArgS(args, 2)); optional<ListDir> src_dir = ParseDir(ArgS(args, 2));
optional<ListDir> dest_dir = ParseDir(ArgS(args, 3)); optional<ListDir> dest_dir = ParseDir(ArgS(args, 3));
if (!src_dir || !dest_dir) { if (!src_dir || !dest_dir) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
BPopPusher bpop_pusher(src, dest, *src_dir, *dest_dir); BPopPusher bpop_pusher(src, dest, *src_dir, *dest_dir);
OpResult<string> op_res = bpop_pusher.Run(cntx->transaction, unsigned(timeout * 1000)); OpResult<string> op_res = bpop_pusher.Run(cntx->transaction, unsigned(timeout * 1000));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (op_res) { if (op_res) {
return (*cntx)->SendBulkString(*op_res); return rb->SendBulkString(*op_res);
} }
switch (op_res.status()) { switch (op_res.status()) {
case OpStatus::TIMED_OUT: case OpStatus::TIMED_OUT:
return (*cntx)->SendNull(); return rb->SendNull();
break; break;
default: default:
return (*cntx)->SendError(op_res.status()); return rb->SendError(op_res.status());
break; break;
} }
} }
@ -954,11 +957,11 @@ void ListFamily::LLen(CmdArgList args, ConnectionContext* cntx) {
auto cb = [&](Transaction* t, EngineShard* shard) { return OpLen(t->GetOpArgs(shard), key); }; auto cb = [&](Transaction* t, EngineShard* shard) { return OpLen(t->GetOpArgs(shard), key); };
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} else if (result.status() == OpStatus::KEY_NOTFOUND) { } else if (result.status() == OpStatus::KEY_NOTFOUND) {
(*cntx)->SendLong(0); cntx->SendLong(0);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -976,18 +979,18 @@ void ListFamily::LPos(CmdArgList args, ConnectionContext* cntx) {
const auto& arg_v = ArgS(args, i); const auto& arg_v = ArgS(args, i);
if (arg_v == "RANK") { if (arg_v == "RANK") {
if (!absl::SimpleAtoi(ArgS(args, (i + 1)), &rank) || rank == 0) { if (!absl::SimpleAtoi(ArgS(args, (i + 1)), &rank) || rank == 0) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
} }
if (arg_v == "COUNT") { if (arg_v == "COUNT") {
if (!absl::SimpleAtoi(ArgS(args, (i + 1)), &count)) { if (!absl::SimpleAtoi(ArgS(args, (i + 1)), &count)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
skip_count = false; skip_count = false;
} }
if (arg_v == "MAXLEN") { if (arg_v == "MAXLEN") {
if (!absl::SimpleAtoi(ArgS(args, (i + 1)), &max_len)) { if (!absl::SimpleAtoi(ArgS(args, (i + 1)), &max_len)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
} }
} }
@ -1000,23 +1003,24 @@ void ListFamily::LPos(CmdArgList args, ConnectionContext* cntx) {
OpResult<vector<uint32_t>> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<vector<uint32_t>> result = trans->ScheduleSingleHopT(std::move(cb));
if (result.status() == OpStatus::WRONG_TYPE) { if (result.status() == OpStatus::WRONG_TYPE) {
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
} else if (result.status() == OpStatus::INVALID_VALUE) { } else if (result.status() == OpStatus::INVALID_VALUE) {
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
} }
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (skip_count) { if (skip_count) {
if (result->empty()) { if (result->empty()) {
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
(*cntx)->SendLong((*result)[0]); rb->SendLong((*result)[0]);
} }
} else { } else {
SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder()); SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder());
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
const auto& array = result.value(); const auto& array = result.value();
for (const auto& v : array) { for (const auto& v : array) {
(*cntx)->SendLong(v); rb->SendLong(v);
} }
} }
} }
@ -1026,7 +1030,7 @@ void ListFamily::LIndex(CmdArgList args, ConnectionContext* cntx) {
std::string_view index_str = ArgS(args, 1); std::string_view index_str = ArgS(args, 1);
int32_t index; int32_t index;
if (!absl::SimpleAtoi(index_str, &index)) { if (!absl::SimpleAtoi(index_str, &index)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1034,13 +1038,14 @@ void ListFamily::LIndex(CmdArgList args, ConnectionContext* cntx) {
return OpIndex(t->GetOpArgs(shard), key, index); return OpIndex(t->GetOpArgs(shard), key, index);
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendBulkString(result.value()); rb->SendBulkString(result.value());
} else if (result.status() == OpStatus::WRONG_TYPE) { } else if (result.status() == OpStatus::WRONG_TYPE) {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
} }
@ -1058,7 +1063,7 @@ void ListFamily::LInsert(CmdArgList args, ConnectionContext* cntx) {
} else if (param == "BEFORE") { } else if (param == "BEFORE") {
where = LIST_HEAD; where = LIST_HEAD;
} else { } else {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {
@ -1067,10 +1072,10 @@ void ListFamily::LInsert(CmdArgList args, ConnectionContext* cntx) {
OpResult<int> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<int> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
} }
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
void ListFamily::LTrim(CmdArgList args, ConnectionContext* cntx) { void ListFamily::LTrim(CmdArgList args, ConnectionContext* cntx) {
@ -1080,7 +1085,7 @@ void ListFamily::LTrim(CmdArgList args, ConnectionContext* cntx) {
int32_t start, end; int32_t start, end;
if (!absl::SimpleAtoi(s_str, &start) || !absl::SimpleAtoi(e_str, &end)) { if (!absl::SimpleAtoi(s_str, &start) || !absl::SimpleAtoi(e_str, &end)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1088,7 +1093,7 @@ void ListFamily::LTrim(CmdArgList args, ConnectionContext* cntx) {
return OpTrim(t->GetOpArgs(shard), key, start, end); return OpTrim(t->GetOpArgs(shard), key, start, end);
}; };
cntx->transaction->ScheduleSingleHop(std::move(cb)); cntx->transaction->ScheduleSingleHop(std::move(cb));
(*cntx)->SendOk(); cntx->SendOk();
} }
void ListFamily::LRange(CmdArgList args, ConnectionContext* cntx) { void ListFamily::LRange(CmdArgList args, ConnectionContext* cntx) {
@ -1098,7 +1103,7 @@ void ListFamily::LRange(CmdArgList args, ConnectionContext* cntx) {
int32_t start, end; int32_t start, end;
if (!absl::SimpleAtoi(s_str, &start) || !absl::SimpleAtoi(e_str, &end)) { if (!absl::SimpleAtoi(s_str, &start) || !absl::SimpleAtoi(e_str, &end)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1108,10 +1113,11 @@ void ListFamily::LRange(CmdArgList args, ConnectionContext* cntx) {
auto res = cntx->transaction->ScheduleSingleHopT(std::move(cb)); auto res = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (!res && res.status() != OpStatus::KEY_NOTFOUND) { if (!res && res.status() != OpStatus::KEY_NOTFOUND) {
return (*cntx)->SendError(res.status()); return cntx->SendError(res.status());
} }
(*cntx)->SendStringArr(*res); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendStringArr(*res);
} }
// lrem key 5 foo, will remove foo elements from the list if exists at most 5 times. // lrem key 5 foo, will remove foo elements from the list if exists at most 5 times.
@ -1122,7 +1128,7 @@ void ListFamily::LRem(CmdArgList args, ConnectionContext* cntx) {
int32_t count; int32_t count;
if (!absl::SimpleAtoi(index_str, &count)) { if (!absl::SimpleAtoi(index_str, &count)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1131,9 +1137,9 @@ void ListFamily::LRem(CmdArgList args, ConnectionContext* cntx) {
}; };
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} else { } else {
(*cntx)->SendLong(0); cntx->SendLong(0);
} }
} }
@ -1144,7 +1150,7 @@ void ListFamily::LSet(CmdArgList args, ConnectionContext* cntx) {
int32_t count; int32_t count;
if (!absl::SimpleAtoi(index_str, &count)) { if (!absl::SimpleAtoi(index_str, &count)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1153,9 +1159,9 @@ void ListFamily::LSet(CmdArgList args, ConnectionContext* cntx) {
}; };
OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (result) { if (result) {
(*cntx)->SendOk(); cntx->SendOk();
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1179,7 +1185,7 @@ void ListFamily::LMove(CmdArgList args, ConnectionContext* cntx) {
optional<ListDir> src_dir = ParseDir(src_dir_str); optional<ListDir> src_dir = ParseDir(src_dir_str);
optional<ListDir> dest_dir = ParseDir(dest_dir_str); optional<ListDir> dest_dir = ParseDir(dest_dir_str);
if (!src_dir || !dest_dir) { if (!src_dir || !dest_dir) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
MoveGeneric(cntx, src, dest, *src_dir, *dest_dir); MoveGeneric(cntx, src, dest, *src_dir, *dest_dir);
@ -1191,10 +1197,10 @@ void ListFamily::BPopGeneric(ListDir dir, CmdArgList args, ConnectionContext* cn
float timeout; float timeout;
auto timeout_str = ArgS(args, args.size() - 1); auto timeout_str = ArgS(args, args.size() - 1);
if (!absl::SimpleAtof(timeout_str, &timeout)) { if (!absl::SimpleAtof(timeout_str, &timeout)) {
return (*cntx)->SendError("timeout is not a float or out of range"); return cntx->SendError("timeout is not a float or out of range");
} }
if (timeout < 0) { if (timeout < 0) {
return (*cntx)->SendError("timeout is negative"); return cntx->SendError("timeout is negative");
} }
VLOG(1) << "BPop timeout(" << timeout << ")"; VLOG(1) << "BPop timeout(" << timeout << ")";
@ -1209,23 +1215,24 @@ void ListFamily::BPopGeneric(ListDir dir, CmdArgList args, ConnectionContext* cn
OpResult<string> popped_key = container_utils::RunCbOnFirstNonEmptyBlocking( OpResult<string> popped_key = container_utils::RunCbOnFirstNonEmptyBlocking(
transaction, OBJ_LIST, move(cb), unsigned(timeout * 1000)); transaction, OBJ_LIST, move(cb), unsigned(timeout * 1000));
cntx->conn_state.is_blocking = false; cntx->conn_state.is_blocking = false;
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (popped_key) { if (popped_key) {
DVLOG(1) << "BPop " << transaction->DebugId() << " popped from key " << popped_key; // key. DVLOG(1) << "BPop " << transaction->DebugId() << " popped from key " << popped_key; // key.
std::string_view str_arr[2] = {*popped_key, popped_value}; std::string_view str_arr[2] = {*popped_key, popped_value};
return (*cntx)->SendStringArr(str_arr); return rb->SendStringArr(str_arr);
} }
DVLOG(1) << "result for " << transaction->DebugId() << " is " << popped_key.status(); DVLOG(1) << "result for " << transaction->DebugId() << " is " << popped_key.status();
switch (popped_key.status()) { switch (popped_key.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
return (*cntx)->SendError(kWrongTypeErr); return rb->SendError(kWrongTypeErr);
case OpStatus::TIMED_OUT: case OpStatus::TIMED_OUT:
return (*cntx)->SendNullArray(); return rb->SendNullArray();
default: default:
LOG(ERROR) << "Unexpected error " << popped_key.status(); LOG(ERROR) << "Unexpected error " << popped_key.status();
} }
return (*cntx)->SendNullArray(); return rb->SendNullArray();
} }
void ListFamily::PushGeneric(ListDir dir, bool skip_notexists, CmdArgList args, void ListFamily::PushGeneric(ListDir dir, bool skip_notexists, CmdArgList args,
@ -1242,10 +1249,10 @@ void ListFamily::PushGeneric(ListDir dir, bool skip_notexists, CmdArgList args,
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
} }
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
} }
void ListFamily::PopGeneric(ListDir dir, CmdArgList args, ConnectionContext* cntx) { void ListFamily::PopGeneric(ListDir dir, CmdArgList args, ConnectionContext* cntx) {
@ -1255,16 +1262,16 @@ void ListFamily::PopGeneric(ListDir dir, CmdArgList args, ConnectionContext* cnt
if (args.size() > 1) { if (args.size() > 1) {
if (args.size() > 2) { if (args.size() > 2) {
return (*cntx)->SendError(WrongNumArgsError(cntx->cid->name())); return cntx->SendError(WrongNumArgsError(cntx->cid->name()));
} }
string_view count_s = ArgS(args, 1); string_view count_s = ArgS(args, 1);
if (!absl::SimpleAtoi(count_s, &count)) { if (!absl::SimpleAtoi(count_s, &count)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (count < 0) { if (count < 0) {
return (*cntx)->SendError(kUintErr); return cntx->SendError(kUintErr);
} }
return_arr = true; return_arr = true;
} }
@ -1274,27 +1281,27 @@ void ListFamily::PopGeneric(ListDir dir, CmdArgList args, ConnectionContext* cnt
}; };
OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
switch (result.status()) { switch (result.status()) {
case OpStatus::KEY_NOTFOUND: case OpStatus::KEY_NOTFOUND:
return (*cntx)->SendNull(); return rb->SendNull();
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
return (*cntx)->SendError(kWrongTypeErr); return rb->SendError(kWrongTypeErr);
default:; default:;
} }
if (return_arr) { if (return_arr) {
if (result->empty()) { if (result->empty()) {
(*cntx)->SendNullArray(); rb->SendNullArray();
} else { } else {
(*cntx)->StartArray(result->size()); rb->StartArray(result->size());
for (const auto& k : *result) { for (const auto& k : *result) {
(*cntx)->SendBulkString(k); rb->SendBulkString(k);
} }
} }
} else { } else {
DCHECK_EQ(1u, result->size()); DCHECK_EQ(1u, result->size());
(*cntx)->SendBulkString(result->front()); rb->SendBulkString(result->front());
} }
} }

View file

@ -1115,7 +1115,7 @@ void Service::DispatchCommand(CmdArgList args, facade::ConnectionContext* cntx)
dfly_cntx->transaction->InitByArgs(dfly_cntx->conn_state.db_index, args_no_cmd); dfly_cntx->transaction->InitByArgs(dfly_cntx->conn_state.db_index, args_no_cmd);
if (status != OpStatus::OK) if (status != OpStatus::OK)
return (*cntx)->SendError(status); return cntx->SendError(status);
} }
} else { } else {
DCHECK(dfly_cntx->transaction == nullptr); DCHECK(dfly_cntx->transaction == nullptr);
@ -1126,7 +1126,7 @@ void Service::DispatchCommand(CmdArgList args, facade::ConnectionContext* cntx)
if (!dist_trans->IsMulti()) { // Multi command initialize themself based on their mode. if (!dist_trans->IsMulti()) { // Multi command initialize themself based on their mode.
if (auto st = dist_trans->InitByArgs(dfly_cntx->conn_state.db_index, args_no_cmd); if (auto st = dist_trans->InitByArgs(dfly_cntx->conn_state.db_index, args_no_cmd);
st != OpStatus::OK) st != OpStatus::OK)
return (*cntx)->SendError(st); return cntx->SendError(st);
} }
dfly_cntx->transaction = dist_trans.get(); dfly_cntx->transaction = dist_trans.get();
@ -1184,7 +1184,7 @@ bool Service::InvokeCmd(const CommandId* cid, CmdArgList tail_args, ConnectionCo
DCHECK(!cid->Validate(tail_args)); DCHECK(!cid->Validate(tail_args));
if (auto err = VerifyCommandExecution(cid, cntx); err) { if (auto err = VerifyCommandExecution(cid, cntx); err) {
(*cntx)->SendError(std::move(*err)); cntx->SendError(std::move(*err));
return true; // return false only for internal error aborts return true; // return false only for internal error aborts
} }
@ -1462,7 +1462,7 @@ absl::flat_hash_map<std::string, unsigned> Service::UknownCmdMap() const {
void Service::Quit(CmdArgList args, ConnectionContext* cntx) { void Service::Quit(CmdArgList args, ConnectionContext* cntx) {
if (cntx->protocol() == facade::Protocol::REDIS) if (cntx->protocol() == facade::Protocol::REDIS)
(*cntx)->SendOk(); cntx->SendOk();
using facade::SinkReplyBuilder; using facade::SinkReplyBuilder;
SinkReplyBuilder* builder = cntx->reply_builder(); SinkReplyBuilder* builder = cntx->reply_builder();
@ -1474,11 +1474,11 @@ void Service::Quit(CmdArgList args, ConnectionContext* cntx) {
void Service::Multi(CmdArgList args, ConnectionContext* cntx) { void Service::Multi(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.exec_info.IsCollecting()) { if (cntx->conn_state.exec_info.IsCollecting()) {
return (*cntx)->SendError("MULTI calls can not be nested"); return cntx->SendError("MULTI calls can not be nested");
} }
cntx->conn_state.exec_info.state = ConnectionState::ExecInfo::EXEC_COLLECT; cntx->conn_state.exec_info.state = ConnectionState::ExecInfo::EXEC_COLLECT;
// TODO: to protect against huge exec transactions. // TODO: to protect against huge exec transactions.
return (*cntx)->SendOk(); return cntx->SendOk();
} }
void Service::Watch(CmdArgList args, ConnectionContext* cntx) { void Service::Watch(CmdArgList args, ConnectionContext* cntx) {
@ -1486,7 +1486,7 @@ void Service::Watch(CmdArgList args, ConnectionContext* cntx) {
// Skip if EXEC will already fail due previous WATCH. // Skip if EXEC will already fail due previous WATCH.
if (exec_info.watched_dirty.load(memory_order_relaxed)) { if (exec_info.watched_dirty.load(memory_order_relaxed)) {
return (*cntx)->SendOk(); return cntx->SendOk();
} }
atomic_uint32_t keys_existed = 0; atomic_uint32_t keys_existed = 0;
@ -1508,12 +1508,12 @@ void Service::Watch(CmdArgList args, ConnectionContext* cntx) {
exec_info.watched_keys.emplace_back(cntx->db_index(), ArgS(args, i)); exec_info.watched_keys.emplace_back(cntx->db_index(), ArgS(args, i));
} }
return (*cntx)->SendOk(); return cntx->SendOk();
} }
void Service::Unwatch(CmdArgList args, ConnectionContext* cntx) { void Service::Unwatch(CmdArgList args, ConnectionContext* cntx) {
UnwatchAllKeys(cntx); UnwatchAllKeys(cntx);
return (*cntx)->SendOk(); return cntx->SendOk();
} }
template <typename F> void WithReplies(CapturingReplyBuilder* crb, ConnectionContext* cntx, F&& f) { template <typename F> void WithReplies(CapturingReplyBuilder* crb, ConnectionContext* cntx, F&& f) {
@ -1593,14 +1593,15 @@ void Service::CallFromScript(ConnectionContext* cntx, Interpreter::CallArgs& ca)
void Service::Eval(CmdArgList args, ConnectionContext* cntx) { void Service::Eval(CmdArgList args, ConnectionContext* cntx) {
string_view body = ArgS(args, 0); string_view body = ArgS(args, 0);
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (body.empty()) { if (body.empty()) {
return (*cntx)->SendNull(); return rb->SendNull();
} }
BorrowedInterpreter interpreter{cntx}; BorrowedInterpreter interpreter{cntx};
auto res = server_family_.script_mgr()->Insert(body, interpreter); auto res = server_family_.script_mgr()->Insert(body, interpreter);
if (!res) if (!res)
return (*cntx)->SendError(res.error().Format(), facade::kScriptErrType); return rb->SendError(res.error().Format(), facade::kScriptErrType);
string sha{std::move(res.value())}; string sha{std::move(res.value())};
@ -1677,7 +1678,7 @@ optional<bool> StartMultiEval(DbIndex dbid, CmdArgList keys, ScriptMgr::ScriptPa
string err = StrCat( string err = StrCat(
"Multi mode conflict when running eval in multi transaction. Multi mode is: ", multi_mode, "Multi mode conflict when running eval in multi transaction. Multi mode is: ", multi_mode,
" eval mode is: ", script_mode); " eval mode is: ", script_mode);
(*cntx)->SendError(err); cntx->SendError(err);
return nullopt; return nullopt;
} }
return false; return false;
@ -1758,12 +1759,12 @@ void Service::EvalInternal(CmdArgList args, const EvalArgs& eval_args, Interpret
// Sanitizing the input to avoid code injection. // Sanitizing the input to avoid code injection.
if (eval_args.sha.size() != 40 || !IsSHA(eval_args.sha)) { if (eval_args.sha.size() != 40 || !IsSHA(eval_args.sha)) {
return (*cntx)->SendError(facade::kScriptNotFound); return cntx->SendError(facade::kScriptNotFound);
} }
auto params = LoadScipt(eval_args.sha, server_family_.script_mgr(), interpreter); auto params = LoadScipt(eval_args.sha, server_family_.script_mgr(), interpreter);
if (!params) if (!params)
return (*cntx)->SendError(facade::kScriptNotFound); return cntx->SendError(facade::kScriptNotFound);
string error; string error;
@ -1854,7 +1855,7 @@ void Service::EvalInternal(CmdArgList args, const EvalArgs& eval_args, Interpret
if (result == Interpreter::RUN_ERR) { if (result == Interpreter::RUN_ERR) {
string resp = StrCat("Error running script (call to ", eval_args.sha, "): ", error); string resp = StrCat("Error running script (call to ", eval_args.sha, "): ", error);
return (*cntx)->SendError(resp, facade::kScriptErrType); return cntx->SendError(resp, facade::kScriptErrType);
} }
CHECK(result == Interpreter::RUN_OK); CHECK(result == Interpreter::RUN_OK);
@ -1862,14 +1863,14 @@ void Service::EvalInternal(CmdArgList args, const EvalArgs& eval_args, Interpret
SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder()); SinkReplyBuilder::ReplyAggregator agg(cntx->reply_builder());
EvalSerializer ser{static_cast<RedisReplyBuilder*>(cntx->reply_builder())}; EvalSerializer ser{static_cast<RedisReplyBuilder*>(cntx->reply_builder())};
if (!interpreter->IsResultSafe()) { if (!interpreter->IsResultSafe()) {
(*cntx)->SendError("reached lua stack limit"); cntx->SendError("reached lua stack limit");
} else { } else {
interpreter->SerializeResult(&ser); interpreter->SerializeResult(&ser);
} }
} }
void Service::Discard(CmdArgList args, ConnectionContext* cntx) { void Service::Discard(CmdArgList args, ConnectionContext* cntx) {
RedisReplyBuilder* rb = (*cntx).operator->(); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (!cntx->conn_state.exec_info.IsCollecting()) { if (!cntx->conn_state.exec_info.IsCollecting()) {
return rb->SendError("DISCARD without MULTI"); return rb->SendError("DISCARD without MULTI");
@ -1976,7 +1977,7 @@ void StartMultiExec(DbIndex dbid, Transaction* trans, ConnectionState::ExecInfo*
} }
void Service::Exec(CmdArgList args, ConnectionContext* cntx) { void Service::Exec(CmdArgList args, ConnectionContext* cntx) {
RedisReplyBuilder* rb = (*cntx).operator->(); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
absl::Cleanup exec_clear = [&cntx] { MultiCleanup(cntx); }; absl::Cleanup exec_clear = [&cntx] { MultiCleanup(cntx); };
@ -2047,7 +2048,7 @@ void Service::Exec(CmdArgList args, ConnectionContext* cntx) {
if (scmd.Cid()->IsTransactional()) { if (scmd.Cid()->IsTransactional()) {
OpStatus st = cntx->transaction->InitByArgs(cntx->conn_state.db_index, args); OpStatus st = cntx->transaction->InitByArgs(cntx->conn_state.db_index, args);
if (st != OpStatus::OK) { if (st != OpStatus::OK) {
(*cntx)->SendError(st); cntx->SendError(st);
break; break;
} }
} }
@ -2120,7 +2121,7 @@ void Service::Publish(CmdArgList args, ConnectionContext* cntx) {
shard_set->pool()->DispatchBrief(std::move(cb)); shard_set->pool()->DispatchBrief(std::move(cb));
} }
(*cntx)->SendLong(num_published); cntx->SendLong(num_published);
} }
void Service::Subscribe(CmdArgList args, ConnectionContext* cntx) { void Service::Subscribe(CmdArgList args, ConnectionContext* cntx) {
@ -2154,34 +2155,35 @@ void Service::Function(CmdArgList args, ConnectionContext* cntx) {
string_view sub_cmd = ArgS(args, 0); string_view sub_cmd = ArgS(args, 0);
if (sub_cmd == "FLUSH") { if (sub_cmd == "FLUSH") {
return (*cntx)->SendOk(); return cntx->SendOk();
} }
string err = UnknownSubCmd(sub_cmd, "FUNCTION"); string err = UnknownSubCmd(sub_cmd, "FUNCTION");
return (*cntx)->SendError(err, kSyntaxErrType); return cntx->SendError(err, kSyntaxErrType);
} }
void Service::PubsubChannels(string_view pattern, ConnectionContext* cntx) { void Service::PubsubChannels(string_view pattern, ConnectionContext* cntx) {
(*cntx)->SendStringArr(ServerState::tlocal()->channel_store()->ListChannels(pattern)); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendStringArr(ServerState::tlocal()->channel_store()->ListChannels(pattern));
} }
void Service::PubsubPatterns(ConnectionContext* cntx) { void Service::PubsubPatterns(ConnectionContext* cntx) {
size_t pattern_count = ServerState::tlocal()->channel_store()->PatternCount(); size_t pattern_count = ServerState::tlocal()->channel_store()->PatternCount();
(*cntx)->SendLong(pattern_count); cntx->SendLong(pattern_count);
} }
void Service::Monitor(CmdArgList args, ConnectionContext* cntx) { void Service::Monitor(CmdArgList args, ConnectionContext* cntx) {
VLOG(1) << "starting monitor on this connection: " << cntx->conn()->GetClientId(); VLOG(1) << "starting monitor on this connection: " << cntx->conn()->GetClientId();
// we are registering the current connection for all threads so they will be aware of // we are registering the current connection for all threads so they will be aware of
// this connection, to send to it any command // this connection, to send to it any command
(*cntx)->SendOk(); cntx->SendOk();
cntx->ChangeMonitor(true /* start */); cntx->ChangeMonitor(true /* start */);
} }
void Service::Pubsub(CmdArgList args, ConnectionContext* cntx) { void Service::Pubsub(CmdArgList args, ConnectionContext* cntx) {
if (args.size() < 1) { if (args.size() < 1) {
(*cntx)->SendError(WrongNumArgsError(cntx->cid->name())); cntx->SendError(WrongNumArgsError(cntx->cid->name()));
return; return;
} }
@ -2198,7 +2200,8 @@ void Service::Pubsub(CmdArgList args, ConnectionContext* cntx) {
"HELP", "HELP",
"\tPrints this help."}; "\tPrints this help."};
(*cntx)->SendSimpleStrArr(help_arr); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendSimpleStrArr(help_arr);
return; return;
} }
@ -2212,7 +2215,7 @@ void Service::Pubsub(CmdArgList args, ConnectionContext* cntx) {
} else if (subcmd == "NUMPAT") { } else if (subcmd == "NUMPAT") {
PubsubPatterns(cntx); PubsubPatterns(cntx);
} else { } else {
(*cntx)->SendError(UnknownSubCmd(subcmd, "PUBSUB")); cntx->SendError(UnknownSubCmd(subcmd, "PUBSUB"));
} }
} }
@ -2224,28 +2227,29 @@ void Service::Command(CmdArgList args, ConnectionContext* cntx) {
} }
}); });
auto serialize_command = [&cntx](string_view name, const CommandId& cid) { auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->StartArray(6); auto serialize_command = [&rb](string_view name, const CommandId& cid) {
(*cntx)->SendSimpleString(cid.name()); rb->StartArray(6);
(*cntx)->SendLong(cid.arity()); rb->SendSimpleString(cid.name());
(*cntx)->StartArray(CommandId::OptCount(cid.opt_mask())); rb->SendLong(cid.arity());
rb->StartArray(CommandId::OptCount(cid.opt_mask()));
for (uint32_t i = 0; i < 32; ++i) { for (uint32_t i = 0; i < 32; ++i) {
unsigned obit = (1u << i); unsigned obit = (1u << i);
if (cid.opt_mask() & obit) { if (cid.opt_mask() & obit) {
const char* name = CO::OptName(CO::CommandOpt{obit}); const char* name = CO::OptName(CO::CommandOpt{obit});
(*cntx)->SendSimpleString(name); rb->SendSimpleString(name);
} }
} }
(*cntx)->SendLong(cid.first_key_pos()); rb->SendLong(cid.first_key_pos());
(*cntx)->SendLong(cid.last_key_pos()); rb->SendLong(cid.last_key_pos());
(*cntx)->SendLong(cid.opt_mask() & CO::INTERLEAVED_KEYS ? 2 : 1); rb->SendLong(cid.opt_mask() & CO::INTERLEAVED_KEYS ? 2 : 1);
}; };
// If no arguments are specified, reply with all commands // If no arguments are specified, reply with all commands
if (args.empty()) { if (args.empty()) {
(*cntx)->StartArray(cmd_cnt); rb->StartArray(cmd_cnt);
registry_.Traverse([&](string_view name, const CommandId& cid) { registry_.Traverse([&](string_view name, const CommandId& cid) {
if (cid.opt_mask() & CO::HIDDEN) if (cid.opt_mask() & CO::HIDDEN)
return; return;
@ -2259,7 +2263,7 @@ void Service::Command(CmdArgList args, ConnectionContext* cntx) {
// COUNT // COUNT
if (subcmd == "COUNT") { if (subcmd == "COUNT") {
return (*cntx)->SendLong(cmd_cnt); return cntx->SendLong(cmd_cnt);
} }
// INFO [cmd] // INFO [cmd]
@ -2268,16 +2272,16 @@ void Service::Command(CmdArgList args, ConnectionContext* cntx) {
string_view cmd = ArgS(args, 1); string_view cmd = ArgS(args, 1);
if (const auto* cid = registry_.Find(cmd); cid) { if (const auto* cid = registry_.Find(cmd); cid) {
(*cntx)->StartArray(1); rb->StartArray(1);
serialize_command(cmd, *cid); serialize_command(cmd, *cid);
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
return; return;
} }
return (*cntx)->SendError(kSyntaxErr, kSyntaxErrType); return cntx->SendError(kSyntaxErr, kSyntaxErrType);
} }
VarzValue::Map Service::GetVarzStats() { VarzValue::Map Service::GetVarzStats() {

View file

@ -99,7 +99,8 @@ void MemoryCmd::Run(CmdArgList args) {
"DECOMMIT", "DECOMMIT",
" Force decommit the memory freed by the server back to OS.", " Force decommit the memory freed by the server back to OS.",
}; };
return (*cntx_)->SendSimpleStrArr(help_arr); auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
return rb->SendSimpleStrArr(help_arr);
}; };
if (sub_cmd == "STATS") { if (sub_cmd == "STATS") {
@ -116,7 +117,7 @@ void MemoryCmd::Run(CmdArgList args) {
mi_heap_collect(ServerState::tlocal()->data_heap(), true); mi_heap_collect(ServerState::tlocal()->data_heap(), true);
mi_heap_collect(mi_heap_get_backing(), true); mi_heap_collect(mi_heap_get_backing(), true);
}); });
return (*cntx_)->SendSimpleString("OK"); return cntx_->SendSimpleString("OK");
} }
if (sub_cmd == "MALLOC-STATS") { if (sub_cmd == "MALLOC-STATS") {
@ -132,7 +133,7 @@ void MemoryCmd::Run(CmdArgList args) {
} }
if (args.size() > tid_indx && !absl::SimpleAtoi(ArgS(args, tid_indx), &tid)) { if (args.size() > tid_indx && !absl::SimpleAtoi(ArgS(args, tid_indx), &tid)) {
return (*cntx_)->SendError(kInvalidIntErr); return cntx_->SendError(kInvalidIntErr);
} }
} }
@ -147,11 +148,12 @@ void MemoryCmd::Run(CmdArgList args) {
string res = shard_set->pool()->at(tid)->AwaitBrief([=] { return MallocStats(backing, tid); }); string res = shard_set->pool()->at(tid)->AwaitBrief([=] { return MallocStats(backing, tid); });
return (*cntx_)->SendBulkString(res); auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
return rb->SendBulkString(res);
} }
string err = UnknownSubCmd(sub_cmd, "MEMORY"); string err = UnknownSubCmd(sub_cmd, "MEMORY");
return (*cntx_)->SendError(err, kSyntaxErrType); return cntx_->SendError(err, kSyntaxErrType);
} }
namespace { namespace {
@ -270,10 +272,11 @@ void MemoryCmd::Stats() {
// Serialization stats, including both replication-related serialization and saving to RDB files. // Serialization stats, including both replication-related serialization and saving to RDB files.
stats.push_back({"serialization", serialization_memory.load()}); stats.push_back({"serialization", serialization_memory.load()});
(*cntx_)->StartCollection(stats.size(), RedisReplyBuilder::MAP); auto* rb = static_cast<RedisReplyBuilder*>(cntx_->reply_builder());
rb->StartCollection(stats.size(), RedisReplyBuilder::MAP);
for (const auto& [k, v] : stats) { for (const auto& [k, v] : stats) {
(*cntx_)->SendBulkString(k); rb->SendBulkString(k);
(*cntx_)->SendLong(v); rb->SendLong(v);
} }
} }
@ -292,7 +295,7 @@ void MemoryCmd::Usage(std::string_view key) {
if (memory_usage < 0) if (memory_usage < 0)
return cntx_->SendError(kKeyNotFoundErr); return cntx_->SendError(kKeyNotFoundErr);
(*cntx_)->SendLong(memory_usage); cntx_->SendLong(memory_usage);
} }
} // namespace dfly } // namespace dfly

View file

@ -118,7 +118,7 @@ bool MultiCommandSquasher::ExecuteStandalone(StoredCmd* cmd) {
if (verify_commands_) { if (verify_commands_) {
if (auto err = service_->VerifyCommandState(cmd->Cid(), args, *cntx_); err) { if (auto err = service_->VerifyCommandState(cmd->Cid(), args, *cntx_); err) {
(*cntx_)->SendError(move(*err)); cntx_->SendError(move(*err));
return !error_abort_; return !error_abort_;
} }
} }

View file

@ -84,11 +84,11 @@ error_code Replica::Start(ConnectionContext* cntx) {
auto check_connection_error = [this, &cntx](error_code ec, const char* msg) -> error_code { auto check_connection_error = [this, &cntx](error_code ec, const char* msg) -> error_code {
if (cntx_.IsCancelled()) { if (cntx_.IsCancelled()) {
(*cntx)->SendError("replication cancelled"); cntx->SendError("replication cancelled");
return std::make_error_code(errc::operation_canceled); return std::make_error_code(errc::operation_canceled);
} }
if (ec) { if (ec) {
(*cntx)->SendError(absl::StrCat(msg, ec.message())); cntx->SendError(absl::StrCat(msg, ec.message()));
cntx_.Cancel(); cntx_.Cancel();
} }
return ec; return ec;
@ -118,7 +118,7 @@ error_code Replica::Start(ConnectionContext* cntx) {
// 4. Spawn main coordination fiber. // 4. Spawn main coordination fiber.
sync_fb_ = fb2::Fiber("main_replication", &Replica::MainReplicationFb, this); sync_fb_ = fb2::Fiber("main_replication", &Replica::MainReplicationFb, this);
(*cntx)->SendOk(); cntx->SendOk();
return {}; return {};
} }
@ -128,7 +128,7 @@ void Replica::EnableReplication(ConnectionContext* cntx) {
state_mask_.store(R_ENABLED); // set replica state to enabled state_mask_.store(R_ENABLED); // set replica state to enabled
sync_fb_ = MakeFiber(&Replica::MainReplicationFb, this); // call replication fiber sync_fb_ = MakeFiber(&Replica::MainReplicationFb, this); // call replication fiber
(*cntx)->SendOk(); cntx->SendOk();
} }
void Replica::Stop() { void Replica::Stop() {

View file

@ -76,7 +76,8 @@ void ScriptMgr::Run(CmdArgList args, ConnectionContext* cntx) {
" Prints latency histograms in usec for every called function.", " Prints latency histograms in usec for every called function.",
"HELP" "HELP"
" Prints this help."}; " Prints this help."};
return (*cntx)->SendSimpleStrArr(kHelp); auto rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return rb->SendSimpleStrArr(kHelp);
} }
if (subcmd == "EXISTS" && args.size() > 1) if (subcmd == "EXISTS" && args.size() > 1)
@ -96,7 +97,7 @@ void ScriptMgr::Run(CmdArgList args, ConnectionContext* cntx) {
string err = absl::StrCat("Unknown subcommand or wrong number of arguments for '", subcmd, string err = absl::StrCat("Unknown subcommand or wrong number of arguments for '", subcmd,
"'. Try SCRIPT HELP."); "'. Try SCRIPT HELP.");
cntx->reply_builder()->SendError(err, kSyntaxErrType); cntx->SendError(err, kSyntaxErrType);
} }
void ScriptMgr::ExistsCmd(CmdArgList args, ConnectionContext* cntx) const { void ScriptMgr::ExistsCmd(CmdArgList args, ConnectionContext* cntx) const {
@ -107,20 +108,21 @@ void ScriptMgr::ExistsCmd(CmdArgList args, ConnectionContext* cntx) const {
} }
} }
(*cntx)->StartArray(res.size()); auto rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(res.size());
for (uint8_t v : res) { for (uint8_t v : res) {
(*cntx)->SendLong(v); rb->SendLong(v);
} }
return; return;
} }
void ScriptMgr::LoadCmd(CmdArgList args, ConnectionContext* cntx) { void ScriptMgr::LoadCmd(CmdArgList args, ConnectionContext* cntx) {
string_view body = ArgS(args, 1); string_view body = ArgS(args, 1);
auto rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (body.empty()) { if (body.empty()) {
char sha[41]; char sha[41];
Interpreter::FuncSha1(body, sha); Interpreter::FuncSha1(body, sha);
return (*cntx)->SendBulkString(sha); return rb->SendBulkString(sha);
} }
ServerState* ss = ServerState::tlocal(); ServerState* ss = ServerState::tlocal();
@ -129,12 +131,12 @@ void ScriptMgr::LoadCmd(CmdArgList args, ConnectionContext* cntx) {
auto res = Insert(body, interpreter); auto res = Insert(body, interpreter);
if (!res) if (!res)
return (*cntx)->SendError(res.error().Format()); return rb->SendError(res.error().Format());
// Schedule empty callback inorder to journal command via transaction framework. // Schedule empty callback inorder to journal command via transaction framework.
cntx->transaction->ScheduleSingleHop([](auto* t, auto* shard) { return OpStatus::OK; }); cntx->transaction->ScheduleSingleHop([](auto* t, auto* shard) { return OpStatus::OK; });
return (*cntx)->SendBulkString(res.value()); return rb->SendBulkString(res.value());
} }
void ScriptMgr::ConfigCmd(CmdArgList args, ConnectionContext* cntx) { void ScriptMgr::ConfigCmd(CmdArgList args, ConnectionContext* cntx) {
@ -144,7 +146,7 @@ void ScriptMgr::ConfigCmd(CmdArgList args, ConnectionContext* cntx) {
for (auto flag : args.subspan(2)) { for (auto flag : args.subspan(2)) {
if (auto err = ScriptParams::ApplyFlags(facade::ToSV(flag), &data); err) if (auto err = ScriptParams::ApplyFlags(facade::ToSV(flag), &data); err)
return (*cntx)->SendError("Invalid config format: " + err.Format()); return cntx->SendError("Invalid config format: " + err.Format());
} }
UpdateScriptCaches(key, data); UpdateScriptCaches(key, data);
@ -152,18 +154,19 @@ void ScriptMgr::ConfigCmd(CmdArgList args, ConnectionContext* cntx) {
// Schedule empty callback inorder to journal command via transaction framework. // Schedule empty callback inorder to journal command via transaction framework.
cntx->transaction->ScheduleSingleHop([](auto* t, auto* shard) { return OpStatus::OK; }); cntx->transaction->ScheduleSingleHop([](auto* t, auto* shard) { return OpStatus::OK; });
return (*cntx)->SendOk(); return cntx->SendOk();
} }
void ScriptMgr::ListCmd(ConnectionContext* cntx) const { void ScriptMgr::ListCmd(ConnectionContext* cntx) const {
vector<pair<string, ScriptData>> scripts = GetAll(); vector<pair<string, ScriptData>> scripts = GetAll();
(*cntx)->StartArray(scripts.size()); auto rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(scripts.size());
for (const auto& [sha, data] : scripts) { for (const auto& [sha, data] : scripts) {
(*cntx)->StartArray(data.orig_body.empty() ? 2 : 3); rb->StartArray(data.orig_body.empty() ? 2 : 3);
(*cntx)->SendBulkString(sha); rb->SendBulkString(sha);
(*cntx)->SendBulkString(data.body); rb->SendBulkString(data.body);
if (!data.orig_body.empty()) if (!data.orig_body.empty())
(*cntx)->SendBulkString(data.orig_body); rb->SendBulkString(data.orig_body);
} }
} }
@ -180,11 +183,12 @@ void ScriptMgr::LatencyCmd(ConnectionContext* cntx) const {
mu.unlock(); mu.unlock();
}); });
(*cntx)->StartArray(result.size()); auto rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(result.size());
for (const auto& k_v : result) { for (const auto& k_v : result) {
(*cntx)->StartArray(2); rb->StartArray(2);
(*cntx)->SendBulkString(k_v.first); rb->SendBulkString(k_v.first);
(*cntx)->SendBulkString(k_v.second.ToString()); rb->SendBulkString(k_v.second.ToString());
} }
} }

View file

@ -91,7 +91,7 @@ optional<search::Schema> ParseSchemaOrReply(DocIndex::DataType type, CmdArgParse
// Verify json path is correct // Verify json path is correct
if (type == DocIndex::JSON && !IsValidJsonPath(field)) { if (type == DocIndex::JSON && !IsValidJsonPath(field)) {
(*cntx)->SendError("Bad json path: " + string{field}); cntx->SendError("Bad json path: " + string{field});
return nullopt; return nullopt;
} }
@ -105,7 +105,7 @@ optional<search::Schema> ParseSchemaOrReply(DocIndex::DataType type, CmdArgParse
string_view type_str = parser.Next(); string_view type_str = parser.Next();
auto type = ParseSearchFieldType(type_str); auto type = ParseSearchFieldType(type_str);
if (!type) { if (!type) {
(*cntx)->SendError("Invalid field type: " + string{type_str}); cntx->SendError("Invalid field type: " + string{type_str});
return nullopt; return nullopt;
} }
@ -114,7 +114,7 @@ optional<search::Schema> ParseSchemaOrReply(DocIndex::DataType type, CmdArgParse
if (*type == search::SchemaField::VECTOR) { if (*type == search::SchemaField::VECTOR) {
auto vector_params = ParseVectorParams(&parser); auto vector_params = ParseVectorParams(&parser);
if (!parser.HasError() && vector_params.dim == 0) { if (!parser.HasError() && vector_params.dim == 0) {
(*cntx)->SendError("Knn vector dimension cannot be zero"); cntx->SendError("Knn vector dimension cannot be zero");
return nullopt; return nullopt;
} }
params = std::move(vector_params); params = std::move(vector_params);
@ -148,7 +148,7 @@ optional<search::Schema> ParseSchemaOrReply(DocIndex::DataType type, CmdArgParse
schema.field_names[field_info.short_name] = field_ident; schema.field_names[field_info.short_name] = field_ident;
if (auto err = parser.Error(); err) { if (auto err = parser.Error(); err) {
(*cntx)->SendError(err->MakeReply()); cntx->SendError(err->MakeReply());
return nullopt; return nullopt;
} }
@ -206,7 +206,7 @@ optional<SearchParams> ParseSearchParamsOrReply(CmdArgParser parser, ConnectionC
} }
if (auto err = parser.Error(); err) { if (auto err = parser.Error(); err) {
(*cntx)->SendError(err->MakeReply()); cntx->SendError(err->MakeReply());
return nullopt; return nullopt;
} }
@ -214,11 +214,12 @@ optional<SearchParams> ParseSearchParamsOrReply(CmdArgParser parser, ConnectionC
} }
void SendSerializedDoc(const SerializedSearchDoc& doc, ConnectionContext* cntx) { void SendSerializedDoc(const SerializedSearchDoc& doc, ConnectionContext* cntx) {
(*cntx)->SendBulkString(doc.key); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->StartCollection(doc.values.size(), RedisReplyBuilder::MAP); rb->SendBulkString(doc.key);
rb->StartCollection(doc.values.size(), RedisReplyBuilder::MAP);
for (const auto& [k, v] : doc.values) { for (const auto& [k, v] : doc.values) {
(*cntx)->SendBulkString(k); rb->SendBulkString(k);
(*cntx)->SendBulkString(v); rb->SendBulkString(v);
} }
} }
@ -236,8 +237,9 @@ void ReplyWithResults(const SearchParams& params, absl::Span<SearchResult> resul
bool ids_only = params.IdsOnly(); bool ids_only = params.IdsOnly();
size_t reply_size = ids_only ? (result_count + 1) : (result_count * 2 + 1); size_t reply_size = ids_only ? (result_count + 1) : (result_count * 2 + 1);
(*cntx)->StartArray(reply_size); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendLong(total_count); rb->StartArray(reply_size);
rb->SendLong(total_count);
size_t sent = 0; size_t sent = 0;
size_t to_skip = params.limit_offset; size_t to_skip = params.limit_offset;
@ -253,7 +255,7 @@ void ReplyWithResults(const SearchParams& params, absl::Span<SearchResult> resul
return; return;
if (ids_only) if (ids_only)
(*cntx)->SendBulkString(serialized_doc.key); rb->SendBulkString(serialized_doc.key);
else else
SendSerializedDoc(serialized_doc, cntx); SendSerializedDoc(serialized_doc, cntx);
} }
@ -291,12 +293,12 @@ void ReplySorted(search::AggregationInfo agg, const SearchParams& params,
agg.alias = ""; agg.alias = "";
facade::SinkReplyBuilder::ReplyAggregator agg_reply{cntx->reply_builder()}; facade::SinkReplyBuilder::ReplyAggregator agg_reply{cntx->reply_builder()};
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->StartArray(reply_size); rb->StartArray(reply_size);
(*cntx)->SendLong(min(total, agg_limit)); rb->SendLong(min(total, agg_limit));
for (auto* doc : absl::MakeSpan(docs).subspan(start_idx, result_count)) { for (auto* doc : absl::MakeSpan(docs).subspan(start_idx, result_count)) {
if (ids_only) { if (ids_only) {
(*cntx)->SendBulkString(doc->key); rb->SendBulkString(doc->key);
continue; continue;
} }
@ -325,7 +327,7 @@ void SearchFamily::FtCreate(CmdArgList args, ConnectionContext* cntx) {
// PREFIX count prefix [prefix ...] // PREFIX count prefix [prefix ...]
if (parser.Check("PREFIX").ExpectTail(2)) { if (parser.Check("PREFIX").ExpectTail(2)) {
if (size_t num = parser.Next<size_t>(); num != 1) if (size_t num = parser.Next<size_t>(); num != 1)
return (*cntx)->SendError("Multiple prefixes are not supported"); return cntx->SendError("Multiple prefixes are not supported");
index.prefix = string(parser.Next()); index.prefix = string(parser.Next());
continue; continue;
} }
@ -344,7 +346,7 @@ void SearchFamily::FtCreate(CmdArgList args, ConnectionContext* cntx) {
} }
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
cntx->transaction->Schedule(); cntx->transaction->Schedule();
@ -362,7 +364,7 @@ void SearchFamily::FtCreate(CmdArgList args, ConnectionContext* cntx) {
if (exists_cnt.load(memory_order_relaxed) > 0) { if (exists_cnt.load(memory_order_relaxed) > 0) {
cntx->transaction->Conclude(); cntx->transaction->Conclude();
return (*cntx)->SendError("Index already exists"); return cntx->SendError("Index already exists");
} }
auto idx_ptr = make_shared<DocIndex>(move(index)); auto idx_ptr = make_shared<DocIndex>(move(index));
@ -373,7 +375,7 @@ void SearchFamily::FtCreate(CmdArgList args, ConnectionContext* cntx) {
}, },
true); true);
(*cntx)->SendOk(); cntx->SendOk();
} }
void SearchFamily::FtDropIndex(CmdArgList args, ConnectionContext* cntx) { void SearchFamily::FtDropIndex(CmdArgList args, ConnectionContext* cntx) {
@ -389,8 +391,8 @@ void SearchFamily::FtDropIndex(CmdArgList args, ConnectionContext* cntx) {
DCHECK(num_deleted == 0u || num_deleted == shard_set->size()); DCHECK(num_deleted == 0u || num_deleted == shard_set->size());
if (num_deleted == 0u) if (num_deleted == 0u)
return (*cntx)->SendError("-Unknown Index name"); return cntx->SendError("-Unknown Index name");
return (*cntx)->SendOk(); return cntx->SendOk();
} }
void SearchFamily::FtInfo(CmdArgList args, ConnectionContext* cntx) { void SearchFamily::FtInfo(CmdArgList args, ConnectionContext* cntx) {
@ -411,7 +413,7 @@ void SearchFamily::FtInfo(CmdArgList args, ConnectionContext* cntx) {
DCHECK(num_notfound == 0u || num_notfound == shard_set->size()); DCHECK(num_notfound == 0u || num_notfound == shard_set->size());
if (num_notfound > 0u) if (num_notfound > 0u)
return (*cntx)->SendError("Unknown Index name"); return cntx->SendError("Unknown Index name");
DCHECK(infos.front().base_index.schema.fields.size() == DCHECK(infos.front().base_index.schema.fields.size() ==
infos.back().base_index.schema.fields.size()); infos.back().base_index.schema.fields.size());
@ -423,22 +425,23 @@ void SearchFamily::FtInfo(CmdArgList args, ConnectionContext* cntx) {
const auto& info = infos.front(); const auto& info = infos.front();
const auto& schema = info.base_index.schema; const auto& schema = info.base_index.schema;
(*cntx)->StartCollection(4, RedisReplyBuilder::MAP); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartCollection(4, RedisReplyBuilder::MAP);
(*cntx)->SendSimpleString("index_name"); rb->SendSimpleString("index_name");
(*cntx)->SendSimpleString(idx_name); rb->SendSimpleString(idx_name);
(*cntx)->SendSimpleString("index_definition"); rb->SendSimpleString("index_definition");
{ {
(*cntx)->StartCollection(2, RedisReplyBuilder::MAP); rb->StartCollection(2, RedisReplyBuilder::MAP);
(*cntx)->SendSimpleString("key_type"); rb->SendSimpleString("key_type");
(*cntx)->SendSimpleString(info.base_index.type == DocIndex::JSON ? "JSON" : "HASH"); rb->SendSimpleString(info.base_index.type == DocIndex::JSON ? "JSON" : "HASH");
(*cntx)->SendSimpleString("prefix"); rb->SendSimpleString("prefix");
(*cntx)->SendSimpleString(info.base_index.prefix); rb->SendSimpleString(info.base_index.prefix);
} }
(*cntx)->SendSimpleString("attributes"); rb->SendSimpleString("attributes");
(*cntx)->StartArray(schema.fields.size()); rb->StartArray(schema.fields.size());
for (const auto& [field_ident, field_info] : schema.fields) { for (const auto& [field_ident, field_info] : schema.fields) {
vector<string> info; vector<string> info;
@ -453,11 +456,11 @@ void SearchFamily::FtInfo(CmdArgList args, ConnectionContext* cntx) {
if (field_info.flags & search::SchemaField::SORTABLE) if (field_info.flags & search::SchemaField::SORTABLE)
info.push_back("SORTABLE"); info.push_back("SORTABLE");
(*cntx)->SendSimpleStrArr(info); rb->SendSimpleStrArr(info);
} }
(*cntx)->SendSimpleString("num_docs"); rb->SendSimpleString("num_docs");
(*cntx)->SendLong(total_num_docs); rb->SendLong(total_num_docs);
} }
void SearchFamily::FtList(CmdArgList args, ConnectionContext* cntx) { void SearchFamily::FtList(CmdArgList args, ConnectionContext* cntx) {
@ -470,8 +473,8 @@ void SearchFamily::FtList(CmdArgList args, ConnectionContext* cntx) {
names = es->search_indices()->GetIndexNames(); names = es->search_indices()->GetIndexNames();
return OpStatus::OK; return OpStatus::OK;
}); });
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendStringArr(names); rb->SendStringArr(names);
} }
void SearchFamily::FtSearch(CmdArgList args, ConnectionContext* cntx) { void SearchFamily::FtSearch(CmdArgList args, ConnectionContext* cntx) {
@ -485,7 +488,7 @@ void SearchFamily::FtSearch(CmdArgList args, ConnectionContext* cntx) {
search::SearchAlgorithm search_algo; search::SearchAlgorithm search_algo;
search::SortOption* sort_opt = params->sort_option.has_value() ? &*params->sort_option : nullptr; search::SortOption* sort_opt = params->sort_option.has_value() ? &*params->sort_option : nullptr;
if (!search_algo.Init(query_str, &params->query_params, sort_opt)) if (!search_algo.Init(query_str, &params->query_params, sort_opt))
return (*cntx)->SendError("Query syntax error"); return cntx->SendError("Query syntax error");
// Because our coordinator thread may not have a shard, we can't check ahead if the index exists. // Because our coordinator thread may not have a shard, we can't check ahead if the index exists.
atomic<bool> index_not_found{false}; atomic<bool> index_not_found{false};
@ -500,11 +503,11 @@ void SearchFamily::FtSearch(CmdArgList args, ConnectionContext* cntx) {
}); });
if (index_not_found.load()) if (index_not_found.load())
return (*cntx)->SendError(string{index_name} + ": no such index"); return cntx->SendError(string{index_name} + ": no such index");
for (const auto& res : docs) { for (const auto& res : docs) {
if (res.error) if (res.error)
return (*cntx)->SendError(std::move(*res.error)); return cntx->SendError(*res.error);
} }
if (auto agg = search_algo.HasAggregation(); agg) if (auto agg = search_algo.HasAggregation(); agg)
@ -524,7 +527,7 @@ void SearchFamily::FtProfile(CmdArgList args, ConnectionContext* cntx) {
search::SearchAlgorithm search_algo; search::SearchAlgorithm search_algo;
search::SortOption* sort_opt = params->sort_option.has_value() ? &*params->sort_option : nullptr; search::SortOption* sort_opt = params->sort_option.has_value() ? &*params->sort_option : nullptr;
if (!search_algo.Init(query_str, &params->query_params, sort_opt)) if (!search_algo.Init(query_str, &params->query_params, sort_opt))
return (*cntx)->SendError("Query syntax error"); return cntx->SendError("Query syntax error");
search_algo.EnableProfiling(); search_algo.EnableProfiling();
@ -552,24 +555,24 @@ void SearchFamily::FtProfile(CmdArgList args, ConnectionContext* cntx) {
}); });
auto took = absl::Now() - start; auto took = absl::Now() - start;
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->StartArray(results.size() + 1); rb->StartArray(results.size() + 1);
// General stats // General stats
(*cntx)->StartCollection(3, RedisReplyBuilder::MAP); rb->StartCollection(3, RedisReplyBuilder::MAP);
(*cntx)->SendBulkString("took"); rb->SendBulkString("took");
(*cntx)->SendLong(absl::ToInt64Microseconds(took)); rb->SendLong(absl::ToInt64Microseconds(took));
(*cntx)->SendBulkString("hits"); rb->SendBulkString("hits");
(*cntx)->SendLong(total_docs); rb->SendLong(total_docs);
(*cntx)->SendBulkString("serialized"); rb->SendBulkString("serialized");
(*cntx)->SendLong(total_serialized); rb->SendLong(total_serialized);
// Per-shard stats // Per-shard stats
for (const auto& [profile, shard_took] : results) { for (const auto& [profile, shard_took] : results) {
(*cntx)->StartCollection(2, RedisReplyBuilder::MAP); rb->StartCollection(2, RedisReplyBuilder::MAP);
(*cntx)->SendBulkString("took"); rb->SendBulkString("took");
(*cntx)->SendLong(absl::ToInt64Microseconds(shard_took)); rb->SendLong(absl::ToInt64Microseconds(shard_took));
(*cntx)->SendBulkString("tree"); rb->SendBulkString("tree");
for (size_t i = 0; i < profile.events.size(); i++) { for (size_t i = 0; i < profile.events.size(); i++) {
const auto& event = profile.events[i]; const auto& event = profile.events[i];
@ -583,13 +586,13 @@ void SearchFamily::FtProfile(CmdArgList args, ConnectionContext* cntx) {
} }
if (children > 0) if (children > 0)
(*cntx)->StartArray(2); rb->StartArray(2);
(*cntx)->SendSimpleString( rb->SendSimpleString(
absl::StrFormat("t=%-10u n=%-10u %s", event.micros, event.num_processed, event.descr)); absl::StrFormat("t=%-10u n=%-10u %s", event.micros, event.num_processed, event.descr));
if (children > 0) if (children > 0)
(*cntx)->StartArray(children); rb->StartArray(children);
} }
} }
} }

View file

@ -207,13 +207,13 @@ void SlowLogGet(dfly::CmdArgList args, dfly::ConnectionContext* cntx, dfly::Serv
size_t requested_slow_log_length = UINT32_MAX; size_t requested_slow_log_length = UINT32_MAX;
size_t argc = args.size(); size_t argc = args.size();
if (argc >= 3) { if (argc >= 3) {
(*cntx)->SendError(facade::UnknownSubCmd(sub_cmd, "SLOWLOG"), facade::kSyntaxErrType); cntx->SendError(facade::UnknownSubCmd(sub_cmd, "SLOWLOG"), facade::kSyntaxErrType);
return; return;
} else if (argc == 2) { } else if (argc == 2) {
string_view length = facade::ArgS(args, 1); string_view length = facade::ArgS(args, 1);
int64_t num; int64_t num;
if ((!absl::SimpleAtoi(length, &num)) || (num < -1)) { if ((!absl::SimpleAtoi(length, &num)) || (num < -1)) {
(*cntx)->SendError("count should be greater than or equal to -1"); cntx->SendError("count should be greater than or equal to -1");
return; return;
} }
if (num >= 0) { if (num >= 0) {
@ -239,40 +239,41 @@ void SlowLogGet(dfly::CmdArgList args, dfly::ConnectionContext* cntx, dfly::Serv
requested_slow_log_length = std::min(merged_slow_log.size(), requested_slow_log_length); requested_slow_log_length = std::min(merged_slow_log.size(), requested_slow_log_length);
(*cntx)->StartArray(requested_slow_log_length); auto* rb = static_cast<facade::RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(requested_slow_log_length);
for (size_t i = 0; i < requested_slow_log_length; ++i) { for (size_t i = 0; i < requested_slow_log_length; ++i) {
const auto& entry = merged_slow_log[i].first; const auto& entry = merged_slow_log[i].first;
const auto& args = entry.cmd_args; const auto& args = entry.cmd_args;
(*cntx)->StartArray(6); rb->StartArray(6);
(*cntx)->SendLong(entry.entry_id * service.proactor_pool().size() + merged_slow_log[i].second); rb->SendLong(entry.entry_id * service.proactor_pool().size() + merged_slow_log[i].second);
(*cntx)->SendLong(entry.unix_timestamp / 1000000000); rb->SendLong(entry.unix_timestamp / 1000000000);
(*cntx)->SendLong(entry.execution_time_micro); rb->SendLong(entry.execution_time_micro);
// if we truncated the args, there is one pseudo-element containing the number of truncated // if we truncated the args, there is one pseudo-element containing the number of truncated
// args that we must add, so the result length is increased by 1 // args that we must add, so the result length is increased by 1
size_t len = args.size() + int(args.size() < entry.original_length); size_t len = args.size() + int(args.size() < entry.original_length);
(*cntx)->StartArray(len); rb->StartArray(len);
for (const auto& arg : args) { for (const auto& arg : args) {
if (arg.second > 0) { if (arg.second > 0) {
auto suffix = absl::StrCat("... (", arg.second, " more bytes)"); auto suffix = absl::StrCat("... (", arg.second, " more bytes)");
auto cmd_arg = arg.first.substr(0, dfly::kMaximumSlowlogArgLength - suffix.length()); auto cmd_arg = arg.first.substr(0, dfly::kMaximumSlowlogArgLength - suffix.length());
(*cntx)->SendBulkString(absl::StrCat(cmd_arg, suffix)); rb->SendBulkString(absl::StrCat(cmd_arg, suffix));
} else { } else {
(*cntx)->SendBulkString(arg.first); rb->SendBulkString(arg.first);
} }
} }
// if we truncated arguments - add a special string to indicate that. // if we truncated arguments - add a special string to indicate that.
if (args.size() < entry.original_length) { if (args.size() < entry.original_length) {
(*cntx)->SendBulkString( rb->SendBulkString(
absl::StrCat("... (", entry.original_length - args.size(), " more arguments)")); absl::StrCat("... (", entry.original_length - args.size(), " more arguments)"));
} }
(*cntx)->SendBulkString(entry.client_ip); rb->SendBulkString(entry.client_ip);
(*cntx)->SendBulkString(entry.client_name); rb->SendBulkString(entry.client_name);
} }
return; return;
} }
@ -1114,7 +1115,7 @@ void ServerFamily::DbSize(CmdArgList args, ConnectionContext* cntx) {
}, },
[](ShardId) { return true; }); [](ShardId) { return true; });
return (*cntx)->SendLong(num_keys.load(memory_order_relaxed)); return cntx->SendLong(num_keys.load(memory_order_relaxed));
} }
void ServerFamily::BreakOnShutdown() { void ServerFamily::BreakOnShutdown() {
@ -1157,18 +1158,18 @@ void ServerFamily::FlushDb(CmdArgList args, ConnectionContext* cntx) {
void ServerFamily::FlushAll(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::FlushAll(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
DCHECK(cntx->transaction); DCHECK(cntx->transaction);
Drakarys(cntx->transaction, DbSlice::kDbAll); Drakarys(cntx->transaction, DbSlice::kDbAll);
(*cntx)->SendOk(); cntx->SendOk();
} }
void ServerFamily::Auth(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Auth(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 2) { if (args.size() > 2) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
// non admin port auth // non admin port auth
@ -1185,16 +1186,16 @@ void ServerFamily::Auth(CmdArgList args, ConnectionContext* cntx) {
cntx->acl_categories = cred.acl_categories; cntx->acl_categories = cred.acl_categories;
cntx->acl_commands = cred.acl_commands; cntx->acl_commands = cred.acl_commands;
cntx->authenticated = true; cntx->authenticated = true;
return (*cntx)->SendOk(); return cntx->SendOk();
} }
auto& log = ServerState::tlocal()->acl_log; auto& log = ServerState::tlocal()->acl_log;
using Reason = acl::AclLog::Reason; using Reason = acl::AclLog::Reason;
log.Add(*cntx, "AUTH", Reason::AUTH, std::string(username)); log.Add(*cntx, "AUTH", Reason::AUTH, std::string(username));
return (*cntx)->SendError(facade::kAuthRejected); return cntx->SendError(facade::kAuthRejected);
} }
if (!cntx->req_auth) { if (!cntx->req_auth) {
return (*cntx)->SendError( return cntx->SendError(
"AUTH <password> called without any password configured for " "AUTH <password> called without any password configured for "
"admin port. Are you sure your configuration is correct?"); "admin port. Are you sure your configuration is correct?");
} }
@ -1202,9 +1203,9 @@ void ServerFamily::Auth(CmdArgList args, ConnectionContext* cntx) {
string_view pass = ArgS(args, 0); string_view pass = ArgS(args, 0);
if (pass == GetPassword()) { if (pass == GetPassword()) {
cntx->authenticated = true; cntx->authenticated = true;
(*cntx)->SendOk(); cntx->SendOk();
} else { } else {
(*cntx)->SendError(facade::kAuthRejected); cntx->SendError(facade::kAuthRejected);
} }
} }
@ -1224,37 +1225,37 @@ void ServerFamily::Client(CmdArgList args, ConnectionContext* cntx) {
} }
if (sub_cmd == "SETINFO") { if (sub_cmd == "SETINFO") {
return (*cntx)->SendOk(); return cntx->SendOk();
} }
LOG_FIRST_N(ERROR, 10) << "Subcommand " << sub_cmd << " not supported"; LOG_FIRST_N(ERROR, 10) << "Subcommand " << sub_cmd << " not supported";
return (*cntx)->SendError(UnknownSubCmd(sub_cmd, "CLIENT"), kSyntaxErrType); return cntx->SendError(UnknownSubCmd(sub_cmd, "CLIENT"), kSyntaxErrType);
} }
void ServerFamily::ClientSetName(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::ClientSetName(CmdArgList args, ConnectionContext* cntx) {
if (args.size() == 1) { if (args.size() == 1) {
cntx->conn()->SetName(string{ArgS(args, 0)}); cntx->conn()->SetName(string{ArgS(args, 0)});
return (*cntx)->SendOk(); return cntx->SendOk();
} else { } else {
return (*cntx)->SendError(facade::kSyntaxErr); return cntx->SendError(facade::kSyntaxErr);
} }
} }
void ServerFamily::ClientGetName(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::ClientGetName(CmdArgList args, ConnectionContext* cntx) {
if (!args.empty()) { if (!args.empty()) {
return (*cntx)->SendError(facade::kSyntaxErr); return cntx->SendError(facade::kSyntaxErr);
} }
auto name = cntx->conn()->GetName(); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (!name.empty()) { if (auto name = cntx->conn()->GetName(); !name.empty()) {
return (*cntx)->SendBulkString(name); return rb->SendBulkString(name);
} else { } else {
return (*cntx)->SendNull(); return rb->SendNull();
} }
} }
void ServerFamily::ClientList(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::ClientList(CmdArgList args, ConnectionContext* cntx) {
if (!args.empty()) { if (!args.empty()) {
return (*cntx)->SendError(facade::kSyntaxErr); return cntx->SendError(facade::kSyntaxErr);
} }
vector<string> client_info; vector<string> client_info;
@ -1275,7 +1276,8 @@ void ServerFamily::ClientList(CmdArgList args, ConnectionContext* cntx) {
string result = absl::StrJoin(client_info, "\n"); string result = absl::StrJoin(client_info, "\n");
result.append("\n"); result.append("\n");
return (*cntx)->SendBulkString(result); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return rb->SendBulkString(result);
} }
void ServerFamily::ClientPause(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::ClientPause(CmdArgList args, ConnectionContext* cntx) {
@ -1287,7 +1289,7 @@ void ServerFamily::ClientPause(CmdArgList args, ConnectionContext* cntx) {
pause_state = parser.ToUpper().Switch("WRITE", ClientPause::WRITE, "ALL", ClientPause::ALL); pause_state = parser.ToUpper().Switch("WRITE", ClientPause::WRITE, "ALL", ClientPause::ALL);
} }
if (auto err = parser.Error(); err) { if (auto err = parser.Error(); err) {
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
} }
// Set global pause state and track commands that are running when the pause state is flipped. // Set global pause state and track commands that are running when the pause state is flipped.
@ -1309,7 +1311,7 @@ void ServerFamily::ClientPause(CmdArgList args, ConnectionContext* cntx) {
service_.proactor_pool().Await([pause_state](util::ProactorBase* pb) { service_.proactor_pool().Await([pause_state](util::ProactorBase* pb) {
ServerState::tlocal()->SetPauseState(pause_state, false); ServerState::tlocal()->SetPauseState(pause_state, false);
}); });
return (*cntx)->SendError("Failed to pause all running clients"); return cntx->SendError("Failed to pause all running clients");
} }
// We should not expire/evict keys while clients are puased. // We should not expire/evict keys while clients are puased.
@ -1336,7 +1338,7 @@ void ServerFamily::ClientPause(CmdArgList args, ConnectionContext* cntx) {
} }
}).Detach(); }).Detach();
(*cntx)->SendOk(); cntx->SendOk();
} }
void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
@ -1345,7 +1347,7 @@ void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
if (sub_cmd == "SET") { if (sub_cmd == "SET") {
if (args.size() != 3) { if (args.size() != 3) {
return (*cntx)->SendError(WrongNumArgsError("config|set")); return cntx->SendError(WrongNumArgsError("config|set"));
} }
ToLower(&args[1]); ToLower(&args[1]);
@ -1356,19 +1358,19 @@ void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
const char kErrPrefix[] = "CONFIG SET failed (possibly related to argument '"; const char kErrPrefix[] = "CONFIG SET failed (possibly related to argument '";
switch (result) { switch (result) {
case ConfigRegistry::SetResult::OK: case ConfigRegistry::SetResult::OK:
return (*cntx)->SendOk(); return cntx->SendOk();
case ConfigRegistry::SetResult::UNKNOWN: case ConfigRegistry::SetResult::UNKNOWN:
return (*cntx)->SendError( return cntx->SendError(
absl::StrCat("Unknown option or number of arguments for CONFIG SET - '", param, "'"), absl::StrCat("Unknown option or number of arguments for CONFIG SET - '", param, "'"),
kConfigErrType); kConfigErrType);
case ConfigRegistry::SetResult::READONLY: case ConfigRegistry::SetResult::READONLY:
return (*cntx)->SendError( return cntx->SendError(absl::StrCat(kErrPrefix, param, "') - can't set immutable config"),
absl::StrCat(kErrPrefix, param, "') - can't set immutable config"), kConfigErrType); kConfigErrType);
case ConfigRegistry::SetResult::INVALID: case ConfigRegistry::SetResult::INVALID:
return (*cntx)->SendError(absl::StrCat(kErrPrefix, param, "') - argument can not be set"), return cntx->SendError(absl::StrCat(kErrPrefix, param, "') - argument can not be set"),
kConfigErrType); kConfigErrType);
} }
ABSL_UNREACHABLE(); ABSL_UNREACHABLE();
} }
@ -1390,8 +1392,8 @@ void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
res.push_back(flag->CurrentValue()); res.push_back(flag->CurrentValue());
} }
} }
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
return (*cntx)->SendStringArr(res, RedisReplyBuilder::MAP); return rb->SendStringArr(res, RedisReplyBuilder::MAP);
} }
if (sub_cmd == "RESETSTAT") { if (sub_cmd == "RESETSTAT") {
@ -1404,9 +1406,9 @@ void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
stats.pipelined_cmd_cnt = 0; stats.pipelined_cmd_cnt = 0;
}); });
return (*cntx)->SendOk(); return cntx->SendOk();
} else { } else {
return (*cntx)->SendError(UnknownSubCmd(sub_cmd, "CONFIG"), kSyntaxErrType); return cntx->SendError(UnknownSubCmd(sub_cmd, "CONFIG"), kSyntaxErrType);
} }
} }
@ -1433,7 +1435,7 @@ void ServerFamily::Save(CmdArgList args, ConnectionContext* cntx) {
string err_detail; string err_detail;
bool new_version = absl::GetFlag(FLAGS_df_snapshot_format); bool new_version = absl::GetFlag(FLAGS_df_snapshot_format);
if (args.size() > 2) { if (args.size() > 2) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
if (args.size() >= 1) { if (args.size() >= 1) {
@ -1444,7 +1446,7 @@ void ServerFamily::Save(CmdArgList args, ConnectionContext* cntx) {
} else if (sub_cmd == "RDB") { } else if (sub_cmd == "RDB") {
new_version = false; new_version = false;
} else { } else {
return (*cntx)->SendError(UnknownSubCmd(sub_cmd, "SAVE"), kSyntaxErrType); return cntx->SendError(UnknownSubCmd(sub_cmd, "SAVE"), kSyntaxErrType);
} }
} }
@ -1455,9 +1457,9 @@ void ServerFamily::Save(CmdArgList args, ConnectionContext* cntx) {
GenericError ec = DoSave(new_version, basename, cntx->transaction); GenericError ec = DoSave(new_version, basename, cntx->transaction);
if (ec) { if (ec) {
(*cntx)->SendError(ec.Format()); cntx->SendError(ec.Format());
} else { } else {
(*cntx)->SendOk(); cntx->SendOk();
} }
} }
@ -1539,7 +1541,7 @@ Metrics ServerFamily::GetMetrics() const {
void ServerFamily::Info(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Info(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
string_view section; string_view section;
@ -1824,8 +1826,8 @@ void ServerFamily::Info(CmdArgList args, ConnectionContext* cntx) {
if (should_enter("CLUSTER")) { if (should_enter("CLUSTER")) {
append("cluster_enabled", ClusterConfig::IsEnabledOrEmulated()); append("cluster_enabled", ClusterConfig::IsEnabledOrEmulated());
} }
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendBulkString(info); rb->SendBulkString(info);
} }
void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) {
@ -1842,7 +1844,7 @@ void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) {
is_resp3 = proto_version == "3"; is_resp3 = proto_version == "3";
bool valid_proto_version = proto_version == "2" || is_resp3; bool valid_proto_version = proto_version == "2" || is_resp3;
if (!valid_proto_version) { if (!valid_proto_version) {
(*cntx)->SendError(UnknownCmd("HELLO", args)); cntx->SendError(UnknownCmd("HELLO", args));
return; return;
} }
@ -1859,7 +1861,7 @@ void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) {
clientname = ArgS(args, i + 1); clientname = ArgS(args, i + 1);
i += 1; i += 1;
} else { } else {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
} }
@ -1869,13 +1871,13 @@ void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) {
if (username == "default" && password == GetPassword()) { if (username == "default" && password == GetPassword()) {
cntx->authenticated = true; cntx->authenticated = true;
} else { } else {
(*cntx)->SendError(facade::kAuthRejected); cntx->SendError(facade::kAuthRejected);
return; return;
} }
} }
if (cntx->req_auth && !cntx->authenticated) { if (cntx->req_auth && !cntx->authenticated) {
(*cntx)->SendError( cntx->SendError(
"-NOAUTH HELLO must be called with the client already " "-NOAUTH HELLO must be called with the client already "
"authenticated, otherwise the HELLO <proto> AUTH <user> <pass> " "authenticated, otherwise the HELLO <proto> AUTH <user> <pass> "
"option can be used to authenticate the client and " "option can be used to authenticate the client and "
@ -1887,30 +1889,31 @@ void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) {
cntx->conn()->SetName(string{clientname}); cntx->conn()->SetName(string{clientname});
} }
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
int proto_version = 2; int proto_version = 2;
if (is_resp3) { if (is_resp3) {
proto_version = 3; proto_version = 3;
(*cntx)->SetResp3(true); rb->SetResp3(true);
} else { } else {
// Issuing hello 2 again is valid and should switch back to RESP2 // Issuing hello 2 again is valid and should switch back to RESP2
(*cntx)->SetResp3(false); rb->SetResp3(false);
} }
(*cntx)->StartCollection(7, RedisReplyBuilder::MAP); rb->StartCollection(7, RedisReplyBuilder::MAP);
(*cntx)->SendBulkString("server"); rb->SendBulkString("server");
(*cntx)->SendBulkString("redis"); rb->SendBulkString("redis");
(*cntx)->SendBulkString("version"); rb->SendBulkString("version");
(*cntx)->SendBulkString(kRedisVersion); rb->SendBulkString(kRedisVersion);
(*cntx)->SendBulkString("dragonfly_version"); rb->SendBulkString("dragonfly_version");
(*cntx)->SendBulkString(GetVersion()); rb->SendBulkString(GetVersion());
(*cntx)->SendBulkString("proto"); rb->SendBulkString("proto");
(*cntx)->SendLong(proto_version); rb->SendLong(proto_version);
(*cntx)->SendBulkString("id"); rb->SendBulkString("id");
(*cntx)->SendLong(cntx->conn()->GetClientId()); rb->SendLong(cntx->conn()->GetClientId());
(*cntx)->SendBulkString("mode"); rb->SendBulkString("mode");
(*cntx)->SendBulkString("standalone"); rb->SendBulkString("standalone");
(*cntx)->SendBulkString("role"); rb->SendBulkString("role");
(*cntx)->SendBulkString((*ServerState::tlocal()).is_master ? "master" : "slave"); rb->SendBulkString((*ServerState::tlocal()).is_master ? "master" : "slave");
} }
void ServerFamily::ReplicaOfInternal(string_view host, string_view port_sv, ConnectionContext* cntx, void ServerFamily::ReplicaOfInternal(string_view host, string_view port_sv, ConnectionContext* cntx,
@ -1933,12 +1936,12 @@ void ServerFamily::ReplicaOfInternal(string_view host, string_view port_sv, Conn
GlobalState::ACTIVE) GlobalState::ACTIVE)
<< "Server is set to replica no one, yet state is not active!"; << "Server is set to replica no one, yet state is not active!";
return (*cntx)->SendOk(); return cntx->SendOk();
} }
uint32_t port; uint32_t port;
if (!absl::SimpleAtoi(port_sv, &port) || port < 1 || port > 65535) { if (!absl::SimpleAtoi(port_sv, &port) || port < 1 || port > 65535) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
@ -1946,7 +1949,7 @@ void ServerFamily::ReplicaOfInternal(string_view host, string_view port_sv, Conn
if (auto new_state = service_.SwitchState(GlobalState::ACTIVE, GlobalState::LOADING); if (auto new_state = service_.SwitchState(GlobalState::ACTIVE, GlobalState::LOADING);
new_state.first != GlobalState::LOADING) { new_state.first != GlobalState::LOADING) {
LOG(WARNING) << GlobalStateName(new_state.first) << " in progress, ignored"; LOG(WARNING) << GlobalStateName(new_state.first) << " in progress, ignored";
(*cntx)->SendError("Invalid state"); cntx->SendError("Invalid state");
return; return;
} }
@ -2014,37 +2017,37 @@ void ServerFamily::ReplTakeOver(CmdArgList args, ConnectionContext* cntx) {
auto timeout_sec = parser.Next<float>(); auto timeout_sec = parser.Next<float>();
if (timeout_sec < 0) { if (timeout_sec < 0) {
return (*cntx)->SendError("timeout is negative"); return cntx->SendError("timeout is negative");
} }
bool save_flag = static_cast<bool>(parser.Check("SAVE").IgnoreCase()); bool save_flag = static_cast<bool>(parser.Check("SAVE").IgnoreCase());
if (parser.HasNext()) if (parser.HasNext())
return (*cntx)->SendError(absl::StrCat("Unsupported option:", string_view(parser.Next()))); return cntx->SendError(absl::StrCat("Unsupported option:", string_view(parser.Next())));
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
if (ServerState::tlocal()->is_master) if (ServerState::tlocal()->is_master)
return (*cntx)->SendError("Already a master instance"); return cntx->SendError("Already a master instance");
auto repl_ptr = replica_; auto repl_ptr = replica_;
CHECK(repl_ptr); CHECK(repl_ptr);
auto info = replica_->GetInfo(); auto info = replica_->GetInfo();
if (!info.full_sync_done) { if (!info.full_sync_done) {
return (*cntx)->SendError("Full sync not done"); return cntx->SendError("Full sync not done");
} }
std::error_code ec = replica_->TakeOver(ArgS(args, 0), save_flag); std::error_code ec = replica_->TakeOver(ArgS(args, 0), save_flag);
if (ec) if (ec)
return (*cntx)->SendError("Couldn't execute takeover"); return cntx->SendError("Couldn't execute takeover");
LOG(INFO) << "Takeover successful, promoting this instance to master."; LOG(INFO) << "Takeover successful, promoting this instance to master.";
service_.proactor_pool().AwaitFiberOnAll( service_.proactor_pool().AwaitFiberOnAll(
[&](util::ProactorBase* pb) { ServerState::tlocal()->is_master = true; }); [&](util::ProactorBase* pb) { ServerState::tlocal()->is_master = true; });
replica_->Stop(); replica_->Stop();
replica_.reset(); replica_.reset();
return (*cntx)->SendOk(); return cntx->SendOk();
} }
void ServerFamily::ReplConf(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::ReplConf(CmdArgList args, ConnectionContext* cntx) {
@ -2070,17 +2073,18 @@ void ServerFamily::ReplConf(CmdArgList args, ConnectionContext* cntx) {
cntx->replica_conn = true; cntx->replica_conn = true;
// The response for 'capa dragonfly' is: <masterid> <syncid> <numthreads> <version> // The response for 'capa dragonfly' is: <masterid> <syncid> <numthreads> <version>
(*cntx)->StartArray(4); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
(*cntx)->SendSimpleString(master_id_); rb->StartArray(4);
(*cntx)->SendSimpleString(sync_id); rb->SendSimpleString(master_id_);
(*cntx)->SendLong(replica_info->flows.size()); rb->SendSimpleString(sync_id);
(*cntx)->SendLong(unsigned(DflyVersion::CURRENT_VER)); rb->SendLong(replica_info->flows.size());
rb->SendLong(unsigned(DflyVersion::CURRENT_VER));
return; return;
} }
} else if (cmd == "LISTENING-PORT") { } else if (cmd == "LISTENING-PORT") {
uint32_t replica_listening_port; uint32_t replica_listening_port;
if (!absl::SimpleAtoi(arg, &replica_listening_port)) { if (!absl::SimpleAtoi(arg, &replica_listening_port)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
cntx->conn_state.replication_info.repl_listening_port = replica_listening_port; cntx->conn_state.replication_info.repl_listening_port = replica_listening_port;
@ -2092,7 +2096,7 @@ void ServerFamily::ReplConf(CmdArgList args, ConnectionContext* cntx) {
} else if (cmd == "CLIENT-VERSION" && args.size() == 2) { } else if (cmd == "CLIENT-VERSION" && args.size() == 2) {
unsigned version; unsigned version;
if (!absl::SimpleAtoi(arg, &version)) { if (!absl::SimpleAtoi(arg, &version)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
dfly_cmd_->SetDflyClientVersion(cntx, DflyVersion(version)); dfly_cmd_->SetDflyClientVersion(cntx, DflyVersion(version));
} else if (cmd == "ACK" && args.size() == 2) { } else if (cmd == "ACK" && args.size() == 2) {
@ -2118,43 +2122,44 @@ void ServerFamily::ReplConf(CmdArgList args, ConnectionContext* cntx) {
} }
} }
(*cntx)->SendOk(); cntx->SendOk();
return; return;
err: err:
LOG(ERROR) << "Error in receiving command: " << args; LOG(ERROR) << "Error in receiving command: " << args;
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
} }
void ServerFamily::Role(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Role(CmdArgList args, ConnectionContext* cntx) {
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
ServerState& etl = *ServerState::tlocal(); ServerState& etl = *ServerState::tlocal();
if (etl.is_master) { if (etl.is_master) {
(*cntx)->StartArray(2); rb->StartArray(2);
(*cntx)->SendBulkString("master"); rb->SendBulkString("master");
auto vec = dfly_cmd_->GetReplicasRoleInfo(); auto vec = dfly_cmd_->GetReplicasRoleInfo();
(*cntx)->StartArray(vec.size()); rb->StartArray(vec.size());
for (auto& data : vec) { for (auto& data : vec) {
(*cntx)->StartArray(3); rb->StartArray(3);
(*cntx)->SendBulkString(data.address); rb->SendBulkString(data.address);
(*cntx)->SendBulkString(absl::StrCat(data.listening_port)); rb->SendBulkString(absl::StrCat(data.listening_port));
(*cntx)->SendBulkString(data.state); rb->SendBulkString(data.state);
} }
} else { } else {
unique_lock lk{replicaof_mu_}; unique_lock lk{replicaof_mu_};
Replica::Info rinfo = replica_->GetInfo(); Replica::Info rinfo = replica_->GetInfo();
(*cntx)->StartArray(4); rb->StartArray(4);
(*cntx)->SendBulkString("replica"); rb->SendBulkString("replica");
(*cntx)->SendBulkString(rinfo.host); rb->SendBulkString(rinfo.host);
(*cntx)->SendBulkString(absl::StrCat(rinfo.port)); rb->SendBulkString(absl::StrCat(rinfo.port));
if (rinfo.full_sync_done) { if (rinfo.full_sync_done) {
(*cntx)->SendBulkString("stable_sync"); rb->SendBulkString("stable_sync");
} else if (rinfo.full_sync_in_progress) { } else if (rinfo.full_sync_in_progress) {
(*cntx)->SendBulkString("full_sync"); rb->SendBulkString("full_sync");
} else if (rinfo.master_link_established) { } else if (rinfo.master_link_established) {
(*cntx)->SendBulkString("preparation"); rb->SendBulkString("preparation");
} else { } else {
(*cntx)->SendBulkString("connecting"); rb->SendBulkString("connecting");
} }
} }
} }
@ -2179,24 +2184,25 @@ void ServerFamily::LastSave(CmdArgList args, ConnectionContext* cntx) {
lock_guard lk(save_mu_); lock_guard lk(save_mu_);
save_time = last_save_info_->save_time; save_time = last_save_info_->save_time;
} }
(*cntx)->SendLong(save_time); cntx->SendLong(save_time);
} }
void ServerFamily::Latency(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Latency(CmdArgList args, ConnectionContext* cntx) {
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
ToUpper(&args[0]); ToUpper(&args[0]);
string_view sub_cmd = ArgS(args, 0); string_view sub_cmd = ArgS(args, 0);
if (sub_cmd == "LATEST") { if (sub_cmd == "LATEST") {
return (*cntx)->SendEmptyArray(); return rb->SendEmptyArray();
} }
LOG_FIRST_N(ERROR, 10) << "Subcommand " << sub_cmd << " not supported"; LOG_FIRST_N(ERROR, 10) << "Subcommand " << sub_cmd << " not supported";
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
} }
void ServerFamily::ShutdownCmd(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::ShutdownCmd(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
@ -2206,7 +2212,7 @@ void ServerFamily::ShutdownCmd(CmdArgList args, ConnectionContext* cntx) {
} else if (absl::EqualsIgnoreCase(sub_cmd, "NOSAVE")) { } else if (absl::EqualsIgnoreCase(sub_cmd, "NOSAVE")) {
save_on_shutdown_ = false; save_on_shutdown_ = false;
} else { } else {
(*cntx)->SendError(kSyntaxErr); cntx->SendError(kSyntaxErr);
return; return;
} }
} }
@ -2215,7 +2221,7 @@ void ServerFamily::ShutdownCmd(CmdArgList args, ConnectionContext* cntx) {
[](ProactorBase* pb) { ServerState::tlocal()->EnterLameDuck(); }); [](ProactorBase* pb) { ServerState::tlocal()->EnterLameDuck(); });
CHECK_NOTNULL(acceptor_)->Stop(); CHECK_NOTNULL(acceptor_)->Stop();
(*cntx)->SendOk(); cntx->SendOk();
} }
void ServerFamily::SyncGeneric(std::string_view repl_master_id, uint64_t offs, void ServerFamily::SyncGeneric(std::string_view repl_master_id, uint64_t offs,
@ -2223,7 +2229,7 @@ void ServerFamily::SyncGeneric(std::string_view repl_master_id, uint64_t offs,
if (cntx->async_dispatch) { if (cntx->async_dispatch) {
// SYNC is a special command that should not be sent in batch with other commands. // SYNC is a special command that should not be sent in batch with other commands.
// It should be the last command since afterwards the server just dumps the replication data. // It should be the last command since afterwards the server just dumps the replication data.
(*cntx)->SendError("Can not sync in pipeline mode"); cntx->SendError("Can not sync in pipeline mode");
return; return;
} }
@ -2255,7 +2261,8 @@ void ServerFamily::SlowLog(CmdArgList args, ConnectionContext* cntx) {
"HELP", "HELP",
" Prints this help.", " Prints this help.",
}; };
(*cntx)->SendSimpleStrArr(help); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendSimpleStrArr(help);
return; return;
} }
@ -2265,41 +2272,42 @@ void ServerFamily::SlowLog(CmdArgList args, ConnectionContext* cntx) {
lengths[index] = ServerState::tlocal()->GetSlowLog().Length(); lengths[index] = ServerState::tlocal()->GetSlowLog().Length();
}); });
int sum = std::accumulate(lengths.begin(), lengths.end(), 0); int sum = std::accumulate(lengths.begin(), lengths.end(), 0);
return (*cntx)->SendLong(sum); return cntx->SendLong(sum);
} }
if (sub_cmd == "RESET") { if (sub_cmd == "RESET") {
service_.proactor_pool().AwaitFiberOnAll( service_.proactor_pool().AwaitFiberOnAll(
[](auto index, auto* context) { ServerState::tlocal()->GetSlowLog().Reset(); }); [](auto index, auto* context) { ServerState::tlocal()->GetSlowLog().Reset(); });
return (*cntx)->SendOk(); return cntx->SendOk();
} }
if (sub_cmd == "GET") { if (sub_cmd == "GET") {
return SlowLogGet(args, cntx, service_, sub_cmd); return SlowLogGet(args, cntx, service_, sub_cmd);
} }
(*cntx)->SendError(UnknownSubCmd(sub_cmd, "SLOWLOG"), kSyntaxErrType); cntx->SendError(UnknownSubCmd(sub_cmd, "SLOWLOG"), kSyntaxErrType);
} }
void ServerFamily::Module(CmdArgList args, ConnectionContext* cntx) { void ServerFamily::Module(CmdArgList args, ConnectionContext* cntx) {
ToUpper(&args[0]); ToUpper(&args[0]);
if (ArgS(args, 0) != "LIST") if (ArgS(args, 0) != "LIST")
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
(*cntx)->StartArray(2); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(2);
// Json // Json
(*cntx)->StartCollection(2, RedisReplyBuilder::MAP); rb->StartCollection(2, RedisReplyBuilder::MAP);
(*cntx)->SendSimpleString("name"); rb->SendSimpleString("name");
(*cntx)->SendSimpleString("ReJSON"); rb->SendSimpleString("ReJSON");
(*cntx)->SendSimpleString("ver"); rb->SendSimpleString("ver");
(*cntx)->SendLong(20'000); rb->SendLong(20'000);
// Search // Search
(*cntx)->StartCollection(2, RedisReplyBuilder::MAP); rb->StartCollection(2, RedisReplyBuilder::MAP);
(*cntx)->SendSimpleString("name"); rb->SendSimpleString("name");
(*cntx)->SendSimpleString("search"); rb->SendSimpleString("search");
(*cntx)->SendSimpleString("ver"); rb->SendSimpleString("ver");
(*cntx)->SendLong(20'000); // we target v2 rb->SendLong(20'000); // we target v2
} }
#define HFUNC(x) SetHandler(HandlerFunc(this, &ServerFamily::x)) #define HFUNC(x) SetHandler(HandlerFunc(this, &ServerFamily::x))

View file

@ -1083,10 +1083,10 @@ void SAdd(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
} }
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
void SIsMember(CmdArgList args, ConnectionContext* cntx) { void SIsMember(CmdArgList args, ConnectionContext* cntx) {
@ -1107,9 +1107,9 @@ void SIsMember(CmdArgList args, ConnectionContext* cntx) {
OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb));
switch (result.status()) { switch (result.status()) {
case OpStatus::OK: case OpStatus::OK:
return (*cntx)->SendLong(1); return cntx->SendLong(1);
default: default:
return (*cntx)->SendLong(0); return cntx->SendLong(0);
} }
} }
@ -1134,14 +1134,15 @@ void SMIsMember(CmdArgList args, ConnectionContext* cntx) {
return find_res.status(); return find_res.status();
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (result == OpStatus::KEY_NOTFOUND) { if (result == OpStatus::KEY_NOTFOUND) {
memberships.assign(vals.size(), "0"); memberships.assign(vals.size(), "0");
return (*cntx)->SendStringArr(memberships); return rb->SendStringArr(memberships);
} else if (result == OpStatus::OK) { } else if (result == OpStatus::OK) {
return (*cntx)->SendStringArr(memberships); return rb->SendStringArr(memberships);
} }
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
void SMove(CmdArgList args, ConnectionContext* cntx) { void SMove(CmdArgList args, ConnectionContext* cntx) {
@ -1156,11 +1157,11 @@ void SMove(CmdArgList args, ConnectionContext* cntx) {
OpResult<unsigned> result = mover.Commit(cntx->transaction); OpResult<unsigned> result = mover.Commit(cntx->transaction);
if (!result) { if (!result) {
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
return; return;
} }
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} }
void SRem(CmdArgList args, ConnectionContext* cntx) { void SRem(CmdArgList args, ConnectionContext* cntx) {
@ -1178,11 +1179,11 @@ void SRem(CmdArgList args, ConnectionContext* cntx) {
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
return (*cntx)->SendError(kWrongTypeErr); return cntx->SendError(kWrongTypeErr);
case OpStatus::OK: case OpStatus::OK:
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
default: default:
return (*cntx)->SendLong(0); return cntx->SendLong(0);
} }
} }
@ -1202,11 +1203,11 @@ void SCard(CmdArgList args, ConnectionContext* cntx) {
switch (result.status()) { switch (result.status()) {
case OpStatus::OK: case OpStatus::OK:
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
return (*cntx)->SendError(kWrongTypeErr); return cntx->SendError(kWrongTypeErr);
default: default:
return (*cntx)->SendLong(0); return cntx->SendLong(0);
} }
} }
@ -1216,7 +1217,7 @@ void SPop(CmdArgList args, ConnectionContext* cntx) {
if (args.size() > 1) { if (args.size() > 1) {
string_view arg = ArgS(args, 1); string_view arg = ArgS(args, 1);
if (!absl::SimpleAtoi(arg, &count)) { if (!absl::SimpleAtoi(arg, &count)) {
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
return; return;
} }
} }
@ -1225,22 +1226,23 @@ void SPop(CmdArgList args, ConnectionContext* cntx) {
return OpPop(t->GetOpArgs(shard), key, count); return OpPop(t->GetOpArgs(shard), key, count);
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result || result.status() == OpStatus::KEY_NOTFOUND) { if (result || result.status() == OpStatus::KEY_NOTFOUND) {
if (args.size() == 1) { // SPOP key if (args.size() == 1) { // SPOP key
if (result.status() == OpStatus::KEY_NOTFOUND) { if (result.status() == OpStatus::KEY_NOTFOUND) {
(*cntx)->SendNull(); rb->SendNull();
} else { } else {
DCHECK_EQ(1u, result.value().size()); DCHECK_EQ(1u, result.value().size());
(*cntx)->SendBulkString(result.value().front()); rb->SendBulkString(result.value().front());
} }
} else { // SPOP key cnt } else { // SPOP key cnt
(*cntx)->SendStringArr(*result, RedisReplyBuilder::SET); rb->SendStringArr(*result, RedisReplyBuilder::SET);
} }
return; return;
} }
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
void SDiff(CmdArgList args, ConnectionContext* cntx) { void SDiff(CmdArgList args, ConnectionContext* cntx) {
@ -1263,7 +1265,7 @@ void SDiff(CmdArgList args, ConnectionContext* cntx) {
cntx->transaction->ScheduleSingleHop(std::move(cb)); cntx->transaction->ScheduleSingleHop(std::move(cb));
ResultSetView rsv = DiffResultVec(result_set, src_shard); ResultSetView rsv = DiffResultVec(result_set, src_shard);
if (!rsv) { if (!rsv) {
(*cntx)->SendError(rsv.status()); cntx->SendError(rsv.status());
return; return;
} }
@ -1271,7 +1273,8 @@ void SDiff(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script if (cntx->conn_state.script_info) { // sort under script
sort(arr.begin(), arr.end()); sort(arr.begin(), arr.end());
} }
(*cntx)->SendStringArr(arr, RedisReplyBuilder::SET); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendStringArr(arr, RedisReplyBuilder::SET);
} }
void SDiffStore(CmdArgList args, ConnectionContext* cntx) { void SDiffStore(CmdArgList args, ConnectionContext* cntx) {
@ -1311,7 +1314,7 @@ void SDiffStore(CmdArgList args, ConnectionContext* cntx) {
ResultSetView rsv = DiffResultVec(result_set, src_shard); ResultSetView rsv = DiffResultVec(result_set, src_shard);
if (!rsv) { if (!rsv) {
cntx->transaction->Conclude(); cntx->transaction->Conclude();
(*cntx)->SendError(rsv.status()); cntx->SendError(rsv.status());
return; return;
} }
@ -1325,7 +1328,7 @@ void SDiffStore(CmdArgList args, ConnectionContext* cntx) {
}; };
cntx->transaction->Execute(std::move(store_cb), true); cntx->transaction->Execute(std::move(store_cb), true);
(*cntx)->SendLong(result.size()); cntx->SendLong(result.size());
} }
void SMembers(CmdArgList args, ConnectionContext* cntx) { void SMembers(CmdArgList args, ConnectionContext* cntx) {
@ -1339,9 +1342,10 @@ void SMembers(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script if (cntx->conn_state.script_info) { // sort under script
sort(svec.begin(), svec.end()); sort(svec.begin(), svec.end());
} }
(*cntx)->SendStringArr(*result, RedisReplyBuilder::SET); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendStringArr(*result, RedisReplyBuilder::SET);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1353,10 +1357,10 @@ void SRandMember(CmdArgList args, ConnectionContext* cntx) {
int count = is_count ? parser.Next<int>() : 1; int count = is_count ? parser.Next<int>() : 1;
if (parser.HasNext()) if (parser.HasNext())
return (*cntx)->SendError(WrongNumArgsError("SRANDMEMBER")); return cntx->SendError(WrongNumArgsError("SRANDMEMBER"));
if (auto err = parser.Error(); err) if (auto err = parser.Error(); err)
return (*cntx)->SendError(err->MakeReply()); return cntx->SendError(err->MakeReply());
const unsigned ucount = std::abs(count); const unsigned ucount = std::abs(count);
@ -1385,7 +1389,7 @@ void SRandMember(CmdArgList args, ConnectionContext* cntx) {
}; };
OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(cb); OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(cb);
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
if (count < 0 && !result->empty()) { if (count < 0 && !result->empty()) {
for (auto i = result->size(); i < ucount; ++i) { for (auto i = result->size(); i < ucount; ++i) {
@ -1393,15 +1397,15 @@ void SRandMember(CmdArgList args, ConnectionContext* cntx) {
result->push_back(result->front()); result->push_back(result->front());
} }
} }
(*cntx)->SendStringArr(*result, RedisReplyBuilder::SET); rb->SendStringArr(*result, RedisReplyBuilder::SET);
} else if (result.status() == OpStatus::KEY_NOTFOUND) { } else if (result.status() == OpStatus::KEY_NOTFOUND) {
if (is_count) { if (is_count) {
(*cntx)->SendStringArr(StringVec(), RedisReplyBuilder::SET); rb->SendStringArr(StringVec(), RedisReplyBuilder::SET);
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -1421,9 +1425,10 @@ void SInter(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script if (cntx->conn_state.script_info) { // sort under script
sort(arr.begin(), arr.end()); sort(arr.begin(), arr.end());
} }
(*cntx)->SendStringArr(arr, RedisReplyBuilder::SET); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendStringArr(arr, RedisReplyBuilder::SET);
} else { } else {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} }
@ -1451,7 +1456,7 @@ void SInterStore(CmdArgList args, ConnectionContext* cntx) {
OpResult<SvArray> result = InterResultVec(result_set, inter_shard_cnt.load(memory_order_relaxed)); OpResult<SvArray> result = InterResultVec(result_set, inter_shard_cnt.load(memory_order_relaxed));
if (!result) { if (!result) {
cntx->transaction->Conclude(); cntx->transaction->Conclude();
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
return; return;
} }
@ -1464,20 +1469,20 @@ void SInterStore(CmdArgList args, ConnectionContext* cntx) {
}; };
cntx->transaction->Execute(std::move(store_cb), true); cntx->transaction->Execute(std::move(store_cb), true);
(*cntx)->SendLong(result->size()); cntx->SendLong(result->size());
} }
void SInterCard(CmdArgList args, ConnectionContext* cntx) { void SInterCard(CmdArgList args, ConnectionContext* cntx) {
unsigned num_keys; unsigned num_keys;
if (!absl::SimpleAtoi(ArgS(args, 0), &num_keys)) if (!absl::SimpleAtoi(ArgS(args, 0), &num_keys))
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
unsigned limit = 0; unsigned limit = 0;
if (args.size() == (num_keys + 3) && ArgS(args, 1 + num_keys) == "LIMIT") { if (args.size() == (num_keys + 3) && ArgS(args, 1 + num_keys) == "LIMIT") {
if (!absl::SimpleAtoi(ArgS(args, num_keys + 2), &limit)) if (!absl::SimpleAtoi(ArgS(args, num_keys + 2), &limit))
return (*cntx)->SendError("limit can't be negative"); return cntx->SendError("limit can't be negative");
} else if (args.size() > (num_keys + 1)) } else if (args.size() > (num_keys + 1))
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
ResultStringVec result_set(shard_set->size(), OpStatus::SKIPPED); ResultStringVec result_set(shard_set->size(), OpStatus::SKIPPED);
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {
@ -1489,7 +1494,7 @@ void SInterCard(CmdArgList args, ConnectionContext* cntx) {
OpResult<SvArray> result = OpResult<SvArray> result =
InterResultVec(result_set, cntx->transaction->GetUniqueShardCnt(), limit); InterResultVec(result_set, cntx->transaction->GetUniqueShardCnt(), limit);
return (*cntx)->SendLong(result->size()); return cntx->SendLong(result->size());
} }
void SUnion(CmdArgList args, ConnectionContext* cntx) { void SUnion(CmdArgList args, ConnectionContext* cntx) {
@ -1509,9 +1514,10 @@ void SUnion(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script if (cntx->conn_state.script_info) { // sort under script
sort(arr.begin(), arr.end()); sort(arr.begin(), arr.end());
} }
(*cntx)->SendStringArr(arr, RedisReplyBuilder::SET); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendStringArr(arr, RedisReplyBuilder::SET);
} else { } else {
(*cntx)->SendError(unionset.status()); cntx->SendError(unionset.status());
} }
} }
@ -1538,7 +1544,7 @@ void SUnionStore(CmdArgList args, ConnectionContext* cntx) {
ResultSetView unionset = UnionResultVec(result_set); ResultSetView unionset = UnionResultVec(result_set);
if (!unionset) { if (!unionset) {
cntx->transaction->Conclude(); cntx->transaction->Conclude();
(*cntx)->SendError(unionset.status()); cntx->SendError(unionset.status());
return; return;
} }
@ -1553,7 +1559,7 @@ void SUnionStore(CmdArgList args, ConnectionContext* cntx) {
}; };
cntx->transaction->Execute(std::move(store_cb), true); cntx->transaction->Execute(std::move(store_cb), true);
(*cntx)->SendLong(result.size()); cntx->SendLong(result.size());
} }
void SScan(CmdArgList args, ConnectionContext* cntx) { void SScan(CmdArgList args, ConnectionContext* cntx) {
@ -1563,19 +1569,19 @@ void SScan(CmdArgList args, ConnectionContext* cntx) {
uint64_t cursor = 0; uint64_t cursor = 0;
if (!absl::SimpleAtoi(token, &cursor)) { if (!absl::SimpleAtoi(token, &cursor)) {
return (*cntx)->SendError("invalid cursor"); return cntx->SendError("invalid cursor");
} }
// SSCAN key cursor [MATCH pattern] [COUNT count] // SSCAN key cursor [MATCH pattern] [COUNT count]
if (args.size() > 6) { if (args.size() > 6) {
DVLOG(1) << "got " << args.size() << " this is more than it should be"; DVLOG(1) << "got " << args.size() << " this is more than it should be";
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
OpResult<ScanOpts> ops = ScanOpts::TryFrom(args.subspan(2)); OpResult<ScanOpts> ops = ScanOpts::TryFrom(args.subspan(2));
if (!ops) { if (!ops) {
DVLOG(1) << "SScan invalid args - return " << ops << " to the user"; DVLOG(1) << "SScan invalid args - return " << ops << " to the user";
return (*cntx)->SendError(ops.status()); return cntx->SendError(ops.status());
} }
ScanOpts scan_op = ops.value(); ScanOpts scan_op = ops.value();
@ -1584,16 +1590,17 @@ void SScan(CmdArgList args, ConnectionContext* cntx) {
return OpScan(t->GetOpArgs(shard), key, &cursor, scan_op); return OpScan(t->GetOpArgs(shard), key, &cursor, scan_op);
}; };
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<StringVec> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result.status() != OpStatus::WRONG_TYPE) { if (result.status() != OpStatus::WRONG_TYPE) {
(*cntx)->StartArray(2); rb->StartArray(2);
(*cntx)->SendBulkString(absl::StrCat(cursor)); rb->SendBulkString(absl::StrCat(cursor));
(*cntx)->StartArray(result->size()); // Within scan the return page is of type array rb->StartArray(result->size()); // Within scan the return page is of type array
for (const auto& k : *result) { for (const auto& k : *result) {
(*cntx)->SendBulkString(k); rb->SendBulkString(k);
} }
} else { } else {
(*cntx)->SendError(result.status()); rb->SendError(result.status());
} }
} }
@ -1605,7 +1612,7 @@ void SAddEx(CmdArgList args, ConnectionContext* cntx) {
constexpr uint32_t kMaxTtl = (1UL << 26); constexpr uint32_t kMaxTtl = (1UL << 26);
if (!absl::SimpleAtoi(ttl_str, &ttl_sec) || ttl_sec == 0 || ttl_sec > kMaxTtl) { if (!absl::SimpleAtoi(ttl_str, &ttl_sec) || ttl_sec == 0 || ttl_sec > kMaxTtl) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
vector<string_view> vals(args.size() - 2); vector<string_view> vals(args.size() - 2);
@ -1621,10 +1628,10 @@ void SAddEx(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
} }
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} }
} // namespace } // namespace

File diff suppressed because it is too large Load diff

View file

@ -791,11 +791,12 @@ void StringFamily::Set(CmdArgList args, ConnectionContext* cntx) {
const auto result{SetGeneric(cntx, sparams, key, value, true)}; const auto result{SetGeneric(cntx, sparams, key, value, true)};
if (sparams.flags & SetCmd::SET_GET) { if (sparams.flags & SetCmd::SET_GET) {
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
// When SET_GET is used, the reply is not affected by whether anything was set. // When SET_GET is used, the reply is not affected by whether anything was set.
if (result->has_value()) { if (result->has_value()) {
(*cntx)->SendBulkString(result->value()); rb->SendBulkString(result->value());
} else { } else {
(*cntx)->SendNull(); rb->SendNull();
} }
return; return;
} }
@ -853,17 +854,18 @@ void StringFamily::Get(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
DVLOG(1) << "GET " << trans->DebugId() << ": " << key << " " << result.value(); DVLOG(1) << "GET " << trans->DebugId() << ": " << key << " " << result.value();
(*cntx)->SendBulkString(*result); rb->SendBulkString(*result);
} else { } else {
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
(*cntx)->SendError(kWrongTypeErr); rb->SendError(kWrongTypeErr);
break; break;
default: default:
DVLOG(1) << "GET " << key << " nil"; DVLOG(1) << "GET " << key << " nil";
(*cntx)->SendNull(); rb->SendNull();
} }
} }
} }
@ -881,17 +883,18 @@ void StringFamily::GetDel(CmdArgList args, ConnectionContext* cntx) {
Transaction* trans = cntx->transaction; Transaction* trans = cntx->transaction;
OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) { if (result) {
DVLOG(1) << "GET " << trans->DebugId() << ": " << key << " " << result.value(); DVLOG(1) << "GET " << trans->DebugId() << ": " << key << " " << result.value();
(*cntx)->SendBulkString(*result); rb->SendBulkString(*result);
} else { } else {
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
(*cntx)->SendError(kWrongTypeErr); rb->SendError(kWrongTypeErr);
break; break;
default: default:
DVLOG(1) << "GET " << key << " nil"; DVLOG(1) << "GET " << key << " nil";
(*cntx)->SendNull(); rb->SendNull();
} }
} }
} }
@ -912,16 +915,17 @@ void StringFamily::GetSet(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = cntx->transaction->ScheduleSingleHop(std::move(cb));
if (status != OpStatus::OK) { if (status != OpStatus::OK) {
(*cntx)->SendError(status); cntx->SendError(status);
return; return;
} }
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (prev_val) { if (prev_val) {
(*cntx)->SendBulkString(*prev_val); rb->SendBulkString(*prev_val);
return; return;
} }
return (*cntx)->SendNull(); return rb->SendNull();
} }
void StringFamily::GetEx(CmdArgList args, ConnectionContext* cntx) { void StringFamily::GetEx(CmdArgList args, ConnectionContext* cntx) {
@ -938,16 +942,16 @@ void StringFamily::GetEx(CmdArgList args, ConnectionContext* cntx) {
if (cur_arg == "EX" || cur_arg == "PX" || cur_arg == "EXAT" || cur_arg == "PXAT") { if (cur_arg == "EX" || cur_arg == "PX" || cur_arg == "EXAT" || cur_arg == "PXAT") {
i++; i++;
if (i >= args.size()) { if (i >= args.size()) {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
string_view ex = ArgS(args, i); string_view ex = ArgS(args, i);
if (!absl::SimpleAtoi(ex, &int_arg)) { if (!absl::SimpleAtoi(ex, &int_arg)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (int_arg <= 0) { if (int_arg <= 0) {
return (*cntx)->SendError(InvalidExpireTime("getex")); return cntx->SendError(InvalidExpireTime("getex"));
} }
if (cur_arg == "EXAT" || cur_arg == "PXAT") { if (cur_arg == "EXAT" || cur_arg == "PXAT") {
@ -963,7 +967,7 @@ void StringFamily::GetEx(CmdArgList args, ConnectionContext* cntx) {
} else if (cur_arg == "PERSIST") { } else if (cur_arg == "PERSIST") {
exp_params.persist = true; exp_params.persist = true;
} else { } else {
return (*cntx)->SendError(kSyntaxErr); return cntx->SendError(kSyntaxErr);
} }
} }
@ -989,16 +993,17 @@ void StringFamily::GetEx(CmdArgList args, ConnectionContext* cntx) {
OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
if (result) if (result)
return (*cntx)->SendBulkString(*result); return rb->SendBulkString(*result);
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
(*cntx)->SendError(kWrongTypeErr); rb->SendError(kWrongTypeErr);
break; break;
default: default:
DVLOG(1) << "GET " << key << " nil"; DVLOG(1) << "GET " << key << " nil";
(*cntx)->SendNull(); rb->SendNull();
} }
} }
@ -1013,7 +1018,7 @@ void StringFamily::IncrBy(CmdArgList args, ConnectionContext* cntx) {
int64_t val; int64_t val;
if (!absl::SimpleAtoi(sval, &val)) { if (!absl::SimpleAtoi(sval, &val)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
return IncrByGeneric(key, val, cntx); return IncrByGeneric(key, val, cntx);
} }
@ -1024,7 +1029,7 @@ void StringFamily::IncrByFloat(CmdArgList args, ConnectionContext* cntx) {
double val; double val;
if (!absl::SimpleAtod(sval, &val)) { if (!absl::SimpleAtod(sval, &val)) {
return (*cntx)->SendError(kInvalidFloatErr); return cntx->SendError(kInvalidFloatErr);
} }
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {
@ -1036,7 +1041,7 @@ void StringFamily::IncrByFloat(CmdArgList args, ConnectionContext* cntx) {
DVLOG(2) << "IncrByGeneric " << key << "/" << result.value(); DVLOG(2) << "IncrByGeneric " << key << "/" << result.value();
if (!result) { if (!result) {
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
} }
builder->SendDouble(result.value()); builder->SendDouble(result.value());
@ -1053,10 +1058,10 @@ void StringFamily::DecrBy(CmdArgList args, ConnectionContext* cntx) {
int64_t val; int64_t val;
if (!absl::SimpleAtoi(sval, &val)) { if (!absl::SimpleAtoi(sval, &val)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (val == INT64_MIN) { if (val == INT64_MIN) {
return (*cntx)->SendError(kIncrOverflow); return cntx->SendError(kIncrOverflow);
} }
return IncrByGeneric(key, -val, cntx); return IncrByGeneric(key, -val, cntx);
@ -1114,9 +1119,9 @@ void StringFamily::ExtendGeneric(CmdArgList args, bool prepend, ConnectionContex
OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (!result) if (!result)
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
else else
return (*cntx)->SendLong(result.value()); return cntx->SendLong(result.value());
} }
DCHECK(cntx->protocol() == Protocol::MEMCACHE); DCHECK(cntx->protocol() == Protocol::MEMCACHE);
@ -1142,11 +1147,11 @@ void StringFamily::SetExGeneric(bool seconds, CmdArgList args, ConnectionContext
int32_t unit_vals; int32_t unit_vals;
if (!absl::SimpleAtoi(ex, &unit_vals)) { if (!absl::SimpleAtoi(ex, &unit_vals)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (unit_vals < 1 || unit_vals >= kMaxExpireDeadlineSec) { if (unit_vals < 1 || unit_vals >= kMaxExpireDeadlineSec) {
return (*cntx)->SendError(InvalidExpireTime(cntx->cid->name())); return cntx->SendError(InvalidExpireTime(cntx->cid->name()));
} }
SetCmd::SetParams sparams; SetCmd::SetParams sparams;
@ -1163,7 +1168,7 @@ void StringFamily::SetExGeneric(bool seconds, CmdArgList args, ConnectionContext
OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb)); OpResult<void> result = cntx->transaction->ScheduleSingleHop(std::move(cb));
return (*cntx)->SendError(result.status()); return cntx->SendError(result.status());
} }
void StringFamily::MGet(CmdArgList args, ConnectionContext* cntx) { void StringFamily::MGet(CmdArgList args, ConnectionContext* cntx) {
@ -1241,9 +1246,9 @@ void StringFamily::MSet(CmdArgList args, ConnectionContext* cntx) {
OpStatus status = transaction->ScheduleSingleHop(std::move(cb)); OpStatus status = transaction->ScheduleSingleHop(std::move(cb));
if (status == OpStatus::OK) { if (status == OpStatus::OK) {
(*cntx)->SendOk(); cntx->SendOk();
} else { } else {
(*cntx)->SendError(status); cntx->SendError(status);
} }
} }
@ -1280,7 +1285,7 @@ void StringFamily::MSetNx(CmdArgList args, ConnectionContext* cntx) {
transaction->Execute(std::move(epilog_cb), true); transaction->Execute(std::move(epilog_cb), true);
(*cntx)->SendLong(to_skip ? 0 : 1); cntx->SendLong(to_skip ? 0 : 1);
} }
void StringFamily::StrLen(CmdArgList args, ConnectionContext* cntx) { void StringFamily::StrLen(CmdArgList args, ConnectionContext* cntx) {
@ -1298,9 +1303,9 @@ void StringFamily::StrLen(CmdArgList args, ConnectionContext* cntx) {
OpResult<size_t> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<size_t> result = trans->ScheduleSingleHopT(std::move(cb));
if (result.status() == OpStatus::WRONG_TYPE) { if (result.status() == OpStatus::WRONG_TYPE) {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} else { } else {
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} }
} }
@ -1311,7 +1316,7 @@ void StringFamily::GetRange(CmdArgList args, ConnectionContext* cntx) {
int32_t start, end; int32_t start, end;
if (!absl::SimpleAtoi(from, &start) || !absl::SimpleAtoi(to, &end)) { if (!absl::SimpleAtoi(from, &start) || !absl::SimpleAtoi(to, &end)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
auto cb = [&](Transaction* t, EngineShard* shard) { auto cb = [&](Transaction* t, EngineShard* shard) {
@ -1322,9 +1327,10 @@ void StringFamily::GetRange(CmdArgList args, ConnectionContext* cntx) {
OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<string> result = trans->ScheduleSingleHopT(std::move(cb));
if (result.status() == OpStatus::WRONG_TYPE) { if (result.status() == OpStatus::WRONG_TYPE) {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} else { } else {
(*cntx)->SendBulkString(result.value()); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->SendBulkString(result.value());
} }
} }
@ -1335,16 +1341,16 @@ void StringFamily::SetRange(CmdArgList args, ConnectionContext* cntx) {
int32_t start; int32_t start;
if (!absl::SimpleAtoi(offset, &start)) { if (!absl::SimpleAtoi(offset, &start)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
if (start < 0) { if (start < 0) {
return (*cntx)->SendError("offset is out of range"); return cntx->SendError("offset is out of range");
} }
size_t min_size = start + value.size(); size_t min_size = start + value.size();
if (min_size > kMaxStrLen) { if (min_size > kMaxStrLen) {
return (*cntx)->SendError("string exceeds maximum allowed size"); return cntx->SendError("string exceeds maximum allowed size");
} }
auto cb = [&](Transaction* t, EngineShard* shard) -> OpResult<uint32_t> { auto cb = [&](Transaction* t, EngineShard* shard) -> OpResult<uint32_t> {
@ -1355,9 +1361,9 @@ void StringFamily::SetRange(CmdArgList args, ConnectionContext* cntx) {
OpResult<uint32_t> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<uint32_t> result = trans->ScheduleSingleHopT(std::move(cb));
if (result.status() == OpStatus::WRONG_TYPE) { if (result.status() == OpStatus::WRONG_TYPE) {
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
} else { } else {
(*cntx)->SendLong(result.value()); cntx->SendLong(result.value());
} }
} }
@ -1381,21 +1387,21 @@ void StringFamily::ClThrottle(CmdArgList args, ConnectionContext* cntx) {
uint64_t max_burst; uint64_t max_burst;
const string_view max_burst_str = ArgS(args, 1); const string_view max_burst_str = ArgS(args, 1);
if (!absl::SimpleAtoi(max_burst_str, &max_burst)) { if (!absl::SimpleAtoi(max_burst_str, &max_burst)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
// Emit count of tokens per period // Emit count of tokens per period
uint64_t count; uint64_t count;
const string_view count_str = ArgS(args, 2); const string_view count_str = ArgS(args, 2);
if (!absl::SimpleAtoi(count_str, &count)) { if (!absl::SimpleAtoi(count_str, &count)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
// Period of emitting count of tokens // Period of emitting count of tokens
uint64_t period; uint64_t period;
const string_view period_str = ArgS(args, 3); const string_view period_str = ArgS(args, 3);
if (!absl::SimpleAtoi(period_str, &period)) { if (!absl::SimpleAtoi(period_str, &period)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
// Apply quantity of tokens now // Apply quantity of tokens now
@ -1404,22 +1410,22 @@ void StringFamily::ClThrottle(CmdArgList args, ConnectionContext* cntx) {
const string_view quantity_str = ArgS(args, 4); const string_view quantity_str = ArgS(args, 4);
if (!absl::SimpleAtoi(quantity_str, &quantity)) { if (!absl::SimpleAtoi(quantity_str, &quantity)) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
} }
if (max_burst > INT64_MAX - 1) { if (max_burst > INT64_MAX - 1) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
const int64_t limit = max_burst + 1; const int64_t limit = max_burst + 1;
if (period > UINT64_MAX / 1000 || count == 0 || period * 1000 / count > INT64_MAX) { if (period > UINT64_MAX / 1000 || count == 0 || period * 1000 / count > INT64_MAX) {
return (*cntx)->SendError(kInvalidIntErr); return cntx->SendError(kInvalidIntErr);
} }
const int64_t emission_interval_ms = period * 1000 / count; const int64_t emission_interval_ms = period * 1000 / count;
if (emission_interval_ms == 0) { if (emission_interval_ms == 0) {
return (*cntx)->SendError("zero rates are not supported"); return cntx->SendError("zero rates are not supported");
} }
auto cb = [&](Transaction* t, EngineShard* shard) -> OpResult<array<int64_t, 5>> { auto cb = [&](Transaction* t, EngineShard* shard) -> OpResult<array<int64_t, 5>> {
@ -1430,7 +1436,8 @@ void StringFamily::ClThrottle(CmdArgList args, ConnectionContext* cntx) {
OpResult<array<int64_t, 5>> result = trans->ScheduleSingleHopT(std::move(cb)); OpResult<array<int64_t, 5>> result = trans->ScheduleSingleHopT(std::move(cb));
if (result) { if (result) {
(*cntx)->StartArray(result->size()); auto* rb = static_cast<RedisReplyBuilder*>(cntx->reply_builder());
rb->StartArray(result->size());
auto& array = result.value(); auto& array = result.value();
int64_t retry_after_s = array[3] / 1000; int64_t retry_after_s = array[3] / 1000;
@ -1446,22 +1453,22 @@ void StringFamily::ClThrottle(CmdArgList args, ConnectionContext* cntx) {
array[4] = reset_after_s; array[4] = reset_after_s;
for (const auto& v : array) { for (const auto& v : array) {
(*cntx)->SendLong(v); rb->SendLong(v);
} }
} else { } else {
switch (result.status()) { switch (result.status()) {
case OpStatus::WRONG_TYPE: case OpStatus::WRONG_TYPE:
(*cntx)->SendError(kWrongTypeErr); cntx->SendError(kWrongTypeErr);
break; break;
case OpStatus::INVALID_INT: case OpStatus::INVALID_INT:
case OpStatus::INVALID_VALUE: case OpStatus::INVALID_VALUE:
(*cntx)->SendError(kInvalidIntErr); cntx->SendError(kInvalidIntErr);
break; break;
case OpStatus::OUT_OF_MEMORY: case OpStatus::OUT_OF_MEMORY:
(*cntx)->SendError(kOutOfMemory); cntx->SendError(kOutOfMemory);
break; break;
default: default:
(*cntx)->SendError(result.status()); cntx->SendError(result.status());
break; break;
} }
} }

353
src/server/zset_family.cc Executable file → Normal file

File diff suppressed because it is too large Load diff