-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Some Expiry
implementations return currentTime + desiredLength
instead of desiredLength
#1499
Comments
I didn't expect Ideally a At times I've wondered if there was any nice static factory or builder trick that we could provide to make it easier to do the right thing? For example like how |
Oh, that's a great point about the simpler API. Both callers look like this: .expireAfter(
new Expiry<String, Long>() {
@Override
public long expireAfterCreate(String key, Long value, long currentTime) {
return currentTime + EXPIRATION;
}
@Override
public long expireAfterUpdate(
String key, Long value, long currentTime, long currentDuration) {
return currentDuration;
}
@Override
public long expireAfterRead(
String key, Long value, long currentTime, long currentDuration) {
return currentDuration;
}
}) That certainly sounds like they could probably both be just I glanced at a few of our other And for |
Having looked at all of the implementations we have (like Chris said there aren't a ton of them), almost all of them either only set expiration on create (slightly more of these I think), or set expiration on both create and update (in the same way). There's I think one that changes expiration on read, and it sets the expiration to As far as |
I think that I supplied We unfortunately allowed both expireAfterWrite and expireAfterAccess to be set in Guava, and I allowed that here to simplify migrations. We could add a fixed expieAfterCreation as exclusive, it’s just odd for users why some can be mixed as if there’s a good use-case. Similarly if we added a callback version of those builder methods instead of fixed durations. It might be a reasonable restriction to add even if a breaking change as I don’t think multiple expiration settings makes much sense. I suppose my ideal would be lambda versions in the cache builder for one-liners (fixed/variable create, write, access). Then Expiry should almost never be used, but could be to avoid the Duration tax. |
Yeah, lambda equivalents to fixed duration builder methods was an approach I was thinking might be nice. Differing exclusivity of them does sound a bit awkward though. (Aside: Just want to be sure this isn't coming off as "why did you design it this way?!". It's just an interesting (?) observation I made when looking at usages.) |
Unfortunately any evolution of the apis here has some drawback that makes it potentially confusing. Cache builderIf we extended the cache builder to accept lambda expressions then we'd have to decide on some of the forms Caffeine.newBuilder()
.expireAfterCreate(Duration.ofMinutes(5))
.expireAfterCreate((K key, V value) -> duration)
.expireAfterCreate((Map.Entry<K, V> entry) -> duration)
.expireAfterWrite((K key, V value, Duration duration) -> duration)
.expireAfterWrite((Map.Entry<K, V> entry, Duration duration) -> duration) where expireAfterWrite's duration argument on entry creation would be The Another flaw is that This mess stems from the late addition of variable expiration as we required amortized O(1) policies. Therefore, we started with an eager fixed approach (time-bounded linked list) rather than provide a problematic alternatives like a priority queue or require size eviction to lazily discard. If we had the option of a redo then everything would be variable (use our timer wheel) and have the spectrum of convenient builder methods for quick configuration. Thankfully Static factoriesA factory approach is less discoverable but side-steps some of that legacy api. That could be singular or chained, e.g. Caffeine.newBuilder()
.expireAfter(Expiry.creating((Key key, Value value) -> duration))
.expireAfter(Expiry.creating((Key key, Value value) -> duration)
.updating((key, value, currentDuration) -> duration)
.reading((key, value, currentDuration) -> duration))
.expireAfter(Expiry.writing((Key key, Value value, Duration currentDuration) -> duration)) This would also need the BuilderThis would be the same as a static factory, but carry the noise of a full-on type. That would allow for new'ing it up with the type arguments so the Caffeine.newBuilder()
.expireAfter(new Expiry.Builder<K, V>()
.create((key, value) -> duration)
.update((key, value, currentDuration) -> duration)
.read((key, value, currentDuration) -> duration)
.build())
.expireAfter(new Expiry.Builder<K, V>()
.write((key, value, currentDuration) -> duration)
.build())
.build(); It is not too awful to read, though internally a builder is a lot of boilerplate. Preferences? Alternatives?I personally like the discoverability on the cache builder, as it is pretty obvious and natural for users to gravitate towards. Unfortunately how the apis have aged makes me lean towards the other options. The static factory approach is concise. A possible flaw is that often those are overlooked, like our The builder is the most verbose for everyone, but also perhaps the most familiar. It is easiest to extend with more convenience methods if needed, as it gathers the state until construction. thoughts? |
Experimenting with the api a bit, and the ...expireAfter(Expiry.creating((Key key, Graph graph) -> duration))
...expireAfter(Expiry.writing((Key key, Graph graph) -> duration))
...expireAfter(Expiry.accessing((Key key, Graph graph) -> duration)) This isn't that much worse than having it directly on the cache builder, though if the api was redone then I'd have preferred that and reworked the fixed vs variable distinction. Perhaps I am being a bit too anxious as few look at the |
* Add Expiry static factory methods (fixes #1499) * Bump Wandalen/wretry.action from 1.3.0 to 1.4.4 Bumps [Wandalen/wretry.action](https://github.com/wandalen/wretry.action) from 1.3.0 to 1.4.4. - [Release notes](https://github.com/wandalen/wretry.action/releases) - [Commits](Wandalen/wretry.action@a163f62...62451a2) --- updated-dependencies: - dependency-name: Wandalen/wretry.action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add Expiry static factory methods (fixes #1499) * Bump gitleaks/gitleaks-action from 2.3.2 to 2.3.3 Bumps [gitleaks/gitleaks-action](https://github.com/gitleaks/gitleaks-action) from 2.3.2 to 2.3.3. - [Release notes](https://github.com/gitleaks/gitleaks-action/releases) - [Commits](gitleaks/gitleaks-action@1f2d10f...cb7149a) --- updated-dependencies: - dependency-name: gitleaks/gitleaks-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@cgdecker reports that every Google
Expiry
implementation either (a) doesn't usecurrentTime
or (b) uses it wrong.One of the wrong implementations is similar to #441. I see that the docs try to warn people about that misunderstanding, thanks to 1d6fa59, so we can hope that that won't come up again.
The other two wrong implementations do what I said in the bug title: They behave as if they're supposed to return the
Ticker
time at which the entry should expire, rather than the length of time from now.I could imagine ways to try to prevent this if we were writing the interface from scratch, like maybe sneaking "duration" into the method name (though I would have hoped that "expireAfterCreate" would have already hinted at that) or (nowadays) actually using the
Duration
type.At the moment, the best option is some combination of further documentation and maybe renames of parameters. (I don't know what I'd call the parameters, though... "unrelatedTickerTime?" :)) I don't have brilliant thoughts on the exact doc edits to make, either. I'm happy to review your proposal or put together one of my own if you'd like.
The text was updated successfully, but these errors were encountered: