Last active
September 19, 2017 05:14
-
-
Save ksperling/241c865526a2df9ad0dc9d02dac4393c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sandbox; | |
import java.util.Random; | |
import java.util.concurrent.CountDownLatch; | |
import java.util.concurrent.TimeUnit; | |
import java.util.concurrent.atomic.AtomicInteger; | |
import java.util.concurrent.atomic.AtomicReference; | |
import java.util.concurrent.locks.Lock; | |
import java.util.concurrent.locks.ReentrantLock; | |
import com.github.benmanes.caffeine.cache.CacheWriter; | |
import com.github.benmanes.caffeine.cache.Caffeine; | |
import com.github.benmanes.caffeine.cache.LoadingCache; | |
import com.github.benmanes.caffeine.cache.RemovalCause; | |
import org.junit.Test; | |
public class CaffeineEvictionTest { | |
private static class Value { | |
final int key; | |
boolean valid = true; | |
public Value(int key) { | |
this.key = key; | |
} | |
} | |
@Test | |
public void exercise() throws InterruptedException { | |
final int THREADS = 10, ITERATIONS = 100000, RANGE = 1000; | |
Lock[] locks = new Lock[RANGE]; | |
for (int i=0; i<RANGE; i++) locks[i] = new ReentrantLock(); | |
LoadingCache<Integer, Value> cache = Caffeine.newBuilder() | |
.maximumSize(RANGE / 10) // ensure plenty of evictions | |
.writer(new CacheWriter<Integer, Value>() { | |
@Override | |
public void write(Integer key, Value value) { | |
/* do nothing */ | |
} | |
@Override | |
public void delete(Integer key, Value value, RemovalCause cause) { | |
Lock lock = locks[key]; | |
lock.lock(); | |
try { | |
value.valid = false; | |
} finally { | |
lock.unlock(); | |
} | |
} | |
}) | |
.build((k) -> new Value(k)); | |
CountDownLatch latch = new CountDownLatch(THREADS); | |
AtomicInteger progress = new AtomicInteger(); | |
AtomicReference<RuntimeException> failure = new AtomicReference<>(); | |
for (int i=0; i<THREADS; i++) { | |
Random rnd = new Random(i); | |
new Thread(() -> { | |
try { | |
for (int j=0;j<ITERATIONS; j++) { | |
int key = rnd.nextInt(RANGE); | |
Lock lock = locks[key]; | |
lock.lock(); | |
try { | |
Value value = cache.get(key); | |
if (value.key != key) throw new IllegalStateException(String.format("cache.get(%d).key == %d", key, value.key)); | |
else if (!value.valid) throw new IllegalStateException(String.format("cache.get(%d) invalid", key)); | |
} finally { | |
lock.unlock(); | |
} | |
int total = THREADS*ITERATIONS, done = progress.incrementAndGet(); | |
if (done % (total / 10) == 0) System.err.printf("Test %d%% complete\n", done * 100 / total); | |
} | |
} catch (RuntimeException ex) { | |
failure.compareAndSet(null, new RuntimeException(Thread.currentThread().getName() + " failed", ex)); | |
} finally { | |
latch.countDown(); | |
} | |
}).start(); | |
} | |
latch.await(30, TimeUnit.SECONDS); | |
RuntimeException ex = failure.get(); | |
if (ex != null) throw ex; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment