Fix EvictableCacheBuilder#expireAfterWrite behavior#14476
Fix EvictableCacheBuilder#expireAfterWrite behavior#14476atanasenko wants to merge 1 commit intotrinodb:masterfrom
EvictableCacheBuilder#expireAfterWrite behavior#14476Conversation
lib/trino-collect/src/test/java/io/trino/collect/cache/TestEvictableCache.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Will this make this test flaky when there is a GC pause?
lib/trino-collect/src/test/java/io/trino/collect/cache/TestEvictableCache.java
Outdated
Show resolved
Hide resolved
lib/trino-collect/src/test/java/io/trino/collect/cache/TestEvictableCache.java
Outdated
Show resolved
Hide resolved
lib/trino-collect/src/main/java/io/trino/collect/cache/EvictableCache.java
Outdated
Show resolved
Hide resolved
915b59c to
07e67c3
Compare
lib/trino-collect/src/main/java/io/trino/collect/cache/EvictableCache.java
Outdated
Show resolved
Hide resolved
lib/trino-collect/src/main/java/io/trino/collect/cache/EvictableCache.java
Outdated
Show resolved
Hide resolved
c24d3ff to
a4d5430
Compare
|
There is still the |
a4d5430 to
033009e
Compare
|
Actually, disregard that, I went with a simple approach where evicted tokens for the currently loaded value get revived and stored in the map again. There is still a chance that a parallel thread will see the missing token and reload the value, but I think this is acceptable. |
lib/trino-collect/src/test/java/io/trino/collect/cache/TestEvictableCache.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Use Stopwatch -- it's ligheter-weight and easier to lead.
There was a problem hiding this comment.
BTW i wanted to put a PR with a test preventing memory leak (#14476 (comment)) and end-up also writing my version of this test #14502. not sure if this helps
There was a problem hiding this comment.
the timeout until.isAfter(Instant.now()) condition is checked before a call is made, so we should expect allow one more call here
There was a problem hiding this comment.
It never goes past the threshold of the last expiration. Or am I missing something?
There was a problem hiding this comment.
You don't know how much time elapses between checking the clock and invoking cache.get
There was a problem hiding this comment.
i think this boils down to the fact it's much better to use ~Ticker for tests, per @losipiuk #14502 (comment)
lib/trino-collect/src/test/java/io/trino/collect/cache/TestEvictableCache.java
Outdated
Show resolved
Hide resolved
lib/trino-collect/src/main/java/io/trino/collect/cache/EvictableCache.java
Outdated
Show resolved
Hide resolved
1433f14 to
c331728
Compare
When `.expireAfterWrite()` is set, EvictableCache loads the value twice after expiration. On any read/write operation the cache might call a maintenance loop which might evict expired items. Evicted items might include arbitrary set of values, including the one that is currently being read, in this case a reload of the value will follow eviction. With this change tokens that got expired are revived before loading them again.
c331728 to
12f5314
Compare
|
I moved the 'revival' deeper into the loading of the value. It turns out loading is happening after the eviction of expired items, an attempt to read as well as the removal notifications calls. |
| Token<K> token = tokens.computeIfAbsent(key, ignored -> newToken); | ||
| Callable<? extends V> valueLoaderImpl = () -> { | ||
| // revive token if it got expired before reloading | ||
| tokens.computeIfAbsent(token.getKey(), ignored -> token); |
There was a problem hiding this comment.
would this be equivalent?
| tokens.computeIfAbsent(token.getKey(), ignored -> token); | |
| tokens.computeIfAbsent(key, ignored -> token); |
| Callable<? extends V> valueLoaderImpl = () -> { | ||
| // revive token if it got expired before reloading | ||
| tokens.computeIfAbsent(token.getKey(), ignored -> token); | ||
| return valueLoader.call(); |
There was a problem hiding this comment.
just thinking -- should the revival happen before or after valueLoader.call?
or it doesn't matter?
| Callable<? extends V> valueLoaderImpl = () -> { | ||
| // revive token if it got expired before reloading | ||
| tokens.computeIfAbsent(token.getKey(), ignored -> token); | ||
| return valueLoader.call(); |
There was a problem hiding this comment.
related -- if valueLoader throws and this wasn't a new token, we can revive it and leak
|
I tried addressing it slightly differently #14644 |
|
Superseded by #14644 |
Description
When
.expireAfterWrite()is set, EvictableCache loads the value twiceafter expiration. On any read/write operation the cache might call a
maintenance loop which might evict expired items. Evicted items might
include arbitrary set of values, including the one that is currently
being read, in this case a reload of the value will follow eviction.
With this change key tokens for evicted items are not removed on
notification, but instead only marked as expired, while at the end of
operation the currently processed token is restored while all others are
removed.
Release notes
(x) This is not user-visible or docs only and no release notes are required.
( ) Release notes are required, please propose a release note for me.
( ) Release notes are required, with the following suggested text:
Fixes #14545