@Test public void testExpiryUpdateException() throws Exception { final TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, new Expiry<String, String>() { @Override public Duration getExpiryForCreation(String key, String value) { return Duration.FOREVER; } @Override public Duration getExpiryForAccess(String key, String value) { return Duration.FOREVER; } @Override public Duration getExpiryForUpdate(String key, String oldValue, String newValue) { if (timeSource.getTimeMillis() > 0) { throw new RuntimeException(); } return Duration.FOREVER; } }); offHeapStore.put("key", "value"); assertThat(offHeapStore.get("key").value(), is("value")); timeSource.advanceTime(1000); offHeapStore.put("key", "newValue"); assertNull(offHeapStore.get("key")); }
@Test public void testFlushUpdatesAccessStats() throws CacheAccessException { final TestTimeSource timeSource = new TestTimeSource(); final Expiry<Object, Object> expiry = Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS)); final AbstractOffHeapStore<String, String> store = createAndInitStore(timeSource, expiry); try { final String key = "foo"; final String value = "bar"; store.put(key, value); final Store.ValueHolder<String> firstValueHolder = store.getAndFault(key); store.put(key, value); final Store.ValueHolder<String> secondValueHolder = store.getAndFault(key); timeSource.advanceTime(10); ((AbstractValueHolder) firstValueHolder) .accessed(timeSource.getTimeMillis(), expiry.getExpiryForAccess(key, value)); timeSource.advanceTime(10); ((AbstractValueHolder) secondValueHolder) .accessed(timeSource.getTimeMillis(), expiry.getExpiryForAccess(key, value)); assertThat(store.flush(key, new DelegatingValueHolder<String>(firstValueHolder)), is(false)); assertThat(store.flush(key, new DelegatingValueHolder<String>(secondValueHolder)), is(true)); timeSource.advanceTime(10); // this should NOT affect assertThat( store.getAndFault(key).lastAccessTime(TimeUnit.MILLISECONDS), is(secondValueHolder.creationTime(TimeUnit.MILLISECONDS) + 20)); } finally { destroyStore(store); } }
@Test public void testBrokenEvictionVeto() throws CacheAccessException { TestTimeSource timeSource = new TestTimeSource(); Expiry<Object, Object> expiry = Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS)); EvictionVeto<String, byte[]> evictionVeto = new EvictionVeto<String, byte[]>() { @Override public boolean test(Cache.Entry<String, byte[]> entry) { throw new UnsupportedOperationException("Broken veto!"); } }; AbstractOffHeapStore<String, byte[]> offHeapStore = createAndInitStore(timeSource, expiry, evictionVeto); try { StoreEventListener<String, byte[]> mock = mock(StoreEventListener.class); offHeapStore.enableStoreEventNotifications(mock); byte[] value = getBytes(MemoryUnit.KB.toBytes(200)); offHeapStore.put("key1", value); offHeapStore.put("key2", value); offHeapStore.put("key3", value); offHeapStore.put("key4", value); offHeapStore.put("key5", value); offHeapStore.put("key6", value); verify(mock, atLeast(1)).onEviction(anyString(), any(Store.ValueHolder.class)); } finally { destroyStore(offHeapStore); } }
@Test public void testComputeIfAbsentOnExpiredEntry() throws CacheAccessException { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(10L, TimeUnit.MILLISECONDS))); try { offHeapStore.put("key", "value"); timeSource.advanceTime(20L); offHeapStore.computeIfAbsent( "key", new Function<String, String>() { @Override public String apply(String mappedKey) { assertThat(mappedKey, is("key")); return "value2"; } }); assertThat( getExpirationStatistic(offHeapStore) .count(StoreOperationOutcomes.ExpirationOutcome.SUCCESS), is(1L)); } finally { destroyStore(offHeapStore); } }
@Test public void testExpiryAccessException() throws Exception { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, new Expiry<String, String>() { @Override public Duration getExpiryForCreation(String key, String value) { return Duration.FOREVER; } @Override public Duration getExpiryForAccess(String key, String value) { throw new RuntimeException(); } @Override public Duration getExpiryForUpdate(String key, String oldValue, String newValue) { return null; } }); offHeapStore.put("key", "value"); assertNull(offHeapStore.get("key")); }
@Test public void testGetAndRemoveExpiredElementReturnsNull() throws Exception { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS))); try { assertThat(offHeapStore.getAndRemove("1"), is(nullValue())); offHeapStore.put("1", "one"); final AtomicReference<Store.ValueHolder<String>> invalidated = new AtomicReference<Store.ValueHolder<String>>(); offHeapStore.setInvalidationListener( new CachingTier.InvalidationListener<String, String>() { @Override public void onInvalidation(String key, Store.ValueHolder<String> valueHolder) { invalidated.set(valueHolder); } }); timeSource.advanceTime(20); assertThat(offHeapStore.getAndRemove("1"), is(nullValue())); assertThat(invalidated.get().value(), equalTo("one")); assertThat( invalidated.get().isExpired(timeSource.getTimeMillis(), TimeUnit.MILLISECONDS), is(true)); assertThat( getExpirationStatistic(offHeapStore) .count(StoreOperationOutcomes.ExpirationOutcome.SUCCESS), is(1L)); } finally { destroyStore(offHeapStore); } }
@Test public void testIteratorSkipsExpiredEntries() throws Exception { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToLiveExpiration(new Duration(10L, TimeUnit.MILLISECONDS))); try { offHeapStore.put("key1", "value1"); offHeapStore.put("key2", "value2"); timeSource.advanceTime(11L); offHeapStore.put("key3", "value3"); offHeapStore.put("key4", "value4"); final List<String> expiredKeys = new ArrayList<String>(); offHeapStore.enableStoreEventNotifications( new StoreEventListener<String, String>() { @Override public void onEviction(final String key, final Store.ValueHolder<String> valueHolder) { throw new AssertionError("This should not have happened."); } @Override public void onExpiration( final String key, final Store.ValueHolder<String> valueHolder) { expiredKeys.add(key); } }); List<String> iteratedKeys = new ArrayList<String>(); Store.Iterator<Cache.Entry<String, Store.ValueHolder<String>>> iterator = offHeapStore.iterator(); while (iterator.hasNext()) { iteratedKeys.add(iterator.next().getKey()); } assertThat(iteratedKeys, containsInAnyOrder("key3", "key4")); assertThat(expiredKeys, containsInAnyOrder("key1", "key2")); } finally { destroyStore(offHeapStore); } }
@Test public void testClear() throws Exception { final TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS))); try { offHeapStore.put("1", "one"); offHeapStore.put("2", "two"); offHeapStore.put("3", "three"); offHeapStore.clear(); assertThat(offHeapStore.get("1"), is(nullValue())); assertThat(offHeapStore.get("2"), is(nullValue())); assertThat(offHeapStore.get("3"), is(nullValue())); } finally { destroyStore(offHeapStore); } }
@Test public void testInvalidateKeyWithFunction() throws Exception { final TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS))); try { final AtomicReference<Store.ValueHolder<String>> invalidated = new AtomicReference<Store.ValueHolder<String>>(); offHeapStore.setInvalidationListener( new CachingTier.InvalidationListener<String, String>() { @Override public void onInvalidation(String key, Store.ValueHolder<String> valueHolder) { invalidated.set(valueHolder); } }); final AtomicBoolean functionInvoked = new AtomicBoolean(false); NullaryFunction<String> nullaryFunction = new NullaryFunction<String>() { @Override public String apply() { functionInvoked.set(true); return ""; } }; offHeapStore.invalidate("1", nullaryFunction); assertThat(invalidated.get(), is(nullValue())); assertThat(functionInvoked.get(), is(true)); validateStats( offHeapStore, EnumSet.of(LowerCachingTierOperationsOutcome.InvalidateOutcome.MISS)); functionInvoked.set(false); offHeapStore.put("1", "one"); offHeapStore.invalidate("1", nullaryFunction); assertThat(invalidated.get().value(), equalTo("one")); assertThat(functionInvoked.get(), is(true)); validateStats( offHeapStore, EnumSet.of( LowerCachingTierOperationsOutcome.InvalidateOutcome.MISS, LowerCachingTierOperationsOutcome.InvalidateOutcome.REMOVED)); assertThat(offHeapStore.get("1"), is(nullValue())); } finally { destroyStore(offHeapStore); } }
@Test public void testGetAndRemoveValue() throws Exception { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore(timeSource, Expirations.noExpiration()); try { offHeapStore.put("1", "one"); assertThat(offHeapStore.getAndRemove("1").value(), equalTo("one")); validateStats( offHeapStore, EnumSet.of(LowerCachingTierOperationsOutcome.GetAndRemoveOutcome.HIT_REMOVED)); assertThat(offHeapStore.get("1"), is(nullValue())); } finally { destroyStore(offHeapStore); } }
@Test public void testFlushUpdatesHits() throws CacheAccessException { final TestTimeSource timeSource = new TestTimeSource(); final AbstractOffHeapStore<String, String> store = createAndInitStore(timeSource, Expirations.noExpiration()); final String key = "foo1"; final String value = "bar1"; store.put(key, value); for (int i = 0; i < 5; i++) { final Store.ValueHolder<String> valueHolder = store.getAndFault(key); timeSource.advanceTime(1); ((AbstractValueHolder) valueHolder) .accessed(timeSource.getTimeMillis(), new Duration(1L, TimeUnit.MILLISECONDS)); assertThat(store.flush(key, new DelegatingValueHolder<String>(valueHolder)), is(true)); } assertThat(store.getAndFault(key).hits(), is(5l)); }
@Test public void testIteratorWithSingleExpiredEntry() throws Exception { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToLiveExpiration(new Duration(10L, TimeUnit.MILLISECONDS))); try { offHeapStore.put("key1", "value1"); timeSource.advanceTime(11L); Store.Iterator<Cache.Entry<String, Store.ValueHolder<String>>> iterator = offHeapStore.iterator(); assertFalse(iterator.hasNext()); } finally { destroyStore(offHeapStore); } }
@Test public void testGetAndFaultOnExpiredEntry() throws CacheAccessException { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(10L, TimeUnit.MILLISECONDS))); try { offHeapStore.put("key", "value"); timeSource.advanceTime(20L); Store.ValueHolder<String> valueHolder = offHeapStore.getAndFault("key"); assertThat(valueHolder, nullValue()); assertThat( getExpirationStatistic(offHeapStore) .count(StoreOperationOutcomes.ExpirationOutcome.SUCCESS), is(1L)); } finally { destroyStore(offHeapStore); } }
@Test public void testWriteBackOfValueHolder() throws CacheAccessException { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(15L, TimeUnit.MILLISECONDS))); try { offHeapStore.put("key1", "value1"); timeSource.advanceTime(10); OffHeapValueHolder<String> valueHolder = (OffHeapValueHolder<String>) offHeapStore.get("key1"); assertThat(valueHolder.lastAccessTime(TimeUnit.MILLISECONDS), is(10L)); timeSource.advanceTime(10); assertThat(offHeapStore.get("key1"), notNullValue()); timeSource.advanceTime(16); assertThat(offHeapStore.get("key1"), nullValue()); } finally { destroyStore(offHeapStore); } }
@Test public void testExpiryEventFiredOnExpiredCachedEntry() throws CacheAccessException { TestTimeSource timeSource = new TestTimeSource(); AbstractOffHeapStore<String, String> offHeapStore = createAndInitStore( timeSource, Expirations.timeToIdleExpiration(new Duration(10L, TimeUnit.MILLISECONDS))); try { final List<String> expiredKeys = new ArrayList<String>(); offHeapStore.enableStoreEventNotifications( new StoreEventListener<String, String>() { @Override public void onEviction(final String key, final Store.ValueHolder<String> valueHolder) { throw new AssertionError("This should not have happened."); } @Override public void onExpiration( final String key, final Store.ValueHolder<String> valueHolder) { expiredKeys.add(key); } }); offHeapStore.put("key1", "value1"); offHeapStore.put("key2", "value2"); offHeapStore.get("key1"); // Bring the entry to the caching tier timeSource.advanceTime(11); // Expire the elements offHeapStore.get("key1"); offHeapStore.get("key2"); assertThat(expiredKeys, containsInAnyOrder("key1", "key2")); assertThat( getExpirationStatistic(offHeapStore) .count(StoreOperationOutcomes.ExpirationOutcome.SUCCESS), is(2L)); } finally { destroyStore(offHeapStore); } }