Guava Cache - InvalidCacheLoadException при обновлении
Я создал кеш с помощью CacheBuilder. Я использовал ExpireAfterWrite и RefreshAfterWrite. Я переопределил функцию загрузки и перезагрузки кеш-загрузчика. Практически при перезагрузке я вызываю load, создавая ListenableFutureTask и отправляя его в ExecutorService. Ниже приведена трассировка стека, которую я получаю -
ВНИМАНИЕ: Исключение выдается во время обновления [junit] com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader вернул ноль для ключа abc. [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(неизвестный источник) [junit] на com.google.common.cache.LocalCache.get(неизвестный источник) [junit] на com.google.common.cache.LocalCache.getOrLoad(Неизвестный источник)
Я не знаю, почему он не может обновиться, он также не попадает в cacheloader.load. Он возвращает ноль раньше. Я уверен, что я не возвращаю ноль в функции загрузки.
Образец кода -
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 ответ
Таким образом, это было @ mock в SimpleCacheLoader в тестовом файле, который ничего не делал при обновлении, так как я не заглушал метод. Я должен был использовать @ шпион в этом случае. Я исправил это. Спасибо за помощь всем.