refactor(facade): Refactor reply builder (#991)

Refactor RedisReplyBuilder to simplify method overloads
This commit is contained in:
Vladislav 2023-03-26 12:52:40 +03:00 committed by GitHub
parent ef4062d817
commit 72f67eddd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 144 additions and 203 deletions

View file

@ -160,6 +160,14 @@ void MCReplyBuilder::SendNotFound() {
SendSimpleString("NOT_FOUND");
}
size_t RedisReplyBuilder::WrappedStrSpan::Size() const {
return visit([](auto arr) { return arr.size(); }, (const StrSpan&)*this);
}
string_view RedisReplyBuilder::WrappedStrSpan::operator[](size_t i) const {
return visit([i](auto arr) { return string_view{arr[i]}; }, (const StrSpan&)*this);
}
char* RedisReplyBuilder::FormatDouble(double val, char* dest, unsigned dest_len) {
StringBuilder sb(dest, dest_len);
CHECK(dfly_conv.ToShortest(val, &sb));
@ -329,12 +337,11 @@ void RedisReplyBuilder::SendMGetResponse(const OptResp* resp, uint32_t count) {
SendRaw(res);
}
void RedisReplyBuilder::SendSimpleStrArr(const std::string_view* arr, uint32_t count) {
string res = absl::StrCat("*", count, kCRLF);
void RedisReplyBuilder::SendSimpleStrArr(absl::Span<const std::string_view> arr) {
string res = absl::StrCat("*", arr.size(), kCRLF);
for (size_t i = 0; i < count; ++i) {
StrAppend(&res, "+", arr[i], kCRLF);
}
for (auto sv : arr)
StrAppend(&res, "+", sv, kCRLF);
SendRaw(res);
}
@ -347,13 +354,33 @@ void RedisReplyBuilder::SendEmptyArray() {
StartArray(0);
}
void RedisReplyBuilder::SendStringArr(absl::Span<const std::string_view> arr) {
if (arr.empty()) {
void RedisReplyBuilder::SendStringArr(StrSpan arr, CollectionType type) {
WrappedStrSpan warr{arr};
if (type == ARRAY && warr.Size() == 0) {
SendRaw("*0\r\n");
return;
}
SendStringCollection(arr.data(), arr.size(), CollectionType::ARRAY);
SendStringArrInternal(warr, type);
}
void RedisReplyBuilder::StartArray(unsigned len) {
StartCollection(len, ARRAY);
}
constexpr static string_view START_SYMBOLS[] = {"*", "~", "%"};
static_assert(START_SYMBOLS[RedisReplyBuilder::MAP] == "%" &&
START_SYMBOLS[RedisReplyBuilder::SET] == "~");
void RedisReplyBuilder::StartCollection(unsigned len, CollectionType type) {
if (!is_resp3_) { // Flatten for Resp2
if (type == MAP)
len *= 2;
type = ARRAY;
}
SendRaw(absl::StrCat(START_SYMBOLS[type], len, kCRLF));
}
// This implementation a bit complicated because it uses vectorized
@ -361,62 +388,15 @@ void RedisReplyBuilder::SendStringArr(absl::Span<const std::string_view> arr) {
// to low numbers (around 1024). Therefore, to make it robust we send the array in batches.
// We limit the vector length to 256 and when it fills up we flush it to the socket and continue
// iterating.
void RedisReplyBuilder::SendStringArr(absl::Span<const string> arr) {
SendStringCollection(arr.data(), arr.size(), CollectionType::ARRAY);
}
void RedisReplyBuilder::SendStringArrInternal(WrappedStrSpan arr, CollectionType type) {
size_t size = arr.Size();
void RedisReplyBuilder::SendStringArrayAsMap(absl::Span<const std::string_view> arr) {
SendStringCollection(arr.data(), arr.size(), CollectionType::MAP);
}
void RedisReplyBuilder::SendStringArrayAsMap(absl::Span<const std::string> arr) {
SendStringCollection(arr.data(), arr.size(), CollectionType::MAP);
}
void RedisReplyBuilder::SendStringArrayAsSet(absl::Span<const std::string_view> arr) {
SendStringCollection(arr.data(), arr.size(), CollectionType::SET);
}
void RedisReplyBuilder::SendStringArrayAsSet(absl::Span<const std::string> arr) {
SendStringCollection(arr.data(), arr.size(), CollectionType::SET);
}
void RedisReplyBuilder::StartArray(unsigned len) {
SendRaw(absl::StrCat("*", len, kCRLF));
}
void RedisReplyBuilder::StartMap(unsigned num_pairs) {
size_t header_len = size;
string_view type_char = "*";
if (is_resp3_) {
SendRaw(absl::StrCat("%", num_pairs, kCRLF));
return;
}
// Flatten for Resp2.
StartArray(num_pairs * 2);
}
void RedisReplyBuilder::StartSet(unsigned num_elements) {
if (is_resp3_) {
SendRaw(absl::StrCat("~", num_elements, kCRLF));
}
// Flatten for Resp2.
StartArray(num_elements);
}
void RedisReplyBuilder::SendStringCollection(StrPtr str_ptr, uint32_t len, CollectionType type) {
string type_char = "*";
size_t header_len = len;
if (is_resp3_) {
switch (type) {
case CollectionType::ARRAY:
break;
case CollectionType::MAP:
type_char[0] = '%';
header_len = 0.5 * len; // Each key value pair counts as one.
break;
case CollectionType::SET:
type_char[0] = '~';
break;
}
type_char = START_SYMBOLS[type];
if (type == MAP)
header_len /= 2; // Each key value pair counts as one.
}
if (header_len == 0) {
@ -425,7 +405,7 @@ void RedisReplyBuilder::SendStringCollection(StrPtr str_ptr, uint32_t len, Colle
}
// When vector length is too long, Send returns EMSGSIZE.
size_t vec_len = std::min<size_t>(256u, len);
size_t vec_len = std::min<size_t>(256u, size);
absl::FixedArray<iovec, 16> vec(vec_len * 2 + 2);
absl::FixedArray<char, 64> meta((vec_len + 1) * 16);
@ -440,12 +420,8 @@ void RedisReplyBuilder::SendStringCollection(StrPtr str_ptr, uint32_t len, Colle
unsigned vec_indx = 1;
string_view src;
for (unsigned i = 0; i < len; ++i) {
if (holds_alternative<const string_view*>(str_ptr)) {
src = get<const string_view*>(str_ptr)[i];
} else {
src = get<const string*>(str_ptr)[i];
}
for (unsigned i = 0; i < size; ++i) {
src = arr[i];
*next++ = '$';
next = absl::numbers_internal::FastIntToBuffer(src.size(), next);
*next++ = '\r';
@ -463,7 +439,7 @@ void RedisReplyBuilder::SendStringCollection(StrPtr str_ptr, uint32_t len, Colle
++vec_indx;
if (vec_indx + 1 >= vec.size()) {
if (i < len - 1 || vec_indx == vec.size()) {
if (i < size - 1 || vec_indx == vec.size()) {
Send(vec.data(), vec_indx);
if (ec_)
return;

View file

@ -12,6 +12,16 @@
namespace facade {
class SinkReplyBuilder {
public:
struct ResponseValue {
std::string_view key;
std::string value;
uint64_t mc_ver = 0; // 0 means we do not output it (i.e has not been requested).
uint32_t mc_flag = 0;
};
using OptResp = std::optional<ResponseValue>;
public:
SinkReplyBuilder(const SinkReplyBuilder&) = delete;
void operator=(const SinkReplyBuilder&) = delete;
@ -21,6 +31,20 @@ class SinkReplyBuilder {
virtual ~SinkReplyBuilder() {
}
virtual void SendError(std::string_view str, std::string_view type = {}) = 0; // MC and Redis
virtual void SendStored() = 0; // Reply for set commands.
virtual void SendSetSkipped() = 0;
virtual void SendMGetResponse(const OptResp* resp, uint32_t count) = 0;
virtual void SendLong(long val) = 0;
virtual void SendSimpleString(std::string_view str) = 0;
void SendOk() {
SendSimpleString("OK");
}
// In order to reduce interrupt rate we allow coalescing responses together using
// Batch mode. It is controlled by Connection state machine because it makes sense only
// when pipelined requests are arriving.
@ -53,36 +77,10 @@ class SinkReplyBuilder {
return err_count_;
}
//! Sends a string as is without any formatting. raw should be encoded according to the protocol.
void SendRaw(std::string_view str);
protected:
void SendRaw(std::string_view str); // Sends raw without any formatting.
void SendRawVec(absl::Span<const std::string_view> msg_vec);
// Common for both MC and Redis.
virtual void SendError(std::string_view str, std::string_view type = std::string_view{}) = 0;
virtual void SendSimpleString(std::string_view str) = 0;
void SendOk() {
SendSimpleString("OK");
}
struct ResponseValue {
std::string_view key;
std::string value;
uint64_t mc_ver = 0; // 0 means we do not output it (i.e has not been requested).
uint32_t mc_flag = 0;
};
using OptResp = std::optional<ResponseValue>;
virtual void SendMGetResponse(const OptResp* resp, uint32_t count) = 0;
virtual void SendLong(long val) = 0;
// Reply for set commands.
virtual void SendStored() = 0;
virtual void SendSetSkipped() = 0;
protected:
void Send(const iovec* v, uint32_t len);
std::string batch_;
@ -100,6 +98,8 @@ class MCReplyBuilder : public SinkReplyBuilder {
public:
MCReplyBuilder(::io::Sink* stream);
using SinkReplyBuilder::SendRaw;
void SendError(std::string_view str, std::string_view type = std::string_view{}) final;
// void SendGetReply(std::string_view key, uint32_t flags, std::string_view value) final;
@ -116,44 +116,37 @@ class MCReplyBuilder : public SinkReplyBuilder {
class RedisReplyBuilder : public SinkReplyBuilder {
public:
enum CollectionType { ARRAY, SET, MAP };
using StrSpan = std::variant<absl::Span<const std::string>, absl::Span<const std::string_view>>;
RedisReplyBuilder(::io::Sink* stream);
void SetResp3(bool is_resp3);
void SendError(std::string_view str, std::string_view type = std::string_view{}) override;
void SendError(std::string_view str, std::string_view type = {}) override;
void SendMGetResponse(const OptResp* resp, uint32_t count) override;
void SendSimpleString(std::string_view str) override;
void SendStored() override;
void SendLong(long val) override;
void SendSetSkipped() override;
virtual void SendError(OpStatus status);
void SendError(OpStatus status);
virtual void SendSimpleStrArr(const std::string_view* arr, uint32_t count);
// Send *-1
virtual void SendNullArray();
// Send *0
virtual void SendEmptyArray();
virtual void SendStringArr(absl::Span<const std::string_view> arr);
virtual void SendStringArr(absl::Span<const std::string> arr);
virtual void SendStringArrayAsMap(absl::Span<const std::string_view> arr);
virtual void SendStringArrayAsMap(absl::Span<const std::string> arr);
virtual void SendStringArrayAsSet(absl::Span<const std::string_view> arr);
virtual void SendStringArrayAsSet(absl::Span<const std::string> arr);
virtual void SendNullArray(); // Send *-1
virtual void SendEmptyArray(); // Send *0
virtual void SendSimpleStrArr(absl::Span<const std::string_view> arr);
virtual void SendStringArr(StrSpan arr, CollectionType type = ARRAY);
virtual void SendNull();
void SendLong(long val) override;
virtual void SendDouble(double val);
void SendSimpleString(std::string_view str) override;
virtual void SendBulkString(std::string_view str);
virtual void SendScoredArray(const std::vector<std::pair<std::string, double>>& arr,
bool with_scores);
virtual void SendDouble(double val);
virtual void SendBulkString(std::string_view str);
virtual void StartArray(unsigned len);
virtual void StartMap(unsigned num_pairs);
virtual void StartSet(unsigned num_elements);
void StartArray(unsigned len); // StartCollection(len, ARRAY)
virtual void StartCollection(unsigned len, CollectionType type);
static char* FormatDouble(double val, char* dest, unsigned dest_len);
@ -161,18 +154,18 @@ class RedisReplyBuilder : public SinkReplyBuilder {
// into the string that would be sent
static std::string_view StatusToMsg(OpStatus status);
private:
enum CollectionType {
ARRAY,
SET,
MAP,
protected:
struct WrappedStrSpan : public StrSpan {
size_t Size() const;
std::string_view operator[](size_t index) const;
};
using StrPtr = std::variant<const std::string_view*, const std::string*>;
void SendStringCollection(StrPtr str_ptr, uint32_t len, CollectionType type);
private:
void SendStringArrInternal(WrappedStrSpan arr, CollectionType type);
const char* NullString();
bool is_resp3_ = false;
const char* NullString();
};
class ReqSerializer {

View file

@ -357,7 +357,7 @@ TEST_F(RedisReplyBuilderTest, SendSimpleStrArr) {
// random values
"+++", "---", "$$$", "~~~~", "@@@", "^^^", "1234", "foo"};
const std::size_t kArrayLen = sizeof(kArrayMessage) / sizeof(kArrayMessage[0]);
builder_->SendSimpleStrArr(kArrayMessage, kArrayLen);
builder_->SendSimpleStrArr(kArrayMessage);
ASSERT_TRUE(builder_->err_count().empty());
// Tokenize the message and verify content
std::vector<std::string_view> message_tokens = TokenizeMessage();
@ -674,13 +674,13 @@ TEST_F(RedisReplyBuilderTest, TestSendStringArrayAsMap) {
const std::vector<std::string> map_array{"k1", "v1", "k2", "v2"};
builder_->SetResp3(false);
builder_->SendStringArrayAsMap(map_array);
builder_->SendStringArr(map_array, builder_->MAP);
ASSERT_TRUE(builder_->err_count().empty());
ASSERT_EQ(TakePayload(), "*4\r\n$2\r\nk1\r\n$2\r\nv1\r\n$2\r\nk2\r\n$2\r\nv2\r\n")
<< "SendStringArrayAsMap Resp2 Failed.";
builder_->SetResp3(true);
builder_->SendStringArrayAsMap(map_array);
builder_->SendStringArr(map_array, builder_->MAP);
ASSERT_TRUE(builder_->err_count().empty());
ASSERT_EQ(TakePayload(), "%2\r\n$2\r\nk1\r\n$2\r\nv1\r\n$2\r\nk2\r\n$2\r\nv2\r\n")
<< "SendStringArrayAsMap Resp3 Failed.";
@ -690,13 +690,13 @@ TEST_F(RedisReplyBuilderTest, TestSendStringArrayAsSet) {
const std::vector<std::string> set_array{"e1", "e2", "e3"};
builder_->SetResp3(false);
builder_->SendStringArrayAsSet(set_array);
builder_->SendStringArr(set_array, builder_->SET);
ASSERT_TRUE(builder_->err_count().empty());
ASSERT_EQ(TakePayload(), "*3\r\n$2\r\ne1\r\n$2\r\ne2\r\n$2\r\ne3\r\n")
<< "SendStringArrayAsSet Resp2 Failed.";
builder_->SetResp3(true);
builder_->SendStringArrayAsSet(set_array);
builder_->SendStringArr(set_array, builder_->SET);
ASSERT_TRUE(builder_->err_count().empty());
ASSERT_EQ(TakePayload(), "~3\r\n$2\r\ne1\r\n$2\r\ne2\r\n$2\r\ne3\r\n")
<< "SendStringArrayAsSet Resp3 Failed.";

View file

@ -117,7 +117,7 @@ void DebugCmd::Run(CmdArgList args) {
"HELP",
" Prints this help.",
};
return (*cntx_)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
return (*cntx_)->SendSimpleStrArr(help_arr);
}
VLOG(1) << "subcmd " << subcmd;

View file

@ -1012,11 +1012,9 @@ void GenericFamily::Sort(CmdArgList args, ConnectionContext* cntx) {
end_it = entries.begin() + std::min(bounds->first + bounds->second, entries.size());
}
if (result_type == OBJ_SET || result_type == OBJ_ZSET) {
(*cntx)->StartSet(std::distance(start_it, end_it));
} else {
(*cntx)->StartArray(std::distance(start_it, end_it));
}
bool is_set = (result_type == OBJ_SET || result_type == OBJ_ZSET);
(*cntx)->StartCollection(std::distance(start_it, end_it),
is_set ? RedisReplyBuilder::SET : RedisReplyBuilder::ARRAY);
for (auto it = start_it; it != end_it; ++it) {
(*cntx)->SendBulkString(it->key);

View file

@ -697,11 +697,9 @@ void HGetGeneric(CmdArgList args, ConnectionContext* cntx, uint8_t getall_mask)
OpResult<vector<string>> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result) {
if (getall_mask == (VALUES | FIELDS)) {
(*cntx)->SendStringArrayAsMap(absl::Span<const string>{*result});
} else {
(*cntx)->SendStringArr(absl::Span<const string>{*result});
}
bool is_map = (getall_mask == (VALUES | FIELDS));
(*cntx)->SendStringArr(absl::Span<const string>{*result},
is_map ? RedisReplyBuilder::MAP : RedisReplyBuilder::ARRAY);
} else {
(*cntx)->SendError(result.status());
}

View file

@ -206,20 +206,15 @@ class InterpreterReplier : public RedisReplyBuilder {
InterpreterReplier(ObjectExplorer* explr) : RedisReplyBuilder(nullptr), explr_(explr) {
}
void SendError(std::string_view str, std::string_view type = std::string_view{}) override;
void SendStored() override;
void SendError(std::string_view str, std::string_view type = std::string_view{}) final;
void SendStored() final;
void SendSimpleString(std::string_view str) final;
void SendMGetResponse(const OptResp* resp, uint32_t count) final;
void SendSimpleStrArr(const string_view* arr, uint32_t count) final;
void SendStringArrayAsMap(absl::Span<const std::string_view> arr) final;
void SendStringArrayAsMap(absl::Span<const std::string> arr) final;
void SendStringArrayAsSet(absl::Span<const std::string_view> arr) final;
void SendStringArrayAsSet(absl::Span<const std::string> arr) final;
void SendSimpleStrArr(absl::Span<const string_view> arr) final;
void SendNullArray() final;
void SendStringArr(absl::Span<const string_view> arr) final;
void SendStringArr(absl::Span<const string> arr) final;
void SendStringArr(StrSpan arr, CollectionType type) final;
void SendNull() final;
void SendLong(long val) final;
@ -227,7 +222,7 @@ class InterpreterReplier : public RedisReplyBuilder {
void SendBulkString(std::string_view str) final;
void StartArray(unsigned len) final;
void StartCollection(unsigned len, CollectionType type) final;
private:
void PostItem();
@ -335,45 +330,24 @@ void InterpreterReplier::SendMGetResponse(const OptResp* resp, uint32_t count) {
explr_->OnArrayEnd();
}
void InterpreterReplier::SendSimpleStrArr(const string_view* arr, uint32_t count) {
explr_->OnArrayStart(count);
for (uint32_t i = 0; i < count; ++i) {
explr_->OnString(arr[i]);
}
void InterpreterReplier::SendSimpleStrArr(absl::Span<const string_view> arr) {
explr_->OnArrayStart(arr.size());
for (auto sv : arr)
explr_->OnString(sv);
explr_->OnArrayEnd();
}
void InterpreterReplier::SendStringArrayAsMap(absl::Span<const string_view> arr) {
SendStringArr(arr);
}
void InterpreterReplier::SendStringArrayAsMap(absl::Span<const string> arr) {
SendStringArr(arr);
}
void InterpreterReplier::SendStringArrayAsSet(absl::Span<const string_view> arr) {
SendStringArr(arr);
}
void InterpreterReplier::SendStringArrayAsSet(absl::Span<const string> arr) {
SendStringArr(arr);
}
void InterpreterReplier::SendNullArray() {
SendSimpleStrArr(nullptr, 0);
SendSimpleStrArr({});
PostItem();
}
void InterpreterReplier::SendStringArr(absl::Span<const string_view> arr) {
SendSimpleStrArr(arr.data(), arr.size());
PostItem();
}
void InterpreterReplier::SendStringArr(absl::Span<const string> arr) {
explr_->OnArrayStart(arr.size());
for (uint32_t i = 0; i < arr.size(); ++i) {
explr_->OnString(arr[i]);
}
void InterpreterReplier::SendStringArr(StrSpan arr, CollectionType) {
WrappedStrSpan warr{arr};
size_t size = warr.Size();
explr_->OnArrayStart(size);
for (size_t i = 0; i < size; i++)
explr_->OnString(warr[i]);
explr_->OnArrayEnd();
PostItem();
}
@ -398,7 +372,7 @@ void InterpreterReplier::SendBulkString(string_view str) {
PostItem();
}
void InterpreterReplier::StartArray(unsigned len) {
void InterpreterReplier::StartCollection(unsigned len, CollectionType) {
explr_->OnArrayStart(len);
if (len == 0) {
@ -1519,7 +1493,7 @@ void Service::Pubsub(CmdArgList args, ConnectionContext* cntx) {
"HELP",
"\tPrints this help."};
(*cntx)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
(*cntx)->SendSimpleStrArr(help_arr);
return;
}

View file

@ -84,7 +84,7 @@ void MemoryCmd::Run(CmdArgList args) {
"USAGE",
" (not implemented).",
};
return (*cntx_)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
return (*cntx_)->SendSimpleStrArr(help_arr);
};
if (sub_cmd == "USAGE") {

View file

@ -71,7 +71,7 @@ void ScriptMgr::Run(CmdArgList args, ConnectionContext* cntx) {
" Prints latency histograms in usec for every called function.",
"HELP"
" Prints this help."};
return (*cntx)->SendSimpleStrArr(kHelp, ABSL_ARRAYSIZE(kHelp));
return (*cntx)->SendSimpleStrArr(kHelp);
}
if (subcmd == "EXISTS" && args.size() > 1)

View file

@ -1208,7 +1208,7 @@ void ServerFamily::Cluster(CmdArgList args, ConnectionContext* cntx) {
"HELP",
" Prints this help.",
};
return (*cntx)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
return (*cntx)->SendSimpleStrArr(help_arr);
}
if (sub_cmd == "SLOTS") {
@ -1320,7 +1320,7 @@ void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
string_view param = ArgS(args, 2);
string_view res[2] = {param, "tbd"};
return (*cntx)->SendStringArrayAsMap(res);
return (*cntx)->SendStringArr(res, RedisReplyBuilder::MAP);
} else if (sub_cmd == "RESETSTAT") {
shard_set->pool()->Await([](auto*) {
auto* stats = ServerState::tl_connection_stats();
@ -1713,7 +1713,7 @@ void ServerFamily::Hello(CmdArgList args, ConnectionContext* cntx) {
(*cntx)->SetResp3(false);
}
(*cntx)->StartMap(7);
(*cntx)->StartCollection(7, RedisReplyBuilder::MAP);
(*cntx)->SendBulkString("server");
(*cntx)->SendBulkString("redis");
(*cntx)->SendBulkString("version");

View file

@ -27,6 +27,8 @@ ABSL_DECLARE_FLAG(bool, use_set2);
namespace dfly {
using namespace facade;
using namespace std;
using absl::GetFlag;
@ -1205,7 +1207,7 @@ void SPop(CmdArgList args, ConnectionContext* cntx) {
(*cntx)->SendBulkString(result.value().front());
}
} else { // SPOP key cnt
(*cntx)->SendStringArrayAsSet(*result);
(*cntx)->SendStringArr(*result, RedisReplyBuilder::SET);
}
return;
}
@ -1241,7 +1243,7 @@ void SDiff(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script
sort(arr.begin(), arr.end());
}
(*cntx)->SendStringArrayAsSet(arr);
(*cntx)->SendStringArr(arr, RedisReplyBuilder::SET);
}
void SDiffStore(CmdArgList args, ConnectionContext* cntx) {
@ -1309,7 +1311,7 @@ void SMembers(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script
sort(svec.begin(), svec.end());
}
(*cntx)->SendStringArrayAsSet(*result);
(*cntx)->SendStringArr(*result, RedisReplyBuilder::SET);
} else {
(*cntx)->SendError(result.status());
}
@ -1331,7 +1333,7 @@ void SInter(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script
sort(arr.begin(), arr.end());
}
(*cntx)->SendStringArrayAsSet(arr);
(*cntx)->SendStringArr(arr, RedisReplyBuilder::SET);
} else {
(*cntx)->SendError(result.status());
}
@ -1394,7 +1396,7 @@ void SUnion(CmdArgList args, ConnectionContext* cntx) {
if (cntx->conn_state.script_info) { // sort under script
sort(arr.begin(), arr.end());
}
(*cntx)->SendStringArrayAsSet(arr);
(*cntx)->SendStringArr(arr, RedisReplyBuilder::SET);
} else {
(*cntx)->SendError(unionset.status());
}

View file

@ -666,7 +666,7 @@ void StreamFamily::XGroup(CmdArgList args, ConnectionContext* cntx) {
"SETID <key> <groupname> <id|$>",
" Set the current group ID.",
};
return (*cntx)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
return (*cntx)->SendSimpleStrArr(help_arr);
}
if (args.size() >= 3) {
@ -709,7 +709,7 @@ void StreamFamily::XInfo(CmdArgList args, ConnectionContext* cntx) {
"STREAM <key> [FULL [COUNT <count>]",
" Show information about the stream.",
};
return (*cntx)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
return (*cntx)->SendSimpleStrArr(help_arr);
}
if (args.size() >= 3) {
@ -734,7 +734,7 @@ void StreamFamily::XInfo(CmdArgList args, ConnectionContext* cntx) {
string_view arr[8] = {"name", ginfo.name, "consumers", an1.Piece(),
"pending", an2.Piece(), "last-delivered-id", last_id};
(*cntx)->SendStringArrayAsMap(absl::Span<string_view>{arr, 8});
(*cntx)->SendStringArr(absl::Span<string_view>{arr, 8}, RedisReplyBuilder::MAP);
}
return;
}