Skip to content

Commit 2c3b3e0

Browse files
authored
Combine fixups from 8.4 changes (#2979)
* limit [SER002] to the digest-related features, to prevent warnings on overload resolution * use `Expiration` (from MSETEX) in new `SET` API (combined with `ValueCondition`) * fix defaults to minimize build-time impact * chmod +x
1 parent 94e0090 commit 2c3b3e0

21 files changed

+87
-96
lines changed

docs/ReleaseNotes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Current package versions:
1010

1111
- Support Redis 8.4 CAS/CAD operations (`DIGEST`, and the `IFEQ`, `IFNE`, `IFDEQ`, `IFDNE` modifiers on `SET` / `DEL`)
1212
via the new `ValueCondition` abstraction, and use CAS/CAD operations for `Lock*` APIs when possible ([#2978 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2978))
13+
- **note**: overload resolution for `StringSet[Async]` may be impacted in niche cases, requiring trivial build changes (there are no runtime-breaking changes such as missing methods)
1314
- Support `XREADGROUP CLAIM` ([#2972 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2972))
1415
- Support `MSETEX` (Redis 8.4.0) for multi-key operations with expiration ([#2977 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2977))
1516

src/StackExchange.Redis/Expiration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ private static void ThrowMode(ExpirationMode mode) =>
237237
/// <inheritdoc/>
238238
public override bool Equals(object? obj) => obj is Expiration other && _valueAndMode == other._valueAndMode;
239239

240-
internal int Tokens => Mode switch
240+
internal int TokenCount => Mode switch
241241
{
242242
ExpirationMode.Default or ExpirationMode.NotUsed => 0,
243243
ExpirationMode.KeepTtl or ExpirationMode.Persist => 1,

src/StackExchange.Redis/Interfaces/IDatabase.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3182,7 +3182,6 @@ IEnumerable<SortedSetEntry> SortedSetScan(
31823182
/// <param name="when">The condition to enforce.</param>
31833183
/// <param name="flags">The flags to use for this operation.</param>
31843184
/// <remarks>See <seealso href="https://redis.io/commands/delex"/>.</remarks>
3185-
[Experimental(Experiments.Server_8_4, UrlFormat = Experiments.UrlFormat)]
31863185
bool StringDelete(RedisKey key, ValueCondition when, CommandFlags flags = CommandFlags.None);
31873186

31883187
/// <summary>
@@ -3411,7 +3410,7 @@ IEnumerable<SortedSetEntry> SortedSetScan(
34113410
/// <param name="flags">The flags to use for this operation.</param>
34123411
/// <returns><see langword="true"/> if the string was set, <see langword="false"/> otherwise.</returns>
34133412
/// <remarks><seealso href="https://redis.io/commands/set"/></remarks>
3414-
bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None);
3413+
bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, bool keepTtl, When when = When.Always, CommandFlags flags = CommandFlags.None);
34153414

34163415
/// <summary>
34173416
/// Set <paramref name="key"/> to hold the string <paramref name="value"/>, if it matches the given <paramref name="when"/> condition.
@@ -3422,9 +3421,8 @@ IEnumerable<SortedSetEntry> SortedSetScan(
34223421
/// <param name="when">The condition to enforce.</param>
34233422
/// <param name="flags">The flags to use for this operation.</param>
34243423
/// <remarks>See <seealso href="https://redis.io/commands/delex"/>.</remarks>
3425-
[Experimental(Experiments.Server_8_4, UrlFormat = Experiments.UrlFormat)]
3426-
#pragma warning disable RS0027
3427-
bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, ValueCondition when, CommandFlags flags = CommandFlags.None);
3424+
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads
3425+
bool StringSet(RedisKey key, RedisValue value, Expiration expiry = default, ValueCondition when = default, CommandFlags flags = CommandFlags.None);
34283426
#pragma warning restore RS0027
34293427

34303428
/// <summary>

src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,6 @@ IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(
773773
Task<long> StringDecrementAsync(RedisKey key, long value = 1, CommandFlags flags = CommandFlags.None);
774774

775775
/// <inheritdoc cref="IDatabase.StringDelete(RedisKey, ValueCondition, CommandFlags)"/>
776-
[Experimental(Experiments.Server_8_4, UrlFormat = Experiments.UrlFormat)]
777776
Task<bool> StringDeleteAsync(RedisKey key, ValueCondition when, CommandFlags flags = CommandFlags.None);
778777

779778
/// <inheritdoc cref="IDatabase.StringDecrement(RedisKey, double, CommandFlags)"/>
@@ -840,12 +839,11 @@ IAsyncEnumerable<SortedSetEntry> SortedSetScanAsync(
840839
Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, When when, CommandFlags flags);
841840

842841
/// <inheritdoc cref="IDatabase.StringSet(RedisKey, RedisValue, TimeSpan?, bool, When, CommandFlags)"/>
843-
Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry = null, bool keepTtl = false, When when = When.Always, CommandFlags flags = CommandFlags.None);
842+
Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, bool keepTtl, When when = When.Always, CommandFlags flags = CommandFlags.None);
844843

845-
/// <inheritdoc cref="IDatabase.StringSet(RedisKey, RedisValue, TimeSpan?, ValueCondition, CommandFlags)"/>
846-
[Experimental(Experiments.Server_8_4, UrlFormat = Experiments.UrlFormat)]
844+
/// <inheritdoc cref="IDatabase.StringSet(RedisKey, RedisValue, Expiration, ValueCondition, CommandFlags)"/>
847845
#pragma warning disable RS0027 // Public API with optional parameter(s) should have the most parameters amongst its public overloads
848-
Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, ValueCondition when, CommandFlags flags = CommandFlags.None);
846+
Task<bool> StringSetAsync(RedisKey key, RedisValue value, Expiration expiry = default, ValueCondition when = default, CommandFlags flags = CommandFlags.None);
849847
#pragma warning restore RS0027
850848

851849
/// <inheritdoc cref="IDatabase.StringSet(KeyValuePair{RedisKey, RedisValue}[], When, CommandFlags)"/>

src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ public Task<long> StringIncrementAsync(RedisKey key, long value = 1, CommandFlag
783783
public Task<long> StringLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
784784
Inner.StringLengthAsync(ToInner(key), flags);
785785

786-
public Task<bool> StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
786+
public Task<bool> StringSetAsync(RedisKey key, RedisValue value, Expiration expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
787787
=> Inner.StringSetAsync(ToInner(key), value, expiry, when, flags);
788788

789789
public Task<bool> StringSetAsync(KeyValuePair<RedisKey, RedisValue>[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) =>

src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ public long StringIncrement(RedisKey key, long value = 1, CommandFlags flags = C
765765
public long StringLength(RedisKey key, CommandFlags flags = CommandFlags.None) =>
766766
Inner.StringLength(ToInner(key), flags);
767767

768-
public bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
768+
public bool StringSet(RedisKey key, RedisValue value, Expiration expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
769769
=> Inner.StringSet(ToInner(key), value, expiry, when, flags);
770770

771771
public bool StringSet(KeyValuePair<RedisKey, RedisValue>[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) =>

src/StackExchange.Redis/Message.ValueCondition.cs

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ internal partial class Message
77
public static Message Create(int db, CommandFlags flags, RedisCommand command, in RedisKey key, in ValueCondition when)
88
=> new KeyConditionMessage(db, flags, command, key, when);
99

10-
public static Message Create(int db, CommandFlags flags, RedisCommand command, in RedisKey key, in RedisValue value, TimeSpan? expiry, in ValueCondition when)
10+
public static Message Create(int db, CommandFlags flags, RedisCommand command, in RedisKey key, in RedisValue value, Expiration expiry, in ValueCondition when)
1111
=> new KeyValueExpiryConditionMessage(db, flags, command, key, value, expiry, when);
1212

1313
private sealed class KeyConditionMessage(
@@ -36,35 +36,22 @@ private sealed class KeyValueExpiryConditionMessage(
3636
RedisCommand command,
3737
in RedisKey key,
3838
in RedisValue value,
39-
TimeSpan? expiry,
39+
Expiration expiry,
4040
in ValueCondition when)
4141
: CommandKeyBase(db, flags, command, key)
4242
{
4343
private readonly RedisValue _value = value;
4444
private readonly ValueCondition _when = when;
45-
private readonly TimeSpan? _expiry = expiry == TimeSpan.MaxValue ? null : expiry;
45+
private readonly Expiration _expiry = expiry;
4646

47-
public override int ArgCount => 2 + _when.TokenCount + (_expiry is null ? 0 : 2);
47+
public override int ArgCount => 2 + _expiry.TokenCount + _when.TokenCount;
4848

4949
protected override void WriteImpl(PhysicalConnection physical)
5050
{
5151
physical.WriteHeader(Command, ArgCount);
5252
physical.Write(Key);
5353
physical.WriteBulkString(_value);
54-
if (_expiry.HasValue)
55-
{
56-
var ms = (long)_expiry.GetValueOrDefault().TotalMilliseconds;
57-
if ((ms % 1000) == 0)
58-
{
59-
physical.WriteBulkString("EX"u8);
60-
physical.WriteBulkString(ms / 1000);
61-
}
62-
else
63-
{
64-
physical.WriteBulkString("PX"u8);
65-
physical.WriteBulkString(ms);
66-
}
67-
}
54+
_expiry.WriteTo(physical);
6855
_when.WriteTo(physical);
6956
}
7057
}

src/StackExchange.Redis/Message.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1711,7 +1711,7 @@ public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
17111711
// - MSETNX {key1} {value1} [{key2} {value2}...]
17121712
// - MSETEX {count} {key1} {value1} [{key2} {value2}...] [standard-expiry-tokens]
17131713
public override int ArgCount => Command == RedisCommand.MSETEX
1714-
? (1 + (2 * values.Length) + expiry.Tokens + (when is When.Exists or When.NotExists ? 1 : 0))
1714+
? (1 + (2 * values.Length) + expiry.TokenCount + (when is When.Exists or When.NotExists ? 1 : 0))
17151715
: (2 * values.Length); // MSET/MSETNX only support simple syntax
17161716

17171717
protected override void WriteImpl(PhysicalConnection physical)

src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,6 @@ StackExchange.Redis.IDatabase.StringLength(StackExchange.Redis.RedisKey key, Sta
776776
StackExchange.Redis.IDatabase.StringLongestCommonSubsequence(StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> string?
777777
StackExchange.Redis.IDatabase.StringLongestCommonSubsequenceLength(StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
778778
StackExchange.Redis.IDatabase.StringLongestCommonSubsequenceWithMatches(StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, long minLength = 0, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.LCSMatchResult
779-
StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry = null, bool keepTtl = false, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
780779
StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when) -> bool
781780
StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> bool
782781
StackExchange.Redis.IDatabase.StringSet(System.Collections.Generic.KeyValuePair<StackExchange.Redis.RedisKey, StackExchange.Redis.RedisValue>[]! values, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> bool
@@ -1023,7 +1022,6 @@ StackExchange.Redis.IDatabaseAsync.StringLongestCommonSubsequenceLengthAsync(Sta
10231022
StackExchange.Redis.IDatabaseAsync.StringLongestCommonSubsequenceWithMatchesAsync(StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second, long minLength = 0, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.LCSMatchResult>!
10241023
StackExchange.Redis.IDatabaseAsync.StringSetAndGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry = null, bool keepTtl = false, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue>!
10251024
StackExchange.Redis.IDatabaseAsync.StringSetAndGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task<StackExchange.Redis.RedisValue>!
1026-
StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry = null, bool keepTtl = false, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
10271025
StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when) -> System.Threading.Tasks.Task<bool>!
10281026
StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task<bool>!
10291027
StackExchange.Redis.IDatabaseAsync.StringSetAsync(System.Collections.Generic.KeyValuePair<StackExchange.Redis.RedisKey, StackExchange.Redis.RedisValue>[]! values, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task<bool>!
@@ -2073,3 +2071,32 @@ static StackExchange.Redis.Expiration.KeepTtl.get -> StackExchange.Redis.Expirat
20732071
static StackExchange.Redis.Expiration.Persist.get -> StackExchange.Redis.Expiration
20742072
static StackExchange.Redis.Expiration.implicit operator StackExchange.Redis.Expiration(System.DateTime when) -> StackExchange.Redis.Expiration
20752073
static StackExchange.Redis.Expiration.implicit operator StackExchange.Redis.Expiration(System.TimeSpan ttl) -> StackExchange.Redis.Expiration
2074+
override StackExchange.Redis.ValueCondition.Equals(object? obj) -> bool
2075+
override StackExchange.Redis.ValueCondition.GetHashCode() -> int
2076+
override StackExchange.Redis.ValueCondition.ToString() -> string!
2077+
StackExchange.Redis.RedisFeatures.DeleteWithValueCheck.get -> bool
2078+
StackExchange.Redis.RedisFeatures.SetWithValueCheck.get -> bool
2079+
StackExchange.Redis.ValueCondition
2080+
StackExchange.Redis.ValueCondition.ValueCondition() -> void
2081+
static StackExchange.Redis.ValueCondition.Always.get -> StackExchange.Redis.ValueCondition
2082+
static StackExchange.Redis.ValueCondition.Exists.get -> StackExchange.Redis.ValueCondition
2083+
static StackExchange.Redis.ValueCondition.implicit operator StackExchange.Redis.ValueCondition(StackExchange.Redis.When when) -> StackExchange.Redis.ValueCondition
2084+
static StackExchange.Redis.ValueCondition.NotExists.get -> StackExchange.Redis.ValueCondition
2085+
static StackExchange.Redis.ValueCondition.operator !(in StackExchange.Redis.ValueCondition value) -> StackExchange.Redis.ValueCondition
2086+
StackExchange.Redis.IDatabase.StringDelete(StackExchange.Redis.RedisKey key, StackExchange.Redis.ValueCondition when, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
2087+
StackExchange.Redis.IDatabaseAsync.StringDeleteAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.ValueCondition when, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
2088+
StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.Expiration expiry = default(StackExchange.Redis.Expiration), StackExchange.Redis.ValueCondition when = default(StackExchange.Redis.ValueCondition), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
2089+
StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, bool keepTtl, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool
2090+
StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.Expiration expiry = default(StackExchange.Redis.Expiration), StackExchange.Redis.ValueCondition when = default(StackExchange.Redis.ValueCondition), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
2091+
StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, bool keepTtl, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<bool>!
2092+
[SER002]StackExchange.Redis.IDatabase.StringDigest(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.ValueCondition?
2093+
[SER002]StackExchange.Redis.IDatabaseAsync.StringDigestAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task<StackExchange.Redis.ValueCondition?>!
2094+
[SER002]StackExchange.Redis.ValueCondition.AsDigest() -> StackExchange.Redis.ValueCondition
2095+
[SER002]StackExchange.Redis.ValueCondition.Value.get -> StackExchange.Redis.RedisValue
2096+
[SER002]static StackExchange.Redis.ValueCondition.CalculateDigest(System.ReadOnlySpan<byte> value) -> StackExchange.Redis.ValueCondition
2097+
[SER002]static StackExchange.Redis.ValueCondition.DigestEqual(in StackExchange.Redis.RedisValue value) -> StackExchange.Redis.ValueCondition
2098+
[SER002]static StackExchange.Redis.ValueCondition.DigestNotEqual(in StackExchange.Redis.RedisValue value) -> StackExchange.Redis.ValueCondition
2099+
[SER002]static StackExchange.Redis.ValueCondition.Equal(in StackExchange.Redis.RedisValue value) -> StackExchange.Redis.ValueCondition
2100+
[SER002]static StackExchange.Redis.ValueCondition.NotEqual(in StackExchange.Redis.RedisValue value) -> StackExchange.Redis.ValueCondition
2101+
[SER002]static StackExchange.Redis.ValueCondition.ParseDigest(System.ReadOnlySpan<byte> digest) -> StackExchange.Redis.ValueCondition
2102+
[SER002]static StackExchange.Redis.ValueCondition.ParseDigest(System.ReadOnlySpan<char> digest) -> StackExchange.Redis.ValueCondition

0 commit comments

Comments
 (0)