Open ktalg opened 1 week ago
Hi @ktalg. Thanks for your PR.
I'm waiting for a etcd-io member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test
on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.
Once the patch is verified, the new status will be reflected by the ok-to-test
label.
I understand the commands that are listed here.
[APPROVALNOTIFIER] This PR is NOT APPROVED
This pull-request has been approved by: ktalg Once this PR has been reviewed and has the lgtm label, please assign serathius for approval. For more information see the Kubernetes Code Review Process.
The full list of commands accepted by this bot can be found here.
Could you kindly review this? @serathius
Just a friendly reminder to take a look at this PR. Let me know if there’s anything I need to adjust. @ahrtr @jmhbnz @ivanvc @fuweid
Thanks for raising this PR. The root cause is that the etcd client SDK tries to compare two context intances, which might not be comparable.
Is this your a real use case in your production code? The easiest workaround is to pass in a pointer of your customized context intance as mentioned in https://github.com/etcd-io/etcd/pull/18893#discussion_r1848648057.
We can attach an unique ID for each context passed to KeepAlive something like below, and compare the IDs when we need to identify the context,
const uniqueContextKey = "uniqueContextKey"
func withUniqueID(ctx context.Context, id string) context.Context {
return context.WithValue(ctx, uniqueContextKey, id)
}
func getUniqueID(ctx context.Context) (string, bool) {
id, ok := ctx.Value(uniqueContextKey).(string)
return id, ok
}
We also need to review the source code to check if there are other places which compare the context as well.
Or we can add a function something like below,
func CompareContexts(ctx1, ctx2 context.Context, key string) bool {
return ctx1.Value(key) == ctx2.Value(key)
}
I am not sure whether there is an existing linter which can detect direct context comparison like below case. Probably we can add a such linter and integrate into golangci-lint if possible? @ivanvc @mmorel-35 @ldez
Hello,
I am not sure whether there is an existing linter which can detect direct context comparison like below case.
There is no linter on this specific case.
Probably we can add a such linter and integrate into golangci-lint if possible?
The contexts are just structs so they are technically comparable.
https://go.dev/play/p/dyfw2rkHfTi
I understand your current issue, I need to investigate to kown if there are "valid" use cases behind this kind of comparison.
There is no linter on this specific case.
Thanks for the confirmation.
The contexts are just structs so they are technically comparable.
Not really.
Firstly the applications (including etcd) interact with with interface context.Context
https://github.com/golang/go/blob/ed07b321aef7632f956ce991dd10fdd7e1abd827/src/context/context.go#L68
Secondly, struct types are comparable if all their field types are comparable. In other words, if any field isn't comparable, then the struct isn't comparable.
Usually the default implementations of contexts included golang std lib are comparable. But users' customized implementation might not be comparable.
I'm aware of the interface context.Context
, I was talking about the standard context.
Yes, you can create any kind of implementation, I think it's a bit niche, but anyway.
I still need to investigate the feasibility and the relevance of a linter on this topic.
It might not be reasonable to create a linter for this. Comparing structs or interfaces using an comparison operators (i.e. ==
) seems not an anti-pattern. It's totally up to applications.
The problem in this case is that users may pass in a customized implementation of context.Context
instead of using the existing implementations in golang std lib.
The simplest solution is as I mentioned above in https://github.com/etcd-io/etcd/pull/18893#issuecomment-2486156823 and https://github.com/etcd-io/etcd/pull/18893#issuecomment-2486198060. But it's the first time we see such issue, and most likely it isn't a real production use case. So it's low priority to me.
FYI, I created a quick linter about context comparison to evaluate the relevance.
I analyzed several large projects, and I found no context comparison.
But I found 2 comparisons inside Go itself:
And as you said, struct comparison is not an anti-pattern.
For now, I think it's not worth adding a linter for this.
Thanks @ldez
@ktalg Could you please confirm if this is a real use case from your production environment or a hypothetical scenario?
The workaround is to pass in a pointer as mentioned in https://github.com/etcd-io/etcd/pull/18893#discussion_r1848648057
@ahrtr
Thank you very much for reviewing this PR and providing detailed feedback!
Could you please confirm if this is a real use case from your production environment or a hypothetical scenario?
I encountered this issue while working on production-grade project code. The problem arose not from creating a new Context implementation but from passing a struct embedding a Context into the KeepAlive function, as illustrated in the test case I added.
This situation highlights two key points:
I didn't anticipate that KeepAlive would internally use == to compare the type I passed in. My perspective is that robust code should rely solely on the declared contract of an interface, not on implicit assumptions. In this case, it seems to "assume" that the interface implementation is always comparable, which isn't guaranteed.
I believe this change improves the robustness of the code, ensuring it adheres to these principles.
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 68.74%. Comparing base (
19aa0db
) to head (96cab86
). Report is 44 commits behind head on main.:exclamation: Current head 96cab86 differs from pull request most recent head 5617580
Please upload reports for the commit 5617580 to get more accurate results.
🚨 Try these New Features:
Link to https://github.com/etcd-io/etcd/issues/18935
Please also squash the commits. Also do you have time to backport this to 3.5 and 3.4?
/ok-to-test
Fixes a runtime panic that occurs when KeepAlive is called with a Context implemented by an uncomparable type, which is later canceled. The panic message is:
To reproduce the issue, the existing test has been updated to include a custom uncomparable Context implementation. Previously, this test consistently caused a panic due to the uncomparable Context type. The updated implementation modifies keepAliveCtxCloser to properly handle such cases.