v0.27.71
Changes
apiusagemonitoring: prune filter cache of removed routes (#4200)
Fixes #3552.
apiUsageMonitoringSpec.filterMap, added in #3551, is only ever written
and read, never pruned. Every distinct filter-arg key the process sees
stays cached for the lifetime of the instance, so on a long-running
skipper whose routes churn the cache grows without bound. Note it is not
only route churn: when the kubernetes admission webhook is enabled,
ValidateRoute also goes through CreateFilter, so a parseable
apiUsageMonitoring config that never becomes a live route still adds a
permanent entry.
The spec now implements routing.PostProcessor: after each route table
rebuild it collects the *apiUsageMonitoringFilter instances the new
routes reference and drops every cache entry that is not among them.
That is the cleanup you pointed at in the issue; the shape is closest to
OpenPolicyAgentRegistry.Do, which reaps by instance identity rather
than by route id like the scheduler does.
A post-processor rather than a pre-processor because filter instances
only exist after route creation, so that is the first point where "still
in use" is knowable.
No delete delay, unlike the scheduler queues and the OPA instances:
those defer because something gets closed, whereas dropping a map entry
here does not touch the filter. A route still holding the pointer keeps
working, the entry is simply not served from cache again, and the worst
case of a mis-scoped route slice is one recompute rather than a closed
resource.
The mutex in Do is load-bearing, not decoration. Two Do calls cannot
overlap, but the admission webhook path above reaches CreateFilter
from request goroutines, so the sweep genuinely races the cache write.
skipper.go registers it via an interface assertion, which also encodes
"monitoring is disabled": with -api-usage-monitoring=false the spec is
a noopSpec that caches nothing and does not implement Do.
Tested: two cached filters and a route table referencing one of them,
placed on the second route and behind another filter so the test fails
if the scan stops at the first route or the first filter; the referenced
entry survives and still hits the cache, the other is dropped and
recreated. Plus an empty route table emptying the cache. Ran gofmt -s,
go build ./..., go vet, staticcheck, and the package and routing
tests with -race.
Written with AI assistance (Claude); I reviewed the change, wrote and
ran the tests, and I stand behind it.
Multiarch Docker image
Multiarch Docker image is available in GitHub's docker registry:
docker run -it ghcr.io/zalando/skipper:v0.27.71 skipper --help