Skip to content

Commit

Permalink
Fix expiration delay for scheduled cleanup (fixes #431)
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-manes committed Jun 28, 2020
1 parent 3150a92 commit 49fc588
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 9 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,6 @@ tasks.coveralls {
}

dependencyUpdates.resolutionStrategy {
force 'javax.json.bind:javax.json.bind-api:1.0'
force libraries.coherenceProvidedScope
force testLibraries.truth
}
Original file line number Diff line number Diff line change
Expand Up @@ -850,23 +850,23 @@ private long getExpirationDelay(long now) {
if (expiresAfterAccess()) {
Node<K, V> node = accessOrderWindowDeque().peekFirst();
if (node != null) {
delay = Math.min(delay, now - node.getAccessTime() + expiresAfterAccessNanos());
delay = Math.min(delay, expiresAfterAccessNanos() - (now - node.getAccessTime()));
}
if (evicts()) {
node = accessOrderProbationDeque().peekFirst();
if (node != null) {
delay = Math.min(delay, now - node.getAccessTime() + expiresAfterAccessNanos());
delay = Math.min(delay, expiresAfterAccessNanos() - (now - node.getAccessTime()));
}
node = accessOrderProtectedDeque().peekFirst();
if (node != null) {
delay = Math.min(delay, now - node.getAccessTime() + expiresAfterAccessNanos());
delay = Math.min(delay, expiresAfterAccessNanos() - (now - node.getAccessTime()));
}
}
}
if (expiresAfterWrite()) {
Node<K, V> node = writeOrderDeque().peekFirst();
if (node != null) {
delay = Math.min(delay, now - node.getWriteTime() + expiresAfterWriteNanos());
delay = Math.min(delay, expiresAfterWriteNanos() - (now - node.getWriteTime()));
}
}
if (expiresVariable()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand All @@ -48,6 +51,7 @@
import java.util.function.Function;

import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -75,6 +79,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.Futures;

/**
* The test cases for caches that support an expiration policy.
Expand Down Expand Up @@ -149,14 +154,59 @@ public void schedule(Cache<Integer, Integer> cache, CacheContext context) {
scheduler = CacheScheduler.MOCK)
public void schedule_immediate(Cache<Integer, Integer> cache, CacheContext context) {
doAnswer(invocation -> {
((Runnable) invocation.getArgument(1)).run();
invocation.getArgument(1, Runnable.class).run();
return DisabledFuture.INSTANCE;
}).when(context.scheduler()).schedule(any(), any(), anyLong(), any());

cache.put(context.absentKey(), context.absentValue());
verify(context.scheduler(), atMostOnce()).schedule(any(), any(), anyLong(), any());
}

@Test(dataProvider = "caches")
@CacheSpec(implementation = Implementation.Caffeine, population = Population.EMPTY,
mustExpireWithAnyOf = { AFTER_ACCESS, AFTER_WRITE, VARIABLE },
expiry = { CacheExpiry.DISABLED, CacheExpiry.CREATE, CacheExpiry.WRITE, CacheExpiry.ACCESS },
expireAfterAccess = {Expire.DISABLED, Expire.ONE_MINUTE},
expireAfterWrite = {Expire.DISABLED, Expire.ONE_MINUTE}, expiryTime = Expire.ONE_MINUTE,
scheduler = CacheScheduler.MOCK, removalListener = Listener.MOCK)
public void schedule_delay(Cache<Integer, Duration> cache, CacheContext context)
throws InterruptedException {
Map<Integer, Duration> actualExpirationPeriods = new HashMap<>();
ArgumentCaptor<Long> delay = ArgumentCaptor.forClass(long.class);
ArgumentCaptor<Runnable> task = ArgumentCaptor.forClass(Runnable.class);
Answer<Void> onRemoval = invocation -> {
Integer key = invocation.getArgument(0, Integer.class);
Duration value = invocation.getArgument(1, Duration.class);
actualExpirationPeriods.put(key, Duration.ofNanos(context.ticker().read()).minus(value));
return null;
};
doAnswer(onRemoval).when(context.removalListener()).onRemoval(any(), any(), any());
when(context.scheduler().schedule(any(), task.capture(), delay.capture(), any()))
.thenReturn(Futures.immediateFuture(null));

Integer key1 = 1;
cache.put(key1, Duration.ofNanos(context.ticker().read()));

Duration insertDelay = Duration.ofMillis(10);
context.ticker().advance(insertDelay);

Integer key2 = 2;
cache.put(key2, Duration.ofNanos(context.ticker().read()));

Duration expireKey1 = Duration.ofNanos(delay.getValue()).minus(insertDelay);
context.ticker().advance(expireKey1);
task.getValue().run();

Duration expireKey2 = Duration.ofNanos(delay.getValue());
context.ticker().advance(expireKey2);
task.getValue().run();

Duration maxExpirationPeriod = Duration.ofNanos(Pacer.TOLERANCE)
.plusNanos(context.expiryTime().timeNanos());
assertThat(actualExpirationPeriods.get(key1), lessThan(maxExpirationPeriod));
assertThat(actualExpirationPeriods.get(key2), lessThan(maxExpirationPeriod));
}

/* --------------- Cache --------------- */

@Test(dataProvider = "caches", expectedExceptions = DeleteException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,9 @@ enum Expire {
IMMEDIATELY(0L),
/** A configuration where entries are evicted almost immediately. */
ONE_MILLISECOND(TimeUnit.MILLISECONDS.toNanos(1L)),
/** A configuration that holds a single entry. */
/** A configuration where entries are after a time duration. */
ONE_MINUTE(TimeUnit.MINUTES.toNanos(1L)),
/** A configuration where entries should never expire. */
/** A configuration that holds the {@link Population#FULL} count. */
FOREVER(Long.MAX_VALUE);

Expand Down Expand Up @@ -420,6 +421,13 @@ enum Listener {
@Override public <K, V> RemovalListener<K, V> create() {
return RemovalListeners.consuming();
}
},
/** A removal listener that records interactions. */
MOCK {
@SuppressWarnings("unchecked")
@Override public <K, V> RemovalListener<K, V> create() {
return Mockito.mock(RemovalListener.class);
}
};

public abstract <K, V> RemovalListener<K, V> create();
Expand Down
14 changes: 14 additions & 0 deletions checksum.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
<trusted-key id='52931f4b72b4f54c' group='com.koloboke' />
<trusted-key id='0c33729405f13ee8' group='com.oracle.coherence.ce' />
<trusted-key id='1063fe98bcecb758' group='com.puppycrawl.tools' />
<trusted-key id='156e8da37abc7c91' group='com.sleepycat' />
<trusted-key id='685c46769dbb5e5d' group='com.squareup' />
<trusted-key id='80c08b1c29100955' group='com.squareup' />
<trusted-key id='8671a8df71296252' group='com.squareup' />
Expand Down Expand Up @@ -82,11 +83,20 @@
<trusted-key id='a41f13c999945293' group='commons-logging' />
<trusted-key id='379ce192d401ab61' group='info.picocli' />
<trusted-key id='5e2f2b3d474efe6b' group='it.unimi.dsi' />
<trusted-key id='d908a43fb7ec07ac' group='jakarta.activation' />
<trusted-key id='0e325becb6962a24' group='jakarta.annotation' />
<trusted-key id='0aa3e5c3d232e79b' group='jakarta.inject' />
<trusted-key id='c809ca3c41ba6e96' group='jakarta.validation' />
<trusted-key id='b4bf94f677caa76f' group='jakarta.ws.rs' />
<trusted-key id='8118b3bcdb1a5000' group='jakarta.xml.bind' />
<trusted-key id='6425559c47cc79c4' group='javax.activation' />
<trusted-key id='8db9fa0b0718bbf9' group='javax.cache' />
<trusted-key id='d5e520459cb3aa49' group='javax.cache' />
<trusted-key id='6425559c47cc79c4' group='javax.jms' />
<trusted-key id='0315bfb7970a144f' group='javax.json' />
<trusted-key id='3575e1c767076ca8' group='javax.json.bind' />
<trusted-key id='5f46eb39af9138fe' group='javax.json.bind' />
<trusted-key id='6425559c47cc79c4' group='javax.ws.rs' />
<trusted-key id='0315bfb7970a144f' group='javax.xml.bind' />
<trusted-key id='4044edf1bb73efea' group='jaxen' />
<trusted-key id='72385ff0af338d52' group='joda-time' />
Expand Down Expand Up @@ -132,7 +142,11 @@
<trusted-key id='5b05ccde140c2876' group='org.eclipse.jgit' />
<trusted-key id='b83a12a027fc6962' group='org.ehcache' />
<trusted-key id='d27d666cd88e42b4' group='org.elasticsearch' />
<trusted-key id='afc18a2271eddfe1' group='org.glassfish.hk2' />
<trusted-key id='afc18a2271eddfe1' group='org.glassfish.hk2.external' />
<trusted-key id='0315bfb7970a144f' group='org.glassfish.jaxb' />
<trusted-key id='18d239b1cbcd2236' group='org.glassfish.jersey.core' />
<trusted-key id='18d239b1cbcd2236' group='org.glassfish.jersey.media' />
<trusted-key id='a6adfc93ef34893e' group='org.hamcrest' />
<trusted-key id='bede11eaf1164480' group='org.hamcrest' />
<trusted-key id='e7bf252cf360097e' group='org.hdrhistogram' />
Expand Down
14 changes: 12 additions & 2 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,15 @@ ext {
pluginVersions = [
apt: '0.21',
bnd: '5.1.1',
checkstyle: '8.33',
checkstyle: '8.34',
coveralls: '2.8.4',
coverity: '1.0.10',
errorprone: '1.2.1',
jacoco: '0.8.5',
jmh: '0.5.0',
jmhReport: '0.9.0',
nullaway: '1.0.1',
pmd: '6.24.0',
pmd: '6.25.0',
semanticVersioning: '1.1.0',
shadow: '6.0.0',
sonarqube: '3.0',
Expand All @@ -103,6 +103,16 @@ ext {
"com.oracle.coherence.ce:coherence:${versions.coherence}",
'javax.json.bind:javax.json.bind-api:1.0',
],
coherenceProvidedScope: [
'org.glassfish.jersey.core:jersey-server:2.31',
'javax.json.bind:javax.json.bind-api:1.0',
'javax.ws.rs:javax.ws.rs-api:2.0',
'javax.jms:javax.jms-api:2.0.1',
'javax.inject:javax.inject:1',
'org.ow2.asm:asm-commons:8.0.1',
'com.sleepycat:je:18.3.12',
'org.ow2.asm:asm:8.0.1',
],
collision: "systems.comodal:collision:${versions.collision}",
commonsCompress: "org.apache.commons:commons-compress:${versions.commonsCompress}",
commonsLang3: "org.apache.commons:commons-lang3:${versions.commonsLang3}",
Expand Down
1 change: 1 addition & 0 deletions simulator/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ dependencies {
implementation libraries.elasticSearch
implementation libraries.commonsCompress
implementation libraries.univocityParsers
implementation libraries.coherenceProvidedScope

testImplementation testLibraries.testng
}
Expand Down

0 comments on commit 49fc588

Please sign in to comment.