Skip to content
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

Set Command with IFEQ Support #1324

Merged
merged 14 commits into from
Dec 10, 2024

Conversation

sarthakaggarwal97
Copy link
Contributor

@sarthakaggarwal97 sarthakaggarwal97 commented Nov 19, 2024

This PR allows the Valkey users to perform conditional updates where the SET command is completed if the given comparison-value matches the key’s current value.

Syntax:

SET key value IFEQ comparison-value

Behavior:

If the values match, the SET completes as expected. If they do not match, the command returns a (nil), except if the GET argument is also given (see below).

Behavior with Additional Flags:

  1. SET key value IFEQ comparison-value GET returns the existing value, regardless of whether it matches comparison-value or not. The conditional set operation is performed if the given comparison value matches the existing value. To check if the SET succeeded, the caller needs to check if the returned string matches the comparison-value.
  2. SET key value IFEQ comparison-value XX is a syntax error.
  3. SET key value IFEQ comparison-value NX is a syntax error.

Closes: #1215

@sarthakaggarwal97 sarthakaggarwal97 marked this pull request as ready for review November 19, 2024 19:47
@sarthakaggarwal97 sarthakaggarwal97 force-pushed the cas-command branch 2 times, most recently from 6c216c1 to ed26e4b Compare November 19, 2024 21:45
Copy link
Collaborator

@hpatro hpatro left a comment

Choose a reason for hiding this comment

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

Would be nice to think through the behavior with other flags/parameters of SET command https://valkey.io/commands/set/

NX -- Only set the key if it does not already exist - should we error in this case if CAS is provided?
XX -- Only set the key if it already exists - This becomes redundant to use I believe with CAS.
GET -- Return the old string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value stored at key is not a string. - I think we need to support this parameter in some form. The scenario which comes to my mind is when CAS fails and a user doesn't need to send a GET command again to find out the value stored in the engine. However, we need to think about how to differentiate between success scenario vs failure scenario.

Also, we need to document them.

tests/unit/type/string.tcl Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
@hpatro hpatro added the needs-doc-pr This change needs to update a documentation page. Remove label once doc PR is open. label Nov 19, 2024
Copy link

codecov bot commented Nov 19, 2024

Codecov Report

Attention: Patch coverage is 96.66667% with 1 line in your changes missing coverage. Please review.

Project coverage is 70.83%. Comparing base (4f61034) to head (73a7463).
Report is 4 commits behind head on unstable.

Files with missing lines Patch % Lines
src/t_string.c 96.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #1324      +/-   ##
============================================
- Coverage     70.85%   70.83%   -0.02%     
============================================
  Files           118      118              
  Lines         63591    63611      +20     
============================================
+ Hits          45058    45062       +4     
- Misses        18533    18549      +16     
Files with missing lines Coverage Δ
src/commands.def 100.00% <ø> (ø)
src/t_string.c 96.68% <96.66%> (-0.07%) ⬇️

... and 10 files with indirect coverage changes

@sarthakaggarwal97 sarthakaggarwal97 force-pushed the cas-command branch 3 times, most recently from 0652f67 to b7ed294 Compare November 20, 2024 04:25
@hpatro
Copy link
Collaborator

hpatro commented Nov 20, 2024

@sarthakaggarwal97 Please avoid force pushing. force push removes the reviewer's history in Github and one needs to look at the entire change again.

@sarthakaggarwal97
Copy link
Contributor Author

noted @hpatro, will avoid it.

Copy link
Collaborator

@hpatro hpatro left a comment

Choose a reason for hiding this comment

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

Thanks @sarthakaggarwal97 for the PR! Looks pretty good. Could you also document the behavior in the top comment. Will be easier for others to review and we can finalize it.

tests/unit/type/string.tcl Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
tests/unit/type/string.tcl Outdated Show resolved Hide resolved
@sarthakaggarwal97 sarthakaggarwal97 self-assigned this Nov 20, 2024
Copy link
Collaborator

@hpatro hpatro left a comment

Choose a reason for hiding this comment

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

