redis cluster: fix ClusterSlot operator ==#16116
Conversation
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
|
Hi @gaoliangdut, welcome and thank you for your contribution. We will try to review your Pull Request as quickly as possible. In the meantime, please take a look at the contribution guidelines if you have not done so already. |
|
/assign @envoyproxy/first-pass-reviewers |
|
@envoyproxy/first-pass-reviewers cannot be assigned to this issue. |
rojkov
left a comment
There was a problem hiding this comment.
Thank you! Just one suggestion added.
| } else { | ||
| for (auto it1 = replicas_.begin(), it2 = rhs.replicas_.begin(); it1 != replicas_.end(); | ||
| it1++, it2++) { | ||
| if (**it1 != **it2) { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
nit: this loop shouldn't be nested in else to lessen cognitive load.
There was a problem hiding this comment.
Thank you for your suggestion, I have add a new commit.
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
| for (auto it1 = replicas_.begin(), it2 = rhs.replicas_.begin(); it1 != replicas_.end(); | ||
| it1++, it2++) { | ||
| if (**it1 != **it2) { |
There was a problem hiding this comment.
replicas_ is a absl::flat_hash_set which doesn't make any guarantees about iteration order. How can we be sure it's going to work always?
There was a problem hiding this comment.
Oh, that's a problem. how about use this code instead?
`
std::setstd::string replicas_set;
for (auto const& replica : replicas_) {
replicas_set.emplace(replica->asString());
}
for (auto const& replica : rhs.replicas_) {
if (replicas_set.find(replica->asString()) == replicas_set.end()) {
return false;
}
}
return true;
`
There was a problem hiding this comment.
I have add a new commit.
There was a problem hiding this comment.
Probably we could keep the existing code and to instantiate replicas_ as a absl::flat_hash_set with a custom equality function instead?
There was a problem hiding this comment.
OK, I have understood what you said, I will try it in this way.
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
|
Assigning @snowp for second/final pass |
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
| for (auto const& replica : rhs.replicas_) { | ||
| if (replicas_.find(replica) == replicas_.end()) { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
Isn't this loop equivalent to just
return replicas_ == rhs.replicas_;There was a problem hiding this comment.
In the test case, "replicas_ == rhs.replicas_" returns false, but In my code, it returns true. I'm trying to find the reason for this.
| const absl::flat_hash_set<Network::Address::InstanceConstSharedPtr>& replicas() const { | ||
| struct Hash { | ||
| size_t operator()(const Network::Address::InstanceConstSharedPtr& address) const { | ||
| return absl::Hash<std::string>()(address->asString()); |
There was a problem hiding this comment.
As a matter of good practice, you'd want to use return absl::Hash<absl::string_view>()(address->asStringView()); to avoid potentially creating a string temp while hashing. Though actually the name is held in a string in the main impl, so the asString() impl currently won't do that.
But in researching this I found a potential latent crash here. The doc for asString() says:
* @return a human readable string for the address that represents the
* physical/resolved address. (This will not necessarily include port
* information, if applicable, since that may not be resolved until bind()).
This suggests to me that bind() might mutate the name. If that happens after we have already stored an address in a hash-table, I think this can crash in subsequent hash-table operations.
So my suggestion is to convert this to a map<string,Network::Address::InstanceConstSharedPtr>. This will result in string-copies for the map key, but will avoid crashing Envoy if bind() mutates the name. And if you do that you won't need this custom hasher/comparator.
There was a problem hiding this comment.
+1
That's a good catch! There's no good in hashing keys which can mutate.
There was a problem hiding this comment.
Thanks for this suggestion, I have use map instead of flat_hash_set and create a commit.
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
| for (auto const& replica : rhs.replicas_) { | ||
| if (replicas_.find(replica.first) == replicas_.end()) { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
suggestion: Currently this is O(n*log n). If the replicas are std::map then we can rely on how they are ordered like in your first commit to make this loop O(n).
There was a problem hiding this comment.
It's a good suggestion, thank you! I have fixed it and created a new commit.
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
snowp
left a comment
There was a problem hiding this comment.
A few comments questions but overall this looks right!
| return std::equal(replicas_.begin(), replicas_.end(), rhs.replicas_.begin(), rhs.replicas_.end(), | ||
| [](const auto& it1, const auto& it2) { return it1.first == it2.first; }); |
There was a problem hiding this comment.
This might have been covered in PR discussions already but how does this differ from just doing replicas_ = rhs.replicas?
There was a problem hiding this comment.
The value type is shared_ptr, and the shared_ptr is not name one even for same ip:port.
There was a problem hiding this comment.
Oh I see this is just comparing the key. Perhaps add a comment to make this clearer?
There was a problem hiding this comment.
Ping on this ^^
Would be great to have this covered by a comment
There was a problem hiding this comment.
Sorry, I forgot that, now I have add a comment. Thank you!
| int64_t end_; | ||
| Network::Address::InstanceConstSharedPtr primary_; | ||
| absl::flat_hash_set<Network::Address::InstanceConstSharedPtr> replicas_; | ||
| std::map<std::string, Network::Address::InstanceConstSharedPtr> replicas_; |
There was a problem hiding this comment.
I think normally we perfer to use the absl maps, one of absl::flat_hash_map, absl::node_hash_map if you want pointer stability or absl::btree_map if you want an ordered map.
There was a problem hiding this comment.
I have changed it to absl::btree_map and created a new commit.
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
snowp
left a comment
There was a problem hiding this comment.
Thanks for explaining, just one question. Can you also take a look at CI to see if its a real issue?
I have checked it, and I think so. |
|
/retest |
|
Retrying Azure Pipelines: |
|
@gaoliangdut Looks like CI has ignored my retest comment. Could you please push an empty comment to trigger the tests? |
done |
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
snowp
left a comment
There was a problem hiding this comment.
Thanks! Can you merge main? I think that should fix the gcc issue in CI
The 'primary_' and element in 'replicas_' were `shared_ptr`s, this shared_ptr is created from different redis responses, so it would always returns false. Signed-off-by: gaoliangdut <gaoliang_dlut@126.com> Signed-off-by: Sixiang Gu <sgu@twitter.com>
The 'primary_' and element in 'replicas_' were `shared_ptr`s, this shared_ptr is created from different redis responses, so it would always returns false. Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>


Signed-off-by: gaoliangdut gaoliang_dlut@126.com
For an explanation of how to fill out the fields, please see the relevant section
in PULL_REQUESTS.md
Commit Message:The 'primary_' and element in 'replicas_' is type of shared_ptr, this shared_ptr is created from different redis response,so it will always returns false.
Additional Description:
Risk Level: Low
Testing: unit test
Docs Changes: N/A
Release Notes: N/A
Platform Specific Features:
[Optional Runtime guard:]
[Optional Fixes #Issue]
[Optional Deprecated:]
[Optional API Considerations:]