Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions StackExchange.Redis.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<Solution>
<Folder Name="/eng/">
<Project Path="eng/StackExchange.Redis.Build/StackExchange.Redis.Build.csproj" />
</Folder>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path=".github/workflows/CI.yml" />
<File Path=".github/workflows/codeql.yml" />
<File Path="appveyor.yml" />
<File Path="build.cmd" />
<File Path="Build.csproj" />
<File Path="build.ps1" />
<File Path="Directory.Build.props" />
<File Path="Directory.Build.targets" />
<File Path="Directory.Packages.props" />
<File Path="docs/ReleaseNotes.md" />
<File Path="global.json" />
<File Path="NuGet.Config" />
<File Path="README.md" />
<File Path="Shared.ruleset" />
<File Path="tests/RedisConfigs/.docker/Redis/Dockerfile" />
<File Path="tests/RedisConfigs/docker-compose.yml" />
<File Path="version.json" />
</Folder>
<Folder Name="/src/">
<File Path="src/Directory.Build.props" />
<Project Path="src/RESPite/RESPite.csproj" />
<Project Path="src/StackExchange.Redis/StackExchange.Redis.csproj" />
</Folder>
<Folder Name="/tests/">
<File Path="tests/.editorconfig" />
<File Path="tests/Directory.Build.targets" />
<Project Path="tests/BasicTest/BasicTest.csproj" />
<Project Path="tests/BasicTestBaseline/BasicTestBaseline.csproj" />
<Project Path="tests/ConsoleTest/ConsoleTest.csproj" />
<Project Path="tests/ConsoleTestBaseline/ConsoleTestBaseline.csproj" />
<Project Path="tests/RESPite.Tests/RESPite.Tests.csproj" />
<Project Path="tests/StackExchange.Redis.Benchmarks/StackExchange.Redis.Benchmarks.csproj" />
<Project Path="tests/StackExchange.Redis.Tests/StackExchange.Redis.Tests.csproj" />
</Folder>
<Folder Name="/tests/RedisConfigs/">
<File Path="tests/RedisConfigs/cli-master.cmd" />
<File Path="tests/RedisConfigs/cli-secure.cmd" />
<File Path="tests/RedisConfigs/cli-slave.cmd" />
<File Path="tests/RedisConfigs/docker-compose.yml" />
<File Path="tests/RedisConfigs/Dockerfile" />
<File Path="tests/RedisConfigs/start-all.cmd" />
<File Path="tests/RedisConfigs/start-all.sh" />
<File Path="tests/RedisConfigs/start-basic.cmd" />
<File Path="tests/RedisConfigs/start-basic.sh" />
<File Path="tests/RedisConfigs/start-cluster.cmd" />
<File Path="tests/RedisConfigs/start-sentinel.cmd" />
<File Path="tests/RedisConfigs/wsl2.md" />
</Folder>
<Folder Name="/tests/RedisConfigs/Basic/">
<File Path="tests/RedisConfigs/Basic/primary-6379.conf" />
<File Path="tests/RedisConfigs/Basic/replica-6380.conf" />
<File Path="tests/RedisConfigs/Basic/secure-6381.conf" />
<File Path="tests/RedisConfigs/Basic/tls-ciphers-6384.conf" />
</Folder>
<Folder Name="/tests/RedisConfigs/Cluster/">
<File Path="tests/RedisConfigs/Cluster/cluster-7000.conf" />
<File Path="tests/RedisConfigs/Cluster/cluster-7001.conf" />
<File Path="tests/RedisConfigs/Cluster/cluster-7002.conf" />
<File Path="tests/RedisConfigs/Cluster/cluster-7003.conf" />
<File Path="tests/RedisConfigs/Cluster/cluster-7004.conf" />
<File Path="tests/RedisConfigs/Cluster/cluster-7005.conf" />
<File Path="tests/RedisConfigs/Cluster/nodes-7000.conf" />
<File Path="tests/RedisConfigs/Cluster/nodes-7001.conf" />
<File Path="tests/RedisConfigs/Cluster/nodes-7002.conf" />
<File Path="tests/RedisConfigs/Cluster/nodes-7003.conf" />
<File Path="tests/RedisConfigs/Cluster/nodes-7004.conf" />
<File Path="tests/RedisConfigs/Cluster/nodes-7005.conf" />
</Folder>
<Folder Name="/tests/RedisConfigs/Docker/">
<File Path="tests/RedisConfigs/Docker/docker-entrypoint.sh" />
<File Path="tests/RedisConfigs/Docker/supervisord.conf" />
</Folder>
<Folder Name="/tests/RedisConfigs/Failover/">
<File Path="tests/RedisConfigs/Failover/primary-6382.conf" />
<File Path="tests/RedisConfigs/Failover/replica-6383.conf" />
</Folder>
<Folder Name="/tests/RedisConfigs/Sentinel/">
<File Path="tests/RedisConfigs/Sentinel/redis-7010.conf" />
<File Path="tests/RedisConfigs/Sentinel/redis-7011.conf" />
<File Path="tests/RedisConfigs/Sentinel/sentinel-26379.conf" />
<File Path="tests/RedisConfigs/Sentinel/sentinel-26380.conf" />
<File Path="tests/RedisConfigs/Sentinel/sentinel-26381.conf" />
</Folder>
<Folder Name="/toys/">
<Project Path="toys/KestrelRedisServer/KestrelRedisServer.csproj" />
<Project Path="toys/OpBench/OpBench.csproj" />
<Project Path="toys/StackExchange.Redis.Server/StackExchange.Redis.Server.csproj" />
<Project Path="toys/TestConsole/TestConsole.csproj" />
<Project Path="toys/TestConsoleBaseline/TestConsoleBaseline.csproj" />
</Folder>
<Project Path=".github/.github.csproj" />
<Project Path="docs/docs.csproj" />
</Solution>
1 change: 1 addition & 0 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Current package versions:
- Add Redis 8.8 stream negative acknowledgements (`XNACK`) ([#3058 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3058))
- Update experimental `GCRA` APIs and wire protocol terminology from "requests" to "tokens", to match server change ([#3051 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3051))
- Add experimental `Aggregate.Count` support for sorted-set combination operations against Redis 8.8 ([#3059 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3059))
- Add `ValueCondition` overloads for `SortedSetIncrement`/`SortedSetIncrementAsync`, supporting `ZADD INCR` with existence conditions ([#3071 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3071))
- Recognize Azure Managed Redis (AMR) resources in new Azure clouds ([#3068 by @philon-msft](https://github.com/StackExchange/StackExchange.Redis/pull/3068))

## 2.12.14
Expand Down
9 changes: 0 additions & 9 deletions src/StackExchange.Redis/Enums/SortedSetWhen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,6 @@ public enum SortedSetWhen

internal static class SortedSetWhenExtensions
{
internal static uint CountBits(this SortedSetWhen when)
{
uint v = (uint)when;
v -= (v >> 1) & 0x55555555; // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
uint c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
return c;
}

internal static SortedSetWhen Parse(When when) => when switch
{
When.Always => SortedSetWhen.Always,
Expand Down
18 changes: 18 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2101,7 +2101,25 @@ public partial interface IDatabase : IRedis, IDatabaseAsync
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The new score of member.</returns>
/// <remarks><seealso href="https://redis.io/commands/zincrby"/></remarks>
#pragma warning disable RS0027 // conditional overload needs an additional required ValueCondition parameter
double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None);
#pragma warning restore RS0027

/// <summary>
/// Increments the score of member in the sorted set stored at key by increment, when the specified condition is met.
/// If member does not exist in the sorted set and the condition permits it, it is added with increment as its score (as if its previous score was 0.0).
/// </summary>
/// <param name="key">The key of the sorted set.</param>
/// <param name="member">The member to increment.</param>
/// <param name="value">The amount to increment by.</param>
/// <param name="when">The condition to increment the element under; only existence conditions are currently supported.</param>
/// <param name="flags">The flags to use for this operation.</param>
/// <returns>The new score of member, or <see langword="null"/> when the condition was not met.</returns>
/// <remarks>
/// <para>Uses <c>ZINCRBY</c> when <paramref name="when"/> is <see cref="ValueCondition.Always"/>, and <c>ZADD INCR</c> for <see cref="ValueCondition.Exists"/> and <see cref="ValueCondition.NotExists"/>.</para>
/// <para><seealso href="https://redis.io/commands/zadd"/></para>
/// </remarks>
double? SortedSetIncrement(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags);

/// <summary>
/// Returns the cardinality of the intersection of the sorted sets at <paramref name="keys"/>.
Expand Down
5 changes: 5 additions & 0 deletions src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,12 @@ public partial interface IDatabaseAsync : IRedisAsync
Task<double> SortedSetDecrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None);

/// <inheritdoc cref="IDatabase.SortedSetIncrement(RedisKey, RedisValue, double, CommandFlags)"/>
#pragma warning disable RS0027 // conditional overload needs an additional required ValueCondition parameter
Task<double> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None);
#pragma warning restore RS0027

/// <inheritdoc cref="IDatabase.SortedSetIncrement(RedisKey, RedisValue, double, ValueCondition, CommandFlags)"/>
Task<double?> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags);

/// <inheritdoc cref="IDatabase.SortedSetIntersectionLength(RedisKey[], long, CommandFlags)"/>
Task<long> SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None);
Expand Down
3 changes: 3 additions & 0 deletions src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@ public Task<double> SortedSetDecrementAsync(RedisKey key, RedisValue member, dou
public Task<double> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIncrementAsync(ToInner(key), member, value, flags);

public Task<double?> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags) =>
Inner.SortedSetIncrementAsync(ToInner(key), member, value, when, flags);

public Task<long> SortedSetIntersectionLengthAsync(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIntersectionLengthAsync(ToInner(keys), limit, flags);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@ public double SortedSetDecrement(RedisKey key, RedisValue member, double value,
public double SortedSetIncrement(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIncrement(ToInner(key), member, value, flags);

public double? SortedSetIncrement(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags) =>
Inner.SortedSetIncrement(ToInner(key), member, value, when, flags);

public long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None) =>
Inner.SortedSetIntersectionLength(ToInner(keys), limit, flags);

Expand Down
2 changes: 2 additions & 0 deletions src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
StackExchange.Redis.IDatabase.SortedSetIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double value, StackExchange.Redis.ValueCondition when, StackExchange.Redis.CommandFlags flags) -> double?
StackExchange.Redis.IDatabaseAsync.SortedSetIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue member, double value, StackExchange.Redis.ValueCondition when, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task<double?>!
84 changes: 29 additions & 55 deletions src/StackExchange.Redis/RedisDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2435,12 +2435,24 @@ public double SortedSetIncrement(RedisKey key, RedisValue member, double value,
return ExecuteSync(msg, ResultProcessor.Double);
}

public double? SortedSetIncrement(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags)
{
var msg = GetSortedSetIncrementMessage(key, member, value, when, flags);
return ExecuteSync(msg, ResultProcessor.NullableDouble);
}

public Task<double> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, CommandFlags flags = CommandFlags.None)
{
var msg = Message.Create(Database, flags, RedisCommand.ZINCRBY, key, value, member);
return ExecuteAsync(msg, ResultProcessor.Double);
}

public Task<double?> SortedSetIncrementAsync(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags)
{
var msg = GetSortedSetIncrementMessage(key, member, value, when, flags);
return ExecuteAsync(msg, ResultProcessor.NullableDouble);
}

public long SortedSetIntersectionLength(RedisKey[] keys, long limit = 0, CommandFlags flags = CommandFlags.None)
{
var msg = GetSortedSetIntersectionLengthMessage(keys, limit, flags);
Expand Down Expand Up @@ -4399,33 +4411,7 @@ private Message GetSetIntersectionLengthMessage(RedisKey[] keys, long limit = 0,
}

private Message GetSortedSetAddMessage(RedisKey key, RedisValue member, double score, SortedSetWhen when, bool change, CommandFlags flags)
{
RedisValue[] arr = new RedisValue[2 + when.CountBits() + (change ? 1 : 0)];
int index = 0;
if ((when & SortedSetWhen.NotExists) != 0)
{
arr[index++] = RedisLiterals.NX;
}
if ((when & SortedSetWhen.Exists) != 0)
{
arr[index++] = RedisLiterals.XX;
}
if ((when & SortedSetWhen.GreaterThan) != 0)
{
arr[index++] = RedisLiterals.GT;
}
if ((when & SortedSetWhen.LessThan) != 0)
{
arr[index++] = RedisLiterals.LT;
}
if (change)
{
arr[index++] = RedisLiterals.CH;
}
arr[index++] = score;
arr[index++] = member;
return Message.Create(Database, flags, RedisCommand.ZADD, key, arr);
}
=> new SingleSortedSetAddMessage(Database, flags, key, member, score, when, change, increment: false);

private Message? GetSortedSetAddMessage(RedisKey key, SortedSetEntry[] values, SortedSetWhen when, bool change, CommandFlags flags)
{
Expand All @@ -4436,35 +4422,23 @@ private Message GetSortedSetAddMessage(RedisKey key, RedisValue member, double s
case 1:
return GetSortedSetAddMessage(key, values[0].element, values[0].score, when, change, flags);
default:
RedisValue[] arr = new RedisValue[(values.Length * 2) + when.CountBits() + (change ? 1 : 0)];
int index = 0;
if ((when & SortedSetWhen.NotExists) != 0)
{
arr[index++] = RedisLiterals.NX;
}
if ((when & SortedSetWhen.Exists) != 0)
{
arr[index++] = RedisLiterals.XX;
}
if ((when & SortedSetWhen.GreaterThan) != 0)
{
arr[index++] = RedisLiterals.GT;
}
if ((when & SortedSetWhen.LessThan) != 0)
{
arr[index++] = RedisLiterals.LT;
}
if (change)
{
arr[index++] = RedisLiterals.CH;
}
return new MultipleSortedSetAddMessage(Database, flags, key, values, when, change);
}
}

for (int i = 0; i < values.Length; i++)
{
arr[index++] = values[i].score;
arr[index++] = values[i].element;
}
return Message.Create(Database, flags, RedisCommand.ZADD, key, arr);
private Message GetSortedSetIncrementMessage(RedisKey key, RedisValue member, double value, ValueCondition when, CommandFlags flags)
{
switch (when.Kind)
{
case ValueCondition.ConditionKind.Always:
return Message.Create(Database, flags, RedisCommand.ZINCRBY, key, value, member);
case ValueCondition.ConditionKind.Exists:
return new SingleSortedSetAddMessage(Database, flags, key, member, value, SortedSetWhen.Exists, change: false, increment: true);
case ValueCondition.ConditionKind.NotExists:
return new SingleSortedSetAddMessage(Database, flags, key, member, value, SortedSetWhen.NotExists, change: false, increment: true);
default:
when.ThrowInvalidOperation(nameof(SortedSetIncrement));
goto case ValueCondition.ConditionKind.Always; // not reached
}
}

Expand Down
Loading
Loading