From 3403c1d8ffdbe276596e4ce9588a76fb600280c6 Mon Sep 17 00:00:00 2001 From: Daria Korenieva Date: Thu, 23 Jul 2026 13:09:08 -0700 Subject: [PATCH 1/4] docs: add Valkey compatibility notes and CI coverage for redis-backed plugins Valkey is RESP-compatible, so the redis-backed plugins (limit-count, limit-req, limit-conn, graphql-limit-count, ai-rate-limiting, ai-cache, and openid-connect session storage) already work against it unmodified. This makes the compatibility official: - Add a "Valkey Compatibility" note to each redis-backed plugin doc. The ai-cache note calls out that the semantic (L2) layer needs the RediSearch module, which Valkey does not provide. The openid-connect note references the session.redis.* attributes it actually uses. - Add an apisix_valkey service (valkey/valkey:8) on port 6381 to ci/pod/docker-compose.common.yml. - Add t/plugin/limit-count-valkey.t, mirroring the non-auth subset of limit-count-redis.t against the Valkey service. - Flush port 6381 in the test_redis helper's default port list. Ref #13738 --- ci/pod/docker-compose.common.yml | 7 + docs/en/latest/plugins/ai-cache.md | 6 + docs/en/latest/plugins/ai-rate-limiting.md | 4 + docs/en/latest/plugins/graphql-limit-count.md | 4 + docs/en/latest/plugins/limit-conn.md | 4 + docs/en/latest/plugins/limit-count.md | 4 + docs/en/latest/plugins/limit-req.md | 4 + docs/en/latest/plugins/openid-connect.md | 4 + t/lib/test_redis.lua | 2 +- t/plugin/limit-count-valkey.t | 283 ++++++++++++++++++ 10 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 t/plugin/limit-count-valkey.t diff --git a/ci/pod/docker-compose.common.yml b/ci/pod/docker-compose.common.yml index 50d8004c53d1..8398f4b5f007 100644 --- a/ci/pod/docker-compose.common.yml +++ b/ci/pod/docker-compose.common.yml @@ -93,6 +93,13 @@ services: - ./t/certs:/certs + ## Valkey + apisix_valkey: + image: valkey/valkey:8 + restart: unless-stopped + ports: + - "6381:6379" + ## Redis cluster redis-cluster: image: vishnunair/docker-redis-cluster:latest diff --git a/docs/en/latest/plugins/ai-cache.md b/docs/en/latest/plugins/ai-cache.md index 907b8d177719..69ed8d46b1bc 100644 --- a/docs/en/latest/plugins/ai-cache.md +++ b/docs/en/latest/plugins/ai-cache.md @@ -63,6 +63,12 @@ Even with `cache_key.share_across_routes` enabled, the cache key identifies the ::: +## Valkey Compatibility + +[Valkey](https://valkey.io/) works as a drop-in replacement for the exact (L1) cache layer. Because Valkey implements the Redis serialization protocol (RESP), no code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. + +The semantic (L2) layer is an exception: it requires the RediSearch module (available in [Redis Stack](https://redis.io/docs/stack/)), which Valkey does not provide. Use Valkey only when `layers` is omitted or contains only `"exact"`. + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/ai-rate-limiting.md b/docs/en/latest/plugins/ai-rate-limiting.md index acf1367a9bb1..801255f72ccb 100644 --- a/docs/en/latest/plugins/ai-rate-limiting.md +++ b/docs/en/latest/plugins/ai-rate-limiting.md @@ -40,6 +40,10 @@ import TabItem from '@theme/TabItem'; The `ai-rate-limiting` Plugin enforces token-based rate limiting for requests sent to LLM services. It helps manage API usage by controlling the number of tokens consumed within a specified time frame, ensuring fair resource allocation and preventing excessive load on the service. It is often used with [`ai-proxy`](./ai-proxy.md) or [`ai-proxy-multi`](./ai-proxy-multi.md) Plugin. +## Valkey Compatibility + +[Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/graphql-limit-count.md b/docs/en/latest/plugins/graphql-limit-count.md index 2633131ca6a5..389e6e158c3e 100644 --- a/docs/en/latest/plugins/graphql-limit-count.md +++ b/docs/en/latest/plugins/graphql-limit-count.md @@ -48,6 +48,10 @@ You may see the following rate limiting headers in the response: - `X-RateLimit-Remaining`: the remaining quota - `X-RateLimit-Reset`: number of seconds left for the counter to reset +## Valkey Compatibility + +[Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. + ## Attributes This Plugin shares the same schema as the [limit-count](./limit-count.md) Plugin. Refer to that page for the full attribute reference. Key attributes are listed below. diff --git a/docs/en/latest/plugins/limit-conn.md b/docs/en/latest/plugins/limit-conn.md index a92ecf2db36e..ffd149a3054e 100644 --- a/docs/en/latest/plugins/limit-conn.md +++ b/docs/en/latest/plugins/limit-conn.md @@ -37,6 +37,10 @@ import TabItem from '@theme/TabItem'; The `limit-conn` Plugin limits the rate of requests by the number of concurrent connections. Requests exceeding the threshold will be delayed or rejected based on the configuration, ensuring controlled resource usage and preventing overload. +## Valkey Compatibility + +[Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/limit-count.md b/docs/en/latest/plugins/limit-count.md index 38f1d4f59310..9b0ea1f8a3a2 100644 --- a/docs/en/latest/plugins/limit-count.md +++ b/docs/en/latest/plugins/limit-count.md @@ -43,6 +43,10 @@ You may see the following rate limiting headers in the response: * `X-RateLimit-Remaining`: the remaining quota * `X-RateLimit-Reset`: number of seconds left for the counter to reset +## Valkey Compatibility + +[Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/limit-req.md b/docs/en/latest/plugins/limit-req.md index 59c65d8e547f..4fe517c2d7e3 100644 --- a/docs/en/latest/plugins/limit-req.md +++ b/docs/en/latest/plugins/limit-req.md @@ -45,6 +45,10 @@ The `limit-req` Plugin supports two modes of rate limiting: * **Local rate limiting**: Limits are enforced independently on each gateway instance. Each instance maintains its own counters, so the effective limit is roughly (limit × number of instances) when traffic is spread across instances. This is the default when no `policy` is set or when `policy` is `local`. * **Redis-based rate limiting**: Limits are shared across all gateway instances through Redis. All instances share the same quota, so the configured limit applies to all gateway instances. +## Valkey Compatibility + +[Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/openid-connect.md b/docs/en/latest/plugins/openid-connect.md index 5bb74efd3fad..2a728b9895bb 100644 --- a/docs/en/latest/plugins/openid-connect.md +++ b/docs/en/latest/plugins/openid-connect.md @@ -38,6 +38,10 @@ import TabItem from '@theme/TabItem'; The `openid-connect` Plugin supports the integration with [OpenID Connect (OIDC)](https://openid.net/connect/) identity providers, such as Keycloak, Auth0, Microsoft Entra ID, Google, Okta, and more. It allows APISIX to authenticate clients and obtain their information from the identity provider before allowing or denying their access to upstream protected resources. +## Valkey Compatibility + +[Valkey](https://valkey.io/) is fully compatible with this Plugin's Redis session storage. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — when `session.storage` is `redis`, point `session.redis.host`, `session.redis.port`, `session.redis.password`, and the other `session.redis.*` attributes at your Valkey deployment. + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/t/lib/test_redis.lua b/t/lib/test_redis.lua index 7f097a4608c9..70cfdbe316bb 100644 --- a/t/lib/test_redis.lua +++ b/t/lib/test_redis.lua @@ -25,7 +25,7 @@ local _M = {} -- 6479 is the sentinel master used by the redis-sentinel policies; leftover -- counters there survive CI re-runs if it is not flushed -local DEFAULT_PORTS = {6379, 6479, 5000, 5001, 5002, 5003, 5004, 5005, 5006} +local DEFAULT_PORTS = {6379, 6381, 6479, 5000, 5001, 5002, 5003, 5004, 5005, 5006} local DEFAULT_HOST = "127.0.0.1" local function log_warn(...) diff --git a/t/plugin/limit-count-valkey.t b/t/plugin/limit-count-valkey.t new file mode 100644 index 000000000000..dac8fae24fee --- /dev/null +++ b/t/plugin/limit-count-valkey.t @@ -0,0 +1,283 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +BEGIN { + if ($ENV{TEST_NGINX_CHECK_LEAK}) { + $SkipReason = "unavailable for the hup tests"; + + } else { + $ENV{TEST_NGINX_USE_HUP} = 1; + undef $ENV{TEST_NGINX_USE_STAP}; + } +} + +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_shuffle(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->error_log && !$block->no_error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } + + my $extra_init_worker_by_lua = $block->extra_init_worker_by_lua // ""; + $extra_init_worker_by_lua .= <<_EOC_; + require("lib.test_redis").flush_port("127.0.0.1", 6381) +_EOC_ + + $block->set_value("extra_init_worker_by_lua", $extra_init_worker_by_lua); +}); + +run_tests; + +__DATA__ + +=== TEST 1: set route with Valkey (redis policy, port 6381) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "policy": "redis", + "redis_host": "127.0.0.1", + "redis_port": 6381, + "redis_timeout": 1001 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: up the limit (Valkey) +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello"] +--- error_code eval +[200, 200, 503] + + + +=== TEST 3: up the limit with different URIs (Valkey) +--- pipelined_requests eval +["GET /hello1", "GET /hello", "GET /hello2", "GET /hello", "GET /hello"] +--- error_code eval +[404, 200, 404, 200, 503] + + + +=== TEST 4: set route with invalid redis host (Valkey) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "policy": "redis" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"failed to check the configuration of plugin limit-count err: then clause did not match"} + + + +=== TEST 5: set route with allow_degradation (Valkey) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "policy": "redis", + "redis_host": "127.0.0.1", + "redis_port": 6381, + "redis_timeout": 1001, + "allow_degradation": true + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 6: verify allow_degradation works (Valkey) +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello"] +--- error_code eval +[200, 200, 503] + + + +=== TEST 7: set route with group (Valkey) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503, + "key": "remote_addr", + "policy": "redis", + "redis_host": "127.0.0.1", + "redis_port": 6381, + "redis_timeout": 1001, + "group": "valkey-test-group" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 8: up the limit with group (Valkey) +--- pipelined_requests eval +["GET /hello", "GET /hello", "GET /hello"] +--- error_code eval +[200, 200, 503] + + + +=== TEST 9: flush Valkey script cache +--- config + location /t { + content_by_lua_block { + local redis = require("resty.redis") + local red = redis:new() + red:set_timeout(1000) + local ok, err = red:connect("127.0.0.1", 6381) + if not ok then + ngx.say("failed to connect: ", err) + return + end + red:script("FLUSH") + red:flushall() + red:set_keepalive(10000, 100) + ngx.say("done") + } + } +--- response_body +done + + + +=== TEST 10: evalsha NOSCRIPT fallback after SCRIPT FLUSH (Valkey) +--- request +GET /hello +--- error_code: 200 +--- grep_error_log eval +qr/redis evalsha failed:.*Falling back to eval/ +--- grep_error_log_out +redis evalsha failed: NOSCRIPT No matching script. Please use EVAL.. Falling back to eval From db638ceebbb311b1d753a79eac2853ec791aac82 Mon Sep 17 00:00:00 2001 From: Daria Korenieva Date: Thu, 23 Jul 2026 13:12:53 -0700 Subject: [PATCH 2/4] docs: add Valkey connection examples to compatibility notes Each Valkey Compatibility note now includes a JSON connection example. The flat-schema plugins (limit-count, limit-req, limit-conn, graphql-limit-count, ai-rate-limiting) show the redis_* fields; ai-cache shows an L1-only ("exact") example; openid-connect shows the nested session.redis.* form it actually uses. Ref #13738 --- docs/en/latest/plugins/ai-cache.md | 13 +++++++++++++ docs/en/latest/plugins/ai-rate-limiting.md | 13 +++++++++++++ docs/en/latest/plugins/graphql-limit-count.md | 13 +++++++++++++ docs/en/latest/plugins/limit-conn.md | 13 +++++++++++++ docs/en/latest/plugins/limit-count.md | 13 +++++++++++++ docs/en/latest/plugins/limit-req.md | 13 +++++++++++++ docs/en/latest/plugins/openid-connect.md | 17 +++++++++++++++++ 7 files changed, 95 insertions(+) diff --git a/docs/en/latest/plugins/ai-cache.md b/docs/en/latest/plugins/ai-cache.md index 69ed8d46b1bc..6552e47f3499 100644 --- a/docs/en/latest/plugins/ai-cache.md +++ b/docs/en/latest/plugins/ai-cache.md @@ -69,6 +69,19 @@ Even with `cache_key.share_across_routes` enabled, the cache key identifies the The semantic (L2) layer is an exception: it requires the RediSearch module (available in [Redis Stack](https://redis.io/docs/stack/)), which Valkey does not provide. Use Valkey only when `layers` is omitted or contains only `"exact"`. +For example, to back the exact (L1) cache with a Valkey instance: + +```json +{ + "layers": ["exact"], + "redis_host": "valkey.example.com", + "redis_port": 6379, + "redis_password": "secret", + "redis_database": 0, + "redis_timeout": 1000 +} +``` + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/ai-rate-limiting.md b/docs/en/latest/plugins/ai-rate-limiting.md index 801255f72ccb..664227475c60 100644 --- a/docs/en/latest/plugins/ai-rate-limiting.md +++ b/docs/en/latest/plugins/ai-rate-limiting.md @@ -44,6 +44,19 @@ The `ai-rate-limiting` Plugin enforces token-based rate limiting for requests se [Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. +For example, to point the `redis` policy at a Valkey instance: + +```json +{ + "policy": "redis", + "redis_host": "valkey.example.com", + "redis_port": 6379, + "redis_password": "secret", + "redis_database": 0, + "redis_timeout": 1000 +} +``` + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/graphql-limit-count.md b/docs/en/latest/plugins/graphql-limit-count.md index 389e6e158c3e..76af1434f11f 100644 --- a/docs/en/latest/plugins/graphql-limit-count.md +++ b/docs/en/latest/plugins/graphql-limit-count.md @@ -52,6 +52,19 @@ You may see the following rate limiting headers in the response: [Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. +For example, to point the `redis` policy at a Valkey instance: + +```json +{ + "policy": "redis", + "redis_host": "valkey.example.com", + "redis_port": 6379, + "redis_password": "secret", + "redis_database": 0, + "redis_timeout": 1000 +} +``` + ## Attributes This Plugin shares the same schema as the [limit-count](./limit-count.md) Plugin. Refer to that page for the full attribute reference. Key attributes are listed below. diff --git a/docs/en/latest/plugins/limit-conn.md b/docs/en/latest/plugins/limit-conn.md index ffd149a3054e..73c27df7d292 100644 --- a/docs/en/latest/plugins/limit-conn.md +++ b/docs/en/latest/plugins/limit-conn.md @@ -41,6 +41,19 @@ The `limit-conn` Plugin limits the rate of requests by the number of concurrent [Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. +For example, to point the `redis` policy at a Valkey instance: + +```json +{ + "policy": "redis", + "redis_host": "valkey.example.com", + "redis_port": 6379, + "redis_password": "secret", + "redis_database": 0, + "redis_timeout": 1000 +} +``` + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/limit-count.md b/docs/en/latest/plugins/limit-count.md index 9b0ea1f8a3a2..30063cb2fb29 100644 --- a/docs/en/latest/plugins/limit-count.md +++ b/docs/en/latest/plugins/limit-count.md @@ -47,6 +47,19 @@ You may see the following rate limiting headers in the response: [Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. +For example, to point the `redis` policy at a Valkey instance: + +```json +{ + "policy": "redis", + "redis_host": "valkey.example.com", + "redis_port": 6379, + "redis_password": "secret", + "redis_database": 0, + "redis_timeout": 1000 +} +``` + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/limit-req.md b/docs/en/latest/plugins/limit-req.md index 4fe517c2d7e3..e6c119b8ffb7 100644 --- a/docs/en/latest/plugins/limit-req.md +++ b/docs/en/latest/plugins/limit-req.md @@ -49,6 +49,19 @@ The `limit-req` Plugin supports two modes of rate limiting: [Valkey](https://valkey.io/) is fully compatible with this Plugin. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — configure the same `redis_host`, `redis_port`, `redis_password`, and other Redis attributes to point to your Valkey deployment. +For example, to point the `redis` policy at a Valkey instance: + +```json +{ + "policy": "redis", + "redis_host": "valkey.example.com", + "redis_port": 6379, + "redis_password": "secret", + "redis_database": 0, + "redis_timeout": 1000 +} +``` + ## Attributes | Name | Type | Required | Default | Valid values | Description | diff --git a/docs/en/latest/plugins/openid-connect.md b/docs/en/latest/plugins/openid-connect.md index 2a728b9895bb..3a8e3316056f 100644 --- a/docs/en/latest/plugins/openid-connect.md +++ b/docs/en/latest/plugins/openid-connect.md @@ -42,6 +42,23 @@ The `openid-connect` Plugin supports the integration with [OpenID Connect (OIDC) [Valkey](https://valkey.io/) is fully compatible with this Plugin's Redis session storage. Because Valkey implements the Redis serialization protocol (RESP), you can use a Valkey instance as a drop-in replacement wherever a Redis instance is configured. No code or schema changes are required — when `session.storage` is `redis`, point `session.redis.host`, `session.redis.port`, `session.redis.password`, and the other `session.redis.*` attributes at your Valkey deployment. +For example, to store sessions in a Valkey instance: + +```json +{ + "session": { + "secret": "your-session-secret-min-16-chars", + "storage": "redis", + "redis": { + "host": "valkey.example.com", + "port": 6379, + "password": "secret", + "database": 0 + } + } +} +``` + ## Attributes | Name | Type | Required | Default | Valid values | Description | From 67aaa3f8daedcdf15056bc4f5e8baf9410a04984 Mon Sep 17 00:00:00 2001 From: Daria Korenieva Date: Thu, 23 Jul 2026 14:16:55 -0700 Subject: [PATCH 3/4] ci: move Valkey service next to Redis in common compose Place apisix_valkey directly after apisix_redis so the two data-store services sit together. Add a comment noting the Valkey service omits the TLS/ACL command and cert volume that apisix_redis carries, because the Valkey tests only exercise the plaintext, non-auth path. --- ci/pod/docker-compose.common.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ci/pod/docker-compose.common.yml b/ci/pod/docker-compose.common.yml index 8398f4b5f007..0d387e2fe2ec 100644 --- a/ci/pod/docker-compose.common.yml +++ b/ci/pod/docker-compose.common.yml @@ -35,6 +35,16 @@ services: - "6379:6379" - "6380:6380" + ## Valkey + # No TLS/ACL config is needed here (unlike apisix_redis) because the Valkey + # tests only cover the plaintext, non-auth path. Add a command/volumes block + # mirroring apisix_redis if TLS or ACL coverage is introduced later. + apisix_valkey: + image: valkey/valkey:8 + restart: unless-stopped + ports: + - "6381:6379" + ## Etcd etcd_old: image: bitnamilegacy/etcd:3.3.8 @@ -93,13 +103,6 @@ services: - ./t/certs:/certs - ## Valkey - apisix_valkey: - image: valkey/valkey:8 - restart: unless-stopped - ports: - - "6381:6379" - ## Redis cluster redis-cluster: image: vishnunair/docker-redis-cluster:latest From 15afd5ed15c807d0654fcba2aefce59d20456f1f Mon Sep 17 00:00:00 2001 From: Daria Korenieva Date: Fri, 24 Jul 2026 10:42:26 -0700 Subject: [PATCH 4/4] test: address review feedback on limit-count-valkey.t - Check return values of SCRIPT FLUSH / FLUSHALL in TEST 9 so TEST 10's NOSCRIPT-fallback assertion can't pass vacuously on a failed flush - Rename TEST 6 to reflect it only proves rate limiting with the flag set, not degradation behavior (unreachable Valkey is never exercised) - Document why no_shuffle() is required (order-dependent tests) --- t/plugin/limit-count-valkey.t | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/t/plugin/limit-count-valkey.t b/t/plugin/limit-count-valkey.t index dac8fae24fee..f941bbf0867e 100644 --- a/t/plugin/limit-count-valkey.t +++ b/t/plugin/limit-count-valkey.t @@ -28,6 +28,9 @@ use t::APISIX 'no_plan'; repeat_each(1); no_long_string(); +# Tests are order-dependent: route-setup tests precede their assertion tests +# (TESTs 2/3 depend on TEST 1, TEST 6 on TEST 5, TEST 8 on TEST 7, TEST 10 on TEST 9), +# so shuffling would break them. no_shuffle(); no_root_location(); @@ -192,7 +195,7 @@ passed -=== TEST 6: verify allow_degradation works (Valkey) +=== TEST 6: up the limit with allow_degradation set (Valkey) --- pipelined_requests eval ["GET /hello", "GET /hello", "GET /hello"] --- error_code eval @@ -262,8 +265,16 @@ passed ngx.say("failed to connect: ", err) return end - red:script("FLUSH") - red:flushall() + local ok, err = red:script("FLUSH") + if not ok then + ngx.say("failed to flush scripts: ", err) + return + end + ok, err = red:flushall() + if not ok then + ngx.say("failed to flushall: ", err) + return + end red:set_keepalive(10000, 100) ngx.say("done") }