Skip to content

Commit

Permalink
Split Policy.Expiration into fixed and refresh interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-manes committed Feb 8, 2021
1 parent e640f10 commit 81de512
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3385,9 +3385,9 @@ static final class BoundedPolicy<K, V> implements Policy<K, V> {
final boolean isWeighted;

@Nullable Optional<Eviction<K, V>> eviction;
@Nullable Optional<Expiration<K, V>> refreshes;
@Nullable Optional<Expiration<K, V>> afterWrite;
@Nullable Optional<Expiration<K, V>> afterAccess;
@Nullable Optional<FixedRefresh<K, V>> refreshes;
@Nullable Optional<FixedExpiration<K, V>> afterWrite;
@Nullable Optional<FixedExpiration<K, V>> afterAccess;
@Nullable Optional<VarExpiration<K, V>> variable;

BoundedPolicy(BoundedLocalCache<K, V> cache, Function<V, V> transformer, boolean isWeighted) {
Expand All @@ -3411,15 +3411,15 @@ static final class BoundedPolicy<K, V> implements Policy<K, V> {
? (eviction == null) ? (eviction = Optional.of(new BoundedEviction())) : eviction
: Optional.empty();
}
@Override public Optional<Expiration<K, V>> expireAfterAccess() {
@Override public Optional<FixedExpiration<K, V>> expireAfterAccess() {
if (!cache.expiresAfterAccess()) {
return Optional.empty();
}
return (afterAccess == null)
? (afterAccess = Optional.of(new BoundedExpireAfterAccess()))
: afterAccess;
}
@Override public Optional<Expiration<K, V>> expireAfterWrite() {
@Override public Optional<FixedExpiration<K, V>> expireAfterWrite() {
if (!cache.expiresAfterWrite()) {
return Optional.empty();
}
Expand All @@ -3435,7 +3435,7 @@ static final class BoundedPolicy<K, V> implements Policy<K, V> {
? (variable = Optional.of(new BoundedVarExpiration()))
: variable;
}
@Override public Optional<Expiration<K, V>> refreshAfterWrite() {
@Override public Optional<FixedRefresh<K, V>> refreshAfterWrite() {
if (!cache.refreshAfterWrite()) {
return Optional.empty();
}
Expand Down Expand Up @@ -3498,7 +3498,7 @@ final class BoundedEviction implements Eviction<K, V> {
}

@SuppressWarnings("PreferJavaTimeOverload")
final class BoundedExpireAfterAccess implements Expiration<K, V> {
final class BoundedExpireAfterAccess implements FixedExpiration<K, V> {
@Override public OptionalLong ageOf(K key, TimeUnit unit) {
requireNonNull(key);
requireNonNull(unit);
Expand Down Expand Up @@ -3529,7 +3529,7 @@ final class BoundedExpireAfterAccess implements Expiration<K, V> {
}

@SuppressWarnings("PreferJavaTimeOverload")
final class BoundedExpireAfterWrite implements Expiration<K, V> {
final class BoundedExpireAfterWrite implements FixedExpiration<K, V> {
@Override public OptionalLong ageOf(K key, TimeUnit unit) {
requireNonNull(key);
requireNonNull(unit);
Expand Down Expand Up @@ -3635,7 +3635,7 @@ public long expireAfterRead(K key, V value, long currentTime, long currentDurati
}

@SuppressWarnings("PreferJavaTimeOverload")
final class BoundedRefreshAfterWrite implements Expiration<K, V> {
final class BoundedRefreshAfterWrite implements FixedRefresh<K, V> {
@Override public OptionalLong ageOf(K key, TimeUnit unit) {
requireNonNull(key);
requireNonNull(unit);
Expand All @@ -3649,32 +3649,14 @@ final class BoundedRefreshAfterWrite implements Expiration<K, V> {
? OptionalLong.empty()
: OptionalLong.of(unit.convert(age, TimeUnit.NANOSECONDS));
}
@Override public long getExpiresAfter(TimeUnit unit) {
@Override public long getRefreshesAfter(TimeUnit unit) {
return unit.convert(cache.refreshAfterWriteNanos(), TimeUnit.NANOSECONDS);
}
@Override public void setExpiresAfter(long duration, TimeUnit unit) {
@Override public void setRefreshesAfter(long duration, TimeUnit unit) {
requireArgument(duration >= 0);
cache.setRefreshAfterWriteNanos(unit.toNanos(duration));
cache.scheduleAfterWrite();
}
@SuppressWarnings("PMD.SimplifiedTernary") // false positive (#1424)
@Override public Map<K, V> oldest(int limit) {
return cache.expiresAfterWrite()
? expireAfterWrite().get().oldest(limit)
: sortedByWriteTime(limit, /* ascending */ true);
}
@SuppressWarnings("PMD.SimplifiedTernary") // false positive (#1424)
@Override public Map<K, V> youngest(int limit) {
return cache.expiresAfterWrite()
? expireAfterWrite().get().youngest(limit)
: sortedByWriteTime(limit, /* ascending */ false);
}
Map<K, V> sortedByWriteTime(int limit, boolean ascending) {
Comparator<Node<K, V>> comparator = Comparator.comparingLong(Node::getWriteTime);
Iterator<Node<K, V>> iterator = cache.data.values().stream().parallel().sorted(
ascending ? comparator : comparator.reversed()).limit(limit).iterator();
return cache.fixedSnapshot(() -> iterator, limit, transformer);
}
}
}

Expand Down
101 changes: 97 additions & 4 deletions caffeine/src/main/java/com/github/benmanes/caffeine/cache/Policy.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ default V getIfPresentQuietly(@NonNull @CompatibleWith("K") Object key) {
* used
*/
@NonNull
Optional<Expiration<K, V>> expireAfterAccess();
Optional<FixedExpiration<K, V>> expireAfterAccess();

/**
* Returns access to perform operations based on the time-to-live expiration policy. This policy
Expand All @@ -100,7 +100,7 @@ default V getIfPresentQuietly(@NonNull @CompatibleWith("K") Object key) {
* used
*/
@NonNull
Optional<Expiration<K, V>> expireAfterWrite();
Optional<FixedExpiration<K, V>> expireAfterWrite();

/**
* Returns access to perform operations based on the variable expiration policy. This policy
Expand Down Expand Up @@ -129,7 +129,7 @@ default Optional<VarExpiration<K, V>> expireVariably() {
* @return access to low-level operations for this cache if a time-to-live refresh policy is used
*/
@NonNull
Optional<Expiration<K, V>> refreshAfterWrite();
Optional<FixedRefresh<K, V>> refreshAfterWrite();

/** The low-level operations for a cache with a size-based eviction policy. */
interface Eviction<K, V> {
Expand Down Expand Up @@ -223,7 +223,7 @@ default OptionalInt weightOf(@NonNull K key) {
}

/** The low-level operations for a cache with a fixed expiration policy. */
interface Expiration<K, V> { // To be renamed FixedExpiration in version 3.0.0
interface FixedExpiration<K, V> {

/**
* Returns the age of the entry based on the expiration policy. The entry's age is the cache's
Expand Down Expand Up @@ -515,4 +515,97 @@ default void put(@NonNull K key, @NonNull V value, @NonNull Duration duration) {
@NonNull
Map<@NonNull K, @NonNull V> youngest(@NonNegative int limit);
}

/** The low-level operations for a cache with a fixed refresh policy. */
interface FixedRefresh<K, V> {

/**
* Returns the age of the entry based on the refresh policy. The entry's age is the cache's
* estimate of the amount of time since the entry's refresh time was last reset.
* <p>
* An expiration policy uses the age to determine if an entry is fresh or stale by comparing it
* to the freshness lifetime. This is calculated as {@code fresh = freshnessLifetime > age}
* where {@code freshnessLifetime = expires - currentTime}.
* <p>
* This method is scheduled for removal in version 3.0.0.
*
* @param key the key for the entry being queried
* @param unit the unit that {@code age} is expressed in
* @return the age if the entry is present in the cache
*/
@NonNull
OptionalLong ageOf(@NonNull K key, @NonNull TimeUnit unit);

/**
* Returns the age of the entry based on the expiration policy. The entry's age is the cache's
* estimate of the amount of time since the entry's expiration was last reset.
* <p>
* An expiration policy uses the age to determine if an entry is fresh or stale by comparing it
* to the freshness lifetime. This is calculated as {@code fresh = freshnessLifetime > age}
* where {@code freshnessLifetime = expires - currentTime}.
*
* @param key the key for the entry being queried
* @return the age if the entry is present in the cache
*/
@NonNull
default Optional<Duration> ageOf(@NonNull K key) {
// This method will be abstract in version 3.0.0
OptionalLong duration = ageOf(key, TimeUnit.NANOSECONDS);
return duration.isPresent()
? Optional.of(Duration.ofNanos(duration.getAsLong()))
: Optional.empty();
}

/**
* Returns the fixed duration used to determine if an entry should be eligible for reloading due
* to elapsing this time bound. An entry is considered fresh if its age is less than this
* duration, and stale otherwise. The refresh policy determines when the entry's age is
* reset.
* <p>
* This method is scheduled for removal in version 3.0.0.
*
* @param unit the unit that duration is expressed in
* @return the length of time after which an entry is eligible to be reloaded
*/
@NonNegative
long getRefreshesAfter(@NonNull TimeUnit unit);

/**
* Returns the fixed duration used to determine if an entry should be eligible for reloading due
* to elapsing this time bound. An entry is considered fresh if its age is less than this
* duration, and stale otherwise. The refresh policy determines when the entry's age is
* reset.
*
* @return the length of time after which an entry is eligible to be reloaded
*/
@NonNull
default Duration getRefreshesAfter() {
// This method will be abstract in version 3.0.0
return Duration.ofNanos(getRefreshesAfter(TimeUnit.NANOSECONDS));
}

/**
* Specifies that each entry should be eligible for reloading once a fixed duration has elapsed.
* The refresh policy determines when the entry's age is reset.
* <p>
* This method is scheduled for removal in version 3.0.0.
*
* @param duration the length of time after which an entry is eligible to be reloaded
* @param unit the unit that {@code duration} is expressed in
* @throws IllegalArgumentException if {@code duration} is negative
*/
void setRefreshesAfter(@NonNegative long duration, @NonNull TimeUnit unit);

/**
* Specifies that each entry should be eligible for reloading once a fixed duration has elapsed.
* The refresh policy determines when the entry's age is reset.
*
* @param duration the length of time after which an entry is eligible to be reloaded
* @throws IllegalArgumentException if {@code duration} is negative
*/
default void setRefreshesAfter(@NonNull Duration duration) {
// This method will be abstract in version 3.0.0
setRefreshesAfter(duration.toNanos(), TimeUnit.NANOSECONDS);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -906,13 +906,13 @@ static final class UnboundedPolicy<K, V> implements Policy<K, V> {
@Override public Optional<Eviction<K, V>> eviction() {
return Optional.empty();
}
@Override public Optional<Expiration<K, V>> expireAfterAccess() {
@Override public Optional<FixedExpiration<K, V>> expireAfterAccess() {
return Optional.empty();
}
@Override public Optional<Expiration<K, V>> expireAfterWrite() {
@Override public Optional<FixedExpiration<K, V>> expireAfterWrite() {
return Optional.empty();
}
@Override public Optional<Expiration<K, V>> refreshAfterWrite() {
@Override public Optional<FixedRefresh<K, V>> refreshAfterWrite() {
return Optional.empty();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import org.testng.annotations.Test;

import com.github.benmanes.caffeine.cache.Policy.Eviction;
import com.github.benmanes.caffeine.cache.Policy.Expiration;
import com.github.benmanes.caffeine.cache.Policy.FixedExpiration;
import com.github.benmanes.caffeine.cache.stats.StatsCounter;
import com.google.common.testing.FakeTicker;
import com.google.common.util.concurrent.MoreExecutors;
Expand Down Expand Up @@ -324,7 +324,7 @@ public void expireAfterAccess_twice() {
public void expireAfterAccess_small() {
Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfterAccess(0, TimeUnit.MILLISECONDS);
assertThat(builder.expireAfterAccessNanos, is(0L));
Expiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
assertThat(expiration.getExpiresAfter(TimeUnit.MILLISECONDS), is(0L));
}

Expand All @@ -333,7 +333,7 @@ public void expireAfterAccess_large() {
Caffeine<?, ?> builder = Caffeine.newBuilder()
.expireAfterAccess(Integer.MAX_VALUE, TimeUnit.NANOSECONDS);
assertThat(builder.expireAfterAccessNanos, is((long) Integer.MAX_VALUE));
Expiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
assertThat(expiration.getExpiresAfter(TimeUnit.NANOSECONDS), is((long) Integer.MAX_VALUE));
}

Expand All @@ -359,7 +359,7 @@ public void expireAfterAccess_duration_twice() {
public void expireAfterAccess_duration_small() {
Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfterAccess(Duration.ofMillis(0));
assertThat(builder.expireAfterAccessNanos, is(0L));
Expiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
assertThat(expiration.getExpiresAfter(TimeUnit.MILLISECONDS), is(0L));
}

Expand All @@ -368,7 +368,7 @@ public void expireAfterAccess_duration_large() {
Caffeine<?, ?> builder = Caffeine.newBuilder()
.expireAfterAccess(Duration.ofNanos(Integer.MAX_VALUE));
assertThat(builder.expireAfterAccessNanos, is((long) Integer.MAX_VALUE));
Expiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterAccess().get();
assertThat(expiration.getExpiresAfter(TimeUnit.NANOSECONDS), is((long) Integer.MAX_VALUE));
}

Expand All @@ -394,7 +394,7 @@ public void expireAfterWrite_twice() {
public void expireAfterWrite_small() {
Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfterWrite(0, TimeUnit.MILLISECONDS);
assertThat(builder.expireAfterWriteNanos, is(0L));
Expiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
assertThat(expiration.getExpiresAfter(TimeUnit.MILLISECONDS), is(0L));
}

Expand All @@ -403,7 +403,7 @@ public void expireAfterWrite_large() {
Caffeine<?, ?> builder = Caffeine.newBuilder()
.expireAfterWrite(Integer.MAX_VALUE, TimeUnit.NANOSECONDS);
assertThat(builder.expireAfterWriteNanos, is((long) Integer.MAX_VALUE));
Expiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
assertThat(expiration.getExpiresAfter(TimeUnit.NANOSECONDS), is((long) Integer.MAX_VALUE));
}

Expand All @@ -429,7 +429,7 @@ public void expireAfterWrite_duration_twice() {
public void expireAfterWrite_duration_small() {
Caffeine<?, ?> builder = Caffeine.newBuilder().expireAfterWrite(Duration.ofMillis(0));
assertThat(builder.expireAfterWriteNanos, is(0L));
Expiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
assertThat(expiration.getExpiresAfter(TimeUnit.MILLISECONDS), is(0L));
}

Expand All @@ -438,7 +438,7 @@ public void expireAfterWrite_duration_large() {
Caffeine<?, ?> builder = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofNanos(Integer.MAX_VALUE));
assertThat(builder.expireAfterWriteNanos, is((long) Integer.MAX_VALUE));
Expiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
FixedExpiration<?, ?> expiration = builder.build().policy().expireAfterWrite().get();
assertThat(expiration.getExpiresAfter(TimeUnit.NANOSECONDS), is((long) Integer.MAX_VALUE));
}

Expand Down
Loading

0 comments on commit 81de512

Please sign in to comment.