-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 #2643
Conversation
bytestreme
commented
Jul 16, 2023
•
edited
Loading
edited
- You have read the Spring Data contribution guidelines.
- You use the code formatters provided here and have them applied to your changes. Don’t submit any formatting related changes.
- You submit test cases (unit or integration tests) that back your changes.
- You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).
resolves #2351 |
@@ -133,7 +144,7 @@ public byte[] get(String name, byte[] key) { | |||
} | |||
|
|||
@Override | |||
public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) { | |||
public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl, @Nullable Duration maxIdle) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change doesn't make sense because there can be only one TTL and instead of changing public API, we rather should apply maxIdle defaulting on the calling side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about default
method in RedisCacheWriter
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this signature change makes any sense in any of the changed RedisCacheWriter
methods: get(..)
, put(..)
or putIfAbsent(..)
; see below.
I'd prefer, if we decide to add any option to configure TTI (overriding TTL when TTI < TTL on gets
) per cache entry, by key, at all, then the configuration would be included in the RedisCacheConfiguration
per cache (not unlike TTL) and the RedisCacheConfiguration
object would be passed to the components of the cache implementation to alter the caching behavior as needed. Otherwise, each new option will only increase the signature footprint, which is not desirable.
@@ -93,7 +94,7 @@ class DefaultRedisCacheWriter implements RedisCacheWriter { | |||
} | |||
|
|||
@Override | |||
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) { | |||
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl, @Nullable Duration maxIdle) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change doesn't make sense because there can be only one TTL and instead of changing public API, we rather should apply maxIdle defaulting on the calling side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jxblum can you have a look as well? Introducing an expire after idle (Setting the TTL on GET) makes sense as we have already support for |
if (shouldExpireWithin(ttl)) { | ||
connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert()); | ||
} else { | ||
connection.set(key, value); | ||
if (shouldExpireMaxIdleWithin(maxIdle)) { | ||
connection.set(key, value, Expiration.from(maxIdle), SetOption.upsert()); | ||
} else { | ||
connection.set(key, value); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @mp911de alluded to above, there is technically only 1 expiration policy in Redis for a given key: TTL.
Expiration (and specifically, TTL) is something that is already handled in Spring Data Redis as of 2.7.x
(and beyond: 3.0.x
, 3.1.x
, and currently, 3.2.x
), which is apparent in lines 105-107 shown above.
Plus, what happens when TTL is less than TTI? The lesser of TTL and TTI should take precedence. (???)
Also note, as of Spring Data Redis 3.2.x
(currently in development; latest version is 3.2.0-M1
) TTL expiration can now be computed dynamically at runtime, per cache entry by key and value using the new TtlFunction interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, TTI expiration policies (e.g. timeouts) have no bearing on cache entry creates or updates (upserts). TTL expiration is only updated when another create or update occurs after the initial create. Read access (e.g. get(key)
) does not change TTL.
This would apply to RedisCacheWriter.putIfAbsent(..)
as well.
byte[] result; | ||
|
||
if (shouldExpireMaxIdleWithin(maxIdle)) { | ||
result = execute(name, connection -> connection.getEx(key, Expiration.from(maxIdle))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lesser of TTL and TTI should take precedence.
if (shouldExpireWithin(ttl)) { | ||
put = connection.set(key, value, Expiration.from(ttl), SetOption.ifAbsent()); | ||
} else { | ||
put = connection.setNX(key, value); | ||
if (shouldExpireMaxIdleWithin(maxIdle)) { | ||
put = connection.set(key, value, Expiration.from(maxIdle), SetOption.ifAbsent()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lesser of TTL and TTI should take precedence.
@mp911de - I agree. Adding an expiration on However, I would caution that Redis really does not technically have a proper notion of cache entry, idle-timeout expiration (TTI). It is essentially only TTL, or effectively TTI when It seems to me, frameworks like Redisson offer TTI as an additional feature implemented and supported by the framework itself outside of Redis. Without the use of As such, I think we should keep with our current class RedisCache {
protected Object lookup(Object key) {
byte[] value = getCacheWriter().get(getName(), createAndConvertCacheKey(key),
getTtlFunction().getTimeToLive(key, null));
// return deserialized, non-null value or null
}
} And: class DefaultRedisCacheWriter {
public byte[] get(String name, byte[] key, Duration ttl) {
// ...
byte[] result = ttl != null
? execute(name, connection -> connection.getEx(key, toExpiration(ttl)))
: execute(name, connection -> connection.get(key));
} |
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
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
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
See PR #2649. |
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 #2351 Original pull request: #2643