Skip to content
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

feat: Use const ConnectionContext in VerifyCommand #1633

Merged
merged 5 commits into from
Aug 6, 2023

Conversation

dranikpg
Copy link
Contributor

@dranikpg dranikpg commented Aug 3, 2023

Its an anti-pattern to use a mutable ConnectionContext pointer for both connection state queries and error replies. As a result we have a lot of excessive mutability and we pass it everywhere around. This might have not been an issue so far, but it becomes hard to manage with squashed command execution.

Its needed for squashed pipeline execution. Pipeline commands can make state changes (context & global), so command verification needs to happen immediately before execution. With squashed execution, this verification will take place from multiple threads -> possible trouble if not carefully done

@dranikpg dranikpg marked this pull request as ready for review August 3, 2023 10:25
@dranikpg dranikpg requested review from chakaz and romange and removed request for chakaz August 3, 2023 10:25
Comment on lines 63 to 80
struct ErrorReply {
explicit ErrorReply(std::string msg, std::string_view kind = {})
: message{move(msg)}, kind{kind} {
}
explicit ErrorReply(std::string_view msg, std::string_view kind = {}) : message{msg}, kind{kind} {
}
explicit ErrorReply(const char* msg, std::string_view kind = {})
: message{std::string_view{msg}}, kind{kind} {
}
explicit ErrorReply(OpStatus status) : message{}, kind{}, status{status} {
}

std::variant<std::string, std::string_view> message;
std::string_view kind;
std::optional<OpStatus> status{std::nullopt};
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strong about having such a heavy type inside facade_types (which is included almost everywhere, but not sure where else to put it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know its not used everywhere where it fits in, I'll have more iterations when I check other parts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The const char msg* overload is to avoid ambiguity (const char* converts to both string and string_view implicitly)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in order to reduce ambiguity, the std::string c'tor could accept a std::string&& instead? (I imagine this will allow removing the const char* one, which is confusing).
If for some reason that doesn't work, I'd write that in a comment.

Copy link
Contributor Author

@dranikpg dranikpg Aug 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷🏻
image

Added a comment instead

@dranikpg dranikpg requested a review from chakaz August 3, 2023 10:27
Copy link
Collaborator

@chakaz chakaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see some comments, mostly nits

Comment on lines 63 to 80
struct ErrorReply {
explicit ErrorReply(std::string msg, std::string_view kind = {})
: message{move(msg)}, kind{kind} {
}
explicit ErrorReply(std::string_view msg, std::string_view kind = {}) : message{msg}, kind{kind} {
}
explicit ErrorReply(const char* msg, std::string_view kind = {})
: message{std::string_view{msg}}, kind{kind} {
}
explicit ErrorReply(OpStatus status) : message{}, kind{}, status{status} {
}

std::variant<std::string, std::string_view> message;
std::string_view kind;
std::optional<OpStatus> status{std::nullopt};
};

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in order to reduce ambiguity, the std::string c'tor could accept a std::string&& instead? (I imagine this will allow removing the const char* one, which is confusing).
If for some reason that doesn't work, I'd write that in a comment.

explicit ErrorReply(OpStatus status) : message{}, kind{}, status{status} {
}

std::variant<std::string, std::string_view> message;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have message of type string_view, and some other member (like private message_memory_) which message may or may not point into?
It should make using this struct easier

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember you don't like using variants for storage at all, we already had this case once.
But I like to avoid self referential structs - it requires defining move/copy constructors and operators

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not worried about performance implications of using std::string in case we return errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I use string_view whenever its possible, in other cases you construct a string with StrCat and others anyways

I'll check once again for performance degradation, but it shouldn't be the case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw I once thought of supporting an absl::Span<absl::Alphanumeric> interface to write directly into the conn buffer without the intermediary string, but its just a random comment in this context 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So yes, I don't see any place where the number of intermediaries increased

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant - I am NOT worried - i.e. I think having a string here is perfectly fine since it's not on a hot path.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any problem with storing variant as a member, or using it as storage. I actually think variants are great.
I dislike the API that it brings in, though. I definitely understand your comment re/ having to implement an explicit move / copy operations.
I guess we could:

  1. Keep as is
  2. Provide a wrapper get-method that returns string_view which points to the right string
  3. do my trick, but have the string inside a unique_ptr
  4. always use a string (Roman's suggestion)
    Your call :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you decided to keep as is. I would still say it's not a great API to force a call to std::visit with a templated lambda instead of having a

string_view GetMessage() const { visit... }

To wrap that, but again, your call :)

For the case where you want to move if possible, you could have another version (GetMessageDestructive()?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😝
As soon as it'll be used more than in once place I'll add those

@@ -220,6 +220,14 @@ void RedisReplyBuilder::SendError(string_view str, string_view err_type) {
}
}

void RedisReplyBuilder::SendError(ErrorReply error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see that you move anything from error, so can you accept it by const&?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. Forgot to comment about it. I want the CapturingReplyBuilder to move out of the string so it can re-use its memory. The downside is that it requires passing the error by value everyewhere to comply with the interface

src/server/main_service.h Outdated Show resolved Hide resolved
src/server/main_service.cc Outdated Show resolved Hide resolved
src/server/main_service.cc Outdated Show resolved Hide resolved
lock_guard lk(mu_);
if (unknown_cmds_.size() < 1024)
unknown_cmds_[cmd_str]++;
return false;

return facade::ErrorReply{StrCat("unknown command `", cmd_str, "`"), "unknown_cmd"};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add using facade::ErrorReply at the beginning?
too much horizontal space is wasted on these namespaces.

@dranikpg
Copy link
Contributor Author

dranikpg commented Aug 3, 2023

Fixed comments except those that are debatable 🙂

@dranikpg dranikpg force-pushed the fix-command-verify branch from ff34107 to fe43785 Compare August 3, 2023 14:25
@dranikpg dranikpg requested review from chakaz and romange August 3, 2023 16:27
Copy link
Collaborator

@romange romange left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM at high level.
@chakaz will continue :)

chakaz
chakaz previously approved these changes Aug 6, 2023
bool is_trans_cmd = CO::IsTransKind(cid->name());
bool under_script = bool(dfly_cntx->conn_state.script_info);
bool under_script = bool(dfly_cntx.conn_state.script_info);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while here, maybe instead of casting to bool do:

bool under_script = dfly_cntx.conn_state.script_info != nullptr;

Signed-off-by: Vladislav Oleshko <[email protected]>
Signed-off-by: Vladislav Oleshko <[email protected]>
Signed-off-by: Vladislav Oleshko <[email protected]>
@dranikpg
Copy link
Contributor Author

dranikpg commented Aug 6, 2023

while here, maybe instead of casting to bool do:
bool under_script = dfly_cntx.conn_state.script_info != nullptr;

You already did it in your previous PR, rebased

@dranikpg dranikpg force-pushed the fix-command-verify branch from 62bfefc to f79e619 Compare August 6, 2023 07:20
@dranikpg dranikpg requested a review from chakaz August 6, 2023 07:41
explicit ErrorReply(OpStatus status) : message{}, kind{}, status{status} {
}

std::variant<std::string, std::string_view> message;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you decided to keep as is. I would still say it's not a great API to force a call to std::visit with a templated lambda instead of having a

string_view GetMessage() const { visit... }

To wrap that, but again, your call :)

For the case where you want to move if possible, you could have another version (GetMessageDestructive()?)

@dranikpg dranikpg merged commit 3bc1e26 into dragonflydb:main Aug 6, 2023
@dranikpg dranikpg deleted the fix-command-verify branch August 10, 2023 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants