Skip to content

(fixes): local cache stat test fixes - #1013

Merged
collin-lee merged 2 commits into
envoyproxy:mainfrom
sagarsuperuser:fix/tests
Dec 3, 2025
Merged

collin-lee merged 2 commits into
envoyproxy:mainfrom
sagarsuperuser:fix/tests

Conversation

@sagarsuperuser

@sagarsuperuser sagarsuperuser commented Nov 27, 2025

Copy link
Copy Markdown
Contributor

What was the issue

testLocalCacheStats never ran, the store used a null sink, expected counts were off, and assertions cast to the wrong types, so local cache metrics weren’t actually validated.

Fixes

Run testLocalCacheStats as subtests in each scenario, wire gostats.NewStore to TestStatSink so stats are flushed and visible, correct the expected hit/miss/lookup/expired/entry counts, and assert using uint64 to match emitted metrics.

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
…nfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
@collin-lee
collin-lee merged commit 21e2655 into envoyproxy:main Dec 3, 2025
6 checks passed
timcovar added a commit to goatapp/ratelimit that referenced this pull request Jun 10, 2026
* Add detailed_metric support for xds-config (#465)

Signed-off-by: alekhya.kondapuram <alekhya.kondapuram@salesforce.com>

* Bump actions/setup-go from 2.2.0 to 5.0.0 (#484)

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2.2.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2.2.0...0c52d547c9bc32b1aa3301fd7a9cb496313a4491)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Retry the grpc connection when there's an error (#503)

Signed-off-by: alekhya.kondapuram <alekhya.kondapuram@salesforce.com>

* fix: init gostats to not flood log (#520)

Default gostats store flood messages on stderr except if statd or
if GOSTATS_LOGGING_SINK_DISABLED is set to "true".
This commit init store in a unambiguous way.

Signed-off-by: Guilhem Lettron <guilhem@barpilot.io>

* chore: remove trailing line (#545)

fix #520 trailing line

Signed-off-by: Guilhem Lettron <guilhem@barpilot.io>

* Bump github/codeql-action from 2.2.4 to 3.24.9 (#543)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.2.4 to 3.24.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2.2.4...1b1aada464948af03b950897e5eb522f92603cc2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/checkout from 2.7.0 to 4.1.2 (#541)

Bumps [actions/checkout](https://github.com/actions/checkout) from 2.7.0 to 4.1.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2.7.0...9bb56186c3b09b4f86b1c65136769dd318469633)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump docker/setup-buildx-action from 1.7.0 to 3.2.0 (#539)

Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 1.7.0 to 3.2.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/f211e3e9ded2d9377c8cadc4489a4e38014bc4c9...2b51285047da1547ffb1b2203d8be4c0af6b1f20)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* config: fix detailed metric keys missing in leading keys (#528)

only the last descriptor uses the provided descriptor value in case of
detailed metrics. when traversing the list of descriptors, the code
"loses" the previous keys. this leads to metrics like:
"test-domain.first-key_.second-key_second-value", where the last
descriptor properly uses the detailed metric descriptor value, but all
other descriptors (the first one here) are missing the value.

this patch introduces a new string builder, that builds the detailed
metric as the iteration of the input descriptor is happening.
a unit test is attached to show the behavior. it fails without the new
code, and successfully preserves all descriptor keys with the patched
code.

Signed-off-by: Johannes Brüderl <johannes.bruederl@gmail.com>

* fix(Stats): restore stdout stats option, allow flush interval to be configured with env vars (#549)

Signed-off-by: Zak Henry <zak@cloudnc.com>

* Bump github.com/stretchr/testify from 1.8.4 to 1.9.0 (#548)

Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.4 to 1.9.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/setup-python from 5.0.0 to 5.1.0 (#546)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.0.0 to 5.1.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/0a5c61591373683505ea898e09a3ea4f39ef2b9c...82c7e631bb3cdc910f68e0081d67478d79c6982d)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/otel/sdk from 1.21.0 to 1.24.0 (#547)

Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.21.0 to 1.24.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.21.0...v1.24.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump google.golang.org/grpc in /examples/xds-sotw-config-server (#537)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.59.0 to 1.62.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.59.0...v1.62.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.21.5 to 1.22.1 (#536)

Bumps golang from 1.21.5 to 1.22.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.21.5 to 1.22.1 in /examples/xds-sotw-config-server (#534)

Bumps golang from 1.21.5 to 1.22.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump google.golang.org/grpc from 1.59.0 to 1.63.0 (#551)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.59.0 to 1.63.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.59.0...v1.63.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Update alpine to 3.18.6 to address openssl vulnerability (#531)

Signed-off-by: alexhwcheng <121998721+alexhwcheng@users.noreply.github.com>

* use `google.golang.org/protobuf` instead of `github.com/golang/protobuf` (#553)

Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>

* Add support for starting the server on a unix domain sockets (#542)

* Add support for unix domain sockets

Signed-off-by: Alex Reid <areid@akamai.com>

* Add documentation

Signed-off-by: Alex Reid <areid@akamai.com>

---------

Signed-off-by: Alex Reid <areid@akamai.com>

* Bump github/codeql-action from 3.24.9 to 3.24.10 (#554)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.9 to 3.24.10.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1b1aada464948af03b950897e5eb522f92603cc2...4355270be187e1b672a7a1c7c7bae5afdc1ab94a)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.1 to 1.22.2 (#560)

Bumps golang from 1.22.1 to 1.22.2.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump google.golang.org/grpc in /examples/xds-sotw-config-server (#562)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.62.1 to 1.63.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.62.1...v1.63.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump github.com/lyft/gostats from 0.4.12 to 0.4.13 (#558)

Bumps [github.com/lyft/gostats](https://github.com/lyft/gostats) from 0.4.12 to 0.4.13.
- [Release notes](https://github.com/lyft/gostats/releases)
- [Commits](https://github.com/lyft/gostats/compare/v0.4.12...v0.4.13)

---
updated-dependencies:
- dependency-name: github.com/lyft/gostats
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.1 to 1.22.2 in /examples/xds-sotw-config-server (#561)

Bumps golang from 1.22.1 to 1.22.2.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/upload-artifact from 3.1.0 to 4.3.3 (#577)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3.1.0 to 4.3.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/3cea5372237819ed00197afe530f5a7ea3e805c8...65462800fd760344b1a7b4382951275a0abb4808)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump github.com/lyft/gostats from 0.4.13 to 0.4.14 (#609)

Bumps [github.com/lyft/gostats](https://github.com/lyft/gostats) from 0.4.13 to 0.4.14.
- [Release notes](https://github.com/lyft/gostats/releases)
- [Commits](https://github.com/lyft/gostats/compare/v0.4.13...v0.4.14)

---
updated-dependencies:
- dependency-name: github.com/lyft/gostats
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump github/codeql-action from 3.24.10 to 3.25.7 (#608)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.24.10 to 3.25.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/4355270be187e1b672a7a1c7c7bae5afdc1ab94a...f079b8493333aace61c81488f8bd40919487bd9f)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc (#604)

Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) from 1.21.0 to 1.27.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.21.0...v1.27.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump alpine from `51b6726` to `77726ef` in /integration-test (#600)

Bumps alpine from `51b6726` to `77726ef`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump alpine from 3.16 to 3.20 in /examples/xds-sotw-config-server (#599)

Bumps alpine from 3.16 to 3.20.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump alpine from 3.18.6 to 3.20.0 (#598)

Bumps alpine from 3.18.6 to 3.20.0.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Including the name in the ratelimit descriptor so that it is returned… (#596)

Signed-off-by: jespersoderlund <jesper.soderlund@gmail.com>

* Bump golang.org/x/net from 0.25.0 to 0.26.0 (#611)

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.25.0 to 0.26.0.
- [Commits](https://github.com/golang/net/compare/v0.25.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump google.golang.org/grpc in /examples/xds-sotw-config-server (#591)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.63.0 to 1.64.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.63.0...v1.64.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ossf/scorecard-action from 2.3.1 to 2.3.3 (#590)

Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.3.1 to 2.3.3.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/0864cf19026789058feabb7e87baa5f140aac736...dc50aa9510b46c811795eb24b2f1ba02a914e534)

---
updated-dependencies:
- dependency-name: ossf/scorecard-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/checkout from 4.1.2 to 4.1.6 (#589)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9bb56186c3b09b4f86b1c65136769dd318469633...a5ac7e51b41094c92402da3b24376905380afc29)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.2 to 1.22.3 in /examples/xds-sotw-config-server (#583)

Bumps golang from 1.22.2 to 1.22.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/setup-go from 5.0.0 to 5.0.1 (#578)

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/0c52d547c9bc32b1aa3301fd7a9cb496313a4491...cdcb36043654635271a94b9a6d1392de5bb323a7)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.2 to 1.22.3 (#584)

Bumps golang from 1.22.2 to 1.22.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add TLS support to memcache (#594)

Memcache supports TLS connections for client-server communication. Add
support for configuring a TLS connection to memcached using TLS in a way
that mirrors the existing redis configuration options.

Also, ensure that configuration options that apply to the "normal"
enumeration of host:port pairs also apply to the DNS SRV discovery
mechanism. I don't see a reason why they should not.

Signed-off-by: Ian Kerins <git@isk.haus>

* fix(redis): use logger instead of println (#606)

Signed-off-by: frazao.jhonn <frazao.jhonn@gmail.com>

* Initial integration of DogStatsD (#585)

* Initial integration of DogStatsD

including something called mogrifiers

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* fix style errors in doc

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* Fix variable name in README

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* Add validations and test cases

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* Amendment with improvements

- handle out of bounds match in pattern handler
- make it an error if both statsd sink are enabled
- improve error wording
- add more test cases

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* fix incorrect timer manipulation

Signed-off-by: Josh Jaques <jjaques@gmail.com>

---------

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* Bump google.golang.org/protobuf from 1.34.1 to 1.34.2 (#622)

Bumps google.golang.org/protobuf from 1.34.1 to 1.34.2.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump github/codeql-action from 3.25.7 to 3.25.10 (#621)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.7 to 3.25.10.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/f079b8493333aace61c81488f8bd40919487bd9f...23acc5c183826b7a8a97bce3cecc52db901f8251)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/checkout from 4.1.6 to 4.1.7 (#620)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.6 to 4.1.7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/a5ac7e51b41094c92402da3b24376905380afc29...692973e3d937129bcbf40652eb9f2f61becf3332)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.3 to 1.22.4 in /examples/xds-sotw-config-server (#618)

Bumps golang from 1.22.3 to 1.22.4.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.3 to 1.22.4 (#617)

Bumps golang from 1.22.3 to 1.22.4.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc (#615)

Bumps [go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc](https://github.com/open-telemetry/opentelemetry-go-contrib) from 0.46.1 to 0.52.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.46.1...zpages/v0.52.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump github.com/alicebob/miniredis/v2 from 2.31.0 to 2.33.0 (#614)

Bumps [github.com/alicebob/miniredis/v2](https://github.com/alicebob/miniredis) from 2.31.0 to 2.33.0.
- [Release notes](https://github.com/alicebob/miniredis/releases)
- [Changelog](https://github.com/alicebob/miniredis/blob/master/CHANGELOG.md)
- [Commits](https://github.com/alicebob/miniredis/compare/v2.31.0...v2.33.0)

---
updated-dependencies:
- dependency-name: github.com/alicebob/miniredis/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#613)

Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.21.0 to 1.27.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.21.0...v1.27.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: add log error in should ratelimit (#612)

Signed-off-by: frazao.jhonn <frazao.jhonn@gmail.com>

* feat: add stat for domain not found (#624)

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* feat(dogstatsd_sink): support EXTRA_TAGS (#625)

When using the godogstats sink, previously the EXTRA_TAGS would not be
emitted as datadog tags.

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* fix(dogstatsd_sink): switch from pure map to list of key/value pairs (#627)

The test for Two mogrifiers: First match has flaky results because the
underlying iteration order from the map is not guaranteed. Sometimes
matching first and sometimes matching second mogrifier.

Having a consistent mogrifier order is required for some types of
priority based manipulation, and that is how the keys are defined in the
environment, so changing the overall implementation to a slice to
maintain ordered iteration is the best option. And can be done by only
changing the internal private types.

Signed-off-by: Josh Jaques <jjaques@gmail.com>

* Bump alpine from 3.20.0 to 3.20.2 (#661)

Bumps alpine from 3.20.0 to 3.20.2.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump docker/setup-qemu-action from 1.2.0 to 3.2.0 (#660)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 1.2.0 to 3.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/27d0a4f181a40b142cce983c5393082c365d1480...49b3bc8e6bdd4a60e6116a5414239cba5943d3cf)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump github/codeql-action from 3.25.10 to 3.25.15 (#659)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.10 to 3.25.15.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/23acc5c183826b7a8a97bce3cecc52db901f8251...afb54ba388a7dca6ecae48f608c4ff05ff4cc77a)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump docker/setup-buildx-action from 3.2.0 to 3.5.0 (#658)

Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.2.0 to 3.5.0.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/2b51285047da1547ffb1b2203d8be4c0af6b1f20...aa33708b10e362ff993539393ff100fa93ed6a27)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump alpine from `77726ef` to `0a4eaa0` in /integration-test (#657)

Bumps alpine from `77726ef` to `0a4eaa0`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* improve go version, to fix security issue. (#656)

Signed-off-by: John <john.zheng@hp.com>

* Bump actions/setup-python from 5.1.0 to 5.1.1 (#649)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/82c7e631bb3cdc910f68e0081d67478d79c6982d...39cd14951b08e74b54015e9e001cdefcf80e669f)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump google.golang.org/grpc in /examples/xds-sotw-config-server (#646)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.65.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.65.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/upload-artifact from 4.3.3 to 4.3.4 (#644)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.3 to 4.3.4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/65462800fd760344b1a7b4382951275a0abb4808...0b2256b8c012f0828dc542b3febcab082c67f72b)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/otel from 1.27.0 to 1.28.0 (#641)

Bumps [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) from 1.27.0 to 1.28.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.27.0...v1.28.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/otel/trace from 1.27.0 to 1.28.0 (#640)

Bumps [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) from 1.27.0 to 1.28.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.27.0...v1.28.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* runner: close rate limiter on Stop (#635)

redis client is never closed, even after graceful shutdown. It is
possible to observe this by creating a runner, and calling Stop(), and
stopping redis. Runner is expected to be stopped, and all components
closed, however still logs can be seen that redis is unavailable.

this patch allows runner to actually gracefully close rate limit
implementations, so all components are closed gracefully on Stop.

Signed-off-by: Johannes Brüderl <johannes.bruederl@gmail.com>

* Add CertProvider to hot reload TLS certs for gRPC service (#587)

Signed-off-by: Janis Meybohm <jmeybohm@wikimedia.org>

* Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc (#639)

Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) from 1.27.0 to 1.28.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.27.0...v1.28.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump golang from 1.22.4 to 1.22.5 in /examples/xds-sotw-config-server (#647)

Bumps golang from 1.22.4 to 1.22.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore: run gofumpt (#664)

Signed-off-by: zirain <zirain2009@gmail.com>

* Bump golang in /examples/xds-sotw-config-server (#674)

Bumps golang from `fcae9e0` to `86a3c48`.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump google.golang.org/grpc from 1.64.0 to 1.65.0 (#673)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.64.0 to 1.65.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.64.0...v1.65.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc (#672)

Bumps [go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc](https://github.com/open-telemetry/opentelemetry-go-contrib) from 0.52.0 to 0.53.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.52.0...zpages/v0.53.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump actions/upload-artifact from 4.3.4 to 4.3.6 (#679)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.4 to 4.3.6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/0b2256b8c012f0828dc542b3febcab082c67f72b...834a144ee995460fba8ed112a2fc961b36a5ec5a)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang in /examples/xds-sotw-config-server (#676)

Bumps golang from 1.22.5 to 1.22.6.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.22.5 to 1.22.6 (#677)

Bumps golang from 1.22.5 to 1.22.6.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github/codeql-action from 3.25.15 to 3.26.0 (#678)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.15 to 3.26.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/afb54ba388a7dca6ecae48f608c4ff05ff4cc77a...eb055d739abdc2e8de2e5f4ba1a8b246daa779aa)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump actions/setup-go from 5.0.1 to 5.0.2 (#666)

Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/cdcb36043654635271a94b9a6d1392de5bb323a7...0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump docker/setup-buildx-action from 3.5.0 to 3.6.1 (#667)

Bumps [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) from 3.5.0 to 3.6.1.
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](https://github.com/docker/setup-buildx-action/compare/aa33708b10e362ff993539393ff100fa93ed6a27...988b5a0280414f521da01fcc63a27aeeb4b104db)

---
updated-dependencies:
- dependency-name: docker/setup-buildx-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump ossf/scorecard-action from 2.3.3 to 2.4.0 (#669)

Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.3.3 to 2.4.0.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](https://github.com/ossf/scorecard-action/compare/dc50aa9510b46c811795eb24b2f1ba02a914e534...62b2cac7ed8198b15735ed49ab1e5cf35480ba46)

---
updated-dependencies:
- dependency-name: ossf/scorecard-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* implemnt Prometheus sink (#681)

Signed-off-by: zirain <zirain2009@gmail.com>

* build(deps): bump alpine from 3.20.2 to 3.20.3 (#701)

Bumps alpine from 3.20.2 to 3.20.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump alpine in /integration-test (#769)

Bumps alpine from `0a4eaa0` to `1e42bbe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump alpine from `beefdbd` to `1e42bbe` (#768)

Bumps alpine from `beefdbd` to `1e42bbe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.22.6 to 1.23.3 (#763)

Bumps golang from 1.22.6 to 1.23.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang in /examples/xds-sotw-config-server (#762)

Bumps golang from 1.22.6 to 1.23.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump github/codeql-action from 3.26.0 to 3.27.5 (#772)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.0 to 3.27.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/eb055d739abdc2e8de2e5f4ba1a8b246daa779aa...f09c1c0a94de965c15400f5634aa42fac8fb8f88)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from `d56c3e0` to `73f06be` (#771)

Bumps golang from `d56c3e0` to `73f06be`.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang in /examples/xds-sotw-config-server (#770)

Bumps golang from `d56c3e0` to `73f06be`.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix go-fumpt args (#694)

Signed-off-by: zirain <zirain2009@gmail.com>

* build(deps): bump actions/setup-python from 5.1.1 to 5.3.0 (#766)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.1.1 to 5.3.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/39cd14951b08e74b54015e9e001cdefcf80e669f...0b93645e9fea7318ecaed2b359559ac225c90a2b)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* change to github.com/libp2p/go-reuseport (#774)

Signed-off-by: zirain <zirain2009@gmail.com>

* enable rate limit for month and year (#743)

Signed-off-by: Rico Pahlisch <pahli88@googlemail.com>
Signed-off-by: Rico Pahlisch <rico.pahlisch@grayc.de>

* Fix time conversion for year (#775)

* 365 days in a year, not 356

Signed-off-by: Arko Dasgupta <arko@tetrate.io>

* add prometheus sink debug info (#710)

Signed-off-by: zirain <zirain2009@gmail.com>

* build(deps): bump golang.org/x/net from 0.26.0 to 0.33.0 (#803)

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.26.0 to 0.33.0.
- [Commits](https://github.com/golang/net/compare/v0.26.0...v0.33.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ratelimit: per descriptor hits addend support and prefer uint64 (#802)

* ratelimit: per descriptor hits addend support and prefer uint64

Signed-off-by: wangbaiping/wbpcode <wangbaiping@bytedance.com>

* fix some tests

Signed-off-by: wangbaiping/wbpcode <wangbaiping@bytedance.com>

* refactor and fix

Signed-off-by: wangbaiping/wbpcode <wangbaiping@bytedance.com>

* more different tests

Signed-off-by: wangbaiping/wbpcode <wangbaiping@bytedance.com>

* remove max

Signed-off-by: wangbaiping/wbpcode <wangbaiping@bytedance.com>

---------

Signed-off-by: wangbaiping/wbpcode <wangbaiping@bytedance.com>

* Add configuration XDS_CLIENT_MAX_MSG_SIZE_IN_BYTES (#797)

* Add configuration CONFIG_GRPC_XDS_SERVER_CONNECT_RETRY_INTERVAL

Signed-off-by: Renuka Fernando <renukapiyumal@gmail.com>

* Update configuration name

Signed-off-by: Renuka Fernando <renukapiyumal@gmail.com>

---------

Signed-off-by: Renuka Fernando <renukapiyumal@gmail.com>

* feat: add support for weekly ratelimit unit (#812)

Signed-off-by: Stefan Sedich <stefan.sedich@gmail.com>

* Fix Prometheus metrics (#773)

* chore: replace dots in ipv4 addresses with slashes

Signed-off-by: Rolf Ahrenberg <rolf.ahrenberg@saunalahti.fi>

* chore: tweak default mapper configuration for Prometheus

Signed-off-by: Rolf Ahrenberg <rolf.ahrenberg@saunalahti.fi>

---------

Signed-off-by: Rolf Ahrenberg <rolf.ahrenberg@saunalahti.fi>

* build(deps): bump golang from 1.23.3 to 1.23.5 (#819)

Bumps golang from 1.23.3 to 1.23.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang in /examples/xds-sotw-config-server (#820)

Bumps golang from 1.23.3 to 1.23.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.23.5 to 1.24.0 (#841)

Bumps golang from 1.23.5 to 1.24.0.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump alpine from 3.20.3 to 3.21.3 (#842)

Bumps alpine from 3.20.3 to 3.21.3.

---
updated-dependencies:
- dependency-name: alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.24.0 to 1.24.1 (#858)

Bumps golang from 1.24.0 to 1.24.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix CVE-2025-22870 (#864)

Signed-off-by: Prabhjot <psb@tetrate.io>

* add response details to ratelimit debug log (#870)

Signed-off-by: Ignasi Barrera <nacx@apache.org>

* Close out CVE-2025-22871 in golang and CVE-2025-22872 in golang.org/x/net (#894)

Signed-off-by: Emese Ban <emese.ban@cloudnc.com>
Co-authored-by: Emese Ban <emese.ban@cloudnc.com>

* build(deps): bump github/codeql-action from 3.27.5 to 3.29.2 (#917)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.5 to 3.29.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/f09c1c0a94de965c15400f5634aa42fac8fb8f88...181d5eefc20863364f96762470ba6f862bdef56b)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.29.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.24.3 to 1.24.5 (#919)

Bumps golang from 1.24.3 to 1.24.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang in /examples/xds-sotw-config-server (#920)

Bumps golang from 1.23.5 to 1.24.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: update go-control-plane to support MONTH, WEEK, and YEAR rate limit units (#941)

Updates the go-control-plane/ratelimit module to v0.1.1-0.20250812085011
which includes support for MONTH, WEEK, and YEAR rate limit units that
were added in commit acb1b95dadd4859dea1a0c62f1c0997786c102ec.

This fixes the issue where rate limit configurations using "Month" unit
were failing with "invalid rate limit unit '5'" error because the
previous version did not include the enum definitions for these units.

Fixes envoyproxy/ratelimit#XXX

Signed-off-by: Dmitry K <okdas@pm.me>

* build(deps): bump golang from 1.24.5 to 1.24.6 (#935)

Bumps golang from 1.24.5 to 1.24.6.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump alpine from 3.21.3 to 3.22.2 (#971)

Bumps alpine from 3.21.3 to 3.22.2.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 3.22.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>

* Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements

* feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* [Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* (fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* [ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>

* fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>

* upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (envoyproxy#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (envoyproxy#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (envoyproxy#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (envoyproxy#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (envoyproxy#1196)

Follow-up to envoyproxy#1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after envoyproxy#1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (envoyproxy#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (envoyproxy#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](docker/setup-qemu-action@49b3bc8...96fe6ef)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (envoyproxy#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](actions/checkout@692973e...9c091bb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (envoyproxy#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (envoyproxy#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (envoyproxy#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (envoyproxy#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (envoyproxy#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (envoyproxy#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (envoyproxy#1148)
feat: add retry in init phase instead of panic directly (envoyproxy#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (envoyproxy#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (envoyproxy#1131)

PR envoyproxy#1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (envoyproxy#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (envoyproxy#1110)
Fix Prometheus response time units (envoyproxy#1104)
Dockerfile: add ENTRYPOINT (envoyproxy#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (envoyproxy#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (envoyproxy#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (envoyproxy#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (envoyproxy#1091)
Add integration test for token based quota (envoyproxy#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (envoyproxy#1090)
Add debug logging for quota values (envoyproxy#1089)
Wait for sevices to be up before running tests (envoyproxy#1088)
update otel and fix failing tests (envoyproxy#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (envoyproxy#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (envoyproxy#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (envoyproxy#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (envoyproxy#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (envoyproxy#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (envoyproxy#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (envoyproxy#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (envoyproxy#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (envoyproxy#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (envoyproxy#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (envoyproxy#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (envoyproxy#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (envoyproxy#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (envoyproxy#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (envoyproxy#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (envoyproxy#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (envoyproxy#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (envoyproxy#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
oclaw added a commit to oclaw/ratelimit that referenced this pull request Aug 20, 2026
A counter that goes over the limit poisons the local cache of the
replica that observed it, and a refund that brings the counter back
under the limit previously left every replica answering OVER_LIMIT from
the local cache until the window ended.

With the Redis backend, the decrement script now publishes the cache
key on the ratelimit:local_cache_invalidation pub/sub channel when a
refund crosses the counter back under its limit (old > limit &&
new <= limit), keeping the publish rate at one message per poisoning
cycle. Every replica running with negative hits and a local cache
subscribes on one dedicated connection to the main Redis and deletes
published keys from its freecache, reconnecting with exponential
backoff (capped at 30s) on connection errors. Invalidation is
best-effort / at-most-once: missed messages are never buffered or
replayed. Decrements routed to the dedicated per-second Redis never
publish since the subscriber listens only on the main Redis.

New statistics: ratelimit.localcache.invalidation.subscribed (gauge),
.received and .deleted (counters).

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.rate_limit.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add ENABLE_NEGATIVE_HITS flag gating the negative-hits feature

Negative hits (is_negative_hits descriptors) are now gated by the
ENABLE_NEGATIVE_HITS environment variable, default off. While the flag
is off, any request containing a negative-hit descriptor is rejected as
a whole with the gRPC UNIMPLEMENTED code before the cache call, so a
negative-hit descriptor is never silently processed as a positive hit.
Rejections are counted in the new
ratelimit.service.negative_hits_rejected statistic.

The combination negative hits + memcached + local cache refuses to
boot: an over-limit counter poisons the local cache and there is no
invalidation path for memcached. All other combinations boot as before,
including memcached negative hits without local cache.

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to the corresponding multi-arch index digest sha256:b54cbf583d39
0341599d7bcbc062425c081105cc5ef6d170ced98ef9d047c716, which contains
the existing 7095ad02... amd64 manifest as one of its children plus
the arm64/v8 and other platform variants. The amd64 image is
unchanged; arm64 builds now produce arm64 binaries.

Signed-off-by: Harrison Harris <harrison.harris@xapien.com>
Co-authored-by: Harrison Harris <harrison.harris@xapien.com>
Update third party libraries flagged for vulnerability scans (#1124)

Signed-off-by: collin-lee <collin.lee@salesforce.com>
Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: add zipkin b3 header propagation (#1110)
Fix Prometheus response time units (#1104)
Dockerfile: add ENTRYPOINT (#1095)

Make the docker image easier to consume.

Signed-off-by: Ian Kerins <git@isk.haus>
Send user defined metadata to the client (#1112)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build(deps): bump google.golang.org/grpc from v1.74.2 to v1.80.0 (#1111)

Signed-off by: João Pereira <joao@jpereira.me>

Upgrades the gRPC dependency from v1.74.2 to v1.80.0, along with its
transitive dependency updates (golang.org/x/net, google.golang.org/protobuf,
genproto, go-control-plane, etc.).

Signed-off-by: João Pereira <joao.pereira@zwift.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fix quota result when all limits were exceeded (#1059)

* Fix quota result when all limits were exceeded

Signed-off-by: yavlasov <yavlasov@google.com>

* Address comments

Signed-off-by: yavlasov <yavlasov@google.com>

* Fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* fix comment

Signed-off-by: yavlasov <yavlasov@google.com>

* Remove log into a file

Signed-off-by: yavlasov <yavlasov@google.com>

---------

Signed-off-by: yavlasov <yavlasov@google.com>
Update golang references to 1.26.1 (#1091)
Add integration test for token based quota (#1092)

Signed-off-by: yavlasov <yavlasov@google.com>
Add quota integration test (#1090)
Add debug logging for quota values (#1089)
Wait for sevices to be up before running tests (#1088)
update otel and fix failing tests (#1078)

Signed-off-by: Ashish Tiwari <ashishjaitiwari15112000@gmail.com>
feat: Support wildcard in non-trailing positions for rate limit descriptor values (#1085)

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
build(deps): bump golang from 1.25.6 to 1.26.1 (#1079)

Bumps golang from 1.25.6 to 1.26.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bump go version 1.25.6 (#1047)

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
implement quota mode for soft rate limit check (#1045)

* implement quota mode for rate limit check

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix format

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix tests

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

* fix quota mode flag

Signed-off-by: Dan Sun <dsun20@bloomberg.net>

---------

Signed-off-by: Dan Sun <dsun20@bloomberg.net>
upgrade radix from v3 to v4 for improved pipeline handling (#1041)

* feat: upgrade radix from v3 to v4

Upgrade radix Redis client from v3.8.1 to v4.1.4.

Main changes:
- Import paths: radix/v3 -> radix/v4
- Pool/Cluster/Sentinel use Config.New() instead of New()
- All client operations require context.Context parameter
- Dialer setup changed from functional options to struct config
- Pipelining uses radix.NewPipeline() and Append()
- Write buffering via Dialer.WriteFlushInterval

Breaking from v3:
- Pool on-empty behavior (WAIT/CREATE/ERROR) not available
- REDIS_PIPELINE_LIMIT setting deprecated (no effect in v4)

Tested with existing test suite - all tests passing.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: update pipeline settings for radix v4

Update documentation to reflect radix v4's pipeline behavior:

- REDIS_PIPELINE_WINDOW now sets WriteFlushInterval (auto-flush timing)
- REDIS_PIPELINE_LIMIT deprecated - no effect in v4
- Add REDIS_USE_EXPLICIT_PIPELINE for manual pipeline control
- Required for Redis Cluster: PIPELINE_WINDOW must be non-zero

Update terminology from "implicit pipelining" to "write buffering"
to better match radix v4's actual behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: update tests for radix v4

- Add useExplicitPipeline parameter to test client creation
- Update error assertions for v4's error message format
  (v4 prefixes with "response returned from Conn:")
- Handle different connection errors (EOF, connection reset, broken pipe)
- Update radix.FlatCmd usage for v4 API

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* chore: add deprecation warning for REDIS_PIPELINE_LIMIT

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix Redis cluster test config for radix v4

Replace deprecated RedisPipelineLimit with RedisPipelineWindow in
configRedisCluster function. Radix v4 requires WriteFlushInterval
(RedisPipelineWindow) for cluster mode buffering instead of the
deprecated pipeline limit setting.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt struct field alignment in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: fail fast on unsupported REDIS_POOL_ON_EMPTY_BEHAVIOR settings

Radix v4 does not support CREATE or ERROR behaviors for
REDIS_POOL_ON_EMPTY_BEHAVIOR. Previously, these settings were logged
as errors but the application would continue with blocking behavior,
which could cause unexpected issues in production.

Changes:
- Panic at startup when CREATE or ERROR is detected
- Prevent silent behavior changes that could cause blocking
- Update tests to verify panic behavior
- Improve migration documentation in comments

This ensures users are immediately notified of incompatible
configuration rather than experiencing unexpected blocking in production.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* fix: change REDIS_POOL_ON_EMPTY_BEHAVIOR default to WAIT

The default value 'CREATE' is not supported in radix v4 and causes
integration tests to panic at startup. Changed default to 'WAIT' which
matches radix v4's actual pool behavior (always blocks when empty).

This fixes integration test failures where tests without explicit
REDIS_POOL_ON_EMPTY_BEHAVIOR settings would panic during initialization
with: "REDIS_POOL_ON_EMPTY_BEHAVIOR=CREATE is not supported in radix v4"

Also updated documentation to clarify that CREATE/ERROR are not supported
and marked RedisPoolOnEmptyWaitDuration as deprecated.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* test: fix cluster connection timeout and context bug

- Fix WaitForTcpPort to use timeoutCtx instead of ctx
  This ensures the timeout parameter is actually respected when
  dialing TCP connections.

- Increase gRPC server startup timeout from 1s to 10s
  Radix v4 cluster connection initialization takes longer,
  especially when establishing connections to multiple cluster nodes.
  This prevents "connection refused" errors in integration tests.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: extract dialer creation logic to reduce duplication

Consolidates Redis and Sentinel dialer setup into a reusable createDialer
helper function, eliminating ~30 lines of duplicated code. Improves logging
by including connection target details (e.g., "sentinel(master,host1,host2)")
instead of generic "sentinel" string.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: remove deprecated REDIS_POOL_ON_EMPTY_WAIT_DURATION settings

Remove the deprecated poolOnEmptyWaitDuration parameter and related
configuration settings as they have no effect in radix v4. The pool
always blocks until a connection is available when using WAIT behavior.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* refactor: auto-select pipeline mode based on Redis type

Remove REDIS_USE_EXPLICIT_PIPELINE configuration option and
automatically determine pipeline mode based on Redis deployment type:

- Cluster mode: uses grouped pipeline (groups same-key commands)
  - INCRBY + EXPIRE for same key are pipelined together (same slot)
  - Reduces round-trips from 2 to 1 per key in cluster mode

- Single/Sentinel mode: uses explicit pipeline (batches all commands)
  - All commands in one pipeline for minimal latency
  - Optimal for non-cluster deployments

This simplifies configuration by removing user-facing options while
automatically choosing the optimal pipeline strategy for each Redis type.

Breaking changes:
- Remove REDIS_USE_EXPLICIT_PIPELINE env var
- Remove REDIS_PERSECOND_USE_EXPLICIT_PIPELINE env var
- Remove UseExplicitPipeline() interface method

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* docs: remove non-existent REDIS_USE_EXPLICIT_PIPELINE from README

The REDIS_USE_EXPLICIT_PIPELINE and REDIS_PERSECOND_USE_EXPLICIT_PIPELINE
settings were documented in README but do not exist in settings.go.
Removed the documentation to match the actual implementation.

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

* style: fix gofmt formatting in settings.go

Signed-off-by: seonghyun <seonghyunoh@gmail.com>

---------

Signed-off-by: seonghyun <seonghyunoh@gmail.com>
add support for response dynamic metadata (#1027)

* add support for response dynamic metadata

Signed-off-by: zirain <zirain2009@gmail.com>

* address Colin's comment

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
fix: apply TLS and auth config to Redis Sentinel connections (#1015)

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

* fix: apply TLS and auth config to Redis Sentinel connections

When using Redis Sentinel with TLS enabled, the client was failing
to connect to Sentinel nodes because the TLS configuration was not
being applied to the SentinelConnFunc. This caused "SSL wrong version
number" errors and connection resets.

This fix adds a sentinelDialFunc that properly applies:
- TLS configuration (when REDIS_TLS=true)
- Authentication settings (when REDIS_AUTH is set)
- Connection timeout settings

The fix mirrors the approach used for the main Redis connection
dial function, ensuring consistent configuration across both
Sentinel and data node connections.

Fixes connection to Redis Sentinel over TLS.
Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>

---------

Signed-off-by: Stefan Kolesnikowicz <stefan@sandnetworks.com>
Signed-off-by: stekole <stefan@sandnetworks.com>
feat: Add Pool On-Empty Behavior Configuration for Redis Connections (#1018)

* feat: Add Pool On-Empty Behavior Configuration for Redis Connections

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

* update

Signed-off-by: notdu <huudutg@gmail.com>

---------

Signed-off-by: notdu <huudutg@gmail.com>
[ISSUE] Fix: Wildcard Stats Key Behavior Changes (#1017)

* Fix: Preserve metrics for wild card

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Unify matchedWildCardKey for if-else statement

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove hasShareThreshold conditional check in non-wild-card block

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Prevent log debug to creating stats from executing twice per request

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove redundant assignment

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Fix: Remove dead code

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Refactor: unify logic to include value to stats

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
(fixes): local cache stat test fixes (#1013)

* (fixes): local cache stat test fixes

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

* (fixes): invoke TestOverLimitWithStopCacheKeyIncrementWhenOverlimitConfig local cache stat tests

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>

---------

Signed-off-by: Sagar Waidande <sagar10018233@gmail.com>
[Proposal] Share Threshold for Wildcard Rate Limiting (#1016)

* Add share_threshold to make wild card values can share rate limit threshold

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Implement lazy initilization based on reviews

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: Nam Dang <xuannam230201@gmail.com>
feat: Add field to add unspecified value to metric (#996)

* Add field to add unspecified value to metric

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update README.md to pass docs_check_format check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update format to pass pre-commit check

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

* Update based on comments and add more unit tests

Signed-off-by: Nam Dang <xuannam230201@gmail.com>

---------

Signed-off-by: xuannam230201 <xuannam230201@gmail.com>
Signed-off-by: Nam Dang <xuannam230201@gmail.com>
Replace Alpine with Google's distroless static image for enhanced sec… (#993)

* Replace Alpine with Google's distroless static image for enhanced security
and simplified maintenance. Includes CA certificates automatically and
provides debug variant for troubleshooting.

* security: pin distroless image to SHA and use nonroot variant

- Pin gcr.io/distroless/static-debian12:nonroot to specific SHA digest
- Ensures deterministic builds and prevents supply chain attacks
- Use nonroot variant for enhanced security (runs as UID 65532)
- Follows same pattern as Envoy proxy for consistency
- Update documentation to reflect security improvements
feat: add connection timeout configuration for Redis operations (#987)

Signed-off-by: notdu <huudutg@gmail.com>
build(deps): bump google.golang.org/protobuf from 1.36.7 to 1.36.10 (#980)

Bumps google.golang.org/protobuf from 1.36.7 to 1.36.10.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-version: 1.36.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang in /examples/xds-sotw-config-server (#981)

Bumps golang from 1.24.5 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump golang from 1.24.6 to 1.25.3 (#983)

Bumps golang from 1.24.6 to 1.25.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#976)

Bumps alpine from `1e42bbe` to `4b7ce07`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT (#1206)

* Add calendar-aligned MONTH rate limit, gated by USE_CALENDAR_MONTH_RATE_LIMIT

A unit: month rate limit is computed as a fixed 60*60*24*30 second window
counted from the Unix epoch, so it neither aligns with real calendar months
nor accounts for months of different lengths (RDGRS-1999).

Add a USE_CALENDAR_MONTH_RATE_LIMIT setting (default false) that, when
enabled, buckets MONTH cache keys by UTC calendar month, sets their
TTL/expiration to the actual time remaining until month end, and reports
that same value as the reset duration. Defaults to false so existing MONTH
limits keep their current reset behavior unless explicitly opted in.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* Trigger CI

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* docs: regenerate README TOC for new calendar-month section

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* memcached: fix expiration >30 days being misread as a Unix timestamp

Memcached treats any expiration value greater than 30 days as an
absolute Unix timestamp rather than a relative TTL. Calendar-aligned
MONTH limits can produce a relative expiration up to 2,678,400 seconds
(31-day months), which was passed straight into memcache.Item.Expiration
and caused those keys to be treated as already expired.

Convert the expiration to an absolute Unix timestamp whenever it would
exceed memcached's 30-day relative-TTL threshold.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* test: fix call sites left out of sync by main merge

The merge of main (is_negative_hits, []uint64 -> []utils.HitsAddend)
into calendar-month-rate-limit (added useCalendarMonth bool param)
left several test call sites using the pre-merge signatures for
NewBaseRateLimit, NewRateLimitCacheImpl, and NewFixedRateLimitCacheImpl.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Signed-off-by: Kiran Malsetty <93211513+OS-kiranmalsetty@users.noreply.github.com>
feat: implement is_negative_hits in rate limit descriptor (#1140)

* feat: implement is_negative_hits in rate limit descriptor

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

* added details to readme

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>

---------

Signed-off-by: Rudrakh Panigrahi <rudrakh97@gmail.com>
redis: log recovery when a connection succeeds after a prior dial error (#1205)

Operators currently only see repeated dial-error logs and have no
explicit signal when the pool starts succeeding again. Track whether
the last dial attempt failed and log once when a subsequent connection
succeeds.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
Update Go references and vulnerable dependencies (#1204)

* Update Go references to 1.26.5

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

* Update vulnerable Go dependencies

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>

---------

Signed-off-by: Phellippe Lima <phellippe.end@gmail.com>
deps: bump x/net, x/text, x/sys to fix remaining CVEs (#1196)

Follow-up to #1154 (which bumped x/net to 0.55.0). Bumps the golang.org/x
dependencies to their latest patched releases to remediate the CVEs still
outstanding after #1154:

- golang.org/x/net  v0.55.0 -> v0.57.0  (CVE-2026-46600, fixed in 0.56.0)
- golang.org/x/text v0.37.0 -> v0.40.0  (CVE-2026-56852, fixed in 0.39.0)
- golang.org/x/sys  v0.45.0 -> v0.47.0  (already patched; kept current)

No source changes required; `go build ./...` passes.

Signed-off-by: Yitong Feng <yife@microsoft.com>
redis: optionally close pooled connections on READONLY error replies (#1191)

* redis: optionally close pooled connections on READONLY error replies

After a master->replica failover in deployments that fail over by
repointing an address at the new master (a Kubernetes Service, DNS, or a
proxy - e.g. Redis without Sentinel, Dragonfly, KeyDB), the demoted
master keeps already-established connections open. radix only discards
pooled connections on IO errors, so those stale connections are reused
forever and every write on them keeps failing with READONLY until the
process restarts, turning a routine failover into a permanent rate
limiting outage.

Add an opt-in setting, REDIS_CLOSE_CONNECTION_ON_READONLY_ERROR
(default false), that wraps pooled connections so a READONLY error reply
strips radix's resp.ErrConnUsable wrapper. The pool then discards the
connection and re-dials through the configured address, reaching the
current master. The failing command still returns its error to the
caller; only the connection handling changes. Applies to the main and
per-second clients across single, cluster, and sentinel modes.

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

* fix README.md

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>

---------

Signed-off-by: kiran malsetty <kiran.malsetty@outsystems.com>
build(deps): bump docker/setup-qemu-action from 3.2.0 to 4.2.0 (#1179)

Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3.2.0 to 4.2.0.
- [Release notes](https://github.com/docker/setup-qemu-action/releases)
- [Commits](https://github.com/docker/setup-qemu-action/compare/49b3bc8e6bdd4a60e6116a5414239cba5943d3cf...96fe6ef7f33517b61c61be40b68a1882f3264fb8)

---
updated-dependencies:
- dependency-name: docker/setup-qemu-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump actions/checkout from 4.1.7 to 7.0.0 (#1173)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump alpine in /integration-test (#1172)

Bumps alpine from `4b7ce07` to `28bd5fe`.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
add metadata to rate limit descriptors pb to yaml (#1175)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
update proto to support metadata (#1174)

Signed-off-by: achoo30 <achoo30@bloomberg.net>
Update to golang-1.26.4 and update golang.org/x/net to 0.55.0 (#1154)

Signed-off-by: Fred Dafunk <bloomenergyguy@gmail.com>

Co-authored-by: collin-lee <collin.lee@salesforce.com>
feat: bound cluster pipeline parallelism (#1149)

* redis: bound cluster pipeline parallelism

Signed-off-by: dthuynh <dthuynh@axon.com>

* Refactor to address comment: use gRPC request context in PipeDo, cap the parallelism to RedisPoolSize

Signed-off-by: dthuynh <dthuynh@axon.com>

---------

Signed-off-by: dthuynh <dthuynh@axon.com>
Co-authored-by: dthuynh <dthuynh@axon.com>
fix: correct typos in memcache error messages and variable name (#1150)

Signed-off-by: Immanuel Tikhonov <pchpr.00@list.ru>
Signed-off-by: immanuwell <pchpr.00@list.ru>
Update to golang 1.26.3 (#1152)

Co-authored-by: collin-lee <collin.lee@salesforce.com>
Add quota mode to rate limit descriptor proto (#1148)
feat: add retry in init phase instead of panic directly (#1144)

* feat: add retry in init phase instead of panic directly

Signed-off-by: zirain <zirain2009@gmail.com>

* respect signal handling for graceful shutdown

Signed-off-by: zirain <zirain2009@gmail.com>

* fix test

Signed-off-by: zirain <zirain2009@gmail.com>

---------

Signed-off-by: zirain <zirain2009@gmail.com>
Add integration test for quota based service selection. (#1114)

Signed-off-by: Yan Avlasov <yavlasov@google.com>
build: pin golang:1.26.2 to multi-arch index digest (#1131)

PR #1124 updated the golang base image from 1.26.1 to 1.26.2, but the
new digest sha256:7095ad02810845fa35d1fb090b8e57dd20dce4ca36b29b42951
802350d2ec90e is a single-arch (linux/amd64) image manifest rather
than a multi-arch index. The previous 1.26.1 digest sha256:e2ddb153f7
86ee6210bf8c40f7f35490b3ff7d38be70d1a0d358ba64225f6428 is an OCI image
index covering linux/amd64, arm64/v8, arm/v7, 386, ppc64le, riscv64,
s390x and windows/amd64.

When buildx is asked to produce a non-amd64 variant of the published
envoyproxy/ratelimit image, the FROM line resolves to the amd64 base
on every platform, so the resulting binary is amd64 regardless of the
target. The multi-arch publish then stamps that amd64 binary into the
arm64 layer of the released index, producing an image that fails on
arm64 nodes with:

  exec /bin/ratelimit: exec format error

Swap to t…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants