spring-cloud / spring-cloud/spring-cloud-gateway

LocalResponseCacheGatewayFilterFactory recreates Caffeine cache on every route refresh

Open
#4,243 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting-for-triage
Dominant language
Java
Stars
4.9k
Forks
3.5k
Avg merge
20h 57m
Merged PRs (30d)
8

Description

Description

LocalResponseCacheGatewayFilterFactory appears to recreate and register a new Caffeine cache every time Gateway routes are refreshed.

When Spring Cloud Gateway is used with Eureka DiscoveryClient, routes may be refreshed periodically. In my case, this happens every 30 seconds from the following thread:

DiscoveryClient-CacheRefreshExecutor

Each refresh causes LocalResponseCacheGatewayFilterFactory.apply() to be invoked again.

The following code creates and registers a new cache every time:

Caffeine caffeine = LocalResponseCacheUtils.createCaffeine(cacheProperties);
String cacheName = config.getRouteId() + "-cache";

caffeineCacheManager.registerCustomCache(
        cacheName,
        caffeine.build()
);

This produces the following log every 30 seconds:

13:32:25.740 [DiscoveryClient-CacheRefreshExecutor-%d]
INFO LocalResponseCacheUtils - Initializing Caffeine

13:32:55.779 [DiscoveryClient-CacheRefreshExecutor-%d]
INFO LocalResponseCacheUtils - Initializing Caffeine

Expected behavior

The cache associated with a route should be reused when the route is rebuilt and the cache configuration has not changed.

A route refresh should not recreate the underlying Caffeine cache or remove existing cached responses.

Actual behavior

apply() creates a new Caffeine cache and calls registerCustomCache() again using the same cache name:

String cacheName = config.getRouteId() + "-cache";

Depending on the behavior of CaffeineCacheManager.registerCustomCache(), this may replace the existing cache and clear all cached entries.

Even when the cache is not replaced, a new unused Caffeine cache instance is still created during every route refresh.

Steps to reproduce

  1. Configure Spring Cloud Gateway with Eureka DiscoveryClient.
  2. Enable the local response cache filter.
  3. Configure a route with LocalResponseCache.
  4. Start the gateway.
  5. Wait for Eureka registry refresh.
  6. Observe that LocalResponseCacheUtils.createCaffeine() is invoked repeatedly.

Example configuration:

spring:
  cloud:
    gateway:
      filter:
        local-response-cache:
          enabled: true

eureka:
  client:
    registry-fetch-interval-seconds: 30

Suggested fix

Before creating a new cache, check whether a cache with the same name already exists:

String cacheName = config.getRouteId() + "-cache";

Cache routeCache = caffeineCacheManager.getCache(cacheName);

if (routeCache == null) {
    Caffeine caffeine =
            LocalResponseCacheUtils.createCaffeine(cacheProperties);

    caffeineCacheManager.registerCustomCache(
            cacheName,
            caffeine.build()
    );

    routeCache = caffeineCacheManager.getCache(cacheName);
}

A more complete solution could recreate the cache only when the route cache configuration, such as TTL or size, has actually changed.

Environment

Spring Cloud Gateway version:  (v3.4.4)
Spring Boot version:  (v3.4.4)
Spring Cloud version: (v3.4.4)
Java version: 21
Discovery client: Eureka

Additional context

Route refresh itself is expected behavior. The concern is that the filter factory is not idempotent and creates a new cache whenever the route definition is rebuilt.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at LocalResponseCacheGatewayFilterFactory.apply() and trace LocalResponseCacheUtils.createCaffeine() with CaffeineCacheManager.registerCustomCache(). Reproduce with Eureka route refreshes and verify that unchanged route configuration reuses the named cache without recreating it or removing cached responses.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.