-
Notifications
You must be signed in to change notification settings - Fork 999
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: Store and diststore for GeoRadiusByMember #2350
feat: Store and diststore for GeoRadiusByMember #2350
Conversation
Signed-off-by: azuredream <[email protected]>
Signed-off-by: azuredream <[email protected]>
src/server/zset_family.cc
Outdated
return cntx->SendError(kWrongTypeErr); | ||
} else if (!result) { | ||
} else if (!member_score) { | ||
cntx->transaction->Conclude(); |
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.
member_score is null and fail intermittently as mentioned here: #1548 (comment)
dataset:
geoadd Europe 13.4050 52.5200 Berlin 3.7038 40.4168 Madrid 9.1427 38.7369 Lisbon 2.3522 48.8566 Paris 16.3738 48.2082 Vienna 4.8952 52.3702 Amsterdam 10.7522 59.9139 Oslo 23.7275 37.9838 Athens 19.0402 47.4979 Budapest 6.2603 53.3498 Dublin
Not sure if I have registered this command correctly.
src/server/zset_family.cc
Outdated
|
||
rb->SendLong(smvec.size()); | ||
} | ||
if (write_mode) { |
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 do not think you need this conclude step in the normal flow that does not expect errors. You always know what's your last hop is and that should have Execute(..., true)
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.
The last hop could vary. When STORE is not used, the last hop would be hop 2.
If I can't use conclude in this way, I'll have to add a argument "do_store" for the whole workflow.
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.
Also, after hop 2, every conclude() would have to check do_store. If already called conclude after hop 2, skip and return.
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.
There are at most 3 hops
hop1. Getting latitude longitude from member name
hop2. Getting nearby nodes around <latitude ,longitude >
hop3. write to store key if needed
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.
Conclude is also a hop. Seem my previous comment at line 2947
Signed-off-by: azuredream <[email protected]>
Now the code works as expected. |
Signed-off-by: azuredream <[email protected]>
Signed-off-by: azuredream <[email protected]>
According to Redis' behavior, refined the error messages. Added more test cases. |
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.
Thanks! I gave some styling/coding comments. Please take a look
src/server/zset_family.cc
Outdated
} | ||
|
||
OpResult<double> OpScore(const OpArgs& op_args, string_view key, string_view member, | ||
bool use_member_not_found = 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.
Please remove the argument use_member_not_found
, imho, this is not a good practice to dynamically change function behavior in such way. Instead adapt all the callers of the function to cope with MEMBER_NOTFOUND if needed.
src/server/zset_family.cc
Outdated
@@ -1609,7 +1612,14 @@ OpResult<unsigned> OpRem(const OpArgs& op_args, string_view key, ArgSlice member | |||
return deleted; | |||
} | |||
|
|||
OpResult<double> OpScore(const OpArgs& op_args, string_view key, string_view member) { | |||
OpResult<double> OpKeyExisted(const OpArgs& op_args, string_view key) { |
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.
seems you never return double value from this function. something is off here.
src/server/zset_family.cc
Outdated
// False: Using transaction->ScheduleSingleHopT() | ||
OpStatus MembersOfAllNeighbors(ConnectionContext* cntx, string_view key, const GeoHashRadius& n, | ||
const GeoShape& shape_ref, GeoArray* ga, unsigned long limit, | ||
bool write_mode = 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.
again, changing function behavior using write_mode
is not a good approach, imho.
Instead, I would break this function into smaller function. For example, factor out the preparation code into a separate function that just returns range_specs
.
src/server/zset_family.cc
Outdated
return false; | ||
vector<OpResult<vector<ScoredArray>>> result_arrays; | ||
if (write_mode) { | ||
auto cb = [&](Transaction* t, EngineShard* shard) { |
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 would move the transaction related code to the caller (GeoSearchStoreGeneric). Basically, the division to the functions is sub-optimal imho.
src/server/zset_family.cc
Outdated
@@ -3042,24 +3148,51 @@ void ZSetFamily::GeoRadiusByMember(CmdArgList args, ConnectionContext* cntx) { | |||
string_view key = ArgS(args, 0); | |||
// member to latlong, set shape.xy | |||
string_view member = ArgS(args, 1); | |||
bool member_score_set = 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.
Every Dragonfly command has the command parsing part and the execution part. We never start the execution before the parsing is complete and all the arguments are validated.
it seems you start the transaction execution before the parsing is completed. We should avoid it if possible - this just complicates the function flow.
Signed-off-by: azuredream <[email protected]>
…eam/dragonfly into GeoRadiusByMember_store2
Thanks for reviewing. Couldn't fully remove "write_mode", but it's only used in |
Signed-off-by: azuredream <[email protected]>
Hi @romange |
src/server/zset_family.cc
Outdated
auto cb = [&](Transaction* t, EngineShard* shard) { | ||
return OpKeyExisted(t->GetOpArgs(shard), key); | ||
}; | ||
OpResult<void> result = cntx->transaction->ScheduleSingleHopT(std::move(cb)); |
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 do not think the implementation of this function is correct. You use ScheduleSingleHop method on the transaction but this method is intended for operations that require only a single hop. Here you call it multiple times - which breaks the atomicity of the whole command and probably incorrect in general. The correct way is to use Schedule() once, and then Execute(..., false)
one or more times and finalize with Execute(...., true) or Conclude
method in case the transaction finishes prematurely.
// True: Using transaction->Execute() | ||
// False: Using transaction->ScheduleSingleHopT() | ||
// store_key: (Valid if geo_ops.store==GeoStoreType::kNoStore) Store result to store_key | ||
void GeoSearchStoreGeneric(ConnectionContext* cntx, const GeoShape& shape_ref, string_view key, |
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 function is always called on already scheduled transactions. That means it never should call ScheduleSingleHop because this function is only intended to be used for non-scheduled transactions
Signed-off-by: azuredream <[email protected]>
Updated. This is my first time working on async code. Appreciate your patience! |
src/server/zset_family.cc
Outdated
} | ||
return OpStatus::OK; | ||
}; | ||
cntx->transaction->Execute(std::move(store_cb), 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.
Again, this hop is necessary a last hop, so here you should call Execute(..., true);
src/server/zset_family.cc
Outdated
} | ||
cntx->transaction->Conclude(); |
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.
you can move this call into "nostore branch"
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.
In fact you can decide how to call Execute at line 2895 based on whether it's nostore or not, i.e. for nostore option - to conclude it there, and in this case you can eliminate the call to Conclude altogether
src/server/zset_family.cc
Outdated
// if no matching results, the user gets an empty reply. | ||
if (ga.empty()) { | ||
rb->SendNull(); | ||
cntx->transaction->Conclude(); |
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.
what's the expected behaviour for no matched result together with STORE option? do we have a test for that? are we compliant with redis behavior in this case?
Signed-off-by: azuredream <[email protected]>
Updated. Added one more test case where no matching result in search area. This can only happen in |
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.
Thank you very much for progressing with this!
You are very welcome. BTW, if you are planning to implement vector related functions, I'll be glad to help. |
Schedule-Execute
inGeoRadiusByMember
workflow instead ofScheduleSingleHopT()
Implement georadiusbymember #1548