-
Notifications
You must be signed in to change notification settings - Fork 9.8k
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
Compare not equal operator is broken in etcd #10566
Comments
With your reproducer I get the following:
This is with etcd
|
@kragniz Much appreciated, AFAICT, bug is confirmed. If there was NO bug, you would expect to see: |
@purpleidea Can you spend sometime to help us dig into this bug? |
@xiang90 I'm currently unemployed and trying to turn https://github.com/purpleidea/mgmt/ into something that can survive, so tbh, no :( I was hoping my clear bug report and easy reproducer would help! Also willing to write a test and send a PR. I'm fairly certain that any consumer of etcd (eg: kube) will have major issues if they hit this bug. Maybe they can have a look? |
From briefly reading some of the transaction code, it looks like comparisons against values always fail if the key doesn't exist[1]? I can't seem to find any documentation highlighting this behaviour, though. It appears that [1] https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/apply.go#L443-L447 |
@mfycheng No idea, however reversing the operation (shown in my example by switching the comments off) makes things work. So if anything this is a mistake. I was fairly sure this used to work as I expected... I didn't bisect it though. |
If I understand the reverse operation in your example correctly, it appears that it's doing (paraphrasing):
Which would always result in the the |
I think it's a bug, because we only see the error messages In addition, since this basically causes the else case to happen, it is equivalent to the code saying that they are equal, which is obviously wrong. If you really wanted this to be invalid, then resp should contain an error saying "key not exist" or similar. |
@purpleidea @xiang90 Line 448 in 9c2b88d
But As for There is a custom proto Golang file in etcd.
These structs seem only be used in log for format print of the request. |
Can you create a PR and help to fix the stringer thing? Thanks! |
I added this design issue to work around the possibility of needing to do an IF_EXISTS AND IF_VALUE_IS sort of thing. Perhaps someone could have a look and comment on the viability. Thanks! |
so what we expect is result decided by bytes.Compare and NOT_EXISTS mean "" ? |
I agree it is confusing (and probably incorrect) that comparing value always return false if the key does not exist. The behavior should be better defined. At the meantime, I propose to mitigate the issue by adding an additional check when using value compare, i.e., always check if the key exists before comparing its value. |
I do not think the current behavior is logically correct, because both of the following comparisons will be evaluated as
|
Yeah, I agree. I think it is something we should definitely consider for 3.4, and in particular, I do not think it should be a worry to "break" this behaviour because anyone who depended on it before most definitely had either incorrect or buggy code. So in the worst case, someone's code will "break" and they'll notice that they previously had a bug hidden there.
In the above statement, anytime we do something illogical, the err should be set. |
Is this issue still active? and is there any plan to fix it? |
@csuzhangxc patches welcome I think. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
hi bot |
I ran into the same issue and it took me a while to figure it out. I was able to use a workaround: func (s *store) setIfDifferent(key string, value string) (*clientv3.TxnResponse, error) {
ctx, cancel := s.GetContext() // Get a context
txn := s.client.Txn(ctx)
resp, err := txn.If(
clientv3.Compare(clientv3.ModRevision(key), "=", 0),
).Then(
clientv3.OpPut(key, value),
).Commit()
cancel()
if err != nil {
return nil, err
}
if resp.Succeeded {
return resp, nil
}
ctx, cancel = s.GetContext() // Get a context
txn = s.client.Txn(ctx)
resp, err = txn.If(
clientv3.Compare(clientv3.Value(key), "!=", value),
).Then(
clientv3.OpPut(key, value),
).Commit()
cancel()
if err != nil {
return nil, err
}
return resp, nil
} Important: Note that this works for my app because the key I am trying to set to a desired state is never deleted. If the key you want to set can be deleted, there's the potential of a race condition causing unexpected behaviors In general, may I recommend updating the documentation for etcd to clearly explain that comparisons are always false if the key doesn't exist? That would have saved me a couple of hours of debugging. |
@ItalyPaleAle, I think if TXN is to be used seriously, someone should just patch the operator... I'm too buried with other stuff, but would be happy to review your patch to the best of my abilities. |
@purpleidea I totally agree with you. I shared my code in case it can help others, but I clearly wrote it's a workaround that, while it works for me, it might be dangerous for others. In fact, it does not perform the write in a single transaction. I read the code and it looks like a patch won't be very simple. At least, I wouldn't know how to start fixing it. Alternatively, something like #10571 could work as well, allowing us to use OR conditions too. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
Not resolved |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
Hi bot |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
notstale |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
👋 bot! |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
Hi bot 👋 |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
Hi bot 👋 |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
domo arigato mr roboto |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
I couldn't believe it myself, and after a long day of hacking, I finally assumed it was something in etcd's code that was broken instead of mine. Here's a simple reproducer and logs.
What are we using?
Run etcd in an empty dir:
Here's a golang example to trigger the issue:
When the golang code is run, the following appears in the etcd server logs:
And running etcdctl produces no output:
If you modify the above golang example to use
=
instead of!=
, and switch thethen
for theelse
case, then it works correctly as expected.Thoughts welcome!
Thanks
The text was updated successfully, but these errors were encountered: