Skip to content

Feat: Added BZMPOP Command #5436

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

H4R5H1T-007
Copy link
Contributor

Fixes #3877

@@ -1819,12 +1819,23 @@ std::optional<std::string_view> GetFirstNonEmptyKeyFound(EngineShard* shard, Tra
return std::nullopt;
}

// Validates the ZMPop command arguments and extracts the values to the output params.
// Validates the ZMPop and BZMPop command arguments and extracts the values to the output params.
// If the arguments are invalid sends the appropiate error to builder and returns false.
bool ValidateZMPopCommand(CmdArgList args, uint32* num_keys, bool* is_max, int* pop_count,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ok this function has not a very nice interface. Please introduce ValidateZMPopResult consisting of num_keys, is_max, pop_count and timeout.
The interface should be something like this: bool (CmdArgList args, bool is_blocking, SinkReplyBuilder* builder, ValidateZMPopResult* result)

auto* ns = &trans->GetNamespace();

// Concluding transaction if it's multi mode as blocking is not allowed in it.
if (trans->IsMulti()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would unify this and the block at line 2509 before you handle the blocking case:

if (!key_to_pop && (!is_blocking || trans->IsMulti())) {
  ....

@@ -2783,6 +2850,9 @@ void ZSetFamily::Register(CommandRegistry* registry) {
ZInterCard)
<< CI{"ZLEXCOUNT", CO::READONLY, 4, 1, 1, acl::kZLexCount}.HFUNC(ZLexCount)
<< CI{"ZMPOP", CO::SLOW | CO::WRITE | CO::VARIADIC_KEYS, -4, 2, 2, acl::kZMPop}.HFUNC(ZMPop)
<< CI{"BZMPOP", CO::SLOW | CO::WRITE | CO::VARIADIC_KEYS | CO::BLOCKING, -5, 3, 3,
Copy link
Collaborator

Choose a reason for hiding this comment

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

you also need CO::NO_AUTOJOURNAL to replicate this correctly. this also requires a emitting a custom journal command. Look at the other NO_AUTOJOURNAL commands.

// if command conatins no_autojournal and it's BZMPOP then we will write to journal manually.
if ((result == OpStatus::OK) && (cid->opt_mask() & CO::NO_AUTOJOURNAL) &&
(cid->name() == "BZMPOP")) {
OpArgs op_args = t->GetOpArgs(shard);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I just checked and actually we have the same bug in ZMPOP. Even with ZMPOP, there is no need to pass all the keys because the outcome is already known: we need to fetch from key.
Therefore, I suggest to move RecordJournal into OpPopCount and rewrite both commands as ZPOPMIN or ZPOPMAX accordingly. Also please do not forget to mark ZMPOP as NO_AUTOJOURNAL.

Look at how it's done in OpBZPop. Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup I also thought that but then I thought to make it consistent as ZMPOP so I added it like ZMPOP journal. Cool I will move journaling to OpPopCount and add custom journal to ZMPOP as well that will be much better.

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.

Almost there. I appreciate your effort!

bool ValidateZMPopCommand(CmdArgList args, uint32* num_keys, bool* is_max, int* pop_count,
SinkReplyBuilder* builder) {
bool ValidateZMPopCommand(CmdArgList args, ZSetFamily::ValidateZMPopResult* result,
SinkReplyBuilder* builder, bool is_blocking) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: we put output arguments at the end, so is_blocking should be second here

@@ -66,6 +67,13 @@ class ZSetFamily {
bool override = false;
};

struct ValidateZMPopResult {
Copy link
Collaborator

Choose a reason for hiding this comment

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

you do not need this struct to be defined in the header file. please move it to the cc file.

@@ -66,6 +67,13 @@ class ZSetFamily {
bool override = false;
};

struct ValidateZMPopResult {
uint32* num_keys;
Copy link
Collaborator

Choose a reason for hiding this comment

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

no need to pass pointers. you can just define:

  struct ValidateZMPopResult {
    uint32_t num_keys;
    bool is_max;
    int pop_count;
    float timeout;
  };

and everything will be much simpler in the code.

@@ -7,6 +7,7 @@
#include <string_view>
#include <variant>

#include "base/integral_types.h"
Copy link
Collaborator

Choose a reason for hiding this comment

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

not needed

# check BZMPOP turns into ZMPOP command
await c_master.zadd("key", {"a": 1, "b": 2, "c": 3})
await skip_cmd()
await check("BZMPOP 0 3 key3 key2 key MAX COUNT 3", r"ZMPOP 1 key MAX COUNT 3")
Copy link
Collaborator

Choose a reason for hiding this comment

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

very nice, thank you! but it should turn as ZPOPMAX/ZPOPMIN :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure I will change it

if (!ValidateZMPopCommand(args, &num_keys, &is_max, &pop_count, cmd_cntx.rb)) {
// Generic function for ZMPop and BZMPop commands
void ZMPopGeneric(CmdArgList args, const CommandContext& cmd_cntx, bool is_blocking) {
ValidateZMPopResult zmpopArgs;
Copy link
Collaborator

Choose a reason for hiding this comment

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

naming: zmpop_args

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.

minor comments

// Check if we have additional COUNT argument.
if (parser.HasNext()) {
if (!parser.Check("COUNT", pop_count)) {
if (!parser.Check("COUNT", &(result->pop_count))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (!parser.Check("COUNT", &(result->pop_count))) {
if (!parser.Check("COUNT", &result->pop_count)) {

CmdArgParser parser{args};

if (!SimpleAtoi(parser.Next(), num_keys)) {
if (is_blocking) {
if (!absl::SimpleAtof(parser.Next(), &(result->timeout))) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (!absl::SimpleAtof(parser.Next(), &(result->timeout))) {
if (!absl::SimpleAtof(parser.Next(), &result->timeout)) {

Also added test for BZMPOP custom journal by checking replication.
- Added Custom journaling for BZMPOP command.
- Created ValidateZMPopResults Interface.
Added manual journaling for ZMPOP and BZMPOP which turns them into ZPOPMAX/ZPOPMIN.
Also refactored the code a bit.
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.

implement BZMPOP
2 participants