番石榴缓存-刷新时发生InvalidCacheLoadException

时间:2018-06-29 06:47:15

标签: java concurrency guava google-guava-cache

我已经使用CacheBuilder创建了一个缓存。 我用过ExpireAfterWrite和RefreshAfterWrite。我已覆盖cacheloader的加载和重新加载功能。实际上,在重载中,我通过创建ListenableFutureTask并将其提交给ExecutorService来调用重载。 下面是我得到的堆栈跟踪-

  

警告:刷新期间引发异常       [junit] com.google.common.cache.CacheLoader $ InvalidCacheLoadException:   CacheLoader为键abc返回null。       [junit] com.google.common.cache.LocalCache $ Segment.getAndRecordStats(未知   资源)       [junit] com.google.common.cache.LocalCache $ Segment $ 1.run(未知来源)       [junit] com.google.common.util.concurrent.MoreExecutors $ DirectExecutor.execute(未知   资源)       [junit]位于com.google.common.util.concurrent.ImmediateFuture.addListener(未知   资源)       [junit] com.google.common.cache.LocalCache $ Segment.loadAsync(未知源)       [junit]位于com.google.common.cache.LocalCache $ Segment.refresh(未知来源)       [junit] com.google.common.cache.LocalCache $ Segment.scheduleRefresh(未知   资源)       [junit]位于com.google.common.cache.LocalCache $ Segment.get(未知来源)       com.google.common.cache.LocalCache.get上的[junit](未知来源)       [junit] com.google.common.cache.LocalCache.getOrLoad(未知源)

我不知道为什么它不能刷新,也不能到达cacheloader.load。它之前返回null。我很确定我不会在加载函数中返回null。

示例代码-

public class SampleCacheLoader extends CacheLoader<String, Customer> {

    private final DatabaseClient databaseClient;

    private static final ExecutorService ex  = Executors.newSingleThreadScheduledExecutor();

    @Inject
    public SampleCacheLoader(final DatabaseClient databaseClient) {
        this.databaseClient = databaseClient;
    }

    @Override
    public Customer load(final String customerId) throws Exception {
        Customer customer = databaseClient.getCustomer(customerId);
        if (customer == null) {
            throw new Exception("Customer is null");
        }
        return customer;
    }

@Override
public ListenableFuture<Customer> reload(final String customerId,
                                                     final Customer prevCustomer)
        throws Exception {
    ListenableFutureTask<Customer> task = ListenableFutureTask
            .create(new Callable<Customer>() {

        @Override
        public Customer call() {
            try {
                // try to get a new value
                load(customerId);

            } catch (Throwable e) {
                // or return the old one in the event of failure
                return prevCustomer;
            }
        }
    });

    // run in the background so that concurrent get() requests still return values.
    ex.execute(task);
    return task;
}
}

public class SampleCache {

    public  LoadingCache<String, Customer> sampleCache;

    @Inject
    public SampleCache(final SampleCacheLoader sampleCacheLoader,
                                         final int cacheMaxSize,
                                         final int cacheExpireTime,
                                         final int cacheRefreshTime,
                                         final int concurrencryLevel,
                                         final Ticker ticker) {
        this.cache = CacheBuilder.newBuilder()
                .maximumSize(cacheMaxSize)
                .expireAfterWrite(cacheExpireTime, TimeUnit.MINUTES)
                .refreshAfterWrite(cacheRefreshTime, TimeUnit.MINUTES)
                .concurrencyLevel(concurrencryLevel)
                .ticker(ticker)
                .build(sampleCacheLoader);
    }

    public Optional<Customer> get(final String customerId) {
        try {
            Customer customer = cache.get(customerId);
            return Optional.of(customer);
        } catch (ExecutionException e) {
            log.warn(String.format("failed to get customer from cache (customerId=%s)", customerId));
            log.warn(e.getMessage());
        }

        return Optional.empty();
    }

    /**
     * Size of customer cache.
     * @return size of customer cache.
     */
    public long size() {
        return  cache.size();
    }

}

public class Customer {

   private String name;
}

public class SampleCacheTest extends TestCase {

    SampleCache SampleCache;

    SampleCacheLoader SampleCacheLoader;

    // FakeTicker to test cache expiry.
    FakeTicker ft;

    // Max size of cache
    final  int CACHE_MAX_SIZE = 1000;

    // CACHE_EXPIRE_TIME is in minutes.
    final int CACHE_EXPIRE_TIME = 1000;

    // CACHE_REFRESH_TIME is in minutes.
    final int CACHE_REFRESH_TIME = 3;

    // CACHE_CONCURRENCY_LEVEL.
    final int CACHE_CONCURRENCY_LEVEL = 10;

    // Resource arn
    final Static String CUSTOMER_ID =
            "abc";

    @Before
    public void setUp() throws Exception {
        // FaceTicker provided by google source code.
        ft = new FakeTicker();
        SampleCacheLoader sampleCacheLoader = new sampleCacheLoader(new DatabaseClient());
        SampleCache = new SampleCache(sampleCacheLoader,
                CACHE_MAX_SIZE,
                CACHE_EXPIRE_TIME,
                CACHE_REFRESH_TIME,
                CACHE_CONCURRENCY_LEVEL,
                ft);
    }

    @Test
    public void testCacheRefreshTime() throws Exception {

        Optional<Customer> customer1 = SampleCache.get(CUSTOMER_ID);
        assertTrue(customer1.isPresent());
        assertNotNull(customer1.get());

        // Advancing time by 1 minutes and retrieve it from cache
        // So that it won't expire. Gets the old entry.
        advanceTimeForCache(1);

        Optional<Customer> customer2 = SampleCache.get(CUSTOMER_ID);
        assertTrue(customer2.isPresent());
        assertNotNull(customer2.get());

        // After this any get call for CUSTOMER_ID
        // should initiate the refresh and Load.
        // new value from db.
        advanceTimeForCache(4);

        // This is the place where I get CacheInvalidateStateException
        Optional<Customer> customer3 = SampleCache.get(CUSTOMER_ID);


    }
}

public class SampleCacheLoader extends CacheLoader<String, Customer> { private final DatabaseClient databaseClient; private static final ExecutorService ex = Executors.newSingleThreadScheduledExecutor(); @Inject public SampleCacheLoader(final DatabaseClient databaseClient) { this.databaseClient = databaseClient; } @Override public Customer load(final String customerId) throws Exception { Customer customer = databaseClient.getCustomer(customerId); if (customer == null) { throw new Exception("Customer is null"); } return customer; } @Override public ListenableFuture<Customer> reload(final String customerId, final Customer prevCustomer) throws Exception { ListenableFutureTask<Customer> task = ListenableFutureTask .create(new Callable<Customer>() { @Override public Customer call() { try { // try to get a new value load(customerId); } catch (Throwable e) { // or return the old one in the event of failure return prevCustomer; } } }); // run in the background so that concurrent get() requests still return values. ex.execute(task); return task; } } public class SampleCache { public LoadingCache<String, Customer> sampleCache; @Inject public SampleCache(final SampleCacheLoader sampleCacheLoader, final int cacheMaxSize, final int cacheExpireTime, final int cacheRefreshTime, final int concurrencryLevel, final Ticker ticker) { this.cache = CacheBuilder.newBuilder() .maximumSize(cacheMaxSize) .expireAfterWrite(cacheExpireTime, TimeUnit.MINUTES) .refreshAfterWrite(cacheRefreshTime, TimeUnit.MINUTES) .concurrencyLevel(concurrencryLevel) .ticker(ticker) .build(sampleCacheLoader); } public Optional<Customer> get(final String customerId) { try { Customer customer = cache.get(customerId); return Optional.of(customer); } catch (ExecutionException e) { log.warn(String.format("failed to get customer from cache (customerId=%s)", customerId)); log.warn(e.getMessage()); } return Optional.empty(); } /** * Size of customer cache. * @return size of customer cache. */ public long size() { return cache.size(); } } public class Customer { private String name; } public class SampleCacheTest extends TestCase { SampleCache SampleCache; SampleCacheLoader SampleCacheLoader; // FakeTicker to test cache expiry. FakeTicker ft; // Max size of cache final int CACHE_MAX_SIZE = 1000; // CACHE_EXPIRE_TIME is in minutes. final int CACHE_EXPIRE_TIME = 1000; // CACHE_REFRESH_TIME is in minutes. final int CACHE_REFRESH_TIME = 3; // CACHE_CONCURRENCY_LEVEL. final int CACHE_CONCURRENCY_LEVEL = 10; // Resource arn final Static String CUSTOMER_ID = "abc"; @Before public void setUp() throws Exception { // FaceTicker provided by google source code. ft = new FakeTicker(); SampleCacheLoader sampleCacheLoader = new sampleCacheLoader(new DatabaseClient()); SampleCache = new SampleCache(sampleCacheLoader, CACHE_MAX_SIZE, CACHE_EXPIRE_TIME, CACHE_REFRESH_TIME, CACHE_CONCURRENCY_LEVEL, ft); } @Test public void testCacheRefreshTime() throws Exception { Optional<Customer> customer1 = SampleCache.get(CUSTOMER_ID); assertTrue(customer1.isPresent()); assertNotNull(customer1.get()); // Advancing time by 1 minutes and retrieve it from cache // So that it won't expire. Gets the old entry. advanceTimeForCache(1); Optional<Customer> customer2 = SampleCache.get(CUSTOMER_ID); assertTrue(customer2.isPresent()); assertNotNull(customer2.get()); // After this any get call for CUSTOMER_ID // should initiate the refresh and Load. // new value from db. advanceTimeForCache(4); // This is the place where I get CacheInvalidateStateException Optional<Customer> customer3 = SampleCache.get(CUSTOMER_ID); } }

1 个答案:

答案 0 :(得分:0)

所以这是在测试文件中的SimpleCacheLoader上进行的@嘲笑,由于我没有对方法进行存根,因此刷新时未执行任何操作。在这种情况下,我应该使用@spy。我把它修好了。谢谢大家的帮助。