Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for GETEX in Spring Redis Cache #2351

Closed
danibs opened this issue Jun 28, 2022 · 4 comments
Closed

Add support for GETEX in Spring Redis Cache #2351

danibs opened this issue Jun 28, 2022 · 4 comments
Labels
status: ideal-for-contribution An issue that a contributor can help us with type: enhancement A general enhancement

Comments

@danibs
Copy link

danibs commented Jun 28, 2022

Hi,
in version > 2.6:
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#new-in-2.6.0

you support GETEX command that permits (I think) to have a kind of "time to idle" like in EhCache (ExpiryPolicyBuilder.timeToIdleExpiration) where the key doesn't expire if someone get its value.

I updated my pom.xml with:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>2.7.1</version>
</dependency>

but in RedisCacheConfiguration class there is only Duration ttl attribute.
I found this https://dzone.com/articles/how-to-boost-redis-with-local-caching-in-java but it talks about Redisson and I found in library that is possible to have timeToLive and Idle:

LocalCachedMapOptions.defaults()
   .evictionPolicy(EvictionPolicy.LFU)
   .timeToLive(48, TimeUnit.MINUTES)
   .maxIdle(24, TimeUnit.MINUTES);

Do I miss something?
Thanks

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jun 28, 2022
@mp911de mp911de added type: enhancement A general enhancement status: ideal-for-contribution An issue that a contributor can help us with and removed status: waiting-for-triage An issue we've not yet triaged labels Jul 25, 2022
bytestreme pushed a commit to bytestreme/spring-data-redis that referenced this issue Jul 16, 2023
bytestreme pushed a commit to bytestreme/spring-data-redis that referenced this issue Jul 16, 2023
bytestreme pushed a commit to bytestreme/spring-data-redis that referenced this issue Jul 16, 2023
@jxblum
Copy link
Contributor

jxblum commented Jul 19, 2023

@bytestreme - Before discussing and reviewing any changes to Spring Data Redis, I want to clarify a few things.

Your (implied) recommendation is to support cache entry, idle-timeout expiration (TTI) using the Redis GETEX command (as evident in your PR) in addition to cache entry, time-to-live expiration (TTL) currently used by Spring Data Redis's implementation (Javadoc) of Spring Framework's Cache Abstraction.

NOTE: It is true that Spring Data Redis supports the Redis GETEX command via the RedisCommands API (specifically, RedisStringCommands (Javadoc) when given a RedisConnection. The implementation is Redis (client) driver specific, using either Jedis or Lettuce. But, both drivers ultimately invoke the Redis GETEX command "on the server".

Second, I also read the DZone article you shared and a few things stood out to me:

  1. The article is specifically focused on local, "client-side caching" (or "Near Caching"), which is a caching pattern used to address application latency and reduction in system resource allocation (network/load).

  2. Also, local, "client-side caching" as well as cache entry idle-timeout expiration (TTI) is only available in the PRO (aka "commercial, and paid") version of Redisson, as highlighted in the "Conclusion":

"Redisson includes a variety of options for Java developers to perform local caching in Redis: Maps, Spring Cache, Hibernate Cache, and JCache. Note that the last three options — Spring Cache, Hibernate Cache, and JCache — are only available for Redisson PRO users."

As it turns out, local, client-side caching is not a trivial problem. 1 of the most challenging tasks, especially in a distributed topology, is consistency, and not just between server nodes in a cluster (e.g. using replication for HA), but also consistency between client(s) and server(s).

Redis makes no guarantees of consistency on the server-side primarily because replica updates are asynchronous. This problem is further compounded by client-side caching, and is typically only used when data updates are infrequent (i.e. reads far exceed writes) or strong consistency is not a major concern.

Therefore, it is not surprising that these features are only available in the PRO version, or rather commercial/paid option. See the comparisons between the OSS and PRO versions, here.

Next up, "cache entry, idle-timeout expiration (TTI)"...

@jxblum
Copy link
Contributor

jxblum commented Jul 19, 2023

Regarding your specific (implied) ask for cache entry, idle-timeout expiration (TTI), Redis's GETEX command is NOT an implementation of TTI. Rather, it is a per cache entry (key-based) time-to-live expiration (TTL) policy on GET when given a "key". This is even evident in the "Examples" shown in the Redis documentation on the GETEX command.

By way of example, if you were to execute these series of commands:

$ SET sessionId 123
$ GETEX sessionId EX 600   // Session timeout after 5 minutes
"123"
// Wait 4 minutes
$ GET sessionId
"123"
// Wait 1 minute
$ GET sessionId
(nil)

That is, the simple act of "reading" the key (e.g. "sessionId") does NOT reset the expiration timeout (back to 5 minutes) upon (read) access.

Although, if each subsequent GET after the initial GETEX is replaced by a call to GETEX, then, in essence, you do achieve a cheap, half-baked form of cache entry idle-timeout expiration (TTI), which I suppose is OK, but really does not properly take into account the multiple forms of access that might be in use by the application (e.g. via Templating or SD Repositories). In this case, responsibility falls to the application developer to ensure whatever object (not just "sessions") that are set to expire, expire when they are logically no longer in use, and not just when caching, if that is needed.

If another application is not using caching, but accessing the same data for some other purpose while the data is loaded and actively being used, then this will not reset the expiration timeout.

Additionally, even if another application uses caching, but applies different configuration, then it is possible the object will still expire even when being accessed.

All of this is to say that data policies like expiration are best left to the underlying data store (as suggested/recommended), especially in a distributed topology arrangement, so that such policies are uniformly applied across the system and consistently exhibit the desired behavior in application. Otherwise, this becomes quite challenging to debug when unexpected situations arise (e.g. users being unexpectedly logged out in the middle of their session).

@jxblum
Copy link
Contributor

jxblum commented Jul 19, 2023

There is another Redis command OBJECT IDLETIME that properly assesses whether a particular key is "idle".

By way of example:

127.0.0.1:6379> GET sessionId
(nil)
127.0.0.1:6379> SET sessionId ABC
OK
127.0.0.1:6379> GET sessionId
"ABC"
127.0.0.1:6379> OBJECT IDLETIME sessionId
(integer) 30
127.0.0.1:6379> GETEX sessionId EX 30
"ABC"
127.0.0.1:6379> OBJECT IDLETIME sessionId
(integer) 17
127.0.0.1:6379> GET sessionId
"ABC"
127.0.0.1:6379> OBJECT IDLETIME sessionId
(integer) 2
127.0.0.1:6379> GET sessionId
(nil)
127.0.0.1:6379> OBJECT IDLETIME sessionId
(nil)

I performed these commands within a span of only a few seconds apart. Still, the object (ABC) with key (sessionId) expired after 30 seconds (set with GETEX sessionId EX 60), but using OBJECT IDLETIME, it accurately reflected each access, using only GET.

Therefore, without some "external" TTI expiration monitor (perhaps using OBJECT IDLETIME) then the application must consistently using GETEX, or even SET with expiration options consistently.

To be clear, an TTI expiration monitor really belongs in the underlying data store, and is not something that we are interested in adding to and maintaining in Spring Data Redis, even if other frameworks like Redisson support such features to some extent, which is limited by unenforceable, consistent application thereof.

@jxblum jxblum changed the title Adding support to GETEX in Spring Redis Cache Adding support for GETEX in Spring Redis Cache Jul 20, 2023
@jxblum jxblum changed the title Adding support for GETEX in Spring Redis Cache Add support for GETEX in Spring Redis Cache Jul 20, 2023
jxblum added a commit to jxblum/spring-data-redis that referenced this issue Jul 20, 2023
We now support time-to-idle (TTI) expiration policies in Spring Data Redis's Cache implementation for cache reads.

This TTI behavior is achieved with the use of the Redis GETEX command an consistently using the same TTL configuration for all cache operations when TTI is enabled.

Closes spring-projects#2351
Original pull request: spring-projects#2643
jxblum added a commit to jxblum/spring-data-redis that referenced this issue Jul 20, 2023
We now support time-to-idle (TTI) expiration policies for cache reads.

The TTI implementation is achieved with the use of the Redis GETEX command on Cache.get(key) operations as well as consistently using the same TTL configuration for all cache operations when TTI is enabled and TTL expiration has been configured,
with the use of a TtlFunction or fixed Duration.

Closes spring-projects#2351
Original pull request: spring-projects#2643
jxblum added a commit to jxblum/spring-data-redis that referenced this issue Jul 21, 2023
We now support time-to-idle (TTI) expiration policies for cache reads.

The TTI implementation is achieved with the use of the Redis GETEX command on Cache.get(key) operations as well as consistently using the same TTL configuration for all cache operations when TTI is enabled and TTL expiration has been configured,
with the use of a TtlFunction or fixed Duration.

Closes spring-projects#2351
Original pull request: spring-projects#2643
@jxblum
Copy link
Contributor

jxblum commented Jul 21, 2023

See PR #2649.

jxblum added a commit to jxblum/spring-data-redis that referenced this issue Jul 25, 2023
jxblum added a commit to jxblum/spring-data-redis that referenced this issue Jul 27, 2023
… is available.

The Redis GETEX command is only available when the Redis server version is at least 6.2.0.

Closes spring-projects#2351
jxblum added a commit to jxblum/spring-data-redis that referenced this issue Jul 27, 2023
…Spring Data Redis Cache implementation.

Edit Javadoc in RedisCacheManager.

Closes spring-projects#2351
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: ideal-for-contribution An issue that a contributor can help us with type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants