From 4314ef0777efe2538ff4be490a97c318bd8b143f Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 8 May 2026 10:08:01 +0100 Subject: [PATCH 1/5] limit keyspace events to AKE until 8.8 lands --- StackExchange.Redis.sln | 81 ++++--------------- tests/RedisConfigs/Basic/primary-6379.conf | 2 +- tests/RedisConfigs/Basic/replica-6380.conf | 2 +- tests/RedisConfigs/Basic/secure-6381.conf | 2 +- .../RedisConfigs/Basic/tls-ciphers-6384.conf | 2 +- tests/RedisConfigs/Cluster/cluster-7000.conf | 2 +- tests/RedisConfigs/Cluster/cluster-7001.conf | 2 +- tests/RedisConfigs/Cluster/cluster-7002.conf | 2 +- tests/RedisConfigs/Cluster/cluster-7003.conf | 2 +- tests/RedisConfigs/Cluster/cluster-7004.conf | 2 +- tests/RedisConfigs/Cluster/cluster-7005.conf | 2 +- tests/RedisConfigs/Failover/primary-6382.conf | 2 +- tests/RedisConfigs/Failover/replica-6383.conf | 2 +- tests/RedisConfigs/Sentinel/redis-7010.conf | 2 +- tests/RedisConfigs/Sentinel/redis-7011.conf | 2 +- 15 files changed, 28 insertions(+), 81 deletions(-) diff --git a/StackExchange.Redis.sln b/StackExchange.Redis.sln index 514f367bb..e87495265 100644 --- a/StackExchange.Redis.sln +++ b/StackExchange.Redis.sln @@ -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/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/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 From 61b19a242b132b69e20b4546e7e86ba071afc012 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 8 May 2026 10:08:16 +0100 Subject: [PATCH 2/5] improve sln layout --- tests/RedisConfigs/.docker/docker.csproj | 7 +++++++ tests/RedisConfigs/RedisConfigs.csproj | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/RedisConfigs/.docker/docker.csproj create mode 100644 tests/RedisConfigs/RedisConfigs.csproj 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/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 + + From 9915301c59b7e3354902920985626a348e2c9289 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 8 May 2026 10:19:19 +0100 Subject: [PATCH 3/5] yank GCRA --- docs/ReleaseNotes.md | 4 +- docs/exp/SER006.md | 6 +- src/StackExchange.Redis/Enums/RedisCommand.cs | 2 - src/StackExchange.Redis/ExtensionMethods.cs | 67 -------- src/StackExchange.Redis/Gcra.GcraMessage.cs | 40 ----- .../Gcra.GcraRateLimitResult.cs | 49 ------ .../Gcra.ResultProcessor.cs | 70 -------- .../Interfaces/IDatabase.cs | 14 -- .../Interfaces/IDatabaseAsync.cs | 4 - .../KeyspaceIsolation/KeyPrefixed.cs | 3 - .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 3 - .../PublicAPI/PublicAPI.Shipped.txt | 11 -- .../RedisDatabase.Strings.cs | 12 -- src/StackExchange.Redis/ResultProcessor.cs | 3 - .../GcraIntegrationTests.cs | 65 -------- .../GcraTestServer.cs | 87 ---------- .../GcraUnitTests.cs | 155 ------------------ .../ResultProcessorUnitTests/GcraRateLimit.cs | 80 --------- .../GcraRateLimitRoundTrip.cs | 71 -------- 19 files changed, 6 insertions(+), 740 deletions(-) delete mode 100644 src/StackExchange.Redis/Gcra.GcraMessage.cs delete mode 100644 src/StackExchange.Redis/Gcra.GcraRateLimitResult.cs delete mode 100644 src/StackExchange.Redis/Gcra.ResultProcessor.cs delete mode 100644 tests/StackExchange.Redis.Tests/GcraIntegrationTests.cs delete mode 100644 tests/StackExchange.Redis.Tests/GcraTestServer.cs delete mode 100644 tests/StackExchange.Redis.Tests/GcraUnitTests.cs delete mode 100644 tests/StackExchange.Redis.Tests/ResultProcessorUnitTests/GcraRateLimit.cs delete mode 100644 tests/StackExchange.Redis.Tests/RoundTripUnitTests/GcraRateLimitRoundTrip.cs diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 3202de4e8..2f14c9752 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -10,7 +10,6 @@ Current package versions: - 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 +21,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/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); - } -} -*/ From 141fa37d481995af0be884d01533ab1af9926a31 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 8 May 2026 10:30:44 +0100 Subject: [PATCH 4/5] prefer slnx and re-apply folder fixes --- ...e.Redis.sln => StackExchange.Redis.sln.old | 0 StackExchange.Redis.slnx | 51 +------------------ 2 files changed, 2 insertions(+), 49 deletions(-) rename StackExchange.Redis.sln => StackExchange.Redis.sln.old (100%) diff --git a/StackExchange.Redis.sln b/StackExchange.Redis.sln.old similarity index 100% rename from StackExchange.Redis.sln rename to StackExchange.Redis.sln.old 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 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From cb01a1c9c9c66ac59e677f0e4687f091c65270a6 Mon Sep 17 00:00:00 2001 From: Marc Gravell Date: Fri, 8 May 2026 10:34:13 +0100 Subject: [PATCH 5/5] release notes --- docs/ReleaseNotes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 2f14c9752..20d650658 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -8,6 +8,7 @@ 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)) - 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))