-
Notifications
You must be signed in to change notification settings - Fork 998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: broken memcached error reporting #1741
Changes from 2 commits
eca852b
93118cc
d12367d
7f185b7
caaddd9
cad0b95
0bbd14b
811a31d
63c267e
ae6957d
b47fb59
f601021
0c96524
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -161,8 +161,20 @@ void MCReplyBuilder::SendMGetResponse(absl::Span<const OptResp> arr) { | |
SendSimpleString("END"); | ||
} | ||
|
||
void MCReplyBuilder::SendError(string_view str, std::string_view type) { | ||
SendSimpleString("ERROR"); | ||
void MCReplyBuilder::SendError(string_view str, [[maybe_unused]] std::string_view type) { | ||
SendClientError(str); | ||
} | ||
|
||
void MCReplyBuilder::SendError(ErrorReply error) { | ||
if (error.status) | ||
return SendError(*error.status); | ||
|
||
string_view message_sv = visit([](auto&& str) -> string_view { return str; }, error.message); | ||
SendError(message_sv, error.kind); | ||
} | ||
|
||
void MCReplyBuilder::SendError(OpStatus status) { | ||
SendError(StatusToMsg(status)); | ||
} | ||
|
||
void MCReplyBuilder::SendProtocolError(std::string_view str) { | ||
|
@@ -277,7 +289,7 @@ void RedisReplyBuilder::SendBulkString(std::string_view str) { | |
return Send(v, ABSL_ARRAYSIZE(v)); | ||
} | ||
|
||
std::string_view RedisReplyBuilder::StatusToMsg(OpStatus status) { | ||
std::string_view SinkReplyBuilder::StatusToMsg(OpStatus status) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why did you change it? that's not good because these errors are not part of memcached protocol. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not true:
Andddd
So memcached commands are transactional. Same applies for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We basically validate everything for memcached already at the parser level There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. really bug prone tbh, some of the error reporting is already handled while some other doesn't(in case of this bug). Anyway, beyond the scope of this PR. I will see at some point how we can simplify this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed this for now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually no, we kinda need this because we need to be fair, it won't ever arrive to that block that uses status so I ended up removing this and added a |
||
switch (status) { | ||
case OpStatus::OK: | ||
return "OK"; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,8 @@ class SinkReplyBuilder { | |
} | ||
|
||
virtual void SendError(std::string_view str, std::string_view type = {}) = 0; // MC and Redis | ||
virtual void SendError(ErrorReply error) = 0; | ||
virtual void SendError(OpStatus status) = 0; | ||
|
||
virtual void SendStored() = 0; // Reply for set commands. | ||
virtual void SendSetSkipped() = 0; | ||
|
@@ -57,6 +59,10 @@ class SinkReplyBuilder { | |
|
||
virtual void SendProtocolError(std::string_view str) = 0; | ||
|
||
// You normally should not call this - maps the status | ||
// into the string that would be sent | ||
static std::string_view StatusToMsg(OpStatus status); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not your fault but those local fixes make the whole thing more confusing.
I also changed how SendError is implemented for MC reply builder, I will send you a PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We can't really do that because of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It overwrites the others as well (string_view), it just becomes a little less efficient because it can't move out of the string IIRC |
||
|
||
// 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. | ||
|
@@ -148,6 +154,8 @@ class MCReplyBuilder : public SinkReplyBuilder { | |
using SinkReplyBuilder::SendRaw; | ||
|
||
void SendError(std::string_view str, std::string_view type = std::string_view{}) final; | ||
void SendError(ErrorReply error) final; | ||
void SendError(OpStatus status) final; | ||
|
||
// void SendGetReply(std::string_view key, uint32_t flags, std::string_view value) final; | ||
void SendMGetResponse(absl::Span<const OptResp>) final; | ||
|
@@ -177,13 +185,13 @@ class RedisReplyBuilder : public SinkReplyBuilder { | |
void SetResp3(bool is_resp3); | ||
|
||
void SendError(std::string_view str, std::string_view type = {}) override; | ||
virtual void SendError(ErrorReply error); | ||
void SendError(ErrorReply error) override; | ||
|
||
void SendMGetResponse(absl::Span<const OptResp>) override; | ||
|
||
void SendStored() override; | ||
void SendSetSkipped() override; | ||
virtual void SendError(OpStatus status); | ||
void SendError(OpStatus status) override; | ||
void SendProtocolError(std::string_view str) override; | ||
|
||
virtual void SendNullArray(); // Send *-1 | ||
|
@@ -206,10 +214,6 @@ class RedisReplyBuilder : public SinkReplyBuilder { | |
|
||
static char* FormatDouble(double val, char* dest, unsigned dest_len); | ||
|
||
// You normally should not call this - maps the status | ||
// into the string that would be sent | ||
static std::string_view StatusToMsg(OpStatus status); | ||
|
||
protected: | ||
struct WrappedStrSpan : public StrSpan { | ||
size_t Size() const; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -976,7 +976,7 @@ void StringFamily::IncrBy(CmdArgList args, ConnectionContext* cntx) { | |
int64_t val; | ||
|
||
if (!absl::SimpleAtoi(sval, &val)) { | ||
return (*cntx)->SendError(kInvalidIntErr); | ||
return cntx->SendError(kInvalidIntErr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i know it looks fragile but memcache parser already does all the parsing checks because those conditions should not trigger with memcache. |
||
} | ||
return IncrByGeneric(key, val, cntx); | ||
} | ||
|
@@ -1016,10 +1016,10 @@ void StringFamily::DecrBy(CmdArgList args, ConnectionContext* cntx) { | |
int64_t val; | ||
|
||
if (!absl::SimpleAtoi(sval, &val)) { | ||
return (*cntx)->SendError(kInvalidIntErr); | ||
return cntx->SendError(kInvalidIntErr); | ||
} | ||
if (val == INT64_MIN) { | ||
return (*cntx)->SendError(kIncrOverflow); | ||
return cntx->SendError(kIncrOverflow); | ||
} | ||
|
||
return IncrByGeneric(key, -val, cntx); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One day I'll make ErrorReply's constructor implicit and remove all the overloads 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it could be nice.