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(server): Support CLIENT TRACKING subcommand (1/2) #2277

Merged
merged 5 commits into from Dec 9, 2023

Conversation

theyueli
Copy link
Contributor

@theyueli theyueli commented Dec 8, 2023

fixes #2139

Implement CLIENT TRACKING subcommand.

Track the keys of a readonly command by maintaining mapping that maps keys to the sets of tracking clients.

Also include a transaction interface Refurbish() that allows the reuse of a transaction, which is used for tracking the keys involved in a readonly command.

The client tracking state is set by CLIENT TRACKING subcommand as well
as upon client disconnection.
Track the keys of a readonly command by maintaining mapping that maps
keys to the sets of tracking clients.
@theyueli theyueli added the enhancement New feature or request label Dec 8, 2023
@theyueli theyueli self-assigned this Dec 8, 2023
@theyueli theyueli changed the title feat(server): Support Client Tracking (1/2) feat(server): Support CLIENT TRACKING subcommand (1/2) Dec 8, 2023
@@ -1465,6 +1485,9 @@ void Service::Quit(CmdArgList args, ConnectionContext* cntx) {
cntx->SendOk();
using facade::SinkReplyBuilder;

// turn off tracking for this client
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for it here,.this path calls OnClose

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

@@ -1012,6 +1012,7 @@ string ServerFamily::GetReplicaMasterId() const {

void ServerFamily::OnClose(ConnectionContext* cntx) {
dfly_cmd_->OnClose(cntx);
cntx->conn()->SetClientTrackingSwitch(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume we had MainService:: OnClose where watch, pubsub and monitoring are cleaning up. Let's add it there so all of them are in one place

Copy link
Contributor Author

@theyueli theyueli Dec 8, 2023

Choose a reason for hiding this comment

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

yes, good catch! I used correct location in the draft PR and but made a mistake here.

return OpTrackKeys(t->GetOpArgs(shard), dfly_cntx, keys_to_track);
};

dfly_cntx->transaction->Refurbish();
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. Refurbish() should be after you use it with schedulesinglehop, please add a test with move, zinterstore etc (commands that have two hops)
  2. DispatchCommand is not used for all commands, but we can ignore that for now
  3. Still reminding that we can track keys when the transaction concludes and no "hop" is needed solely for this, but I also assume it's not a must for the first version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. Refurbish() should be after you use it with schedulesinglehop, please add a test with move, zinterstore etc (commands that have two hops)

I'm wondering, the transaction probably has already been used before the tracking (e.g. in the previous command invoking?) and I need to do a refurbish here before doing my own singlehop?

Copy link
Contributor Author

@theyueli theyueli Dec 8, 2023

Choose a reason for hiding this comment

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

  1. Still reminding that we can track keys when the transaction concludes and no "hop" is needed solely for this, but I also assume it's not a must for the first version

Sounds good to me, I will look into this later.

Copy link
Contributor

Choose a reason for hiding this comment

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

Re 1: Ah, it's placed below the command invocation... yes, then refurbish needs to be above 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.

  1. please add a test with move, zinterstore etc (commands that have two hops)

sounds good, I will include all the functional tests in the second PR, which will completes the implementation with invalidation logic.

if ((cid->opt_mask() & CO::READONLY) && dfly_cntx->conn()->IsTrackingOn()) {
auto cb = [&](Transaction* t, EngineShard* shard) {
auto keys = t->GetShardArgs(shard->shard_id());
vector<string_view> keys_to_track{keys.begin(), keys.end()};
Copy link
Contributor

Choose a reason for hiding this comment

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

Why copy it into a vector and not use the span?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, fixed.

} else {
DVLOG(2) << "The key " << key << " exists in the client tracking table.";
DVLOG(2) << "Inserting client ID " << conn.GetClientId() << " into its tracking client set: ";
client_tracking_map_[key].insert(conn);
Copy link
Contributor

Choose a reason for hiding this comment

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

I know you have different output for now, but at the end you don't need this if

map[key].insert() works even if key is not in map

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, fixed

src/facade/dragonfly_connection.h Show resolved Hide resolved
@theyueli
Copy link
Contributor Author

theyueli commented Dec 9, 2023

@dranikpg i addressed all the comments, please take another look.

@dranikpg dranikpg self-requested a review December 9, 2023 07:08
@theyueli theyueli merged commit 64bbfc7 into dragonflydb:main Dec 9, 2023
7 checks passed
@theyueli theyueli deleted the ct-capturing branch December 9, 2023 07:14
@@ -1338,4 +1338,19 @@ void DbSlice::ResetUpdateEvents() {
events_.update = 0;
}

void DbSlice::TrackKeys(const facade::Connection::WeakRef& conn, const ArgSlice& keys) {
if (conn.IsExpired()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is impossible, I'd do DCHECK(!conn.Expired()) (in the next pr)

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 impossible, I'd do DCHECK(!conn.Expired()) (in the next pr)

got it, will do it there.

return cntx->SendError(
"Client tracking is currently not supported for RESP2. Please use RESP3.");

ToUpper(&args[0]);
Copy link
Collaborator

Choose a reason for hiding this comment

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

CmdArgParser parser(args);
bool is_on = false;
if (parser.HasNext()) {
pause_state = parser.ToUpper().Switch("ON", true, "OFF", false);
}
if (auto err = parser.Error(); err) {
return (*cntx)->SendError(err->MakeReply());
}

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.

Subcommand TRACKING not supported
3 participants