Optimistic locking for string objects - compare-and-set and compare-and-delete#14435
Optimistic locking for string objects - compare-and-set and compare-and-delete#14435minchopaskal merged 20 commits intoredis:unstablefrom
Conversation
Take care of async del in DELEX
|
Could you please clarify and document in the PR description how the new conditional operations handle key expiration scenarios? Specifically:
|
0c5c125 to
8c4cb8d
Compare
|
A key could be deleted, renamed, evicted, or expired between the read and the conditional SET. We should not implement any special logic for these use cases. To the best of my knowledge, we don't do that in other places. For |
Co-authored-by: debing.sun <debing.sun@redis.com>
Co-authored-by: debing.sun <debing.sun@redis.com>
Co-authored-by: debing.sun <debing.sun@redis.com>
Co-authored-by: debing.sun <debing.sun@redis.com>
why do we use XXH3 hash? |
It is 64-bit, which, I believe, is a good tradeoff for our use case. It has good properties. Specifically, speed and quality. See table here. It is widely adopted. |
ShooterIT
left a comment
There was a problem hiding this comment.
great job, developers in the community have wanted this feature for too long, thanks
src/commands/delex.json
Outdated
| "key_specs": [ | ||
| { | ||
| "flags": [ | ||
| "RW", |
There was a problem hiding this comment.
| "RW", | |
| "RM", |
There was a problem hiding this comment.
I'm not sure that's right.
This mutually exclusive flag defines what Redis physically does to the key (the other one is a set of logical operations from the user's perspective).
Considering that, this command is similar to GETDEL. e.g. for ROF, it'll need to load the data to compare it (it doesn't rely on that, but it's an indication this could be wrong).
@guybe7 please correct me if I'm wrong.
There was a problem hiding this comment.
WDYM it's similar to GETDEL? It does not return the old value. Maybe you mean we need the ACCESS flag?
There was a problem hiding this comment.
no, i mean there's one set of (mutually exclusive) flags that define how redis uses the data in the key, in that set this command is similar to GETDEL since redis does need the data in the key, so it'w RM.
the other set of (non-mutually exclusive) flags, is the user's perspective, and the user doesn't ACCESS the data (note that we've documented that side-channel, like the user being able to observe the content of the key via DELEX doesn't count as ACCESS)
/* The following refer what the command actually does with the value or metadata
* of the key, and not necessarily the user data or how it affects it.
* Each key-spec may must have exactly one of these. Any operation that's not
* distinctly deletion, overwrite or read-only would be marked as RW. */
#define CMD_KEY_RO (1ULL<<0) /* Read-Only - Reads the value of the key, but
* doesn't necessarily returns it. */
#define CMD_KEY_RW (1ULL<<1) /* Read-Write - Modifies the data stored in the
* value of the key or its metadata. */
#define CMD_KEY_OW (1ULL<<2) /* Overwrite - Overwrites the data stored in
* the value of the key. */
#define CMD_KEY_RM (1ULL<<3) /* Deletes the key. */
/* The following refer to user data inside the value of the key, not the metadata
* like LRU, type, cardinality. It refers to the logical operation on the user's
* data (actual input strings / TTL), being used / returned / copied / changed,
* It doesn't refer to modification or returning of metadata (like type, count,
* presence of data). Any write that's not INSERT or DELETE, would be an UPDATE.
* Each key-spec may have one of the writes with or without access, or none: */
#define CMD_KEY_ACCESS (1ULL<<4) /* Returns, copies or uses the user data from
* the value of the key. */
#define CMD_KEY_UPDATE (1ULL<<5) /* Updates data to the value, new value may
* depend on the old value. */
#define CMD_KEY_INSERT (1ULL<<6) /* Adds data to the value with no chance of
* modification or deletion of existing data. */
#define CMD_KEY_DELETE (1ULL<<7) /* Explicitly deletes some content
* from the value of the key. */There was a problem hiding this comment.
I see GETDEL uses RW instead of RM but I'm not sure why exactly. For DELEX the key is deleted, conditionally but still deleted, so not sure why RM is not the correct one for DELEX (and GETDEL for that matter).
Also from the ACCESS docs it feels like it might be correct for DELEX as it uses the data. Or maybe I don't get the terminology correctly.
There was a problem hiding this comment.
/* The following refer what the command actually does with the value or metadata
* of the key, and not necessarily the user data or how it affects it.this refers to the redis code, does redis access the value?
try thinking of a key that's on flash in ROF, it does need to load the data to ram in order to read it.
Also from the ACCESS docs it feels like it might be correct for DELEX as it uses the data. Or maybe I don't get the terminology correctly.
i think you might have got it reverse, the short ones (RM and RW) are about what redis does with the data, and the long one (ACCESS and DELETE) are the logical operation from the user's perspective
There was a problem hiding this comment.
thanks for the clarification.
The redis code does access the data and does modify it (delete).
So RM is specific for delete only cases - which are DEL and UNLINK currently.
I'll open a PR
There was a problem hiding this comment.
@oranagra I'm wondering if we can optimize the comments; otherwise, it's very likely to cause confusion.
#define CMD_KEY_RW (1ULL<<1) /* Read-Write - Modifies or Deletes the data stored in the
* value of the key or its metadata. */
#define CMD_KEY_RM (1ULL<<3) /* Deletes the key without reading it. */
There was a problem hiding this comment.
ok..
maybe also: Deletes the key without reading it's value
There was a problem hiding this comment.
What about
Read-Write - Reads and modifies/deletes the data stored in the value of the key or its metadata.
Co-authored-by: Yuan Wang <wangyuancode@163.com>
Co-authored-by: debing.sun <debing.sun@redis.com>
|
@minchopaskal don't forget to generate commands.def again. |
Co-authored-by: debing.sun <debing.sun@redis.com>
In #14435 the `RM` flag was incorrect for the DELEX command as the Redis command code accesses and uses the value of the key, it needs to be RW (just like `GETDEL` command).
Description
Add optimistic locking for string objects via compare-and-set and compare-and-delete mechanism.
What's changed
Introduction of new DIGEST command for string objects calculated via XXH3 hash.
Extend SET command with new parameters supporting optimistic locking. The new value is set only if checks against a given (old) value or a given string digest pass.
Introduction of new DELEX command to support conditionally deleting a key. Conditions are also checks against string value or string digest.
Motivation
For developers who need to to implement a compare-and-set and compare-and-delete single-key optimistic concurrency control this PR provides single-command based implementation.
Compare-and-set and compare-and-delete are mostly used for Optimistic concurrency control: a client (1) fetches the value, keeps the old value (or its digest, for a large string) in memory, (2) manipulates a local copy of the value, (3) applies the local changes to the server, but only if the server’s value hasn’t been changed (still equal to the old value).
Note that compare-and-set can also be implemented with WATCH … MULTI … EXEC and Lua scripts. The new SET optional arguments and the DELEX command do not enable new functionality, however, they are much simpler and faster to use for the very common use case of single-key optimistic concurrency control.
Related issues and PRs
#12485
#8361
#4258
Description of the new commands
DIGEST
Get the hash digest of the value stored in key, as an hex string.
Reply:
SET
IFEQ match-value- Set the key’s value and expiration only if its current value is equal to match-value. If key doesn’t exist - it won’t be created.IFNE match-value- Set the key’s value and expiration only if its current value is not equal to match-value. If key doesn’t exist - it will be created.IFDEQ match-digest- Set the key’s value and expiration only if the digest of its current value is equal to match-digest. If key doesn’t exist - it won’t be created.IFDNE match-digest- Set the key’s value and expiration only if the digest of its current value is not equal to match-digest. If key doesn’t exist - it will be created.Reply update:
DELEX
Conditionally removes the specified key. A key is ignored if it does not exist.
IFEQ match-value- Delete the key only if its value is equal to match-valueIFNE match-value- Delete the key only if its value is not equal to match-valueIFDEQ match-digest- Delete the key only if the digest of its value is equal to match-digestIFDNE match-digest- Delete the key only if the digest of its value is not equal to match-digestReply:
Notes
Added copy of xxhash repo to deps - version