diff --git a/StackExchange.Redis.sln b/StackExchange.Redis.sln.old
similarity index 71%
rename from StackExchange.Redis.sln
rename to StackExchange.Redis.sln.old
index 514f367bb..e87495265 100644
--- a/StackExchange.Redis.sln
+++ b/StackExchange.Redis.sln.old
@@ -24,71 +24,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\codeql.yml = .github\workflows\codeql.yml
EndProjectSection
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RedisConfigs", "RedisConfigs", "{96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}"
- ProjectSection(SolutionItems) = preProject
- tests\RedisConfigs\cli-master.cmd = tests\RedisConfigs\cli-master.cmd
- tests\RedisConfigs\cli-secure.cmd = tests\RedisConfigs\cli-secure.cmd
- tests\RedisConfigs\cli-slave.cmd = tests\RedisConfigs\cli-slave.cmd
- tests\RedisConfigs\docker-compose.yml = tests\RedisConfigs\docker-compose.yml
- tests\RedisConfigs\Dockerfile = tests\RedisConfigs\Dockerfile
- tests\RedisConfigs\start-all.cmd = tests\RedisConfigs\start-all.cmd
- tests\RedisConfigs\start-all.sh = tests\RedisConfigs\start-all.sh
- tests\RedisConfigs\start-basic.cmd = tests\RedisConfigs\start-basic.cmd
- tests\RedisConfigs\start-basic.sh = tests\RedisConfigs\start-basic.sh
- tests\RedisConfigs\start-cluster.cmd = tests\RedisConfigs\start-cluster.cmd
- tests\RedisConfigs\start-sentinel.cmd = tests\RedisConfigs\start-sentinel.cmd
- tests\RedisConfigs\wsl2.md = tests\RedisConfigs\wsl2.md
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackExchange.Redis", "src\StackExchange.Redis\StackExchange.Redis.csproj", "{EF84877F-59BE-41BE-9013-E765AF0BB72E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackExchange.Redis.Tests", "tests\StackExchange.Redis.Tests\StackExchange.Redis.Tests.csproj", "{3B8BD8F1-8BFC-4D8C-B4DA-25FFAF3D1DBE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicTest", "tests\BasicTest\BasicTest.csproj", "{939FA5F7-16AA-4847-812B-6EBC3748A86D}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sentinel", "Sentinel", "{36255A0A-89EC-43C8-A642-F4C1ACAEF5BC}"
- ProjectSection(SolutionItems) = preProject
- tests\RedisConfigs\Sentinel\redis-7010.conf = tests\RedisConfigs\Sentinel\redis-7010.conf
- tests\RedisConfigs\Sentinel\redis-7011.conf = tests\RedisConfigs\Sentinel\redis-7011.conf
- tests\RedisConfigs\Sentinel\sentinel-26379.conf = tests\RedisConfigs\Sentinel\sentinel-26379.conf
- tests\RedisConfigs\Sentinel\sentinel-26380.conf = tests\RedisConfigs\Sentinel\sentinel-26380.conf
- tests\RedisConfigs\Sentinel\sentinel-26381.conf = tests\RedisConfigs\Sentinel\sentinel-26381.conf
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cluster", "Cluster", "{A3B4B972-5BD2-4D90-981F-7E51E350E628}"
- ProjectSection(SolutionItems) = preProject
- tests\RedisConfigs\Cluster\cluster-7000.conf = tests\RedisConfigs\Cluster\cluster-7000.conf
- tests\RedisConfigs\Cluster\cluster-7001.conf = tests\RedisConfigs\Cluster\cluster-7001.conf
- tests\RedisConfigs\Cluster\cluster-7002.conf = tests\RedisConfigs\Cluster\cluster-7002.conf
- tests\RedisConfigs\Cluster\cluster-7003.conf = tests\RedisConfigs\Cluster\cluster-7003.conf
- tests\RedisConfigs\Cluster\cluster-7004.conf = tests\RedisConfigs\Cluster\cluster-7004.conf
- tests\RedisConfigs\Cluster\cluster-7005.conf = tests\RedisConfigs\Cluster\cluster-7005.conf
- tests\RedisConfigs\Cluster\nodes-7000.conf = tests\RedisConfigs\Cluster\nodes-7000.conf
- tests\RedisConfigs\Cluster\nodes-7001.conf = tests\RedisConfigs\Cluster\nodes-7001.conf
- tests\RedisConfigs\Cluster\nodes-7002.conf = tests\RedisConfigs\Cluster\nodes-7002.conf
- tests\RedisConfigs\Cluster\nodes-7003.conf = tests\RedisConfigs\Cluster\nodes-7003.conf
- tests\RedisConfigs\Cluster\nodes-7004.conf = tests\RedisConfigs\Cluster\nodes-7004.conf
- tests\RedisConfigs\Cluster\nodes-7005.conf = tests\RedisConfigs\Cluster\nodes-7005.conf
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Basic", "Basic", "{38BDEEED-7BEB-4B1F-9CE0-256D63F9C502}"
- ProjectSection(SolutionItems) = preProject
- tests\RedisConfigs\Basic\primary-6379.conf = tests\RedisConfigs\Basic\primary-6379.conf
- tests\RedisConfigs\Basic\replica-6380.conf = tests\RedisConfigs\Basic\replica-6380.conf
- tests\RedisConfigs\Basic\secure-6381.conf = tests\RedisConfigs\Basic\secure-6381.conf
- tests\RedisConfigs\Basic\tls-ciphers-6384.conf = tests\RedisConfigs\Basic\tls-ciphers-6384.conf
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BasicTestBaseline", "tests\BasicTestBaseline\BasicTestBaseline.csproj", "{8FDB623D-779B-4A84-BC6B-75106E41D8A4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsole", "toys\TestConsole\TestConsole.csproj", "{651FDB97-9DE3-4BD9-9A05-827AF8F1A94A}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Failover", "Failover", "{D082703F-1652-4C35-840D-7D377F6B9979}"
- ProjectSection(SolutionItems) = preProject
- tests\RedisConfigs\Failover\primary-6382.conf = tests\RedisConfigs\Failover\primary-6382.conf
- tests\RedisConfigs\Failover\replica-6383.conf = tests\RedisConfigs\Failover\replica-6383.conf
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StackExchange.Redis.Server", "toys\StackExchange.Redis.Server\StackExchange.Redis.Server.csproj", "{8375813E-FBAF-4DA3-A2C7-E4645B39B931}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KestrelRedisServer", "toys\KestrelRedisServer\KestrelRedisServer.csproj", "{3DA1EEED-E9FE-43D9-B293-E000CFCCD91A}"
@@ -110,12 +55,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConsoleBaseline", "toys
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = ".github", ".github\.github.csproj", "{8FB98E7D-DAE2-4465-BD9A-104000E0A2D4}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docker", "Docker", "{A9F81DA3-DA82-423E-A5DD-B11C37548E06}"
- ProjectSection(SolutionItems) = preProject
- tests\RedisConfigs\Docker\docker-entrypoint.sh = tests\RedisConfigs\Docker\docker-entrypoint.sh
- tests\RedisConfigs\Docker\supervisord.conf = tests\RedisConfigs\Docker\supervisord.conf
- EndProjectSection
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTest", "tests\ConsoleTest\ConsoleTest.csproj", "{A0F89B8B-32A3-4C28-8F1B-ADE343F16137}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleTestBaseline", "tests\ConsoleTestBaseline\ConsoleTestBaseline.csproj", "{69A0ACF2-DF1F-4F49-B554-F732DCA938A3}"
@@ -134,6 +73,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RESPite.Tests", "tests\RESP
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpBench", "toys\OpBench\OpBench.csproj", "{43D3CD21-1E7E-4D28-B1BA-B6DAB3E19183}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedisConfigs", "tests\RedisConfigs\RedisConfigs.csproj", "{CAE96C6D-145B-489B-9DC8-5CFBA4CC5076}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "docker", "tests\RedisConfigs\.docker\docker.csproj", "{8ECDE6D4-37B0-4A54-8E21-B0BB3666378D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -208,25 +151,27 @@ Global
{43D3CD21-1E7E-4D28-B1BA-B6DAB3E19183}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43D3CD21-1E7E-4D28-B1BA-B6DAB3E19183}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43D3CD21-1E7E-4D28-B1BA-B6DAB3E19183}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CAE96C6D-145B-489B-9DC8-5CFBA4CC5076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CAE96C6D-145B-489B-9DC8-5CFBA4CC5076}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CAE96C6D-145B-489B-9DC8-5CFBA4CC5076}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CAE96C6D-145B-489B-9DC8-5CFBA4CC5076}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8ECDE6D4-37B0-4A54-8E21-B0BB3666378D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8ECDE6D4-37B0-4A54-8E21-B0BB3666378D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8ECDE6D4-37B0-4A54-8E21-B0BB3666378D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8ECDE6D4-37B0-4A54-8E21-B0BB3666378D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
{EF84877F-59BE-41BE-9013-E765AF0BB72E} = {00CA0876-DA9F-44E8-B0DC-A88716BF347A}
{3B8BD8F1-8BFC-4D8C-B4DA-25FFAF3D1DBE} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
{939FA5F7-16AA-4847-812B-6EBC3748A86D} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
- {36255A0A-89EC-43C8-A642-F4C1ACAEF5BC} = {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}
- {A3B4B972-5BD2-4D90-981F-7E51E350E628} = {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}
- {38BDEEED-7BEB-4B1F-9CE0-256D63F9C502} = {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}
{8FDB623D-779B-4A84-BC6B-75106E41D8A4} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
{651FDB97-9DE3-4BD9-9A05-827AF8F1A94A} = {E25031D3-5C64-430D-B86F-697B66816FD8}
- {D082703F-1652-4C35-840D-7D377F6B9979} = {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}
{8375813E-FBAF-4DA3-A2C7-E4645B39B931} = {E25031D3-5C64-430D-B86F-697B66816FD8}
{3DA1EEED-E9FE-43D9-B293-E000CFCCD91A} = {E25031D3-5C64-430D-B86F-697B66816FD8}
{D58114AE-4998-4647-AFCA-9353D20495AE} = {E25031D3-5C64-430D-B86F-697B66816FD8}
- {A9F81DA3-DA82-423E-A5DD-B11C37548E06} = {96E891CD-2ED7-4293-A7AB-4C6F5D8D2B05}
{A0F89B8B-32A3-4C28-8F1B-ADE343F16137} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
{69A0ACF2-DF1F-4F49-B554-F732DCA938A3} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
{59889284-FFEE-82E7-94CB-3B43E87DA6CF} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
@@ -234,6 +179,8 @@ Global
{05761CF5-CC46-43A6-814B-6BD2ECC1F0ED} = {00CA0876-DA9F-44E8-B0DC-A88716BF347A}
{CA67D8CA-6CC9-40E2-8CAC-F0B1401BEF7B} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
{43D3CD21-1E7E-4D28-B1BA-B6DAB3E19183} = {E25031D3-5C64-430D-B86F-697B66816FD8}
+ {CAE96C6D-145B-489B-9DC8-5CFBA4CC5076} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
+ {8ECDE6D4-37B0-4A54-8E21-B0BB3666378D} = {73A5C363-CA1F-44C4-9A9B-EF791A76BA6A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {193AA352-6748-47C1-A5FC-C9AA6B5F000B}
diff --git a/StackExchange.Redis.slnx b/StackExchange.Redis.slnx
index 853429fb3..02f110acd 100644
--- a/StackExchange.Redis.slnx
+++ b/StackExchange.Redis.slnx
@@ -34,59 +34,12 @@
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md
index 3202de4e8..20d650658 100644
--- a/docs/ReleaseNotes.md
+++ b/docs/ReleaseNotes.md
@@ -8,9 +8,9 @@ Current package versions:
## Unreleased
+- Remove `[Experimental]` 8.8 `GCRA` feature ([#3074 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3074))
- Detect server-mode correctly on Valkey 8+ instances ([#3050 by @wipiano](https://github.com/StackExchange/StackExchange.Redis/pull/3050))
- 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))
- Support sub-key (hash field) notifications ([#3062 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3062))
- Add `ValueCondition` overloads for `SortedSetIncrement`/`SortedSetIncrementAsync`, supporting `ZADD INCR` with existence conditions ([#3071 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3071))
@@ -22,7 +22,8 @@ Current package versions:
## 2.12.8
-- Add [`GCRA`](https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm) support (and remove experimental flag on `VSIM`) ([#3041 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3041))
+- (this release also includes experimental 8.8 `GCRA` support; this feature is not being shipped yet, and it is being removed from later releases)
+- Remove experimental flag on `VSIM` ([#3041 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3041))
- Add `IServer.GetProductVariant` to detect the product variant and version of the connected server, and use that internally
to enable multi-DB operations on Valkey clusters ([#3040 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3040))
- Ignore cluster nodes with the `handshake` flag ([#3043 by @TimLovellSmith](https://github.com/StackExchange/StackExchange.Redis/pull/3043))
diff --git a/docs/exp/SER006.md b/docs/exp/SER006.md
index 8ccea3365..ff92a58a8 100644
--- a/docs/exp/SER006.md
+++ b/docs/exp/SER006.md
@@ -2,9 +2,11 @@ Redis 8.8 is currently in preview and may be subject to change.
New features in Redis 8.8:
-- `GCRA` for rate-limiting
+- `XNACK` for stream negative acknowledgements
+- `Aggregate.Count` for sorted-set combination operations
+- Sub-key notifications
-The corresponding library feature must also be considered subject to change:
+The corresponding library features must also be considered subject to change:
1. Existing bindings may cease working correctly if the underlying server API changes.
2. Changes to the server API may require changes to the library API, manifesting in either/both of build-time
diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs
index 4f294f46b..4c8f3be5b 100644
--- a/src/StackExchange.Redis/Enums/RedisCommand.cs
+++ b/src/StackExchange.Redis/Enums/RedisCommand.cs
@@ -60,7 +60,6 @@ internal enum RedisCommand
GEOSEARCH,
GEOSEARCHSTORE,
- GCRA,
GET,
GETBIT,
GETDEL,
@@ -325,7 +324,6 @@ internal static bool IsPrimaryOnly(this RedisCommand command)
case RedisCommand.EXPIREAT:
case RedisCommand.FLUSHALL:
case RedisCommand.FLUSHDB:
- case RedisCommand.GCRA:
case RedisCommand.GEOSEARCHSTORE:
case RedisCommand.GETDEL:
case RedisCommand.GETEX:
diff --git a/src/StackExchange.Redis/ExtensionMethods.cs b/src/StackExchange.Redis/ExtensionMethods.cs
index b7f175763..e5a5c4d4d 100644
--- a/src/StackExchange.Redis/ExtensionMethods.cs
+++ b/src/StackExchange.Redis/ExtensionMethods.cs
@@ -7,10 +7,8 @@
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
-using System.Threading;
using System.Threading.Tasks;
using Pipelines.Sockets.Unofficial.Arenas;
-using RESPite;
namespace StackExchange.Redis
{
@@ -339,70 +337,5 @@ internal static int VectorSafeIndexOfCRLF(this ReadOnlySpan span)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static TTo[]? ToArray(in this RawResult result, Projection selector, in TState state)
=> result.IsNull ? null : result.GetItems().ToArray(selector, in state);
-
- ///
- /// Attempts to acquire a GCRA rate limit token, retrying with delays if rate limited.
- ///
- /// The database instance.
- /// The key for the rate limiter.
- /// The maximum burst size.
- /// The number of tokens allowed per period.
- /// The maximum time to wait for a successful acquisition.
- /// The period in seconds (default: 1.0).
- /// The number of tokens to acquire (default: 1).
- /// The command flags to use.
- /// The cancellation token.
- /// True if the token was acquired within the allowed time; false otherwise.
- [Experimental(Experiments.Server_8_8, UrlFormat = Experiments.UrlFormat)]
- public static async ValueTask TryAcquireGcraAsync(
- this IDatabaseAsync database,
- RedisKey key,
- int maxBurst,
- int tokensPerPeriod,
- TimeSpan allow,
- double periodSeconds = 1.0,
- int count = 1,
- CommandFlags flags = CommandFlags.None,
- CancellationToken cancellationToken = default)
- {
- cancellationToken.ThrowIfCancellationRequested();
-
- var startTime = DateTime.UtcNow;
- var allowMilliseconds = allow.TotalMilliseconds;
-
- while (true)
- {
- var result = await database.StringGcraRateLimitAsync(key, maxBurst, tokensPerPeriod, periodSeconds, count, flags).ConfigureAwait(false);
-
- if (!result.Limited)
- {
- return true;
- }
-
- var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds;
- var remaining = allowMilliseconds - elapsed;
-
- if (remaining <= 0)
- {
- return false;
- }
-
- var delaySeconds = result.RetryAfterSeconds;
- if (delaySeconds <= 0)
- {
- // Shouldn't happen when Limited is true, but handle defensively
- return false;
- }
-
- var delayMilliseconds = delaySeconds * 1000.0;
- if (delayMilliseconds > remaining)
- {
- // Not enough time left to wait for retry
- return false;
- }
-
- await Task.Delay(TimeSpan.FromSeconds(delaySeconds), cancellationToken).ConfigureAwait(false);
- }
- }
}
}
diff --git a/src/StackExchange.Redis/Gcra.GcraMessage.cs b/src/StackExchange.Redis/Gcra.GcraMessage.cs
deleted file mode 100644
index cc79e4be4..000000000
--- a/src/StackExchange.Redis/Gcra.GcraMessage.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-namespace StackExchange.Redis;
-
-internal partial class RedisDatabase
-{
- internal sealed class GcraMessage(
- int database,
- CommandFlags flags,
- RedisKey key,
- int maxBurst,
- int tokensPerPeriod,
- double periodSeconds,
- int count) : Message(database, flags, RedisCommand.GCRA)
- {
- protected override void WriteImpl(PhysicalConnection connection)
- {
- // GCRA key max_burst tokens_per_period period [TOKENS count]
- connection.WriteHeader(Command, ArgCount);
- connection.WriteBulkString(key);
- connection.WriteBulkString(maxBurst);
- connection.WriteBulkString(tokensPerPeriod);
- connection.WriteBulkString(periodSeconds);
-
- if (count != 1)
- {
- connection.WriteBulkString("TOKENS"u8);
- connection.WriteBulkString(count);
- }
- }
-
- public override int ArgCount
- {
- get
- {
- int argCount = 4; // key, max_burst, tokens_per_period, period
- if (count != 1) argCount += 2; // TOKENS, count
- return argCount;
- }
- }
- }
-}
diff --git a/src/StackExchange.Redis/Gcra.GcraRateLimitResult.cs b/src/StackExchange.Redis/Gcra.GcraRateLimitResult.cs
deleted file mode 100644
index 7b292cb57..000000000
--- a/src/StackExchange.Redis/Gcra.GcraRateLimitResult.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System.Diagnostics.CodeAnalysis;
-using RESPite;
-
-namespace StackExchange.Redis;
-
-///
-/// Represents the result of a GCRA (Generic Cell Rate Algorithm) rate limit check.
-///
-[Experimental(Experiments.Server_8_8, UrlFormat = Experiments.UrlFormat)]
-public readonly partial struct GcraRateLimitResult
-{
- ///
- /// Indicates whether the token acquisition was rate limited (true) or allowed (false).
- ///
- public bool Limited { get; }
-
- ///
- /// The maximum number of tokens allowed. Always equal to max_burst + 1.
- ///
- public int MaxTokens { get; }
-
- ///
- /// The number of tokens available immediately without being rate limited.
- ///
- public int AvailableTokens { get; }
-
- ///
- /// The number of seconds after which the caller should retry.
- /// Returns -1 if the token acquisition is not limited.
- ///
- public int RetryAfterSeconds { get; }
-
- ///
- /// The number of seconds after which a full burst will be allowed.
- ///
- public int FullBurstAfterSeconds { get; }
-
- ///
- /// Initializes a new instance of the struct.
- ///
- public GcraRateLimitResult(bool limited, int maxTokens, int availableTokens, int retryAfterSeconds, int fullBurstAfterSeconds)
- {
- Limited = limited;
- MaxTokens = maxTokens;
- AvailableTokens = availableTokens;
- RetryAfterSeconds = retryAfterSeconds;
- FullBurstAfterSeconds = fullBurstAfterSeconds;
- }
-}
diff --git a/src/StackExchange.Redis/Gcra.ResultProcessor.cs b/src/StackExchange.Redis/Gcra.ResultProcessor.cs
deleted file mode 100644
index aac72daf8..000000000
--- a/src/StackExchange.Redis/Gcra.ResultProcessor.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-namespace StackExchange.Redis;
-
-public readonly partial struct GcraRateLimitResult
-{
- internal static readonly ResultProcessor Processor = new GcraRateLimitResultProcessor();
-
- private sealed class GcraRateLimitResultProcessor : ResultProcessor
- {
- protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result)
- {
- // GCRA returns an array with 5 elements:
- // 1) # 0 or 1
- // 2) # max number of tokens. Always equal to max_burst+1
- // 3) # number of tokens available immediately
- // 4) # number of seconds after which caller should retry. Always returns -1 if acquisition isn't limited.
- // 5) # number of seconds after which a full burst will be allowed
- if (result.Resp2TypeArray == ResultType.Array && result.ItemsCount >= 5)
- {
- var items = result.GetItems();
- bool limited = items[0].GetBoolean();
- if (items[1].TryGetInt64(out long maxTokens)
- && items[2].TryGetInt64(out long availableTokens)
- && items[3].TryGetInt64(out long retryAfterSeconds)
- && items[4].TryGetInt64(out long fullBurstAfterSeconds))
- {
- var grca = new GcraRateLimitResult(
- limited: limited,
- maxTokens: (int)maxTokens,
- availableTokens: (int)availableTokens,
- retryAfterSeconds: (int)retryAfterSeconds,
- fullBurstAfterSeconds: (int)fullBurstAfterSeconds);
- SetResult(message, grca);
- return true;
- }
- }
-
- return false;
- }
-
- /* for v3, already done (due to branch choice)
- protected override bool SetResultCore(PhysicalConnection connection, Message message, ref RespReader reader)
- {
- // GCRA returns an array with 5 elements:
- // 1) # 0 or 1
- // 2) # max number of tokens. Always equal to max_burst+1
- // 3) # number of tokens available immediately
- // 4) # number of seconds after which caller should retry. Always returns -1 if acquisition isn't limited.
- // 5) # number of seconds after which a full burst will be allowed
- if (reader.IsAggregate
- && reader.TryMoveNext() && reader.IsScalar && reader.TryReadBoolean(out bool limited)
- && reader.TryMoveNext() && reader.IsScalar && reader.TryReadInt64(out long maxTokens)
- && reader.TryMoveNext() && reader.IsScalar && reader.TryReadInt64(out long availableTokens)
- && reader.TryMoveNext() && reader.IsScalar && reader.TryReadInt64(out long retryAfterSeconds)
- && reader.TryMoveNext() && reader.IsScalar && reader.TryReadInt64(out long fullBurstAfterSeconds))
- {
- var result = new GcraRateLimitResult(
- limited: limited,
- maxTokens: (int)maxTokens,
- availableTokens: (int)availableTokens,
- retryAfterSeconds: (int)retryAfterSeconds,
- fullBurstAfterSeconds: (int)fullBurstAfterSeconds);
- SetResult(message, result);
- return true;
- }
-
- return false;
- }
- */
- }
-}
diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs
index 8866f5669..b7eb65d5c 100644
--- a/src/StackExchange.Redis/Interfaces/IDatabase.cs
+++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs
@@ -3300,20 +3300,6 @@ IEnumerable SortedSetScan(
[Experimental(Experiments.Server_8_4, UrlFormat = Experiments.UrlFormat)]
ValueCondition? StringDigest(RedisKey key, CommandFlags flags = CommandFlags.None);
- ///
- /// Performs a GCRA (Generic Cell Rate Algorithm) rate limit check on the specified key.
- ///
- /// The key to rate limit.
- /// The maximum burst size.
- /// The number of tokens allowed per period.
- /// The period duration in seconds. Default is 1.0.
- /// The number of tokens to consume. Default is 1.
- /// The flags to use for this operation.
- /// A containing the rate limit decision and metadata.
- ///
- [Experimental(Experiments.Server_8_8, UrlFormat = Experiments.UrlFormat)]
- GcraRateLimitResult StringGcraRateLimit(RedisKey key, int maxBurst, int tokensPerPeriod, double periodSeconds = 1.0, int count = 1, CommandFlags flags = CommandFlags.None);
-
///
/// Get the value of key. If the key does not exist the special value is returned.
/// An error is returned if the value stored at key is not a string, because GET only handles string values.
diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
index ae583295f..1c7555839 100644
--- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
+++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
@@ -808,10 +808,6 @@ IAsyncEnumerable SortedSetScanAsync(
[Experimental(Experiments.Server_8_4, UrlFormat = Experiments.UrlFormat)]
Task StringDigestAsync(RedisKey key, CommandFlags flags = CommandFlags.None);
- ///
- [Experimental(Experiments.Server_8_8, UrlFormat = Experiments.UrlFormat)]
- Task StringGcraRateLimitAsync(RedisKey key, int maxBurst, int tokensPerPeriod, double periodSeconds = 1.0, int count = 1, CommandFlags flags = CommandFlags.None);
-
///
Task StringGetAsync(RedisKey key, CommandFlags flags = CommandFlags.None);
diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
index a880fa672..fb8560d3f 100644
--- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
+++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
@@ -801,9 +801,6 @@ public Task StringIncrementAsync(RedisKey key, long value = 1, CommandFlag
public Task StringLengthAsync(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.StringLengthAsync(ToInner(key), flags);
- public Task StringGcraRateLimitAsync(RedisKey key, int maxBurst, int tokensPerPeriod, double period = 1.0, int count = 1, CommandFlags flags = CommandFlags.None) =>
- Inner.StringGcraRateLimitAsync(ToInner(key), maxBurst, tokensPerPeriod, period, count, flags);
-
public Task StringSetAsync(RedisKey key, RedisValue value, Expiration expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
=> Inner.StringSetAsync(ToInner(key), value, expiry, when, flags);
diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
index 0c1ed5294..aa2eff75b 100644
--- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
+++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
@@ -783,9 +783,6 @@ public long StringIncrement(RedisKey key, long value = 1, CommandFlags flags = C
public long StringLength(RedisKey key, CommandFlags flags = CommandFlags.None) =>
Inner.StringLength(ToInner(key), flags);
- public GcraRateLimitResult StringGcraRateLimit(RedisKey key, int maxBurst, int tokensPerPeriod, double period = 1.0, int count = 1, CommandFlags flags = CommandFlags.None) =>
- Inner.StringGcraRateLimit(ToInner(key), maxBurst, tokensPerPeriod, period, count, flags);
-
public bool StringSet(RedisKey key, RedisValue value, Expiration expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
=> Inner.StringSet(ToInner(key), value, expiry, when, flags);
diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
index 2d3c191fa..129e47d41 100644
--- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
+++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
@@ -483,14 +483,6 @@ StackExchange.Redis.GeoUnit.Feet = 3 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.GeoUnit.Kilometers = 1 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.GeoUnit.Meters = 0 -> StackExchange.Redis.GeoUnit
StackExchange.Redis.GeoUnit.Miles = 2 -> StackExchange.Redis.GeoUnit
-[SER006]StackExchange.Redis.GcraRateLimitResult
-[SER006]StackExchange.Redis.GcraRateLimitResult.AvailableTokens.get -> int
-[SER006]StackExchange.Redis.GcraRateLimitResult.FullBurstAfterSeconds.get -> int
-[SER006]StackExchange.Redis.GcraRateLimitResult.GcraRateLimitResult() -> void
-[SER006]StackExchange.Redis.GcraRateLimitResult.GcraRateLimitResult(bool limited, int maxTokens, int availableTokens, int retryAfterSeconds, int fullBurstAfterSeconds) -> void
-[SER006]StackExchange.Redis.GcraRateLimitResult.Limited.get -> bool
-[SER006]StackExchange.Redis.GcraRateLimitResult.MaxTokens.get -> int
-[SER006]StackExchange.Redis.GcraRateLimitResult.RetryAfterSeconds.get -> int
StackExchange.Redis.HashEntry
StackExchange.Redis.HashEntry.Equals(StackExchange.Redis.HashEntry other) -> bool
StackExchange.Redis.HashEntry.HashEntry() -> void
@@ -787,7 +779,6 @@ StackExchange.Redis.IDatabase.StringGetSet(StackExchange.Redis.RedisKey key, Sta
StackExchange.Redis.IDatabase.StringGetSetExpiry(StackExchange.Redis.RedisKey key, System.TimeSpan? expiry, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue
StackExchange.Redis.IDatabase.StringGetSetExpiry(StackExchange.Redis.RedisKey key, System.DateTime expiry, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue
StackExchange.Redis.IDatabase.StringGetWithExpiry(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValueWithExpiry
-[SER006]StackExchange.Redis.IDatabase.StringGcraRateLimit(StackExchange.Redis.RedisKey key, int maxBurst, int tokensPerPeriod, double periodSeconds = 1, int count = 1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.GcraRateLimitResult
StackExchange.Redis.IDatabase.StringIncrement(StackExchange.Redis.RedisKey key, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> double
StackExchange.Redis.IDatabase.StringIncrement(StackExchange.Redis.RedisKey key, long value = 1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
StackExchange.Redis.IDatabase.StringLength(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long
@@ -1032,7 +1023,6 @@ StackExchange.Redis.IDatabaseAsync.StringGetSetAsync(StackExchange.Redis.RedisKe
StackExchange.Redis.IDatabaseAsync.StringGetSetExpiryAsync(StackExchange.Redis.RedisKey key, System.TimeSpan? expiry, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.StringGetSetExpiryAsync(StackExchange.Redis.RedisKey key, System.DateTime expiry, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.StringGetWithExpiryAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
-[SER006]StackExchange.Redis.IDatabaseAsync.StringGcraRateLimitAsync(StackExchange.Redis.RedisKey key, int maxBurst, int tokensPerPeriod, double periodSeconds = 1, int count = 1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.StringIncrementAsync(StackExchange.Redis.RedisKey key, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.StringIncrementAsync(StackExchange.Redis.RedisKey key, long value = 1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
StackExchange.Redis.IDatabaseAsync.StringLengthAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
@@ -1722,7 +1712,6 @@ static StackExchange.Redis.ExtensionMethods.ToStringArray(this StackExchange.Red
static StackExchange.Redis.ExtensionMethods.ToStringDictionary(this StackExchange.Redis.HashEntry[]? hash) -> System.Collections.Generic.Dictionary?
static StackExchange.Redis.ExtensionMethods.ToStringDictionary(this StackExchange.Redis.SortedSetEntry[]? sortedSet) -> System.Collections.Generic.Dictionary?
static StackExchange.Redis.ExtensionMethods.ToStringDictionary(this System.Collections.Generic.KeyValuePair[]? pairs) -> System.Collections.Generic.Dictionary?
-[SER006]static StackExchange.Redis.ExtensionMethods.TryAcquireGcraAsync(this StackExchange.Redis.IDatabaseAsync! database, StackExchange.Redis.RedisKey key, int maxBurst, int tokensPerPeriod, System.TimeSpan allow, double periodSeconds = 1, int count = 1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
static StackExchange.Redis.GeoEntry.operator !=(StackExchange.Redis.GeoEntry x, StackExchange.Redis.GeoEntry y) -> bool
static StackExchange.Redis.GeoEntry.operator ==(StackExchange.Redis.GeoEntry x, StackExchange.Redis.GeoEntry y) -> bool
static StackExchange.Redis.GeoPosition.operator !=(StackExchange.Redis.GeoPosition x, StackExchange.Redis.GeoPosition y) -> bool
diff --git a/src/StackExchange.Redis/RedisDatabase.Strings.cs b/src/StackExchange.Redis/RedisDatabase.Strings.cs
index 4c5e6e1a6..dfe4c7259 100644
--- a/src/StackExchange.Redis/RedisDatabase.Strings.cs
+++ b/src/StackExchange.Redis/RedisDatabase.Strings.cs
@@ -47,18 +47,6 @@ private Message GetStringDeleteMessage(in RedisKey key, in ValueCondition when,
return ExecuteAsync(msg, ResultProcessor.Digest);
}
- public GcraRateLimitResult StringGcraRateLimit(RedisKey key, int maxBurst, int tokensPerPeriod, double periodSeconds = 1.0, int count = 1, CommandFlags flags = CommandFlags.None)
- {
- var msg = new GcraMessage(Database, flags, key, maxBurst, tokensPerPeriod, periodSeconds, count);
- return ExecuteSync(msg, ResultProcessor.GcraRateLimit);
- }
-
- public Task StringGcraRateLimitAsync(RedisKey key, int maxBurst, int tokensPerPeriod, double periodSeconds = 1.0, int count = 1, CommandFlags flags = CommandFlags.None)
- {
- var msg = new GcraMessage(Database, flags, key, maxBurst, tokensPerPeriod, periodSeconds, count);
- return ExecuteAsync(msg, ResultProcessor.GcraRateLimit);
- }
-
public Task StringSetAsync(RedisKey key, RedisValue value, Expiration expiry, ValueCondition when, CommandFlags flags = CommandFlags.None)
{
var msg = GetStringSetMessage(key, value, expiry, when, flags);
diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs
index 3387d5552..366f5897b 100644
--- a/src/StackExchange.Redis/ResultProcessor.cs
+++ b/src/StackExchange.Redis/ResultProcessor.cs
@@ -113,9 +113,6 @@ public static readonly ResultProcessor
public static readonly ResultProcessor
RedisGeoPosition = new RedisValueGeoPositionProcessor();
- public static readonly ResultProcessor
- GcraRateLimit = GcraRateLimitResult.Processor;
-
public static readonly ResultProcessor
ResponseTimer = new TimingProcessor();
diff --git a/tests/RedisConfigs/.docker/docker.csproj b/tests/RedisConfigs/.docker/docker.csproj
new file mode 100644
index 000000000..d93c54c39
--- /dev/null
+++ b/tests/RedisConfigs/.docker/docker.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard2.0
+ false
+
+
diff --git a/tests/RedisConfigs/Basic/primary-6379.conf b/tests/RedisConfigs/Basic/primary-6379.conf
index 0128e5e22..2da592601 100644
--- a/tests/RedisConfigs/Basic/primary-6379.conf
+++ b/tests/RedisConfigs/Basic/primary-6379.conf
@@ -8,4 +8,4 @@ appendonly no
dbfilename "primary-6379.rdb"
save ""
enable-debug-command yes
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Basic/replica-6380.conf b/tests/RedisConfigs/Basic/replica-6380.conf
index 5cd671f6d..0c1650513 100644
--- a/tests/RedisConfigs/Basic/replica-6380.conf
+++ b/tests/RedisConfigs/Basic/replica-6380.conf
@@ -8,4 +8,4 @@ appendonly no
dir "../Temp"
dbfilename "replica-6380.rdb"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Basic/secure-6381.conf b/tests/RedisConfigs/Basic/secure-6381.conf
index 5037accd8..ad2e380ad 100644
--- a/tests/RedisConfigs/Basic/secure-6381.conf
+++ b/tests/RedisConfigs/Basic/secure-6381.conf
@@ -5,4 +5,4 @@ maxmemory 512mb
dir "../Temp"
dbfilename "secure-6381.rdb"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Basic/tls-ciphers-6384.conf b/tests/RedisConfigs/Basic/tls-ciphers-6384.conf
index 374f272b3..857d5c741 100644
--- a/tests/RedisConfigs/Basic/tls-ciphers-6384.conf
+++ b/tests/RedisConfigs/Basic/tls-ciphers-6384.conf
@@ -9,4 +9,4 @@ tls-protocols "TLSv1.2 TLSv1.3"
tls-cert-file /Certs/redis.crt
tls-key-file /Certs/redis.key
tls-ca-cert-file /Certs/ca.crt
-notify-keyspace-events AKESTIV
+notify-keyspace-events AKE
diff --git a/tests/RedisConfigs/Cluster/cluster-7000.conf b/tests/RedisConfigs/Cluster/cluster-7000.conf
index 033dcba9d..ad11a23fd 100644
--- a/tests/RedisConfigs/Cluster/cluster-7000.conf
+++ b/tests/RedisConfigs/Cluster/cluster-7000.conf
@@ -7,4 +7,4 @@ appendonly yes
dbfilename "dump-7000.rdb"
appendfilename "appendonly-7000.aof"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Cluster/cluster-7001.conf b/tests/RedisConfigs/Cluster/cluster-7001.conf
index 29e3f3de2..589f9ea23 100644
--- a/tests/RedisConfigs/Cluster/cluster-7001.conf
+++ b/tests/RedisConfigs/Cluster/cluster-7001.conf
@@ -7,4 +7,4 @@ appendonly yes
dbfilename "dump-7001.rdb"
appendfilename "appendonly-7001.aof"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Cluster/cluster-7002.conf b/tests/RedisConfigs/Cluster/cluster-7002.conf
index 1e4320095..66a376865 100644
--- a/tests/RedisConfigs/Cluster/cluster-7002.conf
+++ b/tests/RedisConfigs/Cluster/cluster-7002.conf
@@ -7,4 +7,4 @@ appendonly yes
dbfilename "dump-7002.rdb"
appendfilename "appendonly-7002.aof"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Cluster/cluster-7003.conf b/tests/RedisConfigs/Cluster/cluster-7003.conf
index dca308929..1f4883023 100644
--- a/tests/RedisConfigs/Cluster/cluster-7003.conf
+++ b/tests/RedisConfigs/Cluster/cluster-7003.conf
@@ -7,4 +7,4 @@ appendonly yes
dbfilename "dump-7003.rdb"
appendfilename "appendonly-7003.aof"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Cluster/cluster-7004.conf b/tests/RedisConfigs/Cluster/cluster-7004.conf
index 290a37464..93d75f38a 100644
--- a/tests/RedisConfigs/Cluster/cluster-7004.conf
+++ b/tests/RedisConfigs/Cluster/cluster-7004.conf
@@ -7,4 +7,4 @@ appendonly yes
dbfilename "dump-7004.rdb"
appendfilename "appendonly-7004.aof"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Cluster/cluster-7005.conf b/tests/RedisConfigs/Cluster/cluster-7005.conf
index 0f936ee4e..c9b5d55e2 100644
--- a/tests/RedisConfigs/Cluster/cluster-7005.conf
+++ b/tests/RedisConfigs/Cluster/cluster-7005.conf
@@ -7,4 +7,4 @@ appendonly yes
dbfilename "dump-7005.rdb"
appendfilename "appendonly-7005.aof"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Failover/primary-6382.conf b/tests/RedisConfigs/Failover/primary-6382.conf
index de55eea89..6055c0347 100644
--- a/tests/RedisConfigs/Failover/primary-6382.conf
+++ b/tests/RedisConfigs/Failover/primary-6382.conf
@@ -7,4 +7,4 @@ dir "../Temp"
appendonly no
dbfilename "primary-6382.rdb"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Failover/replica-6383.conf b/tests/RedisConfigs/Failover/replica-6383.conf
index 8e3987de2..e07f5a69d 100644
--- a/tests/RedisConfigs/Failover/replica-6383.conf
+++ b/tests/RedisConfigs/Failover/replica-6383.conf
@@ -8,4 +8,4 @@ appendonly no
dir "../Temp"
dbfilename "replica-6383.rdb"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/RedisConfigs.csproj b/tests/RedisConfigs/RedisConfigs.csproj
new file mode 100644
index 000000000..d93c54c39
--- /dev/null
+++ b/tests/RedisConfigs/RedisConfigs.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard2.0
+ false
+
+
diff --git a/tests/RedisConfigs/Sentinel/redis-7010.conf b/tests/RedisConfigs/Sentinel/redis-7010.conf
index 566492e55..878160632 100644
--- a/tests/RedisConfigs/Sentinel/redis-7010.conf
+++ b/tests/RedisConfigs/Sentinel/redis-7010.conf
@@ -6,4 +6,4 @@ appendonly no
dir "../Temp"
dbfilename "sentinel-target-7010.rdb"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/RedisConfigs/Sentinel/redis-7011.conf b/tests/RedisConfigs/Sentinel/redis-7011.conf
index 45598a933..08b8dad1a 100644
--- a/tests/RedisConfigs/Sentinel/redis-7011.conf
+++ b/tests/RedisConfigs/Sentinel/redis-7011.conf
@@ -7,4 +7,4 @@ appendonly no
dir "../Temp"
dbfilename "sentinel-target-7011.rdb"
save ""
-notify-keyspace-events AKESTIV
\ No newline at end of file
+notify-keyspace-events AKE
\ No newline at end of file
diff --git a/tests/StackExchange.Redis.Tests/GcraIntegrationTests.cs b/tests/StackExchange.Redis.Tests/GcraIntegrationTests.cs
deleted file mode 100644
index 627ee311f..000000000
--- a/tests/StackExchange.Redis.Tests/GcraIntegrationTests.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using Xunit;
-
-namespace StackExchange.Redis.Tests;
-
-[RunPerProtocol]
-public class GcraIntegrationTests(ITestOutputHelper output, SharedConnectionFixture fixture) : TestBase(output, fixture)
-{
- [Fact(Timeout = 5000)]
- public async Task GcraRateLimit_NonDefaultCount()
- {
- await using var conn = Create(require: RedisFeatures.v8_8_0);
- var db = conn.GetDatabase();
- var key = Me();
- db.KeyDelete(key, CommandFlags.FireAndForget);
-
- var result = await db.StringGcraRateLimitAsync(key, maxBurst: 10, tokensPerPeriod: 10, periodSeconds: 1.0, count: 8);
- Assert.False(result.Limited);
- Log($"Run 1: Limited: {result.Limited}, Available: {result.AvailableTokens}, RetryAfter: {result.RetryAfterSeconds}, FullBurstAfter: {result.FullBurstAfterSeconds}, MaxTokens: {result.MaxTokens}");
-
- result = await db.StringGcraRateLimitAsync(key, maxBurst: 10, tokensPerPeriod: 10, periodSeconds: 1.0, count: 8);
- Assert.True(result.Limited);
- Log($"Run 2: Limited: {result.Limited}, Available: {result.AvailableTokens}, RetryAfter: {result.RetryAfterSeconds}, FullBurstAfter: {result.FullBurstAfterSeconds}, MaxTokens: {result.MaxTokens}");
- }
-
- [Fact(Timeout = 5000)]
- public async Task GcraRateLimit_SmokeTest()
- {
- await using var conn = Create(require: RedisFeatures.v8_8_0);
- var db = conn.GetDatabase();
- var key = Me() + Guid.NewGuid();
- db.KeyDelete(key, CommandFlags.FireAndForget);
- for (int i = 0; i < 15; i++)
- {
- var result = await db.StringGcraRateLimitAsync(key, maxBurst: 10, tokensPerPeriod: 10, periodSeconds: 1.0);
- Log($"Run {i}: Limited: {result.Limited}, Available: {result.AvailableTokens}, RetryAfter: {result.RetryAfterSeconds}, FullBurstAfter: {result.FullBurstAfterSeconds}, MaxTokens: {result.MaxTokens}");
- if (i <= 10)
- {
- Assert.False(result.Limited, $"run {i}");
- }
- else
- {
- Assert.True(result.Limited, $"run {i}");
- }
- Assert.Equal(11, result.MaxTokens);
- Assert.Equal(Math.Max(0, 10 - i), result.AvailableTokens);
- if (result.Limited)
- {
- Assert.True(result.RetryAfterSeconds > 0);
- }
- else
- {
- Assert.Equal(-1, result.RetryAfterSeconds);
- }
- Assert.True(result.FullBurstAfterSeconds > 0);
- }
- await db.TryAcquireGcraAsync(
- key,
- maxBurst: 10,
- tokensPerPeriod: 10,
- allow: TimeSpan.FromSeconds(5),
- cancellationToken: TestContext.Current.CancellationToken);
- }
-}
diff --git a/tests/StackExchange.Redis.Tests/GcraTestServer.cs b/tests/StackExchange.Redis.Tests/GcraTestServer.cs
deleted file mode 100644
index 65fb17fbd..000000000
--- a/tests/StackExchange.Redis.Tests/GcraTestServer.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-extern alias respite;
-using respite::RESPite.Messages;
-using StackExchange.Redis.Server;
-using Xunit;
-
-namespace StackExchange.Redis.Tests;
-
-///
-/// Test Redis server that simulates GCRA rate limiting responses.
-///
-public class GcraTestServer : InProcessTestServer
-{
- private readonly GcraRateLimitResult _expectedResult;
- private GcraRequestSnapshot? _lastRequest;
-
- public GcraTestServer(GcraRateLimitResult expectedResult, ITestOutputHelper? log = null) : base(log)
- {
- _expectedResult = expectedResult;
- }
-
- ///
- /// Snapshot of the last GCRA request received by the server.
- ///
- public sealed class GcraRequestSnapshot
- {
- public RedisKey Key { get; init; }
- public int MaxBurst { get; init; }
- public int TokensPerPeriod { get; init; }
- public double PeriodSeconds { get; init; }
- public int Count { get; init; }
- }
-
- ///
- /// Gets the last GCRA request received by the server.
- ///
- public GcraRequestSnapshot? LastRequest => _lastRequest;
-
- ///
- /// Handles GCRA commands. Returns the configured result and captures request parameters.
- ///
- [RedisCommand(-5, "GCRA")]
- protected virtual TypedRedisValue Gcra(RedisClient client, in RedisRequest request)
- {
- // Parse request parameters
- var key = request.GetKey(1);
- var maxBurst = request.GetInt32(2);
- var tokensPerPeriod = request.GetInt32(3);
- // Parse period as a string and convert to double
- var periodString = request.GetString(4);
- var periodSeconds = double.Parse(periodString, System.Globalization.CultureInfo.InvariantCulture);
-
- // Optional count parameter (defaults to 1)
- var count = 1;
- if (request.Count >= 7 && request.GetString(5) == "TOKENS")
- {
- count = request.GetInt32(6);
- }
-
- // Capture the request
- _lastRequest = new GcraRequestSnapshot
- {
- Key = key,
- MaxBurst = maxBurst,
- TokensPerPeriod = tokensPerPeriod,
- PeriodSeconds = periodSeconds,
- Count = count,
- };
-
- // Return the configured result as a 5-element array
- var result = TypedRedisValue.Rent(5, out var span, RespPrefix.Array);
- span[0] = TypedRedisValue.Integer(_expectedResult.Limited ? 1 : 0);
- span[1] = TypedRedisValue.Integer(_expectedResult.MaxTokens);
- span[2] = TypedRedisValue.Integer(_expectedResult.AvailableTokens);
- span[3] = TypedRedisValue.Integer(_expectedResult.RetryAfterSeconds);
- span[4] = TypedRedisValue.Integer(_expectedResult.FullBurstAfterSeconds);
- return result;
- }
-
- ///
- /// Resets the last request snapshot.
- ///
- public override void ResetCounters()
- {
- _lastRequest = null;
- base.ResetCounters();
- }
-}
diff --git a/tests/StackExchange.Redis.Tests/GcraUnitTests.cs b/tests/StackExchange.Redis.Tests/GcraUnitTests.cs
deleted file mode 100644
index af8c65922..000000000
--- a/tests/StackExchange.Redis.Tests/GcraUnitTests.cs
+++ /dev/null
@@ -1,155 +0,0 @@
-using System.Runtime.CompilerServices;
-using System.Threading.Tasks;
-using Xunit;
-
-namespace StackExchange.Redis.Tests;
-
-///
-/// Unit tests for GCRA rate limiting functionality.
-///
-public class GcraUnitTests(ITestOutputHelper log)
-{
- private RedisKey Me([CallerMemberName] string callerName = "") => callerName;
-
- [Fact]
- public async Task GcraRateLimit_NotLimited_ReturnsExpectedResult()
- {
- // Arrange
- var expectedResult = new GcraRateLimitResult(
- limited: false,
- maxTokens: 10,
- availableTokens: 9,
- retryAfterSeconds: 0,
- fullBurstAfterSeconds: 1);
-
- using var server = new GcraTestServer(expectedResult, log);
- await using var muxer = await server.ConnectAsync();
- var db = muxer.GetDatabase();
- var key = Me();
-
- // Act
- var result = await db.StringGcraRateLimitAsync(key, maxBurst: 10, tokensPerPeriod: 10, periodSeconds: 1.0, count: 1);
-
- // Assert
- Assert.False(result.Limited);
- Assert.Equal(10, result.MaxTokens);
- Assert.Equal(9, result.AvailableTokens);
- Assert.Equal(0, result.RetryAfterSeconds);
- Assert.Equal(1, result.FullBurstAfterSeconds);
-
- // Verify the request received by the server
- var lastRequest = server.LastRequest;
- Assert.NotNull(lastRequest);
- Assert.Equal(key, lastRequest.Key);
- Assert.Equal(10, lastRequest.MaxBurst);
- Assert.Equal(10, lastRequest.TokensPerPeriod);
- Assert.Equal(1.0, lastRequest.PeriodSeconds);
- Assert.Equal(1, lastRequest.Count);
- }
-
- [Fact]
- public async Task GcraRateLimit_Limited_ReturnsExpectedResult()
- {
- // Arrange
- var expectedResult = new GcraRateLimitResult(
- limited: true,
- maxTokens: 5,
- availableTokens: 0,
- retryAfterSeconds: 2,
- fullBurstAfterSeconds: 10);
-
- using var server = new GcraTestServer(expectedResult, log);
- await using var muxer = await server.ConnectAsync();
- var db = muxer.GetDatabase();
- var key = Me();
-
- // Act
- var result = await db.StringGcraRateLimitAsync(key, maxBurst: 5, tokensPerPeriod: 5, periodSeconds: 1.0);
-
- // Assert
- Assert.True(result.Limited);
- Assert.Equal(5, result.MaxTokens);
- Assert.Equal(0, result.AvailableTokens);
- Assert.Equal(2, result.RetryAfterSeconds);
- Assert.Equal(10, result.FullBurstAfterSeconds);
-
- // Verify the request received by the server
- var lastRequest = server.LastRequest;
- Assert.NotNull(lastRequest);
- Assert.Equal(key, lastRequest.Key);
- Assert.Equal(5, lastRequest.MaxBurst);
- Assert.Equal(5, lastRequest.TokensPerPeriod);
- Assert.Equal(1.0, lastRequest.PeriodSeconds);
- Assert.Equal(1, lastRequest.Count);
- }
-
- [Fact]
- public async Task GcraRateLimit_WithCustomCount_SendsCorrectParameters()
- {
- // Arrange
- var expectedResult = new GcraRateLimitResult(
- limited: false,
- maxTokens: 100,
- availableTokens: 95,
- retryAfterSeconds: 0,
- fullBurstAfterSeconds: 5);
-
- using var server = new GcraTestServer(expectedResult, log);
- await using var muxer = await server.ConnectAsync();
- var db = muxer.GetDatabase();
- var key = Me();
-
- // Act
- var result = await db.StringGcraRateLimitAsync(key, maxBurst: 100, tokensPerPeriod: 100, periodSeconds: 60.0, count: 5);
-
- // Assert
- Assert.False(result.Limited);
- Assert.Equal(100, result.MaxTokens);
- Assert.Equal(95, result.AvailableTokens);
-
- // Verify the request received by the server includes the count parameter
- var lastRequest = server.LastRequest;
- Assert.NotNull(lastRequest);
- Assert.Equal(key, lastRequest.Key);
- Assert.Equal(100, lastRequest.MaxBurst);
- Assert.Equal(100, lastRequest.TokensPerPeriod);
- Assert.Equal(60.0, lastRequest.PeriodSeconds);
- Assert.Equal(5, lastRequest.Count);
- }
-
- [Fact]
- public async Task GcraRateLimit_SyncVersion_ReturnsExpectedResult()
- {
- // Arrange
- var expectedResult = new GcraRateLimitResult(
- limited: false,
- maxTokens: 20,
- availableTokens: 19,
- retryAfterSeconds: 0,
- fullBurstAfterSeconds: 1);
-
- using var server = new GcraTestServer(expectedResult, log);
- await using var muxer = await server.ConnectAsync();
- var db = muxer.GetDatabase();
- var key = Me();
-
- // Act
- var result = db.StringGcraRateLimit(key, maxBurst: 20, tokensPerPeriod: 20, periodSeconds: 1.0);
-
- // Assert
- Assert.False(result.Limited);
- Assert.Equal(20, result.MaxTokens);
- Assert.Equal(19, result.AvailableTokens);
- Assert.Equal(0, result.RetryAfterSeconds);
- Assert.Equal(1, result.FullBurstAfterSeconds);
-
- // Verify the request received by the server
- var lastRequest = server.LastRequest;
- Assert.NotNull(lastRequest);
- Assert.Equal(key, lastRequest.Key);
- Assert.Equal(20, lastRequest.MaxBurst);
- Assert.Equal(20, lastRequest.TokensPerPeriod);
- Assert.Equal(1.0, lastRequest.PeriodSeconds);
- Assert.Equal(1, lastRequest.Count);
- }
-}
diff --git a/tests/StackExchange.Redis.Tests/ResultProcessorUnitTests/GcraRateLimit.cs b/tests/StackExchange.Redis.Tests/ResultProcessorUnitTests/GcraRateLimit.cs
deleted file mode 100644
index 30fc3ecf5..000000000
--- a/tests/StackExchange.Redis.Tests/ResultProcessorUnitTests/GcraRateLimit.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using Xunit;
-
-/* unavailable until v3
-namespace StackExchange.Redis.Tests.ResultProcessorUnitTests;
-
-///
-/// Tests for GCRA rate limit result processor.
-///
-public class GcraRateLimit(ITestOutputHelper log) : ResultProcessorUnitTest(log)
-{
- [Fact]
- public void GcraRateLimit_NotLimited_Success()
- {
- // GCRA response when token acquisition is allowed:
- // 1) 0 (not limited)
- // 2) 11 (max tokens = max_burst + 1)
- // 3) 10 (available tokens)
- // 4) -1 (retry after - always -1 when not limited)
- // 5) 5 (full burst after seconds)
- var resp = "*5\r\n:0\r\n:11\r\n:10\r\n:-1\r\n:5\r\n";
- var processor = ResultProcessor.GcraRateLimit;
- var result = Execute(resp, processor);
-
- Assert.False(result.Limited);
- Assert.Equal(11, result.MaxTokens);
- Assert.Equal(10, result.AvailableTokens);
- Assert.Equal(-1, result.RetryAfterSeconds);
- Assert.Equal(5, result.FullBurstAfterSeconds);
- }
-
- [Fact]
- public void GcraRateLimit_Limited_Success()
- {
- // GCRA response when request is rate limited:
- // 1) 1 (limited)
- // 2) 11 (max tokens = max_burst + 1)
- // 3) 0 (no available tokens)
- // 4) 2 (retry after 2 seconds)
- // 5) 10 (full burst after 10 seconds)
- var resp = "*5\r\n:1\r\n:11\r\n:0\r\n:2\r\n:10\r\n";
- var processor = ResultProcessor.GcraRateLimit;
- var result = Execute(resp, processor);
-
- Assert.True(result.Limited);
- Assert.Equal(11, result.MaxTokens);
- Assert.Equal(0, result.AvailableTokens);
- Assert.Equal(2, result.RetryAfterSeconds);
- Assert.Equal(10, result.FullBurstAfterSeconds);
- }
-
- [Fact]
- public void GcraRateLimit_PartiallyAvailable_Success()
- {
- // GCRA response when some tokens are available:
- // 1) 0 (not limited)
- // 2) 101 (max tokens)
- // 3) 50 (50 tokens available)
- // 4) -1 (retry after - not limited)
- // 5) 100 (full burst after 100 seconds)
- var resp = "*5\r\n:0\r\n:101\r\n:50\r\n:-1\r\n:100\r\n";
- var processor = ResultProcessor.GcraRateLimit;
- var result = Execute(resp, processor);
-
- Assert.False(result.Limited);
- Assert.Equal(101, result.MaxTokens);
- Assert.Equal(50, result.AvailableTokens);
- Assert.Equal(-1, result.RetryAfterSeconds);
- Assert.Equal(100, result.FullBurstAfterSeconds);
- }
-
- [Theory]
- [InlineData("*4\r\n:0\r\n:11\r\n:10\r\n:-1\r\n")] // only 4 elements
- [InlineData(":0\r\n")] // scalar instead of array
- [InlineData("*5\r\n$1\r\n0\r\n:11\r\n:10\r\n:-1\r\n:5\r\n")] // first element is string
- public void GcraRateLimit_InvalidResponse_Failure(string resp)
- {
- ExecuteUnexpected(resp, ResultProcessor.GcraRateLimit);
- }
-}
-*/
diff --git a/tests/StackExchange.Redis.Tests/RoundTripUnitTests/GcraRateLimitRoundTrip.cs b/tests/StackExchange.Redis.Tests/RoundTripUnitTests/GcraRateLimitRoundTrip.cs
deleted file mode 100644
index f86ab4a9e..000000000
--- a/tests/StackExchange.Redis.Tests/RoundTripUnitTests/GcraRateLimitRoundTrip.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System.Threading.Tasks;
-using Xunit;
-
-/* unavailable until v3
-namespace StackExchange.Redis.Tests.RoundTripUnitTests;
-
-/*
-public class GcraRateLimitRoundTrip
-{
- [Theory(Timeout = 1000)]
- [InlineData("mykey", 10, 100, 1.0, "*5\r\n$4\r\nGCRA\r\n$5\r\nmykey\r\n$2\r\n10\r\n$3\r\n100\r\n$1\r\n1\r\n", "*5\r\n:0\r\n:11\r\n:10\r\n:-1\r\n:5\r\n")]
- public async Task GcraRateLimit_DefaultCount_RoundTrip(
- string key,
- int maxBurst,
- int tokensPerPeriod,
- double periodSeconds,
- string requestResp,
- string responseResp)
- {
- var msg = new RedisDatabase.GcraMessage(0, CommandFlags.None, key, maxBurst, tokensPerPeriod, periodSeconds, 1);
- var result = await TestConnection.ExecuteAsync(msg, ResultProcessor.GcraRateLimit, requestResp, responseResp);
-
- Assert.False(result.Limited);
- Assert.Equal(11, result.MaxTokens);
- Assert.Equal(10, result.AvailableTokens);
- Assert.Equal(-1, result.RetryAfterSeconds);
- Assert.Equal(5, result.FullBurstAfterSeconds);
- }
-
- [Theory(Timeout = 1000)]
- [InlineData("mykey", 10, 100, 1.0, 5, "*7\r\n$4\r\nGCRA\r\n$5\r\nmykey\r\n$2\r\n10\r\n$3\r\n100\r\n$1\r\n1\r\n$6\r\nTOKENS\r\n$1\r\n5\r\n", "*5\r\n:1\r\n:11\r\n:0\r\n:2\r\n:10\r\n")]
- public async Task GcraRateLimit_WithCount_RoundTrip(
- string key,
- int maxBurst,
- int tokensPerPeriod,
- double periodSeconds,
- int count,
- string requestResp,
- string responseResp)
- {
- var msg = new RedisDatabase.GcraMessage(0, CommandFlags.None, key, maxBurst, tokensPerPeriod, periodSeconds, count);
- var result = await TestConnection.ExecuteAsync(msg, ResultProcessor.GcraRateLimit, requestResp, responseResp);
-
- Assert.True(result.Limited);
- Assert.Equal(11, result.MaxTokens);
- Assert.Equal(0, result.AvailableTokens);
- Assert.Equal(2, result.RetryAfterSeconds);
- Assert.Equal(10, result.FullBurstAfterSeconds);
- }
-
- [Theory(Timeout = 1000)]
- [InlineData("rate:api", 50, 1000, 60.0, "*5\r\n$4\r\nGCRA\r\n$8\r\nrate:api\r\n$2\r\n50\r\n$4\r\n1000\r\n$2\r\n60\r\n", "*5\r\n:0\r\n:51\r\n:25\r\n:-1\r\n:30\r\n")]
- public async Task GcraRateLimit_CustomPeriod_RoundTrip(
- string key,
- int maxBurst,
- int tokensPerPeriod,
- double periodSeconds,
- string requestResp,
- string responseResp)
- {
- var msg = new RedisDatabase.GcraMessage(0, CommandFlags.None, key, maxBurst, tokensPerPeriod, periodSeconds, 1);
- var result = await TestConnection.ExecuteAsync(msg, ResultProcessor.GcraRateLimit, requestResp, responseResp);
-
- Assert.False(result.Limited);
- Assert.Equal(51, result.MaxTokens);
- Assert.Equal(25, result.AvailableTokens);
- Assert.Equal(-1, result.RetryAfterSeconds);
- Assert.Equal(30, result.FullBurstAfterSeconds);
- }
-}
-*/