public void testRemovalListener_collected() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .concurrencyLevel(1) .softValues() .removalListener(listener)); Segment<Object, Object> segment = map.segments[0]; assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); map.put(one, two); map.put(two, three); assertTrue(listener.isEmpty()); int hash = map.hash(one); ReferenceEntry<Object, Object> entry = segment.getEntry(one, hash); map.reclaimValue(entry.getValueReference()); assertNotified(listener, one, two, RemovalCause.COLLECTED); assertTrue(listener.isEmpty()); }
public void testRemovalListener_size() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .concurrencyLevel(1) .maximumSize(2) .removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); map.put(one, two); map.put(two, three); assertTrue(listener.isEmpty()); map.put(three, four); assertNotified(listener, one, two, RemovalCause.SIZE); assertTrue(listener.isEmpty()); }
public void testRemovalListener_replaced() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); Object five = new Object(); Object six = new Object(); map.put(one, two); map.put(one, three); assertNotified(listener, one, two, RemovalCause.REPLACED); Map<Object, Object> newMap = ImmutableMap.of(one, four); map.putAll(newMap); assertNotified(listener, one, three, RemovalCause.REPLACED); map.replace(one, five); assertNotified(listener, one, four, RemovalCause.REPLACED); map.replace(one, five, six); assertNotified(listener, one, five, RemovalCause.REPLACED); }
public void testRemovalListener_collected() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache( createCacheBuilder().concurrencyLevel(1).softValues().removalListener(listener)); Segment<Object, Object> segment = map.segments[0]; assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); map.put(one, two); map.put(two, three); assertTrue(listener.isEmpty()); int hash = map.hash(one); ReferenceEntry<Object, Object> entry = segment.getEntry(one, hash); map.reclaimValue(entry.getValueReference()); assertNotified(listener, one, two, RemovalCause.COLLECTED); assertTrue(listener.isEmpty()); }
public void testRemovalListener_size() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache( createCacheBuilder().concurrencyLevel(1).maximumSize(2).removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); map.put(one, two); map.put(two, three); assertTrue(listener.isEmpty()); map.put(three, four); assertNotified(listener, one, two, RemovalCause.SIZE); assertTrue(listener.isEmpty()); }
public void testExpiration_invalidateAll() { FakeTicker ticker = new FakeTicker(); QueuingRemovalListener<Integer, Integer> listener = TestingRemovalListeners.queuingRemovalListener(); Cache<Integer, Integer> cache = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.MINUTES) .removalListener(listener) .ticker(ticker) .build(); cache.put(1, 1); ticker.advance(10, TimeUnit.MINUTES); cache.invalidateAll(); assertThat(listener.poll().getCause()).isEqualTo(RemovalCause.EXPIRED); }
public void testRemovalListener_replaced() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); Object five = new Object(); Object six = new Object(); map.put(one, two); map.put(one, three); assertNotified(listener, one, two, RemovalCause.REPLACED); Map<Object, Object> newMap = ImmutableMap.of(one, four); map.putAll(newMap); assertNotified(listener, one, three, RemovalCause.REPLACED); map.replace(one, five); assertNotified(listener, one, four, RemovalCause.REPLACED); map.replace(one, five, six); assertNotified(listener, one, five, RemovalCause.REPLACED); }
public void testRemovalListener_expired() { FakeTicker ticker = new FakeTicker(); QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .concurrencyLevel(1) .expireAfterWrite(3, TimeUnit.NANOSECONDS) .ticker(ticker) .removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); Object five = new Object(); map.put(one, two); ticker.advance(1); map.put(two, three); ticker.advance(1); map.put(three, four); assertTrue(listener.isEmpty()); ticker.advance(1); map.put(four, five); assertNotified(listener, one, two, RemovalCause.EXPIRED); assertTrue(listener.isEmpty()); }
static <K, V> void assertNotified( QueuingRemovalListener<K, V> listener, K key, V value, RemovalCause cause) { RemovalNotification<K, V> notification = listener.remove(); assertSame(key, notification.getKey()); assertSame(value, notification.getValue()); assertSame(cause, notification.getCause()); }
public void testClear_notification() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .concurrencyLevel(1) .initialCapacity(1) .maximumSize(SMALL_MAX_SIZE) .expireAfterWrite(99999, SECONDS) .removalListener(listener)); Segment<Object, Object> segment = map.segments[0]; AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; assertEquals(1, table.length()); Object key = new Object(); Object value = new Object(); int hash = map.hash(key); DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null); segment.recordWrite(entry, 1, map.ticker.read()); segment.table.set(0, entry); segment.readCount.incrementAndGet(); segment.count = 1; segment.totalWeight = 1; assertSame(entry, table.get(0)); assertSame(entry, segment.accessQueue.peek()); assertSame(entry, segment.writeQueue.peek()); segment.clear(); assertNull(table.get(0)); assertTrue(segment.accessQueue.isEmpty()); assertTrue(segment.writeQueue.isEmpty()); assertEquals(0, segment.readCount.get()); assertEquals(0, segment.count); assertEquals(0, segment.totalWeight); assertNotified(listener, key, value, RemovalCause.EXPLICIT); }
/** * Calls get() repeatedly from many different threads, and tests that all of the removed entries * (removed because of size limits or expiration) trigger appropriate removal notifications. */ @GwtIncompatible // QueuingRemovalListener public void testRemovalNotification_get_basher() throws InterruptedException { int nTasks = 1000; int nThreads = 100; final int getsPerTask = 1000; final int nUniqueKeys = 10000; final Random random = new Random(); // Randoms.insecureRandom(); QueuingRemovalListener<String, String> removalListener = queuingRemovalListener(); final AtomicInteger computeCount = new AtomicInteger(); final AtomicInteger exceptionCount = new AtomicInteger(); final AtomicInteger computeNullCount = new AtomicInteger(); CacheLoader<String, String> countingIdentityLoader = new CacheLoader<String, String>() { @Override public String load(String key) throws InterruptedException { int behavior = random.nextInt(4); if (behavior == 0) { // throw an exception exceptionCount.incrementAndGet(); throw new RuntimeException("fake exception for test"); } else if (behavior == 1) { // return null computeNullCount.incrementAndGet(); return null; } else if (behavior == 2) { // slight delay before returning Thread.sleep(5); computeCount.incrementAndGet(); return key; } else { computeCount.incrementAndGet(); return key; } } }; final LoadingCache<String, String> cache = CacheBuilder.newBuilder() .recordStats() .concurrencyLevel(2) .expireAfterWrite(100, TimeUnit.MILLISECONDS) .removalListener(removalListener) .maximumSize(5000) .build(countingIdentityLoader); ExecutorService threadPool = Executors.newFixedThreadPool(nThreads); for (int i = 0; i < nTasks; i++) { @SuppressWarnings("unused") // go/futurereturn-lsc Future<?> possiblyIgnoredError = threadPool.submit( new Runnable() { @Override public void run() { for (int j = 0; j < getsPerTask; j++) { try { cache.getUnchecked("key" + random.nextInt(nUniqueKeys)); } catch (RuntimeException e) { } } } }); } threadPool.shutdown(); threadPool.awaitTermination(300, TimeUnit.SECONDS); // Since we're not doing any more cache operations, and the cache only expires/evicts when doing // other operations, the cache and the removal queue won't change from this point on. // Verify that each received removal notification was valid for (RemovalNotification<String, String> notification : removalListener) { assertEquals("Invalid removal notification", notification.getKey(), notification.getValue()); } CacheStats stats = cache.stats(); assertEquals(removalListener.size(), stats.evictionCount()); assertEquals(computeCount.get(), stats.loadSuccessCount()); assertEquals(exceptionCount.get() + computeNullCount.get(), stats.loadExceptionCount()); // each computed value is still in the cache, or was passed to the removal listener assertEquals(computeCount.get(), cache.size() + removalListener.size()); }
public void testRemovalListener_explicit() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder().removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); Object five = new Object(); Object six = new Object(); map.put(one, two); map.remove(one); assertNotified(listener, one, two, RemovalCause.EXPLICIT); map.put(two, three); map.remove(two, three); assertNotified(listener, two, three, RemovalCause.EXPLICIT); map.put(three, four); Iterator<?> i = map.entrySet().iterator(); i.next(); i.remove(); assertNotified(listener, three, four, RemovalCause.EXPLICIT); map.put(four, five); i = map.keySet().iterator(); i.next(); i.remove(); assertNotified(listener, four, five, RemovalCause.EXPLICIT); map.put(five, six); i = map.values().iterator(); i.next(); i.remove(); assertNotified(listener, five, six, RemovalCause.EXPLICIT); assertTrue(listener.isEmpty()); }
public void testRemovalListener_expired() { FakeTicker ticker = new FakeTicker(); QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache( createCacheBuilder() .concurrencyLevel(1) .expireAfterWrite(3, TimeUnit.NANOSECONDS) .ticker(ticker) .removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); Object five = new Object(); map.put(one, two); ticker.advance(1); map.put(two, three); ticker.advance(1); map.put(three, four); assertTrue(listener.isEmpty()); ticker.advance(1); map.put(four, five); assertNotified(listener, one, two, RemovalCause.EXPIRED); assertTrue(listener.isEmpty()); }
public void testClear_notification() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache( createCacheBuilder() .concurrencyLevel(1) .initialCapacity(1) .maximumSize(SMALL_MAX_SIZE) .expireAfterWrite(99999, SECONDS) .removalListener(listener)); Segment<Object, Object> segment = map.segments[0]; AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; assertEquals(1, table.length()); Object key = new Object(); Object value = new Object(); int hash = map.hash(key); DummyEntry<Object, Object> entry = createDummyEntry(key, hash, value, null); segment.recordWrite(entry, 1, map.ticker.read()); segment.table.set(0, entry); segment.readCount.incrementAndGet(); segment.count = 1; segment.totalWeight = 1; assertSame(entry, table.get(0)); assertSame(entry, segment.accessQueue.peek()); assertSame(entry, segment.writeQueue.peek()); segment.clear(); assertNull(table.get(0)); assertTrue(segment.accessQueue.isEmpty()); assertTrue(segment.writeQueue.isEmpty()); assertEquals(0, segment.readCount.get()); assertEquals(0, segment.count); assertEquals(0, segment.totalWeight); assertNotified(listener, key, value, RemovalCause.EXPLICIT); }
public void testRemovalListener_explicit() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .removalListener(listener)); assertTrue(listener.isEmpty()); Object one = new Object(); Object two = new Object(); Object three = new Object(); Object four = new Object(); Object five = new Object(); Object six = new Object(); map.put(one, two); map.remove(one); assertNotified(listener, one, two, RemovalCause.EXPLICIT); map.put(two, three); map.remove(two, three); assertNotified(listener, two, three, RemovalCause.EXPLICIT); map.put(three, four); Iterator<?> i = map.entrySet().iterator(); i.next(); i.remove(); assertNotified(listener, three, four, RemovalCause.EXPLICIT); map.put(four, five); i = map.keySet().iterator(); i.next(); i.remove(); assertNotified(listener, four, five, RemovalCause.EXPLICIT); map.put(five, six); i = map.values().iterator(); i.next(); i.remove(); assertNotified(listener, five, six, RemovalCause.EXPLICIT); assertTrue(listener.isEmpty()); }
public void testSegmentStoreComputedValue() { QueuingRemovalListener<Object, Object> listener = queuingRemovalListener(); LocalCache<Object, Object> map = makeLocalCache(createCacheBuilder() .concurrencyLevel(1) .removalListener(listener)); Segment<Object, Object> segment = map.segments[0]; Object key = new Object(); int hash = map.hash(key); AtomicReferenceArray<ReferenceEntry<Object, Object>> table = segment.table; int index = hash & (table.length() - 1); DummyEntry<Object, Object> entry = DummyEntry.create(key, hash, null); LoadingValueReference<Object, Object> valueRef = new LoadingValueReference<Object, Object>(); entry.setValueReference(valueRef); // absent Object value = new Object(); assertTrue(listener.isEmpty()); assertEquals(0, segment.count); assertNull(segment.get(key, hash)); assertTrue(segment.storeLoadedValue(key, hash, valueRef, value)); assertSame(value, segment.get(key, hash)); assertEquals(1, segment.count); assertTrue(listener.isEmpty()); // clobbered Object value2 = new Object(); assertFalse(segment.storeLoadedValue(key, hash, valueRef, value2)); assertEquals(1, segment.count); assertSame(value, segment.get(key, hash)); RemovalNotification<Object, Object> notification = listener.remove(); assertEquals(immutableEntry(key, value2), notification); assertEquals(RemovalCause.REPLACED, notification.getCause()); assertTrue(listener.isEmpty()); // inactive Object value3 = new Object(); map.clear(); listener.clear(); assertEquals(0, segment.count); table.set(index, entry); assertTrue(segment.storeLoadedValue(key, hash, valueRef, value3)); assertSame(value3, segment.get(key, hash)); assertEquals(1, segment.count); assertTrue(listener.isEmpty()); // replaced Object value4 = new Object(); DummyValueReference<Object, Object> value3Ref = DummyValueReference.create(value3); valueRef = new LoadingValueReference<Object, Object>(value3Ref); entry.setValueReference(valueRef); table.set(index, entry); assertSame(value3, segment.get(key, hash)); assertEquals(1, segment.count); assertTrue(segment.storeLoadedValue(key, hash, valueRef, value4)); assertSame(value4, segment.get(key, hash)); assertEquals(1, segment.count); notification = listener.remove(); assertEquals(immutableEntry(key, value3), notification); assertEquals(RemovalCause.REPLACED, notification.getCause()); assertTrue(listener.isEmpty()); // collected entry.setValueReference(valueRef); table.set(index, entry); assertSame(value3, segment.get(key, hash)); assertEquals(1, segment.count); value3Ref.clear(); assertTrue(segment.storeLoadedValue(key, hash, valueRef, value4)); assertSame(value4, segment.get(key, hash)); assertEquals(1, segment.count); notification = listener.remove(); assertEquals(immutableEntry(key, null), notification); assertEquals(RemovalCause.COLLECTED, notification.getCause()); assertTrue(listener.isEmpty()); }
@GwtIncompatible // QueuingRemovalListener public void testRemovalNotification_clear() throws InterruptedException { // If a clear() happens while a computation is pending, we should not get a removal // notification. final AtomicBoolean shouldWait = new AtomicBoolean(false); final CountDownLatch computingLatch = new CountDownLatch(1); CacheLoader<String, String> computingFunction = new CacheLoader<String, String>() { @Override public String load(String key) throws InterruptedException { if (shouldWait.get()) { computingLatch.await(); } return key; } }; QueuingRemovalListener<String, String> listener = queuingRemovalListener(); final LoadingCache<String, String> cache = CacheBuilder.newBuilder() .concurrencyLevel(1) .removalListener(listener) .build(computingFunction); // seed the map, so its segment's count > 0 cache.getUnchecked("a"); shouldWait.set(true); final CountDownLatch computationStarted = new CountDownLatch(1); final CountDownLatch computationComplete = new CountDownLatch(1); new Thread(new Runnable() { @Override public void run() { computationStarted.countDown(); cache.getUnchecked("b"); computationComplete.countDown(); } }).start(); // wait for the computingEntry to be created computationStarted.await(); cache.invalidateAll(); // let the computation proceed computingLatch.countDown(); // don't check cache.size() until we know the get("b") call is complete computationComplete.await(); // At this point, the listener should be holding the seed value (a -> a), and the map should // contain the computed value (b -> b), since the clear() happened before the computation // completed. assertEquals(1, listener.size()); RemovalNotification<String, String> notification = listener.remove(); assertEquals("a", notification.getKey()); assertEquals("a", notification.getValue()); assertEquals(1, cache.size()); assertEquals("b", cache.getUnchecked("b")); }