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
Conversation
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.
src/server/main_service.cc
Outdated
@@ -1465,6 +1485,9 @@ void Service::Quit(CmdArgList args, ConnectionContext* cntx) { | |||
cntx->SendOk(); | |||
using facade::SinkReplyBuilder; | |||
|
|||
// turn off tracking for this client |
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.
No need for it here,.this path calls OnClose
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.
fixed.
src/server/server_family.cc
Outdated
@@ -1012,6 +1012,7 @@ string ServerFamily::GetReplicaMasterId() const { | |||
|
|||
void ServerFamily::OnClose(ConnectionContext* cntx) { | |||
dfly_cmd_->OnClose(cntx); | |||
cntx->conn()->SetClientTrackingSwitch(false); |
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 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
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.
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(); |
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.
- Refurbish() should be after you use it with schedulesinglehop, please add a test with move, zinterstore etc (commands that have two hops)
- DispatchCommand is not used for all commands, but we can ignore that for now
- 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
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.
- 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?
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.
- 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.
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.
Re 1: Ah, it's placed below the command invocation... yes, then refurbish needs to be above it
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.
- 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.
src/server/main_service.cc
Outdated
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()}; |
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.
Why copy it into a vector and not use the span?
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.
yes, fixed.
src/server/db_slice.cc
Outdated
} 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); |
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 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
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.
yes, fixed
@dranikpg i addressed all the comments, please take another look. |
@@ -1338,4 +1338,19 @@ void DbSlice::ResetUpdateEvents() { | |||
events_.update = 0; | |||
} | |||
|
|||
void DbSlice::TrackKeys(const facade::Connection::WeakRef& conn, const ArgSlice& keys) { | |||
if (conn.IsExpired()) { |
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.
This is impossible, I'd do DCHECK(!conn.Expired())
(in the next pr)
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.
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]); |
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.
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());
}
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.