Я создал кеш с помощью 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 $ Сегмент $ 1.run (неизвестный источник)
[junit] на com.google.common.util.concurrent.MoreExecutors $ DirectExecutor.execute (Неизвестно
Источник)
[junit] на com.google.common.util.concurrent.ImmediateFuture.addListener (Неизвестно
Источник)
[junit] at com.google.common.cache.LocalCache $ Segment.loadAsync (неизвестный источник)
[junit] at 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] at com.google.common.cache.LocalCache.getOrLoad (неизвестный источник)
Я не знаю, почему он не может обновиться, он также не попадает в cacheloader.load. Он возвращает ноль раньше. Я почти уверен, что не возвращаю ноль в функции загрузки.
Пример кода -
</p>
<pre><code>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);
}
}