alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (CacheExpirationTest.java)

This example Java source code file (CacheExpirationTest.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

atomicinteger, countingremovallistener, eviction, expiring_time, faketicker, identityloader, integer, key_prefix, loadingcache, milliseconds, override, string, threading, threads, util, value_prefix, watchedcreatorloader

The CacheExpirationTest.java Java example source code

/*
 * Copyright (C) 2011 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.common.cache;

import static com.google.common.cache.TestingCacheLoaders.identityLoader;
import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
import static com.google.common.truth.Truth.assertThat;
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

import com.google.common.cache.TestingCacheLoaders.IdentityLoader;
import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
import com.google.common.cache.TestingRemovalListeners.QueuingRemovalListener;
import com.google.common.collect.Iterators;
import com.google.common.testing.FakeTicker;
import com.google.common.util.concurrent.Callables;

import junit.framework.TestCase;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Tests relating to cache expiration: make sure entries expire at the right times, make sure
 * expired entries don't show up, etc.
 *
 * @author mike nonemacher
 */
@SuppressWarnings("deprecation") // tests of deprecated method
public class CacheExpirationTest extends TestCase {

  private static final long EXPIRING_TIME = 1000;
  private static final int VALUE_PREFIX = 12345;
  private static final String KEY_PREFIX = "key prefix:";

  public void testExpiration_expireAfterWrite() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    checkExpiration(cache, loader, ticker, removalListener);
  }

  public void testExpiration_expireAfterAccess() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    checkExpiration(cache, loader, ticker, removalListener);
  }

  private void checkExpiration(LoadingCache<String, Integer> cache, WatchedCreatorLoader loader,
      FakeTicker ticker, CountingRemovalListener<String, Integer> removalListener) {

    for (int i = 0; i < 10; i++) {
      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
    }

    for (int i = 0; i < 10; i++) {
      loader.reset();
      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
      assertFalse("Creator should not have been called @#" + i, loader.wasCalled());
    }

    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);

    assertEquals("Map must be empty by now", 0, cache.size());
    assertEquals("Eviction notifications must be received", 10,
        removalListener.getCount());

    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
    // ensure that no new notifications are sent
    assertEquals("Eviction notifications must be received", 10,
        removalListener.getCount());
  }

  public void testExpiringGet_expireAfterWrite() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    runExpirationTest(cache, loader, ticker, removalListener);
  }

  public void testExpiringGet_expireAfterAccess() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    runExpirationTest(cache, loader, ticker, removalListener);
  }

  private void runExpirationTest(LoadingCache<String, Integer> cache, WatchedCreatorLoader loader,
      FakeTicker ticker, CountingRemovalListener<String, Integer> removalListener) {

    for (int i = 0; i < 10; i++) {
      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
    }

    for (int i = 0; i < 10; i++) {
      loader.reset();
      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
      assertFalse("Loader should NOT have been called @#" + i, loader.wasCalled());
    }

    // wait for entries to expire, but don't call expireEntries
    ticker.advance(EXPIRING_TIME * 10, MILLISECONDS);

    // add a single unexpired entry
    cache.getUnchecked(KEY_PREFIX + 11);

    // collections views shouldn't expose expired entries
    assertEquals(1, Iterators.size(cache.asMap().entrySet().iterator()));
    assertEquals(1, Iterators.size(cache.asMap().keySet().iterator()));
    assertEquals(1, Iterators.size(cache.asMap().values().iterator()));

    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);

    for (int i = 0; i < 11; i++) {
      assertFalse(cache.asMap().containsKey(KEY_PREFIX + i));
    }
    assertEquals(11, removalListener.getCount());

    for (int i = 0; i < 10; i++) {
      assertFalse(cache.asMap().containsKey(KEY_PREFIX + i));
      loader.reset();
      assertEquals(Integer.valueOf(VALUE_PREFIX + i), cache.getUnchecked(KEY_PREFIX + i));
      assertTrue("Creator should have been called @#" + i, loader.wasCalled());
    }

    // expire new values we just created
    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
    assertEquals("Eviction notifications must be received", 21,
        removalListener.getCount());

    CacheTesting.expireEntries((LoadingCache<?, ?>) cache, EXPIRING_TIME, ticker);
    // ensure that no new notifications are sent
    assertEquals("Eviction notifications must be received", 21,
        removalListener.getCount());
  }

  public void testRemovalListener_expireAfterWrite() {
    FakeTicker ticker = new FakeTicker();
    final AtomicInteger evictionCount = new AtomicInteger();
    final AtomicInteger applyCount = new AtomicInteger();
    final AtomicInteger totalSum = new AtomicInteger();

    RemovalListener<Integer, AtomicInteger> removalListener =
        new RemovalListener<Integer, AtomicInteger>() {
          @Override
          public void onRemoval(RemovalNotification<Integer, AtomicInteger> notification) {
            if (notification.wasEvicted()) {
              evictionCount.incrementAndGet();
              totalSum.addAndGet(notification.getValue().get());
            }
          }
        };

    CacheLoader<Integer, AtomicInteger> loader = new CacheLoader() {
      @Override public AtomicInteger load(Integer key) {
        applyCount.incrementAndGet();
        return new AtomicInteger();
      }
    };

    LoadingCache<Integer, AtomicInteger> cache = CacheBuilder.newBuilder()
        .removalListener(removalListener)
        .expireAfterWrite(10, MILLISECONDS)
        .ticker(ticker)
        .build(loader);

    // Increment 100 times
    for (int i = 0; i < 100; ++i) {
      cache.getUnchecked(10).incrementAndGet();
      ticker.advance(1, MILLISECONDS);
    }

    assertEquals(evictionCount.get() + 1, applyCount.get());
    int remaining = cache.getUnchecked(10).get();
    assertEquals(100, totalSum.get() + remaining);
  }

  public void testRemovalScheduler_expireAfterWrite() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    runRemovalScheduler(cache, removalListener, loader, ticker, KEY_PREFIX, EXPIRING_TIME);
  }

  public void testRemovalScheduler_expireAfterAccess() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    runRemovalScheduler(cache, removalListener, loader, ticker, KEY_PREFIX, EXPIRING_TIME);
  }

  public void testRemovalScheduler_expireAfterBoth() {
    FakeTicker ticker = new FakeTicker();
    CountingRemovalListener<String, Integer> removalListener = countingRemovalListener();
    WatchedCreatorLoader loader = new WatchedCreatorLoader();
    LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
        .expireAfterAccess(EXPIRING_TIME, MILLISECONDS)
        .expireAfterWrite(EXPIRING_TIME, MILLISECONDS)
        .removalListener(removalListener)
        .ticker(ticker)
        .build(loader);
    runRemovalScheduler(cache, removalListener, loader, ticker, KEY_PREFIX, EXPIRING_TIME);
  }

  public void testExpirationOrder_access() {
    // test lru within a single segment
    FakeTicker ticker = new FakeTicker();
    IdentityLoader<Integer> loader = identityLoader();
    LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
        .concurrencyLevel(1)
        .expireAfterAccess(11, MILLISECONDS)
        .ticker(ticker)
        .build(loader);
    for (int i = 0; i < 10; i++) {
      cache.getUnchecked(i);
      ticker.advance(1, MILLISECONDS);
    }
    Set<Integer> keySet = cache.asMap().keySet();
    assertThat(keySet).containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

    // 0 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9);

    // reorder
    getAll(cache, asList(0, 1, 2));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(2, MILLISECONDS);
    assertThat(keySet).containsExactly(3, 4, 5, 6, 7, 8, 9, 0, 1, 2);

    // 3 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(4, 5, 6, 7, 8, 9, 0, 1, 2);

    // reorder
    getAll(cache, asList(5, 7, 9));
    CacheTesting.drainRecencyQueues(cache);
    assertThat(keySet).containsExactly(4, 6, 8, 0, 1, 2, 5, 7, 9);

    // 4 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(6, 8, 0, 1, 2, 5, 7, 9);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(6, 8, 0, 1, 2, 5, 7, 9);

    // 6 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(8, 0, 1, 2, 5, 7, 9);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(8, 0, 1, 2, 5, 7, 9);

    // 8 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(0, 1, 2, 5, 7, 9);
  }

  public void testExpirationOrder_write() throws ExecutionException {
    // test lru within a single segment
    FakeTicker ticker = new FakeTicker();
    IdentityLoader<Integer> loader = identityLoader();
    LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
        .concurrencyLevel(1)
        .expireAfterWrite(11, MILLISECONDS)
        .ticker(ticker)
        .build(loader);
    for (int i = 0; i < 10; i++) {
      cache.getUnchecked(i);
      ticker.advance(1, MILLISECONDS);
    }
    Set<Integer> keySet = cache.asMap().keySet();
    assertThat(keySet).containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

    // 0 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9);

    // get doesn't stop 1 from expiring
    getAll(cache, asList(0, 1, 2));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(2, 3, 4, 5, 6, 7, 8, 9, 0);

    // get(K, Callable) doesn't stop 2 from expiring
    cache.get(2, Callables.returning(-2));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(3, 4, 5, 6, 7, 8, 9, 0);

    // asMap.put saves 3
    cache.asMap().put(3, -3);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(4, 5, 6, 7, 8, 9, 0, 3);

    // asMap.replace saves 4
    cache.asMap().replace(4, -4);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(5, 6, 7, 8, 9, 0, 3, 4);

    // 5 expires
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(6, 7, 8, 9, 0, 3, 4);
  }

  public void testExpirationOrder_writeAccess() throws ExecutionException {
    // test lru within a single segment
    FakeTicker ticker = new FakeTicker();
    IdentityLoader<Integer> loader = identityLoader();
    LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder()
        .concurrencyLevel(1)
        .expireAfterWrite(5, MILLISECONDS)
        .expireAfterAccess(3, MILLISECONDS)
        .ticker(ticker)
        .build(loader);
    for (int i = 0; i < 5; i++) {
      cache.getUnchecked(i);
    }
    ticker.advance(1, MILLISECONDS);
    for (int i = 5; i < 10; i++) {
      cache.getUnchecked(i);
    }
    ticker.advance(1, MILLISECONDS);

    Set<Integer> keySet = cache.asMap().keySet();
    assertThat(keySet).containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);

    // get saves 1, 3; 0, 2, 4 expire
    getAll(cache, asList(1, 3));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(5, 6, 7, 8, 9, 1, 3);

    // get saves 6, 8; 5, 7, 9 expire
    getAll(cache, asList(6, 8));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(1, 3, 6, 8);

    // get fails to save 1, put saves 3
    cache.asMap().put(3, -3);
    getAll(cache, asList(1));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(6, 8, 3);

    // get(K, Callable) fails to save 8, replace saves 6
    cache.asMap().replace(6, -6);
    cache.get(8, Callables.returning(-8));
    CacheTesting.drainRecencyQueues(cache);
    ticker.advance(1, MILLISECONDS);
    assertThat(keySet).containsExactly(3, 6);
  }

  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);
  }

  private void runRemovalScheduler(LoadingCache<String, Integer> cache,
      CountingRemovalListener<String, Integer> removalListener,
      WatchedCreatorLoader loader,
      FakeTicker ticker, String keyPrefix, long ttl) {

    int shift1 = 10 + VALUE_PREFIX;
    loader.setValuePrefix(shift1);
    // fill with initial data
    for (int i = 0; i < 10; i++) {
      assertEquals(Integer.valueOf(i + shift1), cache.getUnchecked(keyPrefix + i));
    }
    assertEquals(10, CacheTesting.expirationQueueSize(cache));
    assertEquals(0, removalListener.getCount());

    // wait, so that entries have just 10 ms to live
    ticker.advance(ttl * 2 / 3, MILLISECONDS);

    assertEquals(10, CacheTesting.expirationQueueSize(cache));
    assertEquals(0, removalListener.getCount());

    int shift2 = shift1 + 10;
    loader.setValuePrefix(shift2);
    // fill with new data - has to live for 20 ms more
    for (int i = 0; i < 10; i++) {
      cache.invalidate(keyPrefix + i);
      assertEquals("key: " + keyPrefix + i,
          Integer.valueOf(i + shift2), cache.getUnchecked(keyPrefix + i));
    }
    assertEquals(10, CacheTesting.expirationQueueSize(cache));
    assertEquals(10, removalListener.getCount());  // these are the invalidated ones

    // old timeouts must expire after this wait
    ticker.advance(ttl * 2 / 3, MILLISECONDS);

    assertEquals(10, CacheTesting.expirationQueueSize(cache));
    assertEquals(10, removalListener.getCount());

    // check that new values are still there - they still have 10 ms to live
    for (int i = 0; i < 10; i++) {
      loader.reset();
      assertEquals(Integer.valueOf(i + shift2), cache.getUnchecked(keyPrefix + i));
      assertFalse("Creator should NOT have been called @#" + i, loader.wasCalled());
    }
    assertEquals(10, removalListener.getCount());
  }

  private static void getAll(LoadingCache<Integer, Integer> cache, List keys) {
    for (int i : keys) {
      cache.getUnchecked(i);
    }
  }

  private static class WatchedCreatorLoader extends CacheLoader<String, Integer> {
    boolean wasCalled = false; // must be set in load()
    String keyPrefix = KEY_PREFIX;
    int valuePrefix = VALUE_PREFIX;

    public WatchedCreatorLoader() {
    }

    public void reset() {
      wasCalled = false;
    }

    public boolean wasCalled() {
      return wasCalled;
    }

    public void setKeyPrefix(String keyPrefix) {
      this.keyPrefix = keyPrefix;
    }

    public void setValuePrefix(int valuePrefix) {
      this.valuePrefix = valuePrefix;
    }

    @Override public Integer load(String key) {
      wasCalled = true;
      return valuePrefix + Integer.parseInt(key.substring(keyPrefix.length()));
    }
  }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java CacheExpirationTest.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.