@Test public void asyncExpiry_completed() { AsyncExpiry<Integer, Integer> expiry = makeAsyncExpiry( ONE_MINUTE, 2 * ONE_MINUTE, 3 * ONE_MINUTE); CompletableFuture<Integer> future = CompletableFuture.completedFuture(100); assertThat(expiry.expireAfterCreate(0, future, 1L), is(ONE_MINUTE)); verify(expiry.delegate).expireAfterCreate(0, 100, 1L); assertThat(expiry.expireAfterUpdate(0, future, 1L, 2L), is(2 * ONE_MINUTE)); verify(expiry.delegate).expireAfterUpdate(0, 100, 1L, 2L); assertThat(expiry.expireAfterRead(0, future, 1L, 2L), is(3 * ONE_MINUTE)); verify(expiry.delegate).expireAfterRead(0, 100, 1L, 2L); }
@Override public long expireAfterUpdate(K key, CompletableFuture<V> future, long currentTime, long currentDuration) { if (isReady(future)) { long duration = (currentDuration > MAXIMUM_EXPIRY) ? delegate.expireAfterCreate(key, future.join(), currentTime) : delegate.expireAfterUpdate(key, future.join(), currentTime, currentDuration); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
@Override public long expireAfterCreate(K key, CompletableFuture<V> future, long currentTime) { if (isReady(future)) { long duration = delegate.expireAfterCreate(key, future.join(), currentTime); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
@Override public long expireAfterRead(K key, CompletableFuture<V> future, long currentTime, long currentDuration) { if (isReady(future)) { long duration = delegate.expireAfterRead(key, future.join(), currentTime, currentDuration); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
/** * Returns the expiration time for the entry after being updated. * * @param node the entry in the page replacement policy * @param key the key of the entry that was updated * @param value the value of the entry that was updated * @param expiry the calculator for the expiration time * @param now the current time, in nanoseconds * @return the expiration time */ long expireAfterUpdate(Node<K, V> node, @Nullable K key, @Nullable V value, Expiry<K, V> expiry, long now) { if (expiresVariable() && (key != null) && (value != null)) { long currentDuration = Math.max(1, node.getVariableTime() - now); long duration = expiry.expireAfterUpdate(key, value, now, currentDuration); return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); } return 0L; }
@Override public long expireAfterUpdate(K key, CompletableFuture<V> future, long currentTime, long currentDuration) { if (isReady(future)) { long duration = (currentDuration > MAXIMUM_EXPIRY) ? delegate.expireAfterCreate(key, future.join(), currentTime) : delegate.expireAfterUpdate(key, future.join(), currentTime, currentDuration); return Math.min(duration, MAXIMUM_EXPIRY); } return ASYNC_EXPIRY; }
/** * Returns the expiration time for the entry after being created. * * @param key the key of the entry that was created * @param value the value of the entry that was created * @param expiry the calculator for the expiration time * @param now the current time, in nanoseconds * @return the expiration time */ long expireAfterCreate(@Nullable K key, @Nullable V value, Expiry<K, V> expiry, long now) { if (expiresVariable() && (key != null) && (value != null)) { long duration = expiry.expireAfterCreate(key, value, now); return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); } return 0L; }
/** * Returns the access time for the entry after a read. * * @param node the entry in the page replacement policy * @param key the key of the entry that was read * @param value the value of the entry that was read * @param expiry the calculator for the expiration time * @param now the current time, in nanoseconds * @return the expiration time */ long expireAfterRead(Node<K, V> node, @Nullable K key, @Nullable V value, Expiry<K, V> expiry, long now) { if (expiresVariable() && (key != null) && (value != null)) { long currentDuration = Math.max(1, node.getVariableTime() - now); long duration = expiry.expireAfterRead(key, value, now, currentDuration); return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); } return 0L; }
@Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO, expiryTime = Expire.ONE_MINUTE) public void getExpiresAfter_duration(Cache<Integer, Integer> cache, CacheContext context, VarExpiration<Integer, Integer> expireAfterVar) { assertThat(expireAfterVar.getExpiresAfter(context.absentKey()), is(Optional.empty())); assertThat(expireAfterVar.getExpiresAfter(context.firstKey()), is(Optional.of(Duration.ofMinutes(1L)))); when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) .thenReturn(TimeUnit.HOURS.toNanos(1)); cache.put(context.firstKey(), context.absentValue()); assertThat(expireAfterVar.getExpiresAfter(context.firstKey()), is(Optional.of(Duration.ofHours(1)))); assertThat(expireAfterVar.getExpiresAfter(context.lastKey()), is(Optional.of(Duration.ofMinutes(1)))); }
private static <K, V> AsyncExpiry<K, V> makeAsyncExpiry(long create, long update, long read) { @SuppressWarnings("unchecked") Expiry<K, V> mock = Mockito.mock(Expiry.class); when(mock.expireAfterCreate(any(), any(), anyLong())).thenReturn(create); when(mock.expireAfterUpdate(any(), any(), anyLong(), anyLong())).thenReturn(update); when(mock.expireAfterRead(any(), any(), anyLong(), anyLong())).thenReturn(read); return new AsyncExpiry<>(mock); }
@CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void put_insert_expiryFails(Cache<Integer, Integer> cache, CacheContext context) { try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterCreate(any(), any(), anyLong())) .thenThrow(ExpirationException.class); cache.put(context.absentKey(), context.absentValue()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); } }
@CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void get_expiryFails_read(Cache<Integer, Integer> cache, CacheContext context) { try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterRead(any(), any(), anyLong(), anyLong())) .thenThrow(ExpirationException.class); cache.get(context.firstKey(), identity()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); } }
@Test(dataProvider = "caches") @CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO, expiryTime = Expire.ONE_MINUTE) public void getExpiresAfter(Cache<Integer, Integer> cache, CacheContext context, VarExpiration<Integer, Integer> expireAfterVar) { assertThat(expireAfterVar.getExpiresAfter(context.absentKey(), TimeUnit.MINUTES), is(OptionalLong.empty())); assertThat(expireAfterVar.getExpiresAfter(context.firstKey(), TimeUnit.MINUTES), is(OptionalLong.of(1))); when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) .thenReturn(TimeUnit.HOURS.toNanos(1)); cache.put(context.firstKey(), context.absentValue()); assertThat(expireAfterVar.getExpiresAfter(context.firstKey(), TimeUnit.MINUTES), is(OptionalLong.of(60))); assertThat(expireAfterVar.getExpiresAfter(context.lastKey(), TimeUnit.MINUTES), is(OptionalLong.of(1))); }
@Test public void asyncExpiry_pending() { AsyncExpiry<Integer, Integer> expiry = makeAsyncExpiry(ONE_MINUTE, ONE_MINUTE, ONE_MINUTE); CompletableFuture<Integer> future = new CompletableFuture<Integer>(); assertThat(expiry.expireAfterCreate(0, future, 1L), is(ASYNC_EXPIRY)); verify(expiry.delegate, never()).expireAfterCreate(any(), any(), anyLong()); assertThat(expiry.expireAfterUpdate(0, future, 1L, 2L), is(ASYNC_EXPIRY)); verify(expiry.delegate, never()).expireAfterUpdate(any(), any(), anyLong(), anyLong()); assertThat(expiry.expireAfterRead(0, future, 1L, 2L), is(ASYNC_EXPIRY)); verify(expiry.delegate, never()).expireAfterRead(any(), any(), anyLong(), anyLong()); }
@CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO, writer = Writer.MOCKITO, removalListener = Listener.REJECTING) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void get_expiryFails_create(Cache<Integer, Integer> cache, CacheContext context) { try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterCreate(any(), any(), anyLong())) .thenThrow(ExpirationException.class); cache.get(context.absentKey(), identity()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); verify(context.cacheWriter(), never()).write(anyInt(), anyInt()); } }
@CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void getIfPresent_expiryFails(Cache<Integer, Integer> cache, CacheContext context) { try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterRead(any(), any(), anyLong(), anyLong())) .thenThrow(ExpirationException.class); cache.getIfPresent(context.firstKey()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); } }
@CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void put_update_expiryFails(Cache<Integer, Integer> cache, CacheContext context, VarExpiration<Integer, Integer> expireVariably) { OptionalLong duration = expireVariably.getExpiresAfter(context.firstKey(), NANOSECONDS); try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) .thenThrow(ExpirationException.class); cache.put(context.firstKey(), context.absentValue()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); assertThat(expireVariably.getExpiresAfter(context.firstKey(), NANOSECONDS), is(duration)); } }
@Override public <K, V> Expiry<K, V> createExpiry(Expire expiryTime) { @SuppressWarnings("unchecked") Expiry<K, V> mock = Mockito.mock(Expiry.class); when(mock.expireAfterCreate(any(), any(), anyLong())) .thenReturn(expiryTime.timeNanos()); when(mock.expireAfterUpdate(any(), any(), anyLong(), anyLong())) .thenReturn(expiryTime.timeNanos()); when(mock.expireAfterRead(any(), any(), anyLong(), anyLong())) .thenReturn(expiryTime.timeNanos()); return mock; } },
@CacheSpec(implementation = Implementation.Caffeine, expiryTime = Expire.ONE_MINUTE, population = Population.FULL, expiry = CacheExpiry.MOCKITO) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void put_insert_replaceExpired_expiryFails(Cache<Integer, Integer> cache, CacheContext context, VarExpiration<Integer, Integer> expireVariably) { OptionalLong duration = expireVariably.getExpiresAfter(context.firstKey(), NANOSECONDS); try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterCreate(any(), any(), anyLong())) .thenThrow(ExpirationException.class); cache.put(context.firstKey(), context.absentValue()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); assertThat(expireVariably.getExpiresAfter(context.firstKey(), NANOSECONDS), is(duration)); } }
@CacheSpec(implementation = Implementation.Caffeine, population = Population.FULL, expiry = CacheExpiry.MOCKITO) @Test(dataProvider = "caches", expectedExceptions = ExpirationException.class) public void getAllPresent_expiryFails(Cache<Integer, Integer> cache, CacheContext context) { try { context.ticker().advance(1, TimeUnit.HOURS); when(context.expiry().expireAfterRead(any(), any(), anyLong(), anyLong())) .thenThrow(ExpirationException.class); cache.getAllPresent(context.firstMiddleLastKeys()); } finally { context.ticker().advance(-1, TimeUnit.HOURS); assertThat(cache.asMap(), equalTo(context.original())); } }