From ea61838ed9fc7de2b3868e7a541f92a0a6299952 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 11 Aug 2025 08:49:06 +0300 Subject: [PATCH 01/18] Add documentation for Hash Fields Expiration Signed-off-by: Ran Shidlansik --- commands/hexpire.md | 40 ++++++++++++++++++ commands/hexpireat.md | 46 ++++++++++++++++++++ commands/hexpiretime.md | 31 ++++++++++++++ commands/hgetex.md | 57 +++++++++++++++++++++++++ commands/hpersist.md | 34 +++++++++++++++ commands/hpexpire.md | 40 ++++++++++++++++++ commands/hpexpireat.md | 46 ++++++++++++++++++++ commands/hpexpiretime.md | 31 ++++++++++++++ commands/hpttl.md | 30 ++++++++++++++ commands/hsetex.md | 90 ++++++++++++++++++++++++++++++++++++++++ commands/httl.md | 31 ++++++++++++++ topics/hashes.md | 18 ++++++++ topics/notifications.md | 11 +++-- 13 files changed, 502 insertions(+), 3 deletions(-) create mode 100644 commands/hexpire.md create mode 100644 commands/hexpireat.md create mode 100644 commands/hexpiretime.md create mode 100644 commands/hgetex.md create mode 100644 commands/hpersist.md create mode 100644 commands/hpexpire.md create mode 100644 commands/hpexpireat.md create mode 100644 commands/hpexpiretime.md create mode 100644 commands/hpttl.md create mode 100644 commands/hsetex.md create mode 100644 commands/httl.md diff --git a/commands/hexpire.md b/commands/hexpire.md new file mode 100644 index 000000000..51347bdfc --- /dev/null +++ b/commands/hexpire.md @@ -0,0 +1,40 @@ +The `HEXPIRE` command allows manipulating existing hash fields expiration time. +When set, an expiration time of a hash field determine when the field will be automatically reclaimed. +Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field/s. + +## Synopsis + +``` +HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields + field [field ...] +``` + +## Options + +The `HEXPIRE` command supports a set of options that modify its behavior: + +* NX — For each specified field, set expiration only when the field has no expiration. +* XX — For each specified field, set expiration only when the field has an existing expiration. +* GT — For each specified field, set expiration only when the new expiration is greater than current one. +* LT — For each specified field, set expiration only when the new expiration is less than current one. + +## Notifications + +`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +and there are no more fields in the hash object. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HEXPIRE myhash 10 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 8 +3) (integer) 8 +``` diff --git a/commands/hexpireat.md b/commands/hexpireat.md new file mode 100644 index 000000000..54d417c9a --- /dev/null +++ b/commands/hexpireat.md @@ -0,0 +1,46 @@ +`HEXPIREAT` has the same effect and semantic as [`HEXPIRE`](hexpire.md), but instead of +specifying the number of seconds representing the TTL (time to live), it takes +an absolute [Unix timestamp][hewowu] (seconds since January 1, 1970). A +timestamp in the past will delete the key immediately. + +[hewowu]: http://en.wikipedia.org/wiki/Unix_time + +For the specific semantics of the command refer to the documentation of +[`HEXPIRE`](hexpire.md). + +## Synopsis + +``` +HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields + field [field ...] +``` + +## Options + +The `HEXPIREAT` command supports a set of options that modify its behavior: + +* NX — For each specified field, set expiration only when the field has no expiration. +* XX — For each specified field, set expiration only when the field has an existing expiration. +* GT — For each specified field, set expiration only when the new expiration is greater than current one. +* LT — For each specified field, set expiration only when the new expiration is less than current one. + +## Notifications + +`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +and there are no more fields in the hash object. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HEXPIREAT myhash 1754846600 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HEXPIRETIME myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 1754846600 +3) (integer) 1754846600 +``` diff --git a/commands/hexpiretime.md b/commands/hexpiretime.md new file mode 100644 index 000000000..32b8fd3e1 --- /dev/null +++ b/commands/hexpiretime.md @@ -0,0 +1,31 @@ +The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field/s will expire. + +See also the [`HPEXPIRETIME`](hpexpiretime.md) command which returns the same information with milliseconds resolution. + +## Synopsis + +``` +HEXPIRETIME key FIELDS numfields field [field ...] +``` + +The command can also return the following values: + +* The command returns `-2` if the specified field does not exist in the hash. +* The command returns `-1` if the specified field exists in the hash but has no associated expiration time. + +See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HEXPIREAT myhash 1754846600 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HEXPIRETIME myhash FIELDS 4 f1 f2 f3 non-exist +1) (integer) -1 +2) (integer) 1754846600 +3) (integer) 1754846600 +4) (integer) -2 +``` diff --git a/commands/hgetex.md b/commands/hgetex.md new file mode 100644 index 000000000..e165af385 --- /dev/null +++ b/commands/hgetex.md @@ -0,0 +1,57 @@ +The `HGETEX` command get the value of one or more fields of a given hash key, and optionally manipulates their expiration time. +The command will return an array in the size of the number of requested fields. +Without providing any optional flags, this command behaves exactly like a normal [`HMGET`](hmget.md) command. + +## Synopsis + +``` +HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds | + PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field + [field ...] +``` + +## Options + +The `HGETEX` command supports a set of options that modify its behavior: + +* EX seconds — Set the specified expiration time, in seconds. +* PX milliseconds — Set the specified expiration time, in milliseconds. +* EXAT `unix-time-seconds` — Set the specified Unix time at which the fields will expire, in seconds. +* PXAT `unix-time-milliseconds` — Set the specified Unix time at which the fields will expire, in milliseconds. +* PERSIST — Remove the expiration time associated with the fields. + +Note for the following: + +1. The EX, PX, EXAT, PXAT, and PERSIST options are mutually exclusive. +2. Providing '0' expiration TTL via `EX` or `PX` optional arguments will result in the specified fields to immediately expire and removed from the hash. +3. Providing past expiration time via `EXAT` or `PXAT` optional arguments will result in the specified fields to immediately expire and removed from the hash. + +## Notifications + +`hexpire` keyspace event will be issued once in case in case at least 1 field has been set with an expiration time which is in the future. +`hexpired` keyspace event will be issued once in case at least 1 field has been set with an expiration time which is zero or in the past. +`hpersist` keyspace event will be issued once in case the `PERSIST` option was specified and at least 1 field's expiration time was removed. +`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +and there are no more fields in the hash object. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HGETEX myhash EX 10 FIELDS 2 f2 f3 +1) "v2" +2) "v3" +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 8 +3) (integer) 8 +127.0.0.1:6379> HGETEX myhash EX 0 FIELDS 3 f1 f2 f3 +1) "v1" +2) "v2" +3) "v3" +127.0.0.1:6379> HGETEX myhash FIELDS 3 f1 f2 f3 +1) "nil" +2) "nil" +3) "nil" +``` diff --git a/commands/hpersist.md b/commands/hpersist.md new file mode 100644 index 000000000..3fb45c225 --- /dev/null +++ b/commands/hpersist.md @@ -0,0 +1,34 @@ +The `HPERSIST` remove the existing expiration on a hash key's field(s), turning the field(s) from volatile (a field with expiration set) +to persistent (a field that will never expire as no TTL (time to live) is associated). + +## Synopsis + +``` +HPERSIST key FIELDS numfields field [field ...] +``` + +## Notifications + +`hpersist` keyspace event will be issued once in case at least 1 field's expiration time was removed. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HEXPIRE myhash 1000 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 998 +3) (integer) 998 +127.0.0.1:6379> HPERSIST myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 1 +3) (integer) 1 +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) -1 +3) (integer) -1 +``` diff --git a/commands/hpexpire.md b/commands/hpexpire.md new file mode 100644 index 000000000..70b85bf5f --- /dev/null +++ b/commands/hpexpire.md @@ -0,0 +1,40 @@ +This command works exactly like [`HEXPIRE`](hexpire.md) but the time to live of the key is +specified in milliseconds instead of seconds. +Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field/s. + +## Synopsis + +``` +HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields + field [field ...] +``` + +## Options + +The `HPXPIRE` command supports a set of options that modify its behavior: + +* NX — For each specified field, set expiration only when the field has no expiration. +* XX — For each specified field, set expiration only when the field has an existing expiration. +* GT — For each specified field, set expiration only when the new expiration is greater than current one. +* LT — For each specified field, set expiration only when the new expiration is less than current one. + +## Notifications + +`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +and there are no more fields in the hash object. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HPEXPIRE myhash 10000 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HPTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 9597 +3) (integer) 9597 +``` diff --git a/commands/hpexpireat.md b/commands/hpexpireat.md new file mode 100644 index 000000000..ed67e8d7b --- /dev/null +++ b/commands/hpexpireat.md @@ -0,0 +1,46 @@ +`HEXPIREAT` has the same effect and semantic as [`HPEXPIRE`](hpexpire.md), but instead of +specifying the number of milliseconds representing the TTL (time to live), it takes +an absolute [Unix timestamp][hewowu] (milliseconds since January 1, 1970). A +timestamp in the past will delete the key immediately. + +[hewowu]: http://en.wikipedia.org/wiki/Unix_time + +For the specific semantics of the command refer to the documentation of +[`HPEXPIRE`](hpexpire.md). + +## Synopsis + +``` +HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT] FIELDS numfields + field [field ...] +``` + +## Options + +The `HPEXPIREAT` command supports a set of options that modify its behavior: + +* NX — For each specified field, set expiration only when the field has no expiration. +* XX — For each specified field, set expiration only when the field has an existing expiration. +* GT — For each specified field, set expiration only when the new expiration is greater than current one. +* LT — For each specified field, set expiration only when the new expiration is less than current one. + +## Notifications + +`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +and there are no more fields in the hash object. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HPEXPIREAT myhash 1754847944000 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HPEXPIRETIME myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 1754847944000 +3) (integer) 1754847944000 +``` diff --git a/commands/hpexpiretime.md b/commands/hpexpiretime.md new file mode 100644 index 000000000..6215a3ba2 --- /dev/null +++ b/commands/hpexpiretime.md @@ -0,0 +1,31 @@ +The `HPEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in milliseconds at which the given hash field/s will expire. + +See also the [`HEXPIRETIME`](hexpiretime.md) command which returns the same information with seconds resolution. + +## Synopsis + +``` +HEXPIRETIME key FIELDS numfields field [field ...] +``` + +The command can also return the following values: + +* The command returns `-2` if the specified field does not exist in the hash. +* The command returns `-1` if the specified field exists in the hash but has no associated expiration time. + +See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HPEXPIREAT myhash 1754847944000 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HPEXPIRETIME myhash FIELDS 4 f1 f2 f3 non-exist +1) (integer) -1 +2) (integer) 1754847944000 +3) (integer) 1754847944000 +4) (integer) -2 +``` diff --git a/commands/hpttl.md b/commands/hpttl.md new file mode 100644 index 000000000..6ab32a2d7 --- /dev/null +++ b/commands/hpttl.md @@ -0,0 +1,30 @@ +Like [`HTTL`](httl.md) this command returns the remaining time to live of hash field/s that has an associated expiration time, +with the sole difference that `HTTL` returns the amount of remaining +time in seconds while `HPTTL` returns it in milliseconds. + +## Synopsis + +``` +HTTL key FIELDS numfields field [field ...] +``` + +The command can also return the following values: + +* The command returns `-2` if the key does not exist. +* The command returns `-1` if the key exists but has no associated expire. + +See also the [`HTTL`](httl.md) command that returns the same information with seconds resolution. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HPEXPIRE myhash 10000 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HPTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 9597 +3) (integer) 9597 +``` diff --git a/commands/hsetex.md b/commands/hsetex.md new file mode 100644 index 000000000..a17b470f2 --- /dev/null +++ b/commands/hsetex.md @@ -0,0 +1,90 @@ +The `HSETEX` command allow setting the value of one or more fields of a given hash key, and optionally manipulate their expiration time. +The command will return 1 in case all provided fields have been set or 0 in case `FNX` or `FXX` were provided and non of the specified fields were set. +Without providing any optional flags, this command behaves exactly like a normal [`HSET`](hset.md) command. + +## Synopsis + +``` +HSETEX key [FNX | FXX] [EX seconds | PX milliseconds | + EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL] + FIELDS numfields field value [field value ...] +``` + +## Options + +The `HSETEX` command supports a set of options that modify its behavior: + +* FNX — Only set the fields if none of them already exist. +* FXX — Only set the fields if all of them already exist. +* EX seconds — Set the specified expiration time in seconds. +* PX milliseconds — Set the specified expiration time in milliseconds. +* EXAT `unix-time-seconds` — Set the specified Unix time in seconds at which the fields will expire. +* PXAT `unix-time-milliseconds` — Set the specified Unix time in milliseconds at which the fields will expire. +* KEEPTTL — Retain the TTL associated with the fields. + +Note for the following: + +1. The EX, PX, EXAT, PXAT, and KEEPTTL options are mutually exclusive. +2. Setting a value on a volatile hash field (A field which has an assigned expiration time) will remove the expiration for that field. +3. Providing '0' expiration TTL via `EX` or `PX` optional arguments will result in the specified fields to immediately expire and removed from the hash. +4. Providing past expiration time via `EXAT` or `PXAT` optional arguments will result in the specified fields to immediately expire and removed from the hash. + +## Notifications + +`hset` keyspace event will be issued in case all the specified fields have been set. +`hexpired` keyspace event will be issued in case all the specified fields have been set with an expiration time which is in the future. +`hexpire` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past. +`del` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past, +and there are no more fields in the hash object. + +## Examples + +Add 3 new items without expiration time to a 'myhash' +``` +127.0.0.1:6379> HSETEX myhash FIELDS 3 f1 v1 f2 v2 f3 v3 +(integer) 1 +``` + +Unsuccessful attempt setting expiration time on EXISTING fields +``` +127.0.0.1:6379> HSETEX myhash FNX EX 10 FIELDS 2 f2 v2 f3 v3 +(integer) 0 +``` + +Successful attempt setting expiration time on EXISTING fields +``` +127.0.0.1:6379> HSETEX myhash FXX EX 10 FIELDS 2 f2 v2 f3 v3 +(integer) 1 +``` + +Verify hash fields expiration time: +``` +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) 8 +3) (integer) 8 +``` + +Override all hash items will also persist the fields +``` +127.0.0.1:6379> HSETEX myhash FIELDS 3 f1 v1 f2 v2 f3 v3 +(integer) 1 +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -1 +2) (integer) -1 +3) (integer) -1 +``` + +Setting expiration time in the past will remove all the elements in the hash: +``` +127.0.0.1:6379> HSETEX EX 0 myhash FIELDS 3 f1 v1 f2 v2 f3 v3 +(integer) 1 +127.0.0.1:6379> HTTL myhash FIELDS 3 f1 f2 f3 +1) (integer) -2 +2) (integer) -2 +3) (integer) -2 +127.0.0.1:6379> HLEN myhash +(integer) 0 +127.0.0.1:6379> EXISTS myhash +(integer) 0 +``` diff --git a/commands/httl.md b/commands/httl.md new file mode 100644 index 000000000..39996446d --- /dev/null +++ b/commands/httl.md @@ -0,0 +1,31 @@ +The `HTTL` command returns the remaining time to live of a hash field that has an associated expiration time. +This introspection capability allows a Valkey client to check how many seconds a +given hash field will continue to be part of the hash object. + +## Synopsis + +``` +HTTL key FIELDS numfields field [field ...] +``` + +The command can also return the following values: + +* The command returns `-2` if the specified field does not exist in the hash. +* The command returns `-1` if the specified field exists in the hash but has no associated expiration time. + +See also the [`HPTTL`](hpttl.md) command that returns the same information with milliseconds resolution. + +## Examples + +``` +127.0.0.1:6379> HSET myhash f1 v1 f2 v2 f3 v3 +(integer) 3 +27.0.0.1:6379> HEXPIRE myhash 10 FIELDS 2 f2 f3 +1) (integer) 1 +2) (integer) 1 +127.0.0.1:6379> HTTL myhash FIELDS 4 f1 f2 f3 non-exist +1) (integer) -1 +2) (integer) 8 +3) (integer) 8 +4) (integer) -2 +``` diff --git a/topics/hashes.md b/topics/hashes.md index f5242ffcc..414ec199d 100644 --- a/topics/hashes.md +++ b/topics/hashes.md @@ -82,6 +82,24 @@ See the [complete list of hash commands](../commands/#hash). 2) "1" ``` +## Field expiration + +In valkey 9.0 we added the ability to associate an expiration time to a hash field. When set, the expiration time of a hash field determine when the field will be automatically reclaimed. +Once a hash field expiration time is past, it will no longer be available and all of the hash command API will treat this field as "non exist". +Note, however, that expired hash fields are reclaimed via a background subsystem. For this reason it might take time since the fields was "logically" expired till the hash fields is "actively" expired and reclaimed. During this time, when the fields is only "logically" expired, some hash commands (e.g. [`HLEN`](hlen.md)) might still take into account the "logically" expired fields in the hash object cardinality calculations. Another side effect of using "volatile" fields in hash objects, is the ability to perform random choosing from hash objects. When large hash objects have most of their volatile fields "logically" expired, some commands like [`HRANDFIELD`](hrandfield.md) might not be able to collect elements which are not "logically" expired and return an empty reply. + +### Command API + +[`HEXPIRE`](hexpire.md), [`HPEXPIRE`](hpexpire.md), [`HEXPIREAT`](hexpireat.md), [`HPEXPIREAT`](hpexpireat.md) and [`HPERSIST`](hpersist.md) commands are used in order to set or manipulate the expiration time of specific hash fields. + +[`HEXPIRETIME`](hexpiretime.md), [`HEXPIRETIME`](hpexpiretime.md), [`HTTL`](httl.md) and [`HPTTL]`(hpttl.md) commands are used in order to query the expiration time of specific hash fields. + +[`HSETEX`](hsetex.md) allows setting multiple hash fields and values while also associate an expiration time with each field. + +[`HGETEX`](hgetex.md) allows fetching multiple hash fields values while also mutate their expiration time. + +Note that commands that some commands which override hash fields (e.g. `HSET` and `HMSET`) will cause a hash field associated expiration time to be removed and the field will be "persisted". +The `HSETEX`(hsetex.md) command has a special flag `KEEPTTL` which allows overriding hash fields without mutating their expiration time. ## Performance diff --git a/topics/notifications.md b/topics/notifications.md index 5cbdda492..72a0efb8b 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -108,9 +108,12 @@ Different commands generate different kind of events according to the following * `LTRIM` generates an `ltrim` event, and additionally a `del` event if the resulting list is empty and the key is removed. * `RPOPLPUSH` and `BRPOPLPUSH` generate an `rpop` event and an `lpush` event. In both cases the order is guaranteed (the `lpush` event will always be delivered after the `rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. * `LMOVE` and `BLMOVE` generate an `lpop`/`rpop` event (depending on the wherefrom argument) and an `lpush`/`rpush` event (depending on the whereto argument). In both cases the order is guaranteed (the `lpush`/`rpush` event will always be delivered after the `lpop`/`rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. -* `HSET`, `HSETNX` and `HMSET` all generate a single `hset` event. +* `HSET`, `HSETNX`,`HMSET` and `HSETEX` all generate a single `hset` event. * `HINCRBY` generates an `hincrby` event. * `HINCRBYFLOAT` generates an `hincrbyfloat` event. +* `HSETEX`, `HGETEX`, `HEXPIRE` , `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which is in the future will generate a single `hexpire` event. +* `HSETEX`, `HGETEX`, `HEXPIRE` , `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which indicate zero or past time will generate a single `hexpired` event. +* `HPERSIST` and `HGETEX`, will generate `hpersist` event in case at least 1 hash field expiration time was removed. * `HDEL` generates a single `hdel` event, and an additional `del` event if the resulting hash is empty and the key is removed. * `SADD` generates a single `sadd` event, even in the variadic case. * `SREM` generates a single `srem` event, and an additional `del` event if the resulting set is empty and the key is removed. @@ -163,9 +166,11 @@ Keys with a time to live associated are expired by Valkey in two ways: The `expired` events are generated when a key is accessed and is found to be expired by one of the above systems, as a result there are no guarantees that the Valkey server will be able to generate the `expired` event at the time the key time to live reaches the value of zero. -If no command targets the key constantly, and there are many keys with a TTL associated, there can be a significant delay between the time the key time to live drops to zero, and the time the `expired` event is generated. +Since Valkey 9.0, hash fields can also have a time to live associated. Hash fields are only reclaimed when explicitly provided an expiration time which is in the past or via the same background subsystem which is also responsible to expire keys. For that reason the there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero +Same as for generic keys, there can be a significant delay between the time the key time to live drops to zero, and the time the `hexpired` event is generated. + +Basically `expired` and `hexpired` events **are generated when the Valkey server deletes the key** and not when the time to live theoretically reaches the value of zero. -Basically `expired` events **are generated when the Valkey server deletes the key** and not when the time to live theoretically reaches the value of zero. ### Events in a cluster From 6cdbf2a8f768829a0c54bda64efbd232c4824635 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 11 Aug 2025 16:08:29 +0300 Subject: [PATCH 02/18] refactor followup PR review Signed-off-by: Ran Shidlansik --- commands/hexpire.md | 15 +++------ commands/hexpireat.md | 17 +++-------- commands/hexpiretime.md | 8 ----- commands/hgetex.md | 8 ----- commands/hpersist.md | 6 ---- commands/hpexpire.md | 13 ++------ commands/hpexpireat.md | 15 +++------ commands/hpexpiretime.md | 13 -------- commands/hpttl.md | 11 ------- commands/hsetex.md | 18 +++-------- commands/httl.md | 13 +------- resp2_replies.json | 66 ++++++++++++++++++++++++++++++++++++++++ resp3_replies.json | 66 ++++++++++++++++++++++++++++++++++++++++ topics/notifications.md | 6 ++-- 14 files changed, 156 insertions(+), 119 deletions(-) diff --git a/commands/hexpire.md b/commands/hexpire.md index 51347bdfc..4c1f56a8c 100644 --- a/commands/hexpire.md +++ b/commands/hexpire.md @@ -2,13 +2,6 @@ The `HEXPIRE` command allows manipulating existing hash fields expiration time. When set, an expiration time of a hash field determine when the field will be automatically reclaimed. Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field/s. -## Synopsis - -``` -HEXPIRE key seconds [NX | XX | GT | LT] FIELDS numfields - field [field ...] -``` - ## Options The `HEXPIRE` command supports a set of options that modify its behavior: @@ -20,10 +13,10 @@ The `HEXPIRE` command supports a set of options that modify its behavior: ## Notifications -`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, -and there are no more fields in the hash object. +* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, + and there are no more fields in the hash object. ## Examples diff --git a/commands/hexpireat.md b/commands/hexpireat.md index 54d417c9a..af3e4c85d 100644 --- a/commands/hexpireat.md +++ b/commands/hexpireat.md @@ -1,20 +1,11 @@ `HEXPIREAT` has the same effect and semantic as [`HEXPIRE`](hexpire.md), but instead of specifying the number of seconds representing the TTL (time to live), it takes -an absolute [Unix timestamp][hewowu] (seconds since January 1, 1970). A +an absolute Unix timestamp (seconds since January 1, 1970). A timestamp in the past will delete the key immediately. -[hewowu]: http://en.wikipedia.org/wiki/Unix_time - For the specific semantics of the command refer to the documentation of [`HEXPIRE`](hexpire.md). -## Synopsis - -``` -HEXPIREAT key unix-time-seconds [NX | XX | GT | LT] FIELDS numfields - field [field ...] -``` - ## Options The `HEXPIREAT` command supports a set of options that modify its behavior: @@ -26,9 +17,9 @@ The `HEXPIREAT` command supports a set of options that modify its behavior: ## Notifications -`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples diff --git a/commands/hexpiretime.md b/commands/hexpiretime.md index 32b8fd3e1..8a693250b 100644 --- a/commands/hexpiretime.md +++ b/commands/hexpiretime.md @@ -1,13 +1,5 @@ The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field/s will expire. -See also the [`HPEXPIRETIME`](hpexpiretime.md) command which returns the same information with milliseconds resolution. - -## Synopsis - -``` -HEXPIRETIME key FIELDS numfields field [field ...] -``` - The command can also return the following values: * The command returns `-2` if the specified field does not exist in the hash. diff --git a/commands/hgetex.md b/commands/hgetex.md index e165af385..07a0014f4 100644 --- a/commands/hgetex.md +++ b/commands/hgetex.md @@ -2,14 +2,6 @@ The `HGETEX` command get the value of one or more fields of a given hash key, an The command will return an array in the size of the number of requested fields. Without providing any optional flags, this command behaves exactly like a normal [`HMGET`](hmget.md) command. -## Synopsis - -``` -HGETEX key [EX seconds | PX milliseconds | EXAT unix-time-seconds | - PXAT unix-time-milliseconds | PERSIST] FIELDS numfields field - [field ...] -``` - ## Options The `HGETEX` command supports a set of options that modify its behavior: diff --git a/commands/hpersist.md b/commands/hpersist.md index 3fb45c225..591afe5b2 100644 --- a/commands/hpersist.md +++ b/commands/hpersist.md @@ -1,12 +1,6 @@ The `HPERSIST` remove the existing expiration on a hash key's field(s), turning the field(s) from volatile (a field with expiration set) to persistent (a field that will never expire as no TTL (time to live) is associated). -## Synopsis - -``` -HPERSIST key FIELDS numfields field [field ...] -``` - ## Notifications `hpersist` keyspace event will be issued once in case at least 1 field's expiration time was removed. diff --git a/commands/hpexpire.md b/commands/hpexpire.md index 70b85bf5f..a2aaf5204 100644 --- a/commands/hpexpire.md +++ b/commands/hpexpire.md @@ -2,13 +2,6 @@ This command works exactly like [`HEXPIRE`](hexpire.md) but the time to live of specified in milliseconds instead of seconds. Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field/s. -## Synopsis - -``` -HPEXPIRE key milliseconds [NX | XX | GT | LT] FIELDS numfields - field [field ...] -``` - ## Options The `HPXPIRE` command supports a set of options that modify its behavior: @@ -20,9 +13,9 @@ The `HPXPIRE` command supports a set of options that modify its behavior: ## Notifications -`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples diff --git a/commands/hpexpireat.md b/commands/hpexpireat.md index ed67e8d7b..a382fc8ac 100644 --- a/commands/hpexpireat.md +++ b/commands/hpexpireat.md @@ -8,13 +8,6 @@ timestamp in the past will delete the key immediately. For the specific semantics of the command refer to the documentation of [`HPEXPIRE`](hpexpire.md). -## Synopsis - -``` -HPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT] FIELDS numfields - field [field ...] -``` - ## Options The `HPEXPIREAT` command supports a set of options that modify its behavior: @@ -26,10 +19,10 @@ The `HPEXPIREAT` command supports a set of options that modify its behavior: ## Notifications -`hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -`hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, -and there are no more fields in the hash object. +* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, + and there are no more fields in the hash object. ## Examples diff --git a/commands/hpexpiretime.md b/commands/hpexpiretime.md index 6215a3ba2..4d2a0b394 100644 --- a/commands/hpexpiretime.md +++ b/commands/hpexpiretime.md @@ -2,19 +2,6 @@ The `HPEXPIRETIME` command returns the absolute Unix timestamp (since January 1, See also the [`HEXPIRETIME`](hexpiretime.md) command which returns the same information with seconds resolution. -## Synopsis - -``` -HEXPIRETIME key FIELDS numfields field [field ...] -``` - -The command can also return the following values: - -* The command returns `-2` if the specified field does not exist in the hash. -* The command returns `-1` if the specified field exists in the hash but has no associated expiration time. - -See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. - ## Examples ``` diff --git a/commands/hpttl.md b/commands/hpttl.md index 6ab32a2d7..f852ef317 100644 --- a/commands/hpttl.md +++ b/commands/hpttl.md @@ -2,17 +2,6 @@ Like [`HTTL`](httl.md) this command returns the remaining time to live of hash f with the sole difference that `HTTL` returns the amount of remaining time in seconds while `HPTTL` returns it in milliseconds. -## Synopsis - -``` -HTTL key FIELDS numfields field [field ...] -``` - -The command can also return the following values: - -* The command returns `-2` if the key does not exist. -* The command returns `-1` if the key exists but has no associated expire. - See also the [`HTTL`](httl.md) command that returns the same information with seconds resolution. ## Examples diff --git a/commands/hsetex.md b/commands/hsetex.md index a17b470f2..6116dde14 100644 --- a/commands/hsetex.md +++ b/commands/hsetex.md @@ -2,14 +2,6 @@ The `HSETEX` command allow setting the value of one or more fields of a given ha The command will return 1 in case all provided fields have been set or 0 in case `FNX` or `FXX` were provided and non of the specified fields were set. Without providing any optional flags, this command behaves exactly like a normal [`HSET`](hset.md) command. -## Synopsis - -``` -HSETEX key [FNX | FXX] [EX seconds | PX milliseconds | - EXAT unix-time-seconds | PXAT unix-time-milliseconds | KEEPTTL] - FIELDS numfields field value [field value ...] -``` - ## Options The `HSETEX` command supports a set of options that modify its behavior: @@ -31,11 +23,11 @@ Note for the following: ## Notifications -`hset` keyspace event will be issued in case all the specified fields have been set. -`hexpired` keyspace event will be issued in case all the specified fields have been set with an expiration time which is in the future. -`hexpire` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past. -`del` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past, -and there are no more fields in the hash object. +* `hset` keyspace event will be issued in case all the specified fields have been set. +* `hexpired` keyspace event will be issued in case all the specified fields have been set with an expiration time which is in the future. +* `hexpire` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past, + and there are no more fields in the hash object. ## Examples diff --git a/commands/httl.md b/commands/httl.md index 39996446d..f30ac1956 100644 --- a/commands/httl.md +++ b/commands/httl.md @@ -1,18 +1,7 @@ -The `HTTL` command returns the remaining time to live of a hash field that has an associated expiration time. +The `HTTL` command returns the remaining time to live of hash field/s that has an associated expiration time. This introspection capability allows a Valkey client to check how many seconds a given hash field will continue to be part of the hash object. -## Synopsis - -``` -HTTL key FIELDS numfields field [field ...] -``` - -The command can also return the following values: - -* The command returns `-2` if the specified field does not exist in the hash. -* The command returns `-1` if the specified field exists in the hash but has no associated expiration time. - See also the [`HPTTL`](hpttl.md) command that returns the same information with milliseconds resolution. ## Examples diff --git a/resp2_replies.json b/resp2_replies.json index b6c4ad43d..3d88186bb 100644 --- a/resp2_replies.json +++ b/resp2_replies.json @@ -661,6 +661,72 @@ "HVALS": [ "[Array reply](../topics/protocol.md#arrays): a list of values in the hash, or an empty list when the key does not exist." ], + "HSETEX": [ + "One of the following:", + "* [Integer reply](../topics/protocol.md#integers): `0` if none of the provided fields value and or expiration time was set.", + "* [Integer reply](../topics/protocol.md#integers): `1` if all the fields value and or expiration time was set." + ], + "HGETEX": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the given fields, in the same order as they are requested." + ], + "HEXPIRE": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HPEXPIRE": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HEXPIREAT": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HPEXPIREAT": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HPERSIST": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key does not exist.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* 1: The expiration time was removed from the hash key field." + ], + "HTTL": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the remaining time-to-live of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in seconds." + ], + "HPTTL": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the remaining time-to-live of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in milliseconds." + ], + "HEXPIRETIME": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the absolute expiry timestamp of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in seconds." + ], + "HPEXPIRETIME": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the absolute expiry timestamp of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in milliseconds." + ], "INCR": [ "[Integer reply](../topics/protocol.md#integers): the value of the key after the increment." ], diff --git a/resp3_replies.json b/resp3_replies.json index 167e4b013..5f8a55520 100644 --- a/resp3_replies.json +++ b/resp3_replies.json @@ -663,6 +663,72 @@ "HVALS": [ "[Array reply](../topics/protocol.md#arrays): a list of values in the hash, or an empty list when the key does not exist." ], + "HSETEX": [ + "One of the following:", + "* [Integer reply](../topics/protocol.md#integers): `0` if none of the provided fields value and or expiration time was set.", + "* [Integer reply](../topics/protocol.md#integers): `1` if all the fields value and or expiration time was set." + ], + "HGETEX": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the given fields, in the same order as they are requested." + ], + "HEXPIRE": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HPEXPIRE": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HEXPIREAT": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HPEXPIREAT": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the HASH, or key does not exist.", + "* 0: The specified NX | XX | GT | LT condition has not been met.", + "* 1: The expiration time was applied.", + "* 2: When called with a 0 second." + ], + "HPERSIST": [ + "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key does not exist.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* 1: The expiration time was removed from the hash key field." + ], + "HTTL": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the remaining time-to-live of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in seconds." + ], + "HPTTL": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the remaining time-to-live of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in milliseconds." + ], + "HEXPIRETIME": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the absolute expiry timestamp of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in seconds." + ], + "HPEXPIRETIME": [ + "[Array reply](../topics/protocol.md#arrays): a list of values associated with the result of getting the absolute expiry timestamp of the specific fields, in the same order as they are requested.", + "* -2: Field does not exist in the provided hash key, or the hash key is empty.", + "* -1: Field exists in the provided hash key, but has no expiration associated with it.", + "* [Integer](../topics/protocol.md#integers): The expiration time associated with the hash key field, in milliseconds." + ], "INCR": [ "[Integer reply](../topics/protocol.md#integers): the value of the key after the increment." ], diff --git a/topics/notifications.md b/topics/notifications.md index 72a0efb8b..815a516e1 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -108,11 +108,11 @@ Different commands generate different kind of events according to the following * `LTRIM` generates an `ltrim` event, and additionally a `del` event if the resulting list is empty and the key is removed. * `RPOPLPUSH` and `BRPOPLPUSH` generate an `rpop` event and an `lpush` event. In both cases the order is guaranteed (the `lpush` event will always be delivered after the `rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. * `LMOVE` and `BLMOVE` generate an `lpop`/`rpop` event (depending on the wherefrom argument) and an `lpush`/`rpush` event (depending on the whereto argument). In both cases the order is guaranteed (the `lpush`/`rpush` event will always be delivered after the `lpop`/`rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. -* `HSET`, `HSETNX`,`HMSET` and `HSETEX` all generate a single `hset` event. +* `HSET`, `HSETNX`, `HMSET` and `HSETEX` all generate a single `hset` event. * `HINCRBY` generates an `hincrby` event. * `HINCRBYFLOAT` generates an `hincrbyfloat` event. -* `HSETEX`, `HGETEX`, `HEXPIRE` , `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which is in the future will generate a single `hexpire` event. -* `HSETEX`, `HGETEX`, `HEXPIRE` , `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which indicate zero or past time will generate a single `hexpired` event. +* `HSETEX`, `HGETEX`, `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which is in the future will generate a single `hexpire` event. +* `HSETEX`, `HGETEX`, `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which indicate zero or past time will generate a single `hexpired` event. * `HPERSIST` and `HGETEX`, will generate `hpersist` event in case at least 1 hash field expiration time was removed. * `HDEL` generates a single `hdel` event, and an additional `del` event if the resulting hash is empty and the key is removed. * `SADD` generates a single `sadd` event, even in the variadic case. From d2effd442074605816b183bbfd2e6b458a1b589c Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 11 Aug 2025 17:33:16 +0300 Subject: [PATCH 03/18] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Viktor Söderqvist Signed-off-by: Ran Shidlansik --- commands/hexpireat.md | 2 +- commands/hgetex.md | 2 +- commands/hpexpire.md | 2 +- commands/hpexpireat.md | 4 +--- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/commands/hexpireat.md b/commands/hexpireat.md index af3e4c85d..691b21199 100644 --- a/commands/hexpireat.md +++ b/commands/hexpireat.md @@ -20,7 +20,7 @@ The `HEXPIREAT` command supports a set of options that modify its behavior: * `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. * `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. * `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, -and there are no more fields in the hash object. + and there are no more fields in the hash object. ## Examples diff --git a/commands/hgetex.md b/commands/hgetex.md index 07a0014f4..7a31cb6a6 100644 --- a/commands/hgetex.md +++ b/commands/hgetex.md @@ -24,7 +24,7 @@ Note for the following: `hexpired` keyspace event will be issued once in case at least 1 field has been set with an expiration time which is zero or in the past. `hpersist` keyspace event will be issued once in case the `PERSIST` option was specified and at least 1 field's expiration time was removed. `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, -and there are no more fields in the hash object. + and there are no more fields in the hash object. ## Examples diff --git a/commands/hpexpire.md b/commands/hpexpire.md index a2aaf5204..a70d1cb37 100644 --- a/commands/hpexpire.md +++ b/commands/hpexpire.md @@ -16,7 +16,7 @@ The `HPXPIRE` command supports a set of options that modify its behavior: * `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. * `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. * `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, -and there are no more fields in the hash object. + and there are no more fields in the hash object. ## Examples diff --git a/commands/hpexpireat.md b/commands/hpexpireat.md index a382fc8ac..7ea66a1ab 100644 --- a/commands/hpexpireat.md +++ b/commands/hpexpireat.md @@ -1,10 +1,8 @@ `HEXPIREAT` has the same effect and semantic as [`HPEXPIRE`](hpexpire.md), but instead of specifying the number of milliseconds representing the TTL (time to live), it takes -an absolute [Unix timestamp][hewowu] (milliseconds since January 1, 1970). A +an absolute Unix timestamp (milliseconds since January 1, 1970). A timestamp in the past will delete the key immediately. -[hewowu]: http://en.wikipedia.org/wiki/Unix_time - For the specific semantics of the command refer to the documentation of [`HPEXPIRE`](hpexpire.md). From 7baef7bbfae414f54ccb1160a3c6180e5b6e826e Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 11 Aug 2025 17:34:50 +0300 Subject: [PATCH 04/18] commit changes to unsaved file Signed-off-by: Ran Shidlansik --- commands/hexpiretime.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/commands/hexpiretime.md b/commands/hexpiretime.md index 8a693250b..959235065 100644 --- a/commands/hexpiretime.md +++ b/commands/hexpiretime.md @@ -1,10 +1,5 @@ The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field/s will expire. -The command can also return the following values: - -* The command returns `-2` if the specified field does not exist in the hash. -* The command returns `-1` if the specified field exists in the hash but has no associated expiration time. - See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. ## Examples From e862351523464778b4eecc6336baae609ba06d48 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Mon, 11 Aug 2025 17:56:14 +0300 Subject: [PATCH 05/18] Update commands/hgetex.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Viktor Söderqvist Signed-off-by: Ran Shidlansik --- commands/hgetex.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/hgetex.md b/commands/hgetex.md index 7a31cb6a6..2dcdaa51e 100644 --- a/commands/hgetex.md +++ b/commands/hgetex.md @@ -20,10 +20,10 @@ Note for the following: ## Notifications -`hexpire` keyspace event will be issued once in case in case at least 1 field has been set with an expiration time which is in the future. -`hexpired` keyspace event will be issued once in case at least 1 field has been set with an expiration time which is zero or in the past. -`hpersist` keyspace event will be issued once in case the `PERSIST` option was specified and at least 1 field's expiration time was removed. -`del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once in case in case at least 1 field has been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once in case at least 1 field has been set with an expiration time which is zero or in the past. +* `hpersist` keyspace event will be issued once in case the `PERSIST` option was specified and at least 1 field's expiration time was removed. +* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples From 3a679cc97a31b74448970cd691d9699611477f13 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 07:26:20 +0300 Subject: [PATCH 06/18] Apply suggestions from code review Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- commands/hexpire.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/hexpire.md b/commands/hexpire.md index 4c1f56a8c..c0ce9afd9 100644 --- a/commands/hexpire.md +++ b/commands/hexpire.md @@ -1,6 +1,6 @@ -The `HEXPIRE` command allows manipulating existing hash fields expiration time. -When set, an expiration time of a hash field determine when the field will be automatically reclaimed. -Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field/s. +The `HEXPIRE` command allows manipulating the hash expiration time for existing fields. +When set, the expiration time of a hash field will determine when the field will be automatically reclaimed. +Note, that providing a millisecond time of '0' will cause immediate expiration and reclaiming of the field(s). ## Options @@ -13,9 +13,9 @@ The `HEXPIRE` command supports a set of options that modify its behavior: ## Notifications -* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once if all the specified fields are set to an expiration time in the future. +* `hexpired` keyspace event will be issued once if all the specified fields are set to an expiration time which is zero or in the past. +* `del` keyspace event will be issued once if all the specified fields are set to an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples From 7cfc0f5e93e2fa2341ae08699a754ab23a542461 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 07:27:49 +0300 Subject: [PATCH 07/18] Apply suggestions from code review Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- commands/hexpireat.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/hexpireat.md b/commands/hexpireat.md index 691b21199..fdf95bd11 100644 --- a/commands/hexpireat.md +++ b/commands/hexpireat.md @@ -17,9 +17,9 @@ The `HEXPIREAT` command supports a set of options that modify its behavior: ## Notifications -* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once if all the specified fields have been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples From 4fd9677dcc7ae9b66255031800b09c7d6678bd13 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 07:28:11 +0300 Subject: [PATCH 08/18] Apply suggestions from code review Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- commands/hexpiretime.md | 2 +- commands/hgetex.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/commands/hexpiretime.md b/commands/hexpiretime.md index 959235065..569c718f2 100644 --- a/commands/hexpiretime.md +++ b/commands/hexpiretime.md @@ -1,4 +1,4 @@ -The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field/s will expire. +The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field(s) will expire. See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. diff --git a/commands/hgetex.md b/commands/hgetex.md index 2dcdaa51e..b2bdc01bb 100644 --- a/commands/hgetex.md +++ b/commands/hgetex.md @@ -15,15 +15,15 @@ The `HGETEX` command supports a set of options that modify its behavior: Note for the following: 1. The EX, PX, EXAT, PXAT, and PERSIST options are mutually exclusive. -2. Providing '0' expiration TTL via `EX` or `PX` optional arguments will result in the specified fields to immediately expire and removed from the hash. -3. Providing past expiration time via `EXAT` or `PXAT` optional arguments will result in the specified fields to immediately expire and removed from the hash. +2. Providing '0' expiration TTL via `EX` or `PX` optional arguments will cause the specified fields to expire immediately and be removed from the hash. +3. Providing past expiration time via `EXAT` or `PXAT` optional arguments will cause the specified fields to expire immediately and be removed from the hash. ## Notifications -* `hexpire` keyspace event will be issued once in case in case at least 1 field has been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once in case at least 1 field has been set with an expiration time which is zero or in the past. -* `hpersist` keyspace event will be issued once in case the `PERSIST` option was specified and at least 1 field's expiration time was removed. -* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once if at least one field has been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once if at least one field has been set with an expiration time which is zero or in the past. +* `hpersist` keyspace event will be issued once if the `PERSIST` option was specified and at least one field's expiration time was removed. +* `del` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples From 8501051ef35892084e451fa71db8480e29537bb6 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 07:28:43 +0300 Subject: [PATCH 09/18] Apply suggestions from code review Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- commands/hpersist.md | 2 +- commands/hpexpire.md | 2 +- commands/hpexpiretime.md | 2 +- commands/hpttl.md | 2 +- commands/hsetex.md | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/commands/hpersist.md b/commands/hpersist.md index 591afe5b2..e1cb4139c 100644 --- a/commands/hpersist.md +++ b/commands/hpersist.md @@ -3,7 +3,7 @@ to persistent (a field that will never expire as no TTL (time to live) is associ ## Notifications -`hpersist` keyspace event will be issued once in case at least 1 field's expiration time was removed. +`hpersist` keyspace event will be issued once in case at least one field's expiration time was removed. ## Examples diff --git a/commands/hpexpire.md b/commands/hpexpire.md index a70d1cb37..d4fc47109 100644 --- a/commands/hpexpire.md +++ b/commands/hpexpire.md @@ -1,6 +1,6 @@ This command works exactly like [`HEXPIRE`](hexpire.md) but the time to live of the key is specified in milliseconds instead of seconds. -Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field/s. +Note, that providing a millisecond time of '0' will cause immediate expiration and reclamation of the field(s). ## Options diff --git a/commands/hpexpiretime.md b/commands/hpexpiretime.md index 4d2a0b394..c6c969632 100644 --- a/commands/hpexpiretime.md +++ b/commands/hpexpiretime.md @@ -1,4 +1,4 @@ -The `HPEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in milliseconds at which the given hash field/s will expire. +The `HPEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in milliseconds at which the given hash field(s) will expire. See also the [`HEXPIRETIME`](hexpiretime.md) command which returns the same information with seconds resolution. diff --git a/commands/hpttl.md b/commands/hpttl.md index f852ef317..223027c58 100644 --- a/commands/hpttl.md +++ b/commands/hpttl.md @@ -1,4 +1,4 @@ -Like [`HTTL`](httl.md) this command returns the remaining time to live of hash field/s that has an associated expiration time, +Like [`HTTL`](httl.md) this command returns the remaining time to live of hash field(s) that has an associated expiration time, with the sole difference that `HTTL` returns the amount of remaining time in seconds while `HPTTL` returns it in milliseconds. diff --git a/commands/hsetex.md b/commands/hsetex.md index 6116dde14..faecd0e49 100644 --- a/commands/hsetex.md +++ b/commands/hsetex.md @@ -1,4 +1,4 @@ -The `HSETEX` command allow setting the value of one or more fields of a given hash key, and optionally manipulate their expiration time. +The `HSETEX` command allows setting the value of one or more fields of a given hash key, and optionally manipulating their expiration time. The command will return 1 in case all provided fields have been set or 0 in case `FNX` or `FXX` were provided and non of the specified fields were set. Without providing any optional flags, this command behaves exactly like a normal [`HSET`](hset.md) command. @@ -10,7 +10,7 @@ The `HSETEX` command supports a set of options that modify its behavior: * FXX — Only set the fields if all of them already exist. * EX seconds — Set the specified expiration time in seconds. * PX milliseconds — Set the specified expiration time in milliseconds. -* EXAT `unix-time-seconds` — Set the specified Unix time in seconds at which the fields will expire. +* EXAT `unix-time-seconds` — Set the specified Unix time in seconds for when the fields will expire. * PXAT `unix-time-milliseconds` — Set the specified Unix time in milliseconds at which the fields will expire. * KEEPTTL — Retain the TTL associated with the fields. From abb11bdba5140ec7c17a72cb6a55d784709b95ae Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 07:29:45 +0300 Subject: [PATCH 10/18] Apply suggestions from code review Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- commands/hsetex.md | 4 ++-- commands/httl.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/commands/hsetex.md b/commands/hsetex.md index faecd0e49..52cdd0c94 100644 --- a/commands/hsetex.md +++ b/commands/hsetex.md @@ -18,8 +18,8 @@ Note for the following: 1. The EX, PX, EXAT, PXAT, and KEEPTTL options are mutually exclusive. 2. Setting a value on a volatile hash field (A field which has an assigned expiration time) will remove the expiration for that field. -3. Providing '0' expiration TTL via `EX` or `PX` optional arguments will result in the specified fields to immediately expire and removed from the hash. -4. Providing past expiration time via `EXAT` or `PXAT` optional arguments will result in the specified fields to immediately expire and removed from the hash. +3. Providing '0' expiration TTL via `EX` or `PX` optional arguments will result in the specified fields immediately expiring and being removed from the hash. +4. Providing past expiration time via `EXAT` or `PXAT` optional arguments will result in the specified fields immediately expiring and being removed from the hash. ## Notifications diff --git a/commands/httl.md b/commands/httl.md index f30ac1956..d27bc8b1b 100644 --- a/commands/httl.md +++ b/commands/httl.md @@ -1,4 +1,4 @@ -The `HTTL` command returns the remaining time to live of hash field/s that has an associated expiration time. +The `HTTL` command returns the remaining time to live of hash field(s) with associated expiration time(s). This introspection capability allows a Valkey client to check how many seconds a given hash field will continue to be part of the hash object. From a1ba7f3bc0bc1260949b0a9a05dbc1545b9ac7ed Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 07:31:54 +0300 Subject: [PATCH 11/18] Apply suggestions from code review Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- resp2_replies.json | 12 ++++++------ resp3_replies.json | 8 ++++---- topics/hashes.md | 10 +++++----- topics/notifications.md | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/resp2_replies.json b/resp2_replies.json index 3d88186bb..0c1bffc1f 100644 --- a/resp2_replies.json +++ b/resp2_replies.json @@ -663,8 +663,8 @@ ], "HSETEX": [ "One of the following:", - "* [Integer reply](../topics/protocol.md#integers): `0` if none of the provided fields value and or expiration time was set.", - "* [Integer reply](../topics/protocol.md#integers): `1` if all the fields value and or expiration time was set." + "* [Integer reply](../topics/protocol.md#integers): `0` if none of the provided fields' values and/or expiration times were set.", + "* [Integer reply](../topics/protocol.md#integers): `1` if all the fields' values and/or expiration times were set." ], "HGETEX": [ "[Array reply](../topics/protocol.md#arrays): a list of values associated with the given fields, in the same order as they are requested." @@ -674,28 +674,28 @@ "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HPEXPIRE": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HEXPIREAT": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HPEXPIREAT": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HPERSIST": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", diff --git a/resp3_replies.json b/resp3_replies.json index 5f8a55520..b522a2163 100644 --- a/resp3_replies.json +++ b/resp3_replies.json @@ -676,28 +676,28 @@ "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HPEXPIRE": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HEXPIREAT": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HPEXPIREAT": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", "* -2: Field does not exist in the HASH, or key does not exist.", "* 0: The specified NX | XX | GT | LT condition has not been met.", "* 1: The expiration time was applied.", - "* 2: When called with a 0 second." + "* 2: When called with 0 seconds." ], "HPERSIST": [ "[Array reply](../topics/protocol.md#arrays): a list of integer codes indicating the result of setting expiry on each specified field, in the same order as the fields are requested.", diff --git a/topics/hashes.md b/topics/hashes.md index 414ec199d..96a8e4cb6 100644 --- a/topics/hashes.md +++ b/topics/hashes.md @@ -84,9 +84,9 @@ See the [complete list of hash commands](../commands/#hash). ## Field expiration -In valkey 9.0 we added the ability to associate an expiration time to a hash field. When set, the expiration time of a hash field determine when the field will be automatically reclaimed. -Once a hash field expiration time is past, it will no longer be available and all of the hash command API will treat this field as "non exist". -Note, however, that expired hash fields are reclaimed via a background subsystem. For this reason it might take time since the fields was "logically" expired till the hash fields is "actively" expired and reclaimed. During this time, when the fields is only "logically" expired, some hash commands (e.g. [`HLEN`](hlen.md)) might still take into account the "logically" expired fields in the hash object cardinality calculations. Another side effect of using "volatile" fields in hash objects, is the ability to perform random choosing from hash objects. When large hash objects have most of their volatile fields "logically" expired, some commands like [`HRANDFIELD`](hrandfield.md) might not be able to collect elements which are not "logically" expired and return an empty reply. +In Valkey 9.0 we added the ability to associate an expiration time with a hash field. When set, the expiration time of a hash field determines when the field could be automatically reclaimed. +Once a hash field expiration time is past, it will no longer be available and all of the hash command APIs will treat this field as "nonexistent". +Note that expired hash fields are reclaimed via a background subsystem. For this reason it might take time between when fields "logically" expire and when hash fields are "actively" expired and reclaimed. During this time, when the fields is only "logically" expired, some hash commands (e.g. [`HLEN`](hlen.md)) might still take "logically" expired fields into account in hash object cardinality calculations. Another side effect of using "volatile" fields in hash objects, is the ability to random choose from hash objects. When large hash objects have most of their volatile fields "logically" expired, some commands like [`HRANDFIELD`](hrandfield.md) might not be able to collect elements which are not "logically" expired and may return an empty reply. ### Command API @@ -96,9 +96,9 @@ Note, however, that expired hash fields are reclaimed via a background subsystem [`HSETEX`](hsetex.md) allows setting multiple hash fields and values while also associate an expiration time with each field. -[`HGETEX`](hgetex.md) allows fetching multiple hash fields values while also mutate their expiration time. +[`HGETEX`](hgetex.md) allows fetching multiple hash fields values while also mutating their expiration time. -Note that commands that some commands which override hash fields (e.g. `HSET` and `HMSET`) will cause a hash field associated expiration time to be removed and the field will be "persisted". +Note that some commands which override hash fields (e.g. `HSET` and `HMSET`) will cause a hash field associated expiration time to be removed and the field will be "persisted". The `HSETEX`(hsetex.md) command has a special flag `KEEPTTL` which allows overriding hash fields without mutating their expiration time. ## Performance diff --git a/topics/notifications.md b/topics/notifications.md index 815a516e1..8fb1ef4d8 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -108,12 +108,12 @@ Different commands generate different kind of events according to the following * `LTRIM` generates an `ltrim` event, and additionally a `del` event if the resulting list is empty and the key is removed. * `RPOPLPUSH` and `BRPOPLPUSH` generate an `rpop` event and an `lpush` event. In both cases the order is guaranteed (the `lpush` event will always be delivered after the `rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. * `LMOVE` and `BLMOVE` generate an `lpop`/`rpop` event (depending on the wherefrom argument) and an `lpush`/`rpush` event (depending on the whereto argument). In both cases the order is guaranteed (the `lpush`/`rpush` event will always be delivered after the `lpop`/`rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. -* `HSET`, `HSETNX`, `HMSET` and `HSETEX` all generate a single `hset` event. +* `HSET`, `HSETNX`, `HMSET` and `HSETEX` generate a single `hset` event. * `HINCRBY` generates an `hincrby` event. * `HINCRBYFLOAT` generates an `hincrbyfloat` event. * `HSETEX`, `HGETEX`, `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which is in the future will generate a single `hexpire` event. * `HSETEX`, `HGETEX`, `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which indicate zero or past time will generate a single `hexpired` event. -* `HPERSIST` and `HGETEX`, will generate `hpersist` event in case at least 1 hash field expiration time was removed. +* `HPERSIST` and `HGETEX`, will generate an `hpersist` event if at least 1 hash field expiration time was removed. * `HDEL` generates a single `hdel` event, and an additional `del` event if the resulting hash is empty and the key is removed. * `SADD` generates a single `sadd` event, even in the variadic case. * `SREM` generates a single `srem` event, and an additional `del` event if the resulting set is empty and the key is removed. @@ -166,10 +166,10 @@ Keys with a time to live associated are expired by Valkey in two ways: The `expired` events are generated when a key is accessed and is found to be expired by one of the above systems, as a result there are no guarantees that the Valkey server will be able to generate the `expired` event at the time the key time to live reaches the value of zero. -Since Valkey 9.0, hash fields can also have a time to live associated. Hash fields are only reclaimed when explicitly provided an expiration time which is in the past or via the same background subsystem which is also responsible to expire keys. For that reason the there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero +Since Valkey 9.0, hash fields can also have an associated time to live. Hash fields are only reclaimed when explicitly provided an expiration time which is in the past or via the same background subsystem which is also responsible to expire keys. For that reason the there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero. Same as for generic keys, there can be a significant delay between the time the key time to live drops to zero, and the time the `hexpired` event is generated. -Basically `expired` and `hexpired` events **are generated when the Valkey server deletes the key** and not when the time to live theoretically reaches the value of zero. +Basically `expired` and `hexpired` events **are generated when the Valkey server deletes a key** and not when the time to live theoretically reaches the value of zero. ### Events in a cluster From 856bbfbecb05ba75397e66816c7baeb99517e29f Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 09:21:25 +0300 Subject: [PATCH 12/18] Improve notifications documentation Signed-off-by: Ran Shidlansik --- topics/notifications.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/topics/notifications.md b/topics/notifications.md index 815a516e1..2a2f3a9bf 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -111,8 +111,8 @@ Different commands generate different kind of events according to the following * `HSET`, `HSETNX`, `HMSET` and `HSETEX` all generate a single `hset` event. * `HINCRBY` generates an `hincrby` event. * `HINCRBYFLOAT` generates an `hincrbyfloat` event. -* `HSETEX`, `HGETEX`, `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which is in the future will generate a single `hexpire` event. -* `HSETEX`, `HGETEX`, `HEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`, when provided an expiration time argument which indicate zero or past time will generate a single `hexpired` event. +* `HEXPIRE` and all its variants (`HPEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`) generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and in case the hash object has no more items left it will be deleted and a 'del' event is generated as well. +* `HSETEX` and `HGETEX` generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and in case the hash object has no more items left it will be deleted and a 'del' event is generated as well. * `HPERSIST` and `HGETEX`, will generate `hpersist` event in case at least 1 hash field expiration time was removed. * `HDEL` generates a single `hdel` event, and an additional `del` event if the resulting hash is empty and the key is removed. * `SADD` generates a single `sadd` event, even in the variadic case. @@ -166,10 +166,10 @@ Keys with a time to live associated are expired by Valkey in two ways: The `expired` events are generated when a key is accessed and is found to be expired by one of the above systems, as a result there are no guarantees that the Valkey server will be able to generate the `expired` event at the time the key time to live reaches the value of zero. -Since Valkey 9.0, hash fields can also have a time to live associated. Hash fields are only reclaimed when explicitly provided an expiration time which is in the past or via the same background subsystem which is also responsible to expire keys. For that reason the there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero -Same as for generic keys, there can be a significant delay between the time the key time to live drops to zero, and the time the `hexpired` event is generated. +Since Valkey 9.0, hash fields can also have a time to live associated. Hash fields are only reclaimed when explicitly provided an expiration time which is in the past or via the same background subsystem which is also responsible to expire keys. For that reason the there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero. Basically `expired` and `hexpired` events **are generated when the Valkey server deletes the key** and not when the time to live theoretically reaches the value of zero. +For that reason there can be a significant delay between the time the key time to live drops to zero, and the time the corresponding event is generated. ### Events in a cluster From 9dfb6feaabc7a81f74b631839d9a08b6235ab6fb Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 12 Aug 2025 19:04:13 +0300 Subject: [PATCH 13/18] fix `hset` event notification Signed-off-by: Ran Shidlansik --- commands/hsetex.md | 3 ++- topics/notifications.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/commands/hsetex.md b/commands/hsetex.md index 52cdd0c94..591131f5d 100644 --- a/commands/hsetex.md +++ b/commands/hsetex.md @@ -23,7 +23,8 @@ Note for the following: ## Notifications -* `hset` keyspace event will be issued in case all the specified fields have been set. +* `hset` keyspace event will be issued in case all the specified fields have been added or modified. + Note that `hset` event will not be generated in case a '0' or expired time was provided. * `hexpired` keyspace event will be issued in case all the specified fields have been set with an expiration time which is in the future. * `hexpire` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past. * `del` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past, diff --git a/topics/notifications.md b/topics/notifications.md index 306a88aa6..e2bb5ad82 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -108,7 +108,7 @@ Different commands generate different kind of events according to the following * `LTRIM` generates an `ltrim` event, and additionally a `del` event if the resulting list is empty and the key is removed. * `RPOPLPUSH` and `BRPOPLPUSH` generate an `rpop` event and an `lpush` event. In both cases the order is guaranteed (the `lpush` event will always be delivered after the `rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. * `LMOVE` and `BLMOVE` generate an `lpop`/`rpop` event (depending on the wherefrom argument) and an `lpush`/`rpush` event (depending on the whereto argument). In both cases the order is guaranteed (the `lpush`/`rpush` event will always be delivered after the `lpop`/`rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. -* `HSET`, `HSETNX`, `HMSET` and `HSETEX` generate a single `hset` event. +* `HSET`, `HSETNX`, `HMSET` and `HSETEX` generate a single `hset` event in case the specified fields were added or modified. * `HINCRBY` generates an `hincrby` event. * `HINCRBYFLOAT` generates an `hincrbyfloat` event. * `HEXPIRE` and all its variants (`HPEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`) generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and in case the hash object has no more items left it will be deleted and a 'del' event is generated as well. From c9d058c1d7a248b9e5e7d598b25932bf02f2e200 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Wed, 13 Aug 2025 16:47:08 +0300 Subject: [PATCH 14/18] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Viktor Söderqvist Co-authored-by: Josh Soref <2119212+jsoref@users.noreply.github.com> Signed-off-by: Ran Shidlansik --- commands/hexpire.md | 4 ++-- commands/hpersist.md | 2 +- commands/hpexpire.md | 6 +++--- topics/notifications.md | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/commands/hexpire.md b/commands/hexpire.md index c0ce9afd9..3f6ca39a1 100644 --- a/commands/hexpire.md +++ b/commands/hexpire.md @@ -1,5 +1,5 @@ -The `HEXPIRE` command allows manipulating the hash expiration time for existing fields. -When set, the expiration time of a hash field will determine when the field will be automatically reclaimed. +The `HEXPIRE` command allows manipulating the expiration time for existing hash fields. +When set, the expiration time of a hash field will determine when the field will be automatically deleted. Note, that providing a millisecond time of '0' will cause immediate expiration and reclaiming of the field(s). ## Options diff --git a/commands/hpersist.md b/commands/hpersist.md index e1cb4139c..6f4a78b76 100644 --- a/commands/hpersist.md +++ b/commands/hpersist.md @@ -3,7 +3,7 @@ to persistent (a field that will never expire as no TTL (time to live) is associ ## Notifications -`hpersist` keyspace event will be issued once in case at least one field's expiration time was removed. +`hpersist` keyspace event will be issued once if at least one field's expiration time was removed. ## Examples diff --git a/commands/hpexpire.md b/commands/hpexpire.md index d4fc47109..9a2dddfce 100644 --- a/commands/hpexpire.md +++ b/commands/hpexpire.md @@ -13,9 +13,9 @@ The `HPXPIRE` command supports a set of options that modify its behavior: ## Notifications -* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, +* `hexpire` keyspace event will be issued once if all the specified fields have been set with an expiration time which is in the future. +* `hexpired` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past. +* `del` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past, and there are no more fields in the hash object. ## Examples diff --git a/topics/notifications.md b/topics/notifications.md index e2bb5ad82..b64f7b473 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -108,11 +108,11 @@ Different commands generate different kind of events according to the following * `LTRIM` generates an `ltrim` event, and additionally a `del` event if the resulting list is empty and the key is removed. * `RPOPLPUSH` and `BRPOPLPUSH` generate an `rpop` event and an `lpush` event. In both cases the order is guaranteed (the `lpush` event will always be delivered after the `rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. * `LMOVE` and `BLMOVE` generate an `lpop`/`rpop` event (depending on the wherefrom argument) and an `lpush`/`rpush` event (depending on the whereto argument). In both cases the order is guaranteed (the `lpush`/`rpush` event will always be delivered after the `lpop`/`rpop` event). Additionally a `del` event will be generated if the resulting list is zero length and the key is removed. -* `HSET`, `HSETNX`, `HMSET` and `HSETEX` generate a single `hset` event in case the specified fields were added or modified. +* `HSET`, `HSETNX`, `HMSET` and `HSETEX` generate a single `hset` event if the specified fields were added or modified. * `HINCRBY` generates an `hincrby` event. * `HINCRBYFLOAT` generates an `hincrbyfloat` event. -* `HEXPIRE` and all its variants (`HPEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`) generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and in case the hash object has no more items left it will be deleted and a 'del' event is generated as well. -* `HSETEX` and `HGETEX` generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and in case the hash object has no more items left it will be deleted and a 'del' event is generated as well. +* `HEXPIRE` and all its variants (`HPEXPIRE`, `HPEXPIRE`, `HEXPIREAT` and `HPEXPIREAT`) generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and if the hash object has no more items left it will be deleted and a `del` event is generated as well. +* `HSETEX` and `HGETEX` generate a single `hexpire` event when called with a positive timeout (or a future timestamp). Note that when these commands are called with a negative timeout value or timestamp in the past, only a single `hexpired` event is generated and if the hash object has no more items left it will be deleted and a `del` event is generated as well. * `HPERSIST` and `HGETEX`, will generate an `hpersist` event if at least 1 hash field expiration time was removed. * `HDEL` generates a single `hdel` event, and an additional `del` event if the resulting hash is empty and the key is removed. * `SADD` generates a single `sadd` event, even in the variadic case. @@ -166,7 +166,7 @@ Keys with a time to live associated are expired by Valkey in two ways: The `expired` events are generated when a key is accessed and is found to be expired by one of the above systems, as a result there are no guarantees that the Valkey server will be able to generate the `expired` event at the time the key time to live reaches the value of zero. -Since Valkey 9.0, hash fields can also have an associated time to live. Hash fields are only reclaimed when explicitly provided an expiration time which is in the past or via the same background subsystem which is also responsible to expire keys. For that reason the there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero. +Since Valkey 9.0, hash fields can also have an associated time to live. Hash fields are only reclaimed when explicitly provided an expiration time in the past or via the same background subsystem which is also responsible to expire keys. For that reason there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero. Basically `expired` and `hexpired` events **are generated when the Valkey server deletes a key** and not when the time to live theoretically reaches the value of zero. For that reason there can be a significant delay between the time the key time to live drops to zero, and the time the corresponding event is generated. From 67295e8dc57d1fa774ed37cd9344b5d3aa4e7938 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 26 Aug 2025 08:47:20 +0200 Subject: [PATCH 15/18] Apply suggestions from code review Co-authored-by: Madelyn Olson Signed-off-by: Ran Shidlansik --- commands/hexpiretime.md | 2 +- commands/hgetex.md | 8 ++++---- topics/hashes.md | 11 +++++++---- topics/notifications.md | 3 ++- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/commands/hexpiretime.md b/commands/hexpiretime.md index 569c718f2..a60a90294 100644 --- a/commands/hexpiretime.md +++ b/commands/hexpiretime.md @@ -1,4 +1,4 @@ -The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field(s) will expire. +The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field(s) will expire or -1 if the hash field doesn't not have expiration time. See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. diff --git a/commands/hgetex.md b/commands/hgetex.md index b2bdc01bb..ee39e8f3c 100644 --- a/commands/hgetex.md +++ b/commands/hgetex.md @@ -1,4 +1,4 @@ -The `HGETEX` command get the value of one or more fields of a given hash key, and optionally manipulates their expiration time. +The `HGETEX` command returns the value of one or more fields of a given hash key, and optionally manipulates their expiration time. The command will return an array in the size of the number of requested fields. Without providing any optional flags, this command behaves exactly like a normal [`HMGET`](hmget.md) command. @@ -43,7 +43,7 @@ Note for the following: 2) "v2" 3) "v3" 127.0.0.1:6379> HGETEX myhash FIELDS 3 f1 f2 f3 -1) "nil" -2) "nil" -3) "nil" +1) (nil) +2) (nil) +3) (nil) ``` diff --git a/topics/hashes.md b/topics/hashes.md index 96a8e4cb6..ca4b095b3 100644 --- a/topics/hashes.md +++ b/topics/hashes.md @@ -84,9 +84,12 @@ See the [complete list of hash commands](../commands/#hash). ## Field expiration -In Valkey 9.0 we added the ability to associate an expiration time with a hash field. When set, the expiration time of a hash field determines when the field could be automatically reclaimed. +In Valkey 9.0 we added the ability to associate an expiration time with a hash field. When set, the field and it's corresponding value are automatically deleted after the time has past. Once a hash field expiration time is past, it will no longer be available and all of the hash command APIs will treat this field as "nonexistent". -Note that expired hash fields are reclaimed via a background subsystem. For this reason it might take time between when fields "logically" expire and when hash fields are "actively" expired and reclaimed. During this time, when the fields is only "logically" expired, some hash commands (e.g. [`HLEN`](hlen.md)) might still take "logically" expired fields into account in hash object cardinality calculations. Another side effect of using "volatile" fields in hash objects, is the ability to random choose from hash objects. When large hash objects have most of their volatile fields "logically" expired, some commands like [`HRANDFIELD`](hrandfield.md) might not be able to collect elements which are not "logically" expired and may return an empty reply. +Note that expired hash fields are deleted via a periodic job. +For this reason it might take time between when fields "logically" expire and when hash fields are deleted. +During this time, "logically" expired fields still consume memory and some some hash commands (e.g. [`HLEN`](hlen.md)) might still take "logically" expired fields into account in hash object cardinality calculations. +Another side effect of using "volatile" fields in hash objects, is the ability to random choose from hash objects. When large hash objects have most of their volatile fields "logically" expired, some commands like [`HRANDFIELD`](hrandfield.md) might not be able to collect elements which are not "logically" expired and may return an empty reply. ### Command API @@ -98,8 +101,8 @@ Note that expired hash fields are reclaimed via a background subsystem. For this [`HGETEX`](hgetex.md) allows fetching multiple hash fields values while also mutating their expiration time. -Note that some commands which override hash fields (e.g. `HSET` and `HMSET`) will cause a hash field associated expiration time to be removed and the field will be "persisted". -The `HSETEX`(hsetex.md) command has a special flag `KEEPTTL` which allows overriding hash fields without mutating their expiration time. +Note that some commands which override hash fields (e.g. `HSET` and `HMSET`) will remove an expiration time associated with a hash field. +The [`HSETEX`](hsetex.md) supports the `KEEPTTL` flag, which allows overriding hash fields without mutating their expiration time. ## Performance diff --git a/topics/notifications.md b/topics/notifications.md index b64f7b473..700c35ac6 100644 --- a/topics/notifications.md +++ b/topics/notifications.md @@ -166,7 +166,8 @@ Keys with a time to live associated are expired by Valkey in two ways: The `expired` events are generated when a key is accessed and is found to be expired by one of the above systems, as a result there are no guarantees that the Valkey server will be able to generate the `expired` event at the time the key time to live reaches the value of zero. -Since Valkey 9.0, hash fields can also have an associated time to live. Hash fields are only reclaimed when explicitly provided an expiration time in the past or via the same background subsystem which is also responsible to expire keys. For that reason there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero. +Since Valkey 9.0, hash fields can also have an associated time to live. +Hash fields are deleted when explicitly provided an expiration time in the past or via the same background job which is also responsible to expire keys. For that reason, there are no guarantees that the Valkey server will be able to generate the `hexpired` event at the time the hash field time to live reaches the value of zero. Basically `expired` and `hexpired` events **are generated when the Valkey server deletes a key** and not when the time to live theoretically reaches the value of zero. For that reason there can be a significant delay between the time the key time to live drops to zero, and the time the corresponding event is generated. From 14cb7ad287208325935a87a563c5432a382a40a7 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 26 Aug 2025 08:56:29 +0200 Subject: [PATCH 16/18] change a reclaim to delete Signed-off-by: Ran Shidlansik --- commands/hexpire.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commands/hexpire.md b/commands/hexpire.md index 3f6ca39a1..a3065239f 100644 --- a/commands/hexpire.md +++ b/commands/hexpire.md @@ -1,6 +1,6 @@ The `HEXPIRE` command allows manipulating the expiration time for existing hash fields. When set, the expiration time of a hash field will determine when the field will be automatically deleted. -Note, that providing a millisecond time of '0' will cause immediate expiration and reclaiming of the field(s). +Note, that providing a millisecond time of '0' will cause immediate expiration and deletion of the field(s). ## Options From d72f5e67645e85f256b3372a45828de0e22b0686 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Tue, 26 Aug 2025 12:57:07 +0200 Subject: [PATCH 17/18] remove the specific HFE command notifications Signed-off-by: Ran Shidlansik --- commands/hexpire.md | 7 ------- commands/hexpireat.md | 7 ------- commands/hgetex.md | 8 -------- commands/hpersist.md | 4 ---- commands/hpexpire.md | 7 ------- commands/hpexpireat.md | 7 ------- commands/hsetex.md | 9 --------- 7 files changed, 49 deletions(-) diff --git a/commands/hexpire.md b/commands/hexpire.md index a3065239f..f0143259c 100644 --- a/commands/hexpire.md +++ b/commands/hexpire.md @@ -11,13 +11,6 @@ The `HEXPIRE` command supports a set of options that modify its behavior: * GT — For each specified field, set expiration only when the new expiration is greater than current one. * LT — For each specified field, set expiration only when the new expiration is less than current one. -## Notifications - -* `hexpire` keyspace event will be issued once if all the specified fields are set to an expiration time in the future. -* `hexpired` keyspace event will be issued once if all the specified fields are set to an expiration time which is zero or in the past. -* `del` keyspace event will be issued once if all the specified fields are set to an expiration time which is zero or in the past, - and there are no more fields in the hash object. - ## Examples ``` diff --git a/commands/hexpireat.md b/commands/hexpireat.md index fdf95bd11..32b575cea 100644 --- a/commands/hexpireat.md +++ b/commands/hexpireat.md @@ -15,13 +15,6 @@ The `HEXPIREAT` command supports a set of options that modify its behavior: * GT — For each specified field, set expiration only when the new expiration is greater than current one. * LT — For each specified field, set expiration only when the new expiration is less than current one. -## Notifications - -* `hexpire` keyspace event will be issued once if all the specified fields have been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past, - and there are no more fields in the hash object. - ## Examples ``` diff --git a/commands/hgetex.md b/commands/hgetex.md index ee39e8f3c..95b6868bf 100644 --- a/commands/hgetex.md +++ b/commands/hgetex.md @@ -18,14 +18,6 @@ Note for the following: 2. Providing '0' expiration TTL via `EX` or `PX` optional arguments will cause the specified fields to expire immediately and be removed from the hash. 3. Providing past expiration time via `EXAT` or `PXAT` optional arguments will cause the specified fields to expire immediately and be removed from the hash. -## Notifications - -* `hexpire` keyspace event will be issued once if at least one field has been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once if at least one field has been set with an expiration time which is zero or in the past. -* `hpersist` keyspace event will be issued once if the `PERSIST` option was specified and at least one field's expiration time was removed. -* `del` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past, - and there are no more fields in the hash object. - ## Examples ``` diff --git a/commands/hpersist.md b/commands/hpersist.md index 6f4a78b76..9daa1ecfe 100644 --- a/commands/hpersist.md +++ b/commands/hpersist.md @@ -1,10 +1,6 @@ The `HPERSIST` remove the existing expiration on a hash key's field(s), turning the field(s) from volatile (a field with expiration set) to persistent (a field that will never expire as no TTL (time to live) is associated). -## Notifications - -`hpersist` keyspace event will be issued once if at least one field's expiration time was removed. - ## Examples ``` diff --git a/commands/hpexpire.md b/commands/hpexpire.md index 9a2dddfce..64fc09b31 100644 --- a/commands/hpexpire.md +++ b/commands/hpexpire.md @@ -11,13 +11,6 @@ The `HPXPIRE` command supports a set of options that modify its behavior: * GT — For each specified field, set expiration only when the new expiration is greater than current one. * LT — For each specified field, set expiration only when the new expiration is less than current one. -## Notifications - -* `hexpire` keyspace event will be issued once if all the specified fields have been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued once if all the specified fields have been set with an expiration time which is zero or in the past, - and there are no more fields in the hash object. - ## Examples ``` diff --git a/commands/hpexpireat.md b/commands/hpexpireat.md index 7ea66a1ab..50a577285 100644 --- a/commands/hpexpireat.md +++ b/commands/hpexpireat.md @@ -15,13 +15,6 @@ The `HPEXPIREAT` command supports a set of options that modify its behavior: * GT — For each specified field, set expiration only when the new expiration is greater than current one. * LT — For each specified field, set expiration only when the new expiration is less than current one. -## Notifications - -* `hexpire` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is in the future. -* `hexpired` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued once in case all the specified fields have been set with an expiration time which is zero or in the past, - and there are no more fields in the hash object. - ## Examples ``` diff --git a/commands/hsetex.md b/commands/hsetex.md index 591131f5d..6d95f4666 100644 --- a/commands/hsetex.md +++ b/commands/hsetex.md @@ -21,15 +21,6 @@ Note for the following: 3. Providing '0' expiration TTL via `EX` or `PX` optional arguments will result in the specified fields immediately expiring and being removed from the hash. 4. Providing past expiration time via `EXAT` or `PXAT` optional arguments will result in the specified fields immediately expiring and being removed from the hash. -## Notifications - -* `hset` keyspace event will be issued in case all the specified fields have been added or modified. - Note that `hset` event will not be generated in case a '0' or expired time was provided. -* `hexpired` keyspace event will be issued in case all the specified fields have been set with an expiration time which is in the future. -* `hexpire` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past. -* `del` keyspace event will be issued in case all the specified fields have been set with an expiration time which is zero or in the past, - and there are no more fields in the hash object. - ## Examples Add 3 new items without expiration time to a 'myhash' From e6e6b6ddc9b14db436c511f510f36cddd087eaa9 Mon Sep 17 00:00:00 2001 From: Ran Shidlansik Date: Thu, 4 Sep 2025 15:43:14 +0300 Subject: [PATCH 18/18] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Viktor Söderqvist Signed-off-by: Ran Shidlansik --- commands/hexpiretime.md | 2 +- topics/hashes.md | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/commands/hexpiretime.md b/commands/hexpiretime.md index a60a90294..2fc020a86 100644 --- a/commands/hexpiretime.md +++ b/commands/hexpiretime.md @@ -1,4 +1,4 @@ -The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field(s) will expire or -1 if the hash field doesn't not have expiration time. +The `HEXPIRETIME` command returns the absolute Unix timestamp (since January 1, 1970) in seconds at which the given hash field(s) will expire or -1 if the hash field doesn't have expiration time. See also the [`HPEXPIRETIME`](hpexpiretime.md) command that returns the same information with milliseconds resolution. diff --git a/topics/hashes.md b/topics/hashes.md index ca4b095b3..b0abb34fb 100644 --- a/topics/hashes.md +++ b/topics/hashes.md @@ -87,22 +87,22 @@ See the [complete list of hash commands](../commands/#hash). In Valkey 9.0 we added the ability to associate an expiration time with a hash field. When set, the field and it's corresponding value are automatically deleted after the time has past. Once a hash field expiration time is past, it will no longer be available and all of the hash command APIs will treat this field as "nonexistent". Note that expired hash fields are deleted via a periodic job. -For this reason it might take time between when fields "logically" expire and when hash fields are deleted. -During this time, "logically" expired fields still consume memory and some some hash commands (e.g. [`HLEN`](hlen.md)) might still take "logically" expired fields into account in hash object cardinality calculations. -Another side effect of using "volatile" fields in hash objects, is the ability to random choose from hash objects. When large hash objects have most of their volatile fields "logically" expired, some commands like [`HRANDFIELD`](hrandfield.md) might not be able to collect elements which are not "logically" expired and may return an empty reply. +For this reason it might take time between when fields logically expire and when hash fields are deleted. +During this time, logically expired fields still consume memory and some some hash commands (e.g. [`HLEN`](../commands/hlen.md)) might still take logically expired fields into account in hash object cardinality calculations. +Another side effect of using volatile fields (fields with time to live) in hash objects, is the ability to randomly choose from hash objects. When large hash objects have most of their volatile fields logically expired, some commands like [`HRANDFIELD`](../commands/hrandfield.md) might not be able to collect elements which are not logically expired and may return an empty reply. ### Command API -[`HEXPIRE`](hexpire.md), [`HPEXPIRE`](hpexpire.md), [`HEXPIREAT`](hexpireat.md), [`HPEXPIREAT`](hpexpireat.md) and [`HPERSIST`](hpersist.md) commands are used in order to set or manipulate the expiration time of specific hash fields. +[`HEXPIRE`](../commands/hexpire.md), [`HPEXPIRE`](../commands/hpexpire.md), [`HEXPIREAT`](../commands/hexpireat.md), [`HPEXPIREAT`](../commands/hpexpireat.md) and [`HPERSIST`](../commands/hpersist.md) commands are used in order to set or manipulate the expiration time of specific hash fields. -[`HEXPIRETIME`](hexpiretime.md), [`HEXPIRETIME`](hpexpiretime.md), [`HTTL`](httl.md) and [`HPTTL]`(hpttl.md) commands are used in order to query the expiration time of specific hash fields. +[`HEXPIRETIME`](../commands/hexpiretime.md), [`HEXPIRETIME`](../commands/hpexpiretime.md), [`HTTL`](../commands/httl.md) and [`HPTTL]`(../commands/hpttl.md) commands are used in order to query the expiration time of specific hash fields. -[`HSETEX`](hsetex.md) allows setting multiple hash fields and values while also associate an expiration time with each field. +[`HSETEX`](../commands/hsetex.md) allows setting multiple hash fields and values while also associate an expiration time with each field. -[`HGETEX`](hgetex.md) allows fetching multiple hash fields values while also mutating their expiration time. +[`HGETEX`](../commands/hgetex.md) allows fetching multiple hash fields values while also mutating their expiration time. Note that some commands which override hash fields (e.g. `HSET` and `HMSET`) will remove an expiration time associated with a hash field. -The [`HSETEX`](hsetex.md) supports the `KEEPTTL` flag, which allows overriding hash fields without mutating their expiration time. +The [`HSETEX`](../commands/hsetex.md) supports the `KEEPTTL` flag, which allows overriding hash fields without mutating their expiration time. ## Performance