LGTM overall. The behavior currently is:

  1. if value matches, set the new value.
  2. If existing value is of different type, return error WRONGTYPE Operation against a key holding the wrong kind of value
  3. if existing value is a mismatch, return nil. (want us to finalize on this).

@hpatro hpatro requested a review from madolson November 22, 2024 18:40
src/commands/set.json Outdated Show resolved Hide resolved
src/t_string.c Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
@zuiderkwast
Copy link
Contributor

zuiderkwast commented Nov 27, 2024

Let's make IFEQ mutually exclusive with NX and XX. It doesn't make sense to combine them so let's make it a syntax error now. (If we don't, we can't fix it in the future without a breaking change.)

SET key value [ NX | XX | IFEQ comparison-value ] [ other args... ]

In the JSON file, putting them in the same "oneOf" block, this is also used for the website and man pages where the syntax is rendered.

Regarding the combining IFEQ with the GET parameter will be difficult to use, but not impossible with the behaviour you described. If SET replied with the 'comparison-value', it means the SET has succeeded. I think it's logical, even if it will probably be very rarely used.

Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

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

This looks pretty good. Just don't want to allow IFEQ combined with NX and XX.

src/commands/set.json Outdated Show resolved Hide resolved
src/commands/set.json Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
tests/assets/test_cli_hint_suite.txt Outdated Show resolved Hide resolved
src/commands/set.json Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

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

Great, LGTM now.

Just a few nits. Not a blocker for merging.

src/t_string.c Outdated Show resolved Hide resolved
src/t_string.c Outdated Show resolved Hide resolved
@zuiderkwast
Copy link
Contributor

@madolson @hpatro @enjoy-binbin Let's finalize the exact behavior with the various flags. This is the currently implemented logic:

  • IFEQ is mutually exclusive with NX and XX. They can't be combined.
  • IFEQ without GET: On success +OK and on mismatch (nil) is returned. This matches the behavior of XX and NX.
  • IFEQ with GET: In this case, the old value is returned regardless of whether the comparison succeeded.

Please confirm ( 👍 ) or protest ( 😱 ).

@hpatro
Copy link
Collaborator

hpatro commented Dec 4, 2024

  • IFEQ without GET: On success +OK and on mismatch (nil) is returned. This matches the behavior of XX and NX.
  • IFEQ with GET: In this case, the old value is returned regardless of whether the comparison succeeded.

What I like about this is, it's opt in. If the user actually wants the old value only then they get it. And, if the stored value is large, they could choose to not get it back.

@zuiderkwast zuiderkwast added the release-notes This issue should get a line item in the release notes label Dec 6, 2024
Signed-off-by: Sarthak Aggarwal <[email protected]>
Signed-off-by: Sarthak Aggarwal <[email protected]>
Signed-off-by: Sarthak Aggarwal <[email protected]>
Signed-off-by: Sarthak Aggarwal <[email protected]>
Signed-off-by: Sarthak Aggarwal <[email protected]>
Signed-off-by: Sarthak Aggarwal <[email protected]>
@madolson madolson added client-changes-needed Client changes are required for this feature major-decision-approved Major decision approved by TSC team labels Dec 10, 2024
@madolson
Copy link
Member

madolson commented Dec 10, 2024

We discussed this a bit in the weekly meeting, a callout was that we want to make sure that clients add support for this.

The most significant callout was that there is some uncertainty now if this is the right API. The ask was that we should spend more time to figure out if this is the best way to solve the original use cases, so if we could create some examples in the documentation page about what problem this solves and why it's better than LUA or multi, we will follow up. @hpatro and @sarthakaggarwal97 let me know if you have any questions.

@zuiderkwast
Copy link
Contributor

Client changes?

That's only if the client does some detailed wrapping of each and every command argument, right? Is that really considered good practice? I would like to call it an anti-pattern.

No changes are needed for any of the clients I've been involved in: libvalkey (hiredis-fork), valkey-cli and ered (an Erlang client). A command line is just represented as a list of strings and called like client.call("SET", "foo", "bar", "IFEQ", "baz"). Why do some clients do it differently? It just seems very unpractical.

@soloestoy
Copy link
Member

  • IFEQ with GET: In this case, the old value is returned regardless of whether the comparison succeeded.

When using GET parameters, the result of SET IFEQ is ambiguous, and we cannot intuitively determine whether the command successfully modified the data. There is a similar issue with NX and XX. I have submitted an issue #1415 to discuss this problem.

@zuiderkwast
Copy link
Contributor

why it's better than LUA or multi

For simple use cases, it's simpler than Lua and WATCH + MULTI. It's also faster. (With WATCH + MULTI you need two round trips to the server.)

@zuiderkwast zuiderkwast merged commit 9cfe1b3 into valkey-io:unstable Dec 10, 2024
49 checks passed
@soloestoy soloestoy added the needs-doc-pr This change needs to update a documentation page. Remove label once doc PR is open. label Dec 10, 2024
@madolson madolson added major-decision-pending Major decision pending by TSC team and removed major-decision-approved Major decision approved by TSC team labels Dec 10, 2024
zuiderkwast added a commit to valkey-io/valkey-doc that referenced this pull request Dec 10, 2024
This PR adds the documentation to support
valkey-io/valkey#1324

---------

Signed-off-by: Sarthak Aggarwal <[email protected]>
Signed-off-by: Viktor Söderqvist <[email protected]>
Co-authored-by: Viktor Söderqvist <[email protected]>
@madolson
Copy link
Member

That's only if the client does some detailed wrapping of each and every command argument, right? Is that really considered good practice? I would like to call it an anti-pattern.

I mean, I'll pin it for a future discussion, but all of the GLIDE clients have fully typed inputs and outputs so that it's easier for developers to see what the output will look like instead of having to infer it.

vudiep411 pushed a commit to Autxmaton/valkey that referenced this pull request Dec 15, 2024
This PR allows the Valkey users to perform conditional updates where the
SET command is completed if the given comparison-value matches the key’s
current value.

Syntax:

```
SET key value IFEQ comparison-value
```

Behavior:

If the values match, the SET completes as expected. If they do not
match, the command returns a (nil), except if the GET argument is also
given (see below).

Behavior with Additional Flags:

1. ```SET key value IFEQ comparison-value GET``` returns the existing
value, regardless of whether it matches comparison-value or not. The
conditional set operation is performed if the given comparison value
matches the existing value. To check if the SET succeeded, the caller
needs to check if the returned string matches the comparison-value.
2. ```SET key value IFEQ comparison-value XX``` is a syntax error.
3.  ```SET key value IFEQ comparison-value NX``` is a syntax error.

Closes: valkey-io#1215

---------

Signed-off-by: Sarthak Aggarwal <[email protected]>
@PingXie
Copy link
Member

PingXie commented Dec 19, 2024

but all of the GLIDE clients have fully typed inputs

I think @soloestoy's question in #1415 is about semantics, i.e., nil could mean both failure and success. The type of the return value for set however is still string with this change.

michael-grunder added a commit to phpredis/phpredis that referenced this pull request Jan 19, 2025
Implement the new `IFEQ` `SET` option that will be included in `Valkey`
8.1.

See: valkey-io/valkey#1324
michael-grunder added a commit to phpredis/phpredis that referenced this pull request Jan 19, 2025
Implement the new `IFEQ` `SET` option that will be included in `Valkey`
8.1.

See: valkey-io/valkey#1324
michael-grunder added a commit to phpredis/phpredis that referenced this pull request Jan 19, 2025
Implement the new `IFEQ` `SET` option that will be included in `Valkey`
8.1.

See: valkey-io/valkey#1324
michael-grunder added a commit to phpredis/phpredis that referenced this pull request Jan 20, 2025
Implement the new `IFEQ` `SET` option that will be included in `Valkey`
8.1.

See: valkey-io/valkey#1324
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client-changes-needed Client changes are required for this feature major-decision-pending Major decision pending by TSC team needs-doc-pr This change needs to update a documentation page. Remove label once doc PR is open. release-notes This issue should get a line item in the release notes to-be-merged Almost ready to merge
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[NEW] Support Check and Set functionality
7 participants