Skip to content

redis cluster: fix ClusterSlot operator ==#16116

Merged
snowp merged 11 commits intoenvoyproxy:mainfrom
gaoliangdut:main
May 17, 2021
Merged

redis cluster: fix ClusterSlot operator ==#16116
snowp merged 11 commits intoenvoyproxy:mainfrom
gaoliangdut:main

Conversation

@gaoliangdut
Copy link
Copy Markdown
Contributor

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:]

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
@repokitteh-read-only
Copy link
Copy Markdown

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.

🐱

Caused by: #16116 was opened by gaoliangdut.

see: more, trace.

@asraa
Copy link
Copy Markdown
Contributor

asraa commented Apr 22, 2021

/assign @envoyproxy/first-pass-reviewers

@repokitteh-read-only
Copy link
Copy Markdown

@envoyproxy/first-pass-reviewers cannot be assigned to this issue.

🐱

Caused by: a #16116 (comment) was created by @asraa.

see: more, trace.

Copy link
Copy Markdown
Member

@rojkov rojkov left a comment

Choose a reason for hiding this comment

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

Thank you! Just one suggestion added.

Comment on lines +12 to +18
} else {
for (auto it1 = replicas_.begin(), it2 = rhs.replicas_.begin(); it1 != replicas_.end();
it1++, it2++) {
if (**it1 != **it2) {
return false;
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: this loop shouldn't be nested in else to lessen cognitive load.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your suggestion, I have add a new commit.

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
Comment on lines +13 to +15
for (auto it1 = replicas_.begin(), it2 = rhs.replicas_.begin(); it1 != replicas_.end();
it1++, it2++) {
if (**it1 != **it2) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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;
`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have add a new commit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Probably we could keep the existing code and to instantiate replicas_ as a absl::flat_hash_set with a custom equality function instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK, I have understood what you said, I will try it in this way.

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
@asraa
Copy link
Copy Markdown
Contributor

asraa commented Apr 24, 2021

Assigning @snowp for second/final pass

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
Comment on lines +14 to +18
for (auto const& replica : rhs.replicas_) {
if (replicas_.find(replica) == replicas_.end()) {
return false;
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Isn't this loop equivalent to just

return replicas_ == rhs.replicas_;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is really not equal. Because in absl, "operator ==" uses "==" to compare the value, and "find" uses "eq_ref" to compare the value.
image
image

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());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

+1

That's a good catch! There's no good in hashing keys which can mutate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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>
Comment on lines +14 to +18
for (auto const& replica : rhs.replicas_) {
if (replicas_.find(replica.first) == replicas_.end()) {
return false;
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It's a good suggestion, thank you! I have fixed it and created a new commit.

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
rojkov
rojkov previously approved these changes Apr 29, 2021
Copy link
Copy Markdown
Member

@rojkov rojkov left a comment

Choose a reason for hiding this comment

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

Thank you! I think it's ready for a second pass @snowp

Copy link
Copy Markdown
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

A few comments questions but overall this looks right!

Comment on lines +14 to +15
return std::equal(replicas_.begin(), replicas_.end(), rhs.replicas_.begin(), rhs.replicas_.end(),
[](const auto& it1, const auto& it2) { return it1.first == it2.first; });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This might have been covered in PR discussions already but how does this differ from just doing replicas_ = rhs.replicas?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The value type is shared_ptr, and the shared_ptr is not name one even for same ip:port.

Copy link
Copy Markdown
Contributor

@snowp snowp May 10, 2021

Choose a reason for hiding this comment

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

Oh I see this is just comparing the key. Perhaps add a comment to make this clearer?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ping on this ^^

Would be great to have this covered by a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have changed it to absl::btree_map and created a new commit.

@snowp snowp added the waiting label May 3, 2021
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
Copy link
Copy Markdown
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Thanks for explaining, just one question. Can you also take a look at CI to see if its a real issue?

@gaoliangdut
Copy link
Copy Markdown
Contributor Author

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.

@rojkov
Copy link
Copy Markdown
Member

rojkov commented May 11, 2021

/retest

@repokitteh-read-only
Copy link
Copy Markdown

Retrying Azure Pipelines:
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #16116 (comment) was created by @rojkov.

see: more, trace.

@rojkov
Copy link
Copy Markdown
Member

rojkov commented May 11, 2021

@gaoliangdut Looks like CI has ignored my retest comment. Could you please push an empty comment to trigger the tests?

git commit -s --allow-empty -m 'Kick CI' && git push

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
@gaoliangdut
Copy link
Copy Markdown
Contributor Author

@gaoliangdut Looks like CI has ignored my retest comment. Could you please push an empty comment to trigger the tests?

git commit -s --allow-empty -m 'Kick CI' && git push

done

Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
Copy link
Copy Markdown
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Thanks! Can you merge main? I think that should fix the gcc issue in CI

gaoliangdut and others added 2 commits May 16, 2021 11:34
Signed-off-by: gaoliangdut <gaoliang_dlut@126.com>
@snowp snowp merged commit c5833f2 into envoyproxy:main May 17, 2021
ntgsx92 pushed a commit to ntgsx92/envoy that referenced this pull request May 18, 2021
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>
leyao-daily pushed a commit to leyao-daily/envoy that referenced this pull request Sep 30, 2021
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants