-
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
chore(reply_builder): add dcheck that each command invocation has rep… #2067
Conversation
222bfee
to
ff10ea7
Compare
src/facade/reply_builder.h
Outdated
@@ -134,6 +137,8 @@ class SinkReplyBuilder { | |||
|
|||
// Similarly to batch mode but is controlled by at operation level. | |||
bool should_aggregate_ : 1; | |||
bool has_replied_ : 1; | |||
bool has_batch_replied_ : 1; |
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.
accidental will remove
Some of the tests failing are around memcache flow that have a dedicated reply builder |
src/server/main_service.cc
Outdated
|
||
cntx->reply_builder()->ExpectReply(); | ||
try { | ||
cid->Invoke(tail_args, cntx); | ||
} catch (std::exception& e) { | ||
LOG(ERROR) << "Internal error, system probably unstable " << e.what(); | ||
return false; | ||
} | ||
DCHECK(cntx->reply_builder()->HasReplied()); | ||
|
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.
would be my solution suggestion, not sure if its simpler 🙂
#ifdef NDEBUG
CapturingReplyBuilder crb{ReplyMode::ALL};
auto* old = cntx->Inject(&crb);
#endif
*invoke*
#ifdef NDEBUG
auto reply = crb.Take();
DCHECK(reply.index() > 0);
CRB::Apply(std::move(reply), (*cntx).operator->());
cntx->Inject(old);
#endif
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.
I have some of the mechanics as a convenient RAII in 1924, you could steal it from there if you choose this approach, so all the code doesn't pollute this small function
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 would not work with memcached, because the Apply
function accepts a RedisReplyBuilder
. Moreover, for some reason it made some of the tests stack.
However, I really like this idea, IMO, it's cleaner. What I will try on a seprate PR (because I don't want to block us) is:
- Overload
Apply
to work with MCBuilder (or just make it accept a SinkReplyBuilder). - Figure out what cause the hangs in the tests.
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.
I'd suggest just doing this for non memcached commands because CRB has no support for storing mc-like replies
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.
Other way round its likely not worth the effort
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.
I'd suggest just doing this for non memcached commands because CRB has no support for storing mc-like replies
So just skip
for the memcached
commands? I can do this by dyn_cast
and checking if the reply builder
is MCBuilder
. I will try this out
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.
But second thoughts we would also miss the dcheck
for memcached
commands which I am not really happy about... @romange any opinion on this?
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.
@dranikpg I applied your comments, I don't want to delay this any longer. Took a little bit more time, because I had to add a few exceptions but it passes locally
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.
I do not care about missing memcache - it has a small surface and it does not change a lot.
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.
i saw now the previous commits. I prefer the boolean option
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.
Just merge it, let's not spend this much time discussing such a small issue I agree
src/server/main_service.cc
Outdated
const bool is_repl_conf = cid->name() == "REPLCONF"; \ | ||
const bool is_dfly = cid->name() == "DFLY"; \ | ||
const bool is_exec = cid->name() == "EXEC"; \ |
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.
bool ignore = set{"REPLCONF", "DFLY", "EXEC"}.count(cid->name());
😭
src/server/main_service.cc
Outdated
absl::Cleanup clean = [cntx, old]() { \ | ||
if (old) \ | ||
cntx->Inject(old); \ | ||
}; \ |
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.
what I initially thought was this
struct ReplyGuard {} // do all the stuff in contructor and destructor
Invoke() {
#ifdef NDEBUG
ReplyGuard guard;
#endif
...
}
src/server/main_service.cc
Outdated
@@ -1047,6 +1047,39 @@ void Service::DispatchCommand(CmdArgList args, facade::ConnectionContext* cntx) | |||
} | |||
} | |||
|
|||
#ifndef NDEBUG | |||
|
|||
#define ENABLE_DCHECK_REPLIED \ |
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.
is it the only way to do it? by injecting a reply builder and bunch of preprocesor code?
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.
Injecting the CapturingReplyBuilder is clean enough and I cleaned up the preprocessor code. Do you have any better suggestions?
src/server/main_service.cc
Outdated
if (old) { | ||
auto reply = crb.Take(); | ||
DCHECK(reply.index() > 0); | ||
CapturingReplyBuilder::Apply(std::move(reply), old); |
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.
much nicer! My question is - could it be that injecting CapturingReplyBuilder would actually change the debug version significantly from the release in terms of I/O pattern?
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.
somewhat, now replies are sent only once the command handler finished executing
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 does delay the replies after the invocation of the command. So yes, it does alter the behavior. Originally, I had it implemented differently (see the early commits) but the CapturingReplyBuilder seems a clearer approach.
would actually change the debug version significantly from the release in terms of I/O pattern?
I think it also changes the functionality, as I described above which I agree it is not great but in this context maybe it's ok if you take into account that the release version is also different because of different optimizations.
There is another solution in the previous commits (which can be cleaned up as well) but this also pollutes the RedisReplyBuilder
(but it doesn't delay the replies). What's your take?
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.
I have not see your solution but is not it just about setting a boolean in a few low-level functions?
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.
i mean in replybuilder ? I am ok with this solution but I do preference with poluting replybuilder if it is simple and efficient.
src/facade/reply_builder.cc
Outdated
@@ -98,6 +99,14 @@ void SinkReplyBuilder::SendRaw(std::string_view raw) { | |||
Send(&v, 1); | |||
} | |||
|
|||
void SinkReplyBuilder::ExpectReply() { | |||
has_replied_ = true; |
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.
What does it mean?
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 should be false
hold
.contains(cid_name); | ||
const bool should_dcheck = !is_one_of && !is_script; | ||
if (should_dcheck) { | ||
builder->ExpectReply(); |
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.
I do not understand this logic.
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 should have been false
. Basically, it sets that we expect the reply on each Invoke
which internally sets the has_replied_
to false. If the reply builder is used to reply, then this is set to true, which we DCHECK
on destructor
Some of the replication tests fail, and I suspect it's our little bug :) I am investigating and will update