ROUISSI Mohamed ALi
ROUISSI Mohamed ALi

Reputation: 61

How to acess spring data redis stored object at the expiration event?

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());
        }
    }

Upvotes: 0

Views: 3258

Answers (2)

Nilzao
Nilzao

Reputation: 21

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());
        }
    }
}

Upvotes: 2

Anuj Vishwakarma
Anuj Vishwakarma

Reputation: 842

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].

Upvotes: 0

Related Questions