skip to Main Content

I am storing a Cart in Redis using Spring Data Redis for a specific time. An expiration property annotated with @TimeToLive sets the time to live for the Cart object as described by the code below.
I have set KeyExpirationEventMessageListener type to listen to the expiration event in order to process additional work at the expiration event. I was able to fetch the key from the triggered event of the expiration object and I am trying to access it or its phantom object at the expiry time using the spring data repository but with no result.It returns an empty object which means that the original obeject is likely been deleted. I don’t know if it is the proper way to do it.But, is there a way to fetch the expiry object at expiration or just before it gets deleted to process motre work?

@RedisHash("cart")
public class Cart implements Serializable {
    @Id
    @Indexed
    private String id;
    private long customerId;
    private Set<CartLine> lines = new HashSet<>();

    @TimeToLive
    private long expiration;

}

public interface ShoppingCartRepository extends CrudRepository<Cart, String> {

}


    @Component
    public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

        private RedisTemplate<?, ?> redisTemplate;
        private ShoppingCartRepository repository;
        public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer,
                                          RedisTemplate redisTemplate, ShoppingCartRepository repository) {
            super(listenerContainer);
            this.redisTemplate = redisTemplate;
            this.repository = repository;
        }

        @Override
        public void onMessage(Message message, byte[] pattern) {
            String key = new String(message.getBody());
            try {
                String id = extractId(key);
                Optional<ShoppingCart> cart = repository.findById(id);
            } catch(Exception e) {
                logger.info("something went wrong  ====>  " + e.getStackTrace());
            }
        }
        private String extractId(String key){
            String keyPrefix = ShoppingCart.class.getAnnotation(RedisHash.class).value();
            return key.substring((keyPrefix + ":").length());
        }
    }

2

Answers


  1. There was once a use case in my scenario, you can actually use RedisKeyExpiredEvent,

    • RedisKeyExpiredEvent [0] is a Redis specific ApplicationEvent published
      when a particular key in Redis expires. It can hold the value of the
      expired key next to the key.

    You can proceed with the following implementaion.

    @SpringBootApplication
    @EnableRedisRepositories(considerNestedRepositories = true, enableKeyspaceEvents = EnableKeyspaceEvents.ON_STARTUP)
    static class Config {
    
        /**
         * An {@link ApplicationListener} that captures {@link RedisKeyExpiredEvent}s and just prints the value to the
         * console.
         *
         * @return
         */
        @Bean
        ApplicationListener<RedisKeyExpiredEvent<Person>> eventListener() {
            return event -> {
                System.out.println(String.format("Received expire event for key=%s with value %s.",
                        new String(event.getSource()), event.getValue()));
            };
        }
    }
    

    You can find the sample implementation at [1].

    Login or Signup to reply.
  2. Example using Spring boot 2.3.4.RELEASE

    @RedisHash("cart")
    @AllArgsConstructor
    @Getter
    @Setter
    public class Cart implements Serializable {
        @Id
        @Indexed
        private String id;
        private String customerName;
        @TimeToLive
        private long expiration;
    }
    
    public interface ShoppingCartRepository extends CrudRepository<Cart, String> {
    }
    
    @SpringBootApplication
    @EnableRedisRepositories(enableKeyspaceEvents = RedisKeyValueAdapter.EnableKeyspaceEvents.ON_STARTUP)
    public class RedisExpirationApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisExpirationApplication.class, args);
        }
    
        @Bean
        public CommandLineRunner run(ShoppingCartRepository shoppingCartRepository) {
            return args -> {
                // TTL 1 second
                shoppingCartRepository.save(new Cart("some-id", "My Customer Name", 1));
                // wait 5 seconds
                Thread.sleep(5000L);
            };
        }
    
        @Component
        public class CartListener {
            // need to enable redis notifications, inside redis-cli type:
            // config set notify-keyspace-events KEA
            @EventListener
            public void anything(RedisKeyExpiredEvent<Cart> expiredCart) {
                Cart cart = (Cart) expiredCart.getValue();
                System.out.println(cart.getId());
                System.out.println(cart.getCustomerName());
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search