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(streams): support XACK command #1923

Merged
merged 7 commits into from
Sep 27, 2023
Merged

Conversation

theyueli
Copy link
Contributor

fixes #1869

@theyueli theyueli added the enhancement New feature or request label Sep 24, 2023
@theyueli theyueli marked this pull request as draft September 24, 2023 09:43
src/server/stream_family.cc Outdated Show resolved Hide resolved
src/server/stream_family.cc Outdated Show resolved Hide resolved

OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result || result.status() == OpStatus::KEY_NOTFOUND) {
return (*cntx)->SendLong(*result);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't *result crash if result.status() == OpStatus::KEY_NOTFOUND?
Perhaps you could add an if before, something along the lines of:

if (result.status() == OpStatus::KEY_NOTFOUND) {
  result = 0UL;
}
if (result) {
  // send long...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sorry.. I just verified again. when key is not found, the *result is actually 0...

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes +1, same for when the cgroup does not exist. Good catch @theyueli

@chakaz
Copy link
Collaborator

chakaz commented Sep 26, 2023

Oh, and I just noticed there are no tests (which could have caught the NOTFOUND issue), could you add some please? 🙏

@theyueli theyueli marked this pull request as ready for review September 26, 2023 08:43
@theyueli
Copy link
Contributor Author

Oh, and I just noticed there are no tests (which could have caught the NOTFOUND issue), could you add some please? 🙏

Yes I was working on that, now they are complete.

Copy link
Contributor

@kostasrim kostasrim left a comment

Choose a reason for hiding this comment

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

Good work 👨‍🍳 @theyueli`

Very small nit to replace NULL with nullptr and I will approve.

src/server/stream_family.cc Outdated Show resolved Hide resolved
string_view key = ArgS(args, 0);
string_view group = ArgS(args, 1);
args.remove_prefix(2);
absl::InlinedVector<streamID, 8> ids(args.size());
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice!

@@ -1444,7 +1476,7 @@ void DestroyGroup(string_view key, string_view gname, ConnectionContext* cntx) {
return OpDestroyGroup(t->GetOpArgs(shard), key, gname);
};

OpStatus result = cntx->transaction->ScheduleSingleHop(std::move(cb));
OpStatus result = cntx->transaction->ScheduleSingleHop(cb);
Copy link
Contributor

Choose a reason for hiding this comment

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

💯 for making the changes across the file

Copy link
Collaborator

@romange romange Sep 26, 2023

Choose a reason for hiding this comment

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

question, what's the reason for removing move?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that the claim is that it doesn't do anything (because all captures are by ref, and so "moving" pointers is the same as copying them).
I would, however, counter-claim that it's a better practice to use move even in such cases, because there's no harm, and it's future proof and does not require carefully reviewing the capture list..

Copy link
Collaborator

Choose a reason for hiding this comment

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

I have not seen the original request but +1 to what @chakaz said. If tomorrow cb will capture a 1MB string, it will be copied, and performance bugs are hard to catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, I'm good with either way. I will reverse these changes.

Copy link
Contributor

@kostasrim kostasrim Sep 27, 2023

Choose a reason for hiding this comment

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

Small rant vol2, it captures everything by reference, even if we added a string, we would need to explicitly copy it by value making it nearly impossible for this to happen unless you really want to copy this 1mb string...

Copy link
Collaborator

Choose a reason for hiding this comment

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

Re/ 1: I'd rather have longer compilation times than runtime. When a stressed compiler will be an issue, we can tackle that (and I doubt that this will be the cause of pain for the compiler). I'd even bet that <utility> is included in every (or 99%) of TUs we use (either directly or indirectly).
Re/ 2: I'd argue that:

  1. It's way more common to capture more local variables (we all add them, all the time) than to add members to structs, and so it makes sense to have a different rule of thumb
  2. If you add a 1mb string to your class, it might make sense to also add a move-constructor (if one is not generated for you)
  3. It's much less noisier to move callbacks than to move all structs that are ever assigned :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Small rant vol2, it captures everything by reference, even if we added a string, we would need to explicitly copy it by value making it nearly impossible for this to happen unless you really want to copy this 1mb string...

Simple grepping found 2 cases in which we capture by-ref by-default, but also some vars by value:
https://github.com/romange/helio/blob/937652d5803596893aa774206b36d7b91dccf883/util/fibers/fiberqueue_threadpool.h#L108
https://github.com/romange/helio/blob/937652d5803596893aa774206b36d7b91dccf883/util/fibers/fiberqueue_threadpool.h#L122

Copy link
Contributor

@kostasrim kostasrim Sep 27, 2023

Choose a reason for hiding this comment

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

If you add a 1mb string to your class, it might make sense to also add a move-constructor (if one is not generated for you)

yes but to trigger it you need to call std::move everywhere -- ouch!

Simple grepping found 2 cases in which we capture by-ref by-default, but also some vars by value:

Both of them capture a Done object which internally holds an intrusive_ptr. So no deep copying whatsoever...
And if you tell me that if somebody adds a 1mb string to the class Done I would say it would have the same effect as:

somewhere in our codebase:

void some_func_call(Done done) {

}

void foo() {
   Done copy_1mb;
   some_func_call(copy_1mb);
}

Once you add that 1mb string, it will have the same effect for any function that accepts a Done object

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry Shahar, I really don't see any value in using std::move() like that. It's noise, even for (1) above, if you add a non trivial type either move it or capture it by reference (the same you would do for any object IMO). Again, since you and Roman agree, I think we got a consensus. I don't want to push staff neither do I think this move is the end of the world or high in importance.

auto [stream_inst, cg] = *res;

if (cg == nullptr || stream_inst == nullptr) {
return 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Originally, I wrote this long message here cause I thought you were transforming an error to a valid result. Upon testing this on redis (and going over the ACK docs) it seems to be correct. Therefore, both if the cg and the key does not exist we should return 0.


OpResult<uint32_t> result = cntx->transaction->ScheduleSingleHopT(std::move(cb));
if (result || result.status() == OpStatus::KEY_NOTFOUND) {
return (*cntx)->SendLong(*result);
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes +1, same for when the cgroup does not exist. Good catch @theyueli

@theyueli
Copy link
Contributor Author

Good work 👨‍🍳 @theyueli`

Very small nit to replace NULL with nullptr and I will approve.

Thanks! fixed.

@theyueli theyueli merged commit c47469e into dragonflydb:main Sep 27, 2023
7 checks passed
@theyueli theyueli self-assigned this Sep 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Missing XACK command
4 participants