Skip to content

Commit cf8b6fb

Browse files
atakavcimgravell
andauthored
Support for new HGETDEL, HGETEX and HSETEX commands (#2863)
* Support for HFE API commands, HGETDEL, HGETEX, HSETEX * fix missing ToInner usage in key-prefixed wrapper * simplify and fix key prefix tests * add missing key-prefix tests * CheckCommandResult: disambiguate key to prevent CI race * fix brittle ScanCancellable test * skip WithCancellation_CancelledToken_ThrowsOperationCanceledException on netfx due to unpredictable impl * release notes and version bump --------- Co-authored-by: Marc Gravell <marc.gravell@gmail.com>
1 parent bf972cc commit cf8b6fb

20 files changed

+1984
-119
lines changed

docs/ReleaseNotes.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ Current package versions:
66
| ------------ | ----------------- | ----- |
77
| [![StackExchange.Redis](https://img.shields.io/nuget/v/StackExchange.Redis.svg)](https://www.nuget.org/packages/StackExchange.Redis/) | [![StackExchange.Redis](https://img.shields.io/nuget/vpre/StackExchange.Redis.svg)](https://www.nuget.org/packages/StackExchange.Redis/) | [![StackExchange.Redis MyGet](https://img.shields.io/myget/stackoverflow/vpre/StackExchange.Redis.svg)](https://www.myget.org/feed/stackoverflow/package/nuget/StackExchange.Redis) |
88

9-
## Unreleased
9+
## Unreleased (2.9.xxx)
1010

11-
- nothing yet
11+
- Add `HGETDEL`, `HGETEX` and `HSETEX` support ([#2863 by atakavci](https://github.com/StackExchange/StackExchange.Redis/pull/2863))
12+
- Fix key-prefix omission in `SetIntersectionLength` and `SortedSet{Combine[WithScores]|IntersectionLength}` ([#2863 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2863))
1213

1314
## 2.8.58
1415

src/StackExchange.Redis/Enums/RedisCommand.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ internal enum RedisCommand
7070
HEXPIREAT,
7171
HEXPIRETIME,
7272
HGET,
73+
HGETEX,
74+
HGETDEL,
7375
HGETALL,
7476
HINCRBY,
7577
HINCRBYFLOAT,
@@ -85,6 +87,7 @@ internal enum RedisCommand
8587
HRANDFIELD,
8688
HSCAN,
8789
HSET,
90+
HSETEX,
8891
HSETNX,
8992
HSTRLEN,
9093
HVALS,
@@ -294,13 +297,16 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
294297
case RedisCommand.HDEL:
295298
case RedisCommand.HEXPIRE:
296299
case RedisCommand.HEXPIREAT:
300+
case RedisCommand.HGETDEL:
301+
case RedisCommand.HGETEX:
297302
case RedisCommand.HINCRBY:
298303
case RedisCommand.HINCRBYFLOAT:
299304
case RedisCommand.HMSET:
300305
case RedisCommand.HPERSIST:
301306
case RedisCommand.HPEXPIRE:
302307
case RedisCommand.HPEXPIREAT:
303308
case RedisCommand.HSET:
309+
case RedisCommand.HSETEX:
304310
case RedisCommand.HSETNX:
305311
case RedisCommand.INCR:
306312
case RedisCommand.INCRBY:

src/StackExchange.Redis/Interfaces/IDatabase.cs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,149 @@ public interface IDatabase : IRedis, IDatabaseAsync
516516
/// <remarks><seealso href="https://redis.io/commands/hmget"/></remarks>
517517
RedisValue[] HashGet(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
518518

519+
/// <summary>
520+
/// Returns the value associated with field in the hash stored at key.
521+
/// </summary>
522+
/// <param name="key">The key of the hash.</param>
523+
/// <param name="hashField">The field in the hash to get.</param>
524+
/// <param name="flags">The flags to use for this operation.</param>
525+
/// <returns>The value associated with field, or <see cref="RedisValue.Null"/> when field is not present in the hash or key does not exist.</returns>
526+
/// <remarks><seealso href="https://redis.io/commands/hget"/></remarks>
527+
RedisValue HashFieldGetAndDelete(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
528+
529+
/// <summary>
530+
/// Returns the value associated with field in the hash stored at key.
531+
/// </summary>
532+
/// <param name="key">The key of the hash.</param>
533+
/// <param name="hashField">The field in the hash to get.</param>
534+
/// <param name="flags">The flags to use for this operation.</param>
535+
/// <returns>The value associated with field, or <see langword="null"/> when field is not present in the hash or key does not exist.</returns>
536+
/// <remarks><seealso href="https://redis.io/commands/hget"/></remarks>
537+
Lease<byte>? HashFieldGetLeaseAndDelete(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
538+
539+
/// <summary>
540+
/// Returns the values associated with the specified fields in the hash stored at key.
541+
/// For every field that does not exist in the hash, a <see langword="RedisValue.Null"/> value is returned.
542+
/// Because non-existing keys are treated as empty hashes, running HMGET against a non-existing key will return a list of <see langword="RedisValue.Null"/> values.
543+
/// </summary>
544+
/// <param name="key">The key of the hash.</param>
545+
/// <param name="hashFields">The fields in the hash to get.</param>
546+
/// <param name="flags">The flags to use for this operation.</param>
547+
/// <returns>List of values associated with the given fields, in the same order as they are requested.</returns>
548+
/// <remarks><seealso href="https://redis.io/commands/hmget"/></remarks>
549+
RedisValue[] HashFieldGetAndDelete(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
550+
551+
/// <summary>
552+
/// Gets the value of the specified hash field and sets its expiration time.
553+
/// </summary>
554+
/// <param name="key">The key of the hash.</param>
555+
/// <param name="hashField">The field in the hash to get and set the expiration for.</param>
556+
/// <param name="expiry">The expiration time to set.</param>
557+
/// <param name="persist">If true, the expiration will be removed. And 'expiry' parameter is ignored.</param>
558+
/// <param name="flags">The flags to use for this operation.</param>
559+
/// <returns>The value of the specified hash field.</returns>
560+
RedisValue HashFieldGetAndSetExpiry(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None);
561+
562+
/// <summary>
563+
/// Gets the value of the specified hash field and sets its expiration time.
564+
/// </summary>
565+
/// <param name="key">The key of the hash.</param>
566+
/// <param name="hashField">The field in the hash to get and set the expiration for.</param>
567+
/// <param name="expiry">The exact date and time to set the expiration to.</param>
568+
/// <param name="flags">The flags to use for this operation.</param>
569+
/// <returns>The value of the specified hash field.</returns>
570+
RedisValue HashFieldGetAndSetExpiry(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None);
571+
572+
/// <summary>
573+
/// Gets the value of the specified hash field and sets its expiration time, returning a lease.
574+
/// </summary>
575+
/// <param name="key">The key of the hash.</param>
576+
/// <param name="hashField">The field in the hash to get and set the expiration for.</param>
577+
/// <param name="expiry">The expiration time to set.</param>
578+
/// <param name="persist">If true, the expiration will be removed. And 'expiry' parameter is ignored.</param>
579+
/// <param name="flags">The flags to use for this operation.</param>
580+
/// <returns>The value of the specified hash field as a lease.</returns>
581+
Lease<byte>? HashFieldGetLeaseAndSetExpiry(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None);
582+
583+
/// <summary>
584+
/// Gets the value of the specified hash field and sets its expiration time, returning a lease.
585+
/// </summary>
586+
/// <param name="key">The key of the hash.</param>
587+
/// <param name="hashField">The field in the hash to get and set the expiration for.</param>
588+
/// <param name="expiry">The exact date and time to set the expiration to.</param>
589+
/// <param name="flags">The flags to use for this operation.</param>
590+
/// <returns>The value of the specified hash field as a lease.</returns>
591+
Lease<byte>? HashFieldGetLeaseAndSetExpiry(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None);
592+
593+
/// <summary>
594+
/// Gets the values of the specified hash fields and sets their expiration times.
595+
/// </summary>
596+
/// <param name="key">The key of the hash.</param>
597+
/// <param name="hashFields">The fields in the hash to get and set the expiration for.</param>
598+
/// <param name="expiry">The expiration time to set.</param>
599+
/// <param name="persist">If true, the expiration will be removed. And 'expiry' parameter is ignored.</param>
600+
/// <param name="flags">The flags to use for this operation.</param>
601+
/// <returns>The values of the specified hash fields.</returns>
602+
RedisValue[] HashFieldGetAndSetExpiry(RedisKey key, RedisValue[] hashFields, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None);
603+
604+
/// <summary>
605+
/// Gets the values of the specified hash fields and sets their expiration times.
606+
/// </summary>
607+
/// <param name="key">The key of the hash.</param>
608+
/// <param name="hashFields">The fields in the hash to get and set the expiration for.</param>
609+
/// <param name="expiry">The exact date and time to set the expiration to.</param>
610+
/// <param name="flags">The flags to use for this operation.</param>
611+
/// <returns>The values of the specified hash fields.</returns>
612+
RedisValue[] HashFieldGetAndSetExpiry(RedisKey key, RedisValue[] hashFields, DateTime expiry, CommandFlags flags = CommandFlags.None);
613+
614+
/// <summary>
615+
/// Sets the value of the specified hash field and sets its expiration time.
616+
/// </summary>
617+
/// <param name="key">The key of the hash.</param>
618+
/// <param name="field">The field in the hash to set and set the expiration for.</param>
619+
/// <param name="value">The value in the hash to set and set the expiration for.</param>
620+
/// <param name="expiry">The expiration time to set.</param>
621+
/// <param name="keepTtl">Whether to maintain the existing field's TTL (KEEPTTL flag).</param>
622+
/// <param name="when">Which conditions to set the value under (defaults to always).</param>
623+
/// <param name="flags">The flags to use for this operation.</param>
624+
/// <returns>0 if no fields were set, 1 if all the fields were set.</returns>
625+
RedisValue HashFieldSetAndSetExpiry(RedisKey key, RedisValue field, RedisValue value, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None);
626+
627+
/// <summary>
628+
/// Sets the value of the specified hash field and sets its expiration time.
629+
/// </summary>
630+
/// <param name="key">The key of the hash.</param>
631+
/// <param name="field">The field in the hash to set and set the expiration for.</param>
632+
/// <param name="value">The value in the hash to set and set the expiration for.</param>
633+
/// <param name="expiry">The exact date and time to set the expiration to.</param>
634+
/// <param name="when">Which conditions to set the value under (defaults to always).</param>
635+
/// <param name="flags">The flags to use for this operation.</param>
636+
/// <returns>0 if no fields were set, 1 if all the fields were set.</returns>
637+
RedisValue HashFieldSetAndSetExpiry(RedisKey key, RedisValue field, RedisValue value, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None);
638+
639+
/// <summary>
640+
/// Sets the values of the specified hash fields and sets their expiration times.
641+
/// </summary>
642+
/// <param name="key">The key of the hash.</param>
643+
/// <param name="hashFields">The fields in the hash to set and set the expiration for.</param>
644+
/// <param name="expiry">The expiration time to set.</param>
645+
/// <param name="keepTtl">Whether to maintain the existing fields' TTL (KEEPTTL flag).</param>
646+
/// <param name="when">Which conditions to set the values under (defaults to always).</param>
647+
/// <param name="flags">The flags to use for this operation.</param>
648+
/// <returns>0 if no fields were set, 1 if all the fields were set.</returns>
649+
RedisValue HashFieldSetAndSetExpiry(RedisKey key, HashEntry[] hashFields, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None);
650+
651+
/// <summary>
652+
/// Sets the values of the specified hash fields and sets their expiration times.
653+
/// </summary>
654+
/// <param name="key">The key of the hash.</param>
655+
/// <param name="hashFields">The fields in the hash to set and set the expiration for.</param>
656+
/// <param name="expiry">The exact date and time to set the expiration to.</param>
657+
/// <param name="when">Which conditions to set the values under (defaults to always).</param>
658+
/// <param name="flags">The flags to use for this operation.</param>
659+
/// <returns>0 if no fields were set, 1 if all the fields were set.</returns>
660+
RedisValue HashFieldSetAndSetExpiry(RedisKey key, HashEntry[] hashFields, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None);
661+
519662
/// <summary>
520663
/// Returns all fields and values of the hash stored at key.
521664
/// </summary>

src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,45 @@ public interface IDatabaseAsync : IRedisAsync
8484
/// <inheritdoc cref="IDatabase.HashExists(RedisKey, RedisValue, CommandFlags)"/>
8585
Task<bool> HashExistsAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
8686

87+
/// <inheritdoc cref="IDatabase.HashFieldGetAndDelete(RedisKey, RedisValue, CommandFlags)"/>
88+
Task<RedisValue> HashFieldGetAndDeleteAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
89+
90+
/// <inheritdoc cref="IDatabase.HashFieldGetLeaseAndDelete(RedisKey, RedisValue, CommandFlags)"/>
91+
Task<Lease<byte>?> HashFieldGetLeaseAndDeleteAsync(RedisKey key, RedisValue hashField, CommandFlags flags = CommandFlags.None);
92+
93+
/// <inheritdoc cref="IDatabase.HashFieldGetAndDelete(RedisKey, RedisValue[], CommandFlags)"/>
94+
Task<RedisValue[]> HashFieldGetAndDeleteAsync(RedisKey key, RedisValue[] hashFields, CommandFlags flags = CommandFlags.None);
95+
96+
/// <inheritdoc cref="IDatabase.HashFieldGetAndSetExpiry(RedisKey, RedisValue, TimeSpan?, bool, CommandFlags)"/>
97+
Task<RedisValue> HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None);
98+
99+
/// <inheritdoc cref="IDatabase.HashFieldGetAndSetExpiry(RedisKey, RedisValue, DateTime, CommandFlags)"/>
100+
Task<RedisValue> HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None);
101+
102+
/// <inheritdoc cref="IDatabase.HashFieldGetLeaseAndSetExpiry(RedisKey, RedisValue, TimeSpan?, bool, CommandFlags)"/>
103+
Task<Lease<byte>?> HashFieldGetLeaseAndSetExpiryAsync(RedisKey key, RedisValue hashField, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None);
104+
105+
/// <inheritdoc cref="IDatabase.HashFieldGetLeaseAndSetExpiry(RedisKey, RedisValue, DateTime, CommandFlags)"/>
106+
Task<Lease<byte>?> HashFieldGetLeaseAndSetExpiryAsync(RedisKey key, RedisValue hashField, DateTime expiry, CommandFlags flags = CommandFlags.None);
107+
108+
/// <inheritdoc cref="IDatabase.HashFieldGetAndSetExpiry(RedisKey, RedisValue[], TimeSpan?, bool, CommandFlags)"/>
109+
Task<RedisValue[]> HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue[] hashFields, TimeSpan? expiry = null, bool persist = false, CommandFlags flags = CommandFlags.None);
110+
111+
/// <inheritdoc cref="IDatabase.HashFieldGetAndSetExpiry(RedisKey, RedisValue[], DateTime, CommandFlags)"/>
112+
Task<RedisValue[]> HashFieldGetAndSetExpiryAsync(RedisKey key, RedisValue[] hashFields, DateTime expiry, CommandFlags flags = CommandFlags.None);
113+
114+
/// <inheritdoc cref="IDatabase.HashFieldSetAndSetExpiry(RedisKey, RedisValue, RedisValue, TimeSpan?, bool, When, CommandFlags)"/>
115+
Task<RedisValue> HashFieldSetAndSetExpiryAsync(RedisKey key, RedisValue field, RedisValue value, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None);
116+
117+
/// <inheritdoc cref="IDatabase.HashFieldSetAndSetExpiry(RedisKey, RedisValue, RedisValue, DateTime, When, CommandFlags)"/>
118+
Task<RedisValue> HashFieldSetAndSetExpiryAsync(RedisKey key, RedisValue field, RedisValue value, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None);
119+
120+
/// <inheritdoc cref="IDatabase.HashFieldSetAndSetExpiry(RedisKey, HashEntry[], TimeSpan?, bool, When, CommandFlags)"/>
121+
Task<RedisValue> HashFieldSetAndSetExpiryAsync(RedisKey key, HashEntry[] hashFields, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None);
122+
123+
/// <inheritdoc cref="IDatabase.HashFieldSetAndSetExpiry(RedisKey, HashEntry[], DateTime, When, CommandFlags)"/>
124+
Task<RedisValue> HashFieldSetAndSetExpiryAsync(RedisKey key, HashEntry[] hashFields, DateTime expiry, When when = When.Always, CommandFlags flags = CommandFlags.None);
125+
87126
/// <inheritdoc cref="IDatabase.HashFieldExpire(RedisKey, RedisValue[], TimeSpan, ExpireWhen, CommandFlags)"/>
88127
Task<ExpireResult[]> HashFieldExpireAsync(RedisKey key, RedisValue[] hashFields, TimeSpan expiry, ExpireWhen when = ExpireWhen.Always, CommandFlags flags = CommandFlags.None);
89128

0 commit comments

Comments
 (0)