-1

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

警告:刷新期间引发异常 [junit] com.google.common.cache.CacheLoader$InvalidCacheLoadException:CacheLoader 为键 abc 返回 null。[junit] at com.google.common.cache.LocalCache$Segment.getAndRecordStats(Unknown Source) [junit] at com.google.common.cache.LocalCache$Segment$1.run(Unknown Source) [junit] at com.google .common.util.concurrent.MoreExecutors$DirectExecutor.execute(Unknown Source) [junit] at com.google.common.util.concurrent.ImmediateFuture.addListener(Unknown Source) [junit] at com.google.common.cache.LocalCache $Segment.loadAsync(Unknown Source) [junit] at com.google.common.cache.LocalCache$Segment.refresh(Unknown Source) [junit] at com.google.common.cache.LocalCache$Segment.scheduleRefresh(Unknown Source) [junit] 在 com.google.common。

我不知道为什么它不能刷新,它也不能到达 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);


    }
}

4

1 回答 1

0

所以它是测试文件中 SimpleCacheLoader 的@mock,它在刷新时没有做任何事情,因为我没有存根该方法。在这种情况下,我应该使用@spy。我把它修好了。谢谢大家的帮助。

于 2018-07-01T23:01:47.543 回答