DEV Community

Cover image for Versioning Redis Cache Keys to Prevent Stale Data During Spring Boot Deployments
İbrahim Gündüz
İbrahim Gündüz

Posted on

Versioning Redis Cache Keys to Prevent Stale Data During Spring Boot Deployments

Applications often generate content that needs to be consumed later at runtime. This content may be stored in the file system, written to a database table, shared through a message broker, or placed in a shared cache. Since software systems evolve over time, the consumers of previously generated content are expected to handle these changes. However, this is not always possible.

Let's take a real world example:

Example: Structure Changes On Cached Objects

The class shown below retrieves data for a specific user from the users table, constructs a UserProfile object, and caches the result in Redis.

@Service
public class UserProfileService {
    private final UserRepository userRepository;

    public UserProfileService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Cacheable("userProfile")
    public UserProfile getProfile(Long userId) {
        User user = userRepository.findById(userId).orElseThrow();
        return new UserProfile(
                user.getId(),
                user.getFirstName(),
                user.getLastName()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

At a later point, we decided to extend the UserProfile object by adding an email field and populating it from the database.

         return new UserProfile(
                 user.getId(),
                 user.getFirstName(),
-                user.getLastName()
+                user.getLastName(),
+                user.getEmail()
         );
     }
 }
Enter fullscreen mode Exit fullscreen mode

Because Spring had already populated the cache with instances of the previous object structure, subsequent calls to the getProfile() method would fail due to a deserialization error when reading the outdated cached values.

org.springframework.data.redis.serializer.SerializationException: Cannot serialize
Enter fullscreen mode Exit fullscreen mode

Or something similar depending on your serializer configuration.

How To Solve

Although both the problem and its solution are conceptually simple, the impact can be severe in production environments. Such errors may cause extended downtime, failed deployments, or forced rollbacks — especially when cache entries have long TTL values.

To enable safe deployments and rollbacks, cache entries must be versioned. A common and effective approach is to include a version identifier—optionally combined with the application name when using a shared cache—in the cache key. This ensures that newer application versions do not attempt to read incompatible cached data created by older versions.

@Configuration
public class CacheConfiguration {
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(ObjectProvider<BuildProperties> buildPropertiesProvider) {
        BuildProperties buildProperties = buildPropertiesProvider.getIfAvailable();

        String name = buildProperties != null ? buildProperties.getName() : "application";
        String version = buildProperties != null ? buildProperties.getVersion() : "dev";

        return RedisCacheConfiguration.defaultCacheConfig()
                .prefixCacheNameWith(String.format("%s:%s:", name, version));
    }
}
Enter fullscreen mode Exit fullscreen mode

The cache key can be constructed using build information from Maven (via Maven resource filtering or the spring-boot-maven-plugin with the build-info goal), a Git commit hash (for example, using the git-commit-id-plugin), or any other metadata injected by the CI/CD pipeline.

When Redis is configured with a global key prefix as shown above, cached entries are stored with versioned keys, for example:

127.0.0.1:6379> keys *
1) "payment-gateway:1.1.0:userProfile::1"
2) "payment-gateway:1.1.1:userProfile::1"
Enter fullscreen mode Exit fullscreen mode

Because each application instance accesses only cache keys matching its own version, deployments and rollbacks can be performed safely without encountering deserialization errors caused by incompatible cached data.

--

Hope you find it helpful. As always, you can find code example in the following repository.

Thanks for reading!

Credits

Top comments (0)