Automatically Closing Unpaid Orders Using Redis Key Expiration Events with Spring Boot
This article demonstrates how to use Redis key‑expiration notifications together with Spring Boot to automatically close orders that remain unpaid after a configurable timeout, eliminating inefficient polling and providing a clean backend solution for order lifecycle management.
Hello, I am a senior architect. This article explains how to automatically close unpaid orders by listening to Redis key‑expiration events.
Business scenario : When an order is created, it should be automatically closed if it is not paid within a certain period. Polling with scheduled tasks is inefficient because each order has a different creation time, and a short polling interval hurts performance.
The proposed solution is to store a Redis key for each order (the key is the order ID) with a TTL (e.g., 30 minutes). When the key expires, Redis can notify a listener, which then locates the order by its key and marks it as closed.
Enabling Redis key‑expiration notifications : Edit redis.conf and set the notify-keyspace-events option to include Ex . The flag meanings are:
K : keyspace events (prefix __keyspace@ )
E : keyevent events (prefix __keyevent@ )
g : generic commands (del, expire, rename, …)
$ : string commands
l : list commands
s : set commands
h : hash commands
z : sorted‑set commands
x : expired events (triggered when a key expires)
e : eviction events (triggered by memory‑policy eviction)
A : all events (alias for g$lshzxe )
Dependency : Add the Spring Data Redis starter to your pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>Configuration : Define a RedisListenerConfig class that creates a RedisMessageListenerContainer bean.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Configuration
public class RedisListenerConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
return container;
}
}Listener implementation : Create a RedisKeyExpirationListener that extends KeyExpirationEventMessageListener and overrides onMessage to obtain the expired key and perform order cancellation logic.
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
/**
* Listens to all DB expiration events (__keyevent@*__:expired)
*/
@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
@Override
public void onMessage(Message message, byte[] pattern) {
// The expired key (order ID) is received here; implement order‑cancellation logic.
String expiredKey = message.toString();
System.out.println("Expired order key: " + expiredKey);
// TODO: locate the order in the database using the key and mark it as closed.
}
}With this setup, when an order’s Redis key expires after the configured TTL, the listener receives the event, retrieves the order ID, and can safely update the order status to closed, eliminating the need for periodic polling.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.