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 Jan 2, 2021
1 parent 90028a6 commit d1d2b23
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 215 deletions.
Expand Up @@ -3381,9 +3381,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 @@ -3407,15 +3407,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 @@ -3431,7 +3431,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 @@ -3494,7 +3494,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 @@ -3525,7 +3525,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 @@ -3631,7 +3631,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 @@ -3645,32 +3645,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
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);
}
}
}
Expand Up @@ -905,13 +905,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
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 @@ -323,7 +323,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 @@ -332,7 +332,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 @@ -358,7 +358,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 @@ -367,7 +367,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 @@ -393,7 +393,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 @@ -402,7 +402,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 @@ -428,7 +428,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 @@ -437,7 +437,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

0 comments on commit d1d2b23

Please sign in to comment.