/* * Copyright Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ehcache.impl.internal.store.heap; import org.ehcache.config.EvictionAdvisor; import org.ehcache.config.ResourcePools; import org.ehcache.config.units.EntryUnit; import org.ehcache.core.internal.store.StoreConfigurationImpl; import org.ehcache.core.spi.store.StoreAccessException; import org.ehcache.core.spi.store.events.StoreEvent; import org.ehcache.core.spi.store.events.StoreEventListener; import org.ehcache.event.EventType; import org.ehcache.expiry.Expirations; import org.ehcache.expiry.Expiry; import org.ehcache.core.spi.function.BiFunction; import org.ehcache.core.spi.function.Function; import org.ehcache.impl.copy.IdentityCopier; import org.ehcache.core.events.NullStoreEventDispatcher; import org.ehcache.impl.internal.events.TestStoreEventDispatcher; import org.ehcache.impl.internal.sizeof.NoopSizeOfEngine; import org.ehcache.impl.internal.store.heap.holders.OnHeapValueHolder; import org.ehcache.core.spi.time.SystemTimeSource; import org.ehcache.core.spi.time.TimeSource; import org.ehcache.core.spi.store.Store; import org.ehcache.core.spi.store.Store.ValueHolder; import org.ehcache.internal.TestTimeSource; import org.ehcache.spi.copy.Copier; import org.ehcache.spi.serialization.Serializer; import org.ehcache.core.spi.store.heap.SizeOfEngine; import org.junit.Test; import java.io.Serializable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import static org.ehcache.config.Eviction.noAdvice; import static org.ehcache.config.builders.ResourcePoolsBuilder.heap; import static org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; public class OnHeapStoreEvictionTest { protected <K, V> OnHeapStoreForTests<K, V> newStore() { return newStore(SystemTimeSource.INSTANCE, null); } /** eviction tests : asserting the evict method is called **/ @Test public void testComputeCalledEnforceCapacity() throws Exception { OnHeapStoreForTests<String, String> store = newStore(); store.put("key", "value"); store.compute("key", new BiFunction<String, String, String>() { @Override public String apply(String mappedKey, String mappedValue) { return "value2"; } }); assertThat(store.enforceCapacityWasCalled(), is(true)); } @Test public void testComputeIfAbsentCalledEnforceCapacity() throws Exception { OnHeapStoreForTests<String, String> store = newStore(); store.computeIfAbsent("key", new Function<String, String>() { @Override public String apply(String mappedKey) { return "value2"; } }); assertThat(store.enforceCapacityWasCalled(), is(true)); } @Test public void testFaultsDoNotGetToEvictionAdvisor() throws StoreAccessException { final Semaphore semaphore = new Semaphore(0); final OnHeapStoreForTests<String, String> store = newStore(SystemTimeSource.INSTANCE, noAdvice()); ExecutorService executor = Executors.newCachedThreadPool(); try { executor.submit(new Callable<Store.ValueHolder<String>>() { @Override public Store.ValueHolder<String> call() throws Exception { return store.getOrComputeIfAbsent("prime", new Function<String, ValueHolder<String>>() { @Override public ValueHolder<String> apply(final String key) { semaphore.acquireUninterruptibly(); return new OnHeapValueHolder<String>(0, 0, false) { @Override public String value() { return key; } }; } }); } }); while (!semaphore.hasQueuedThreads()); store.put("boom", "boom"); } finally { semaphore.release(1); executor.shutdown(); } } @Test public void testEvictionCandidateLimits() throws Exception { TestTimeSource timeSource = new TestTimeSource(); StoreConfigurationImpl<String, String> configuration = new StoreConfigurationImpl<String, String>( String.class, String.class, noAdvice(), getClass().getClassLoader(), Expirations.noExpiration(), heap(1).build(), 1, null, null); TestStoreEventDispatcher<String, String> eventDispatcher = new TestStoreEventDispatcher<String, String>(); final String firstKey = "daFirst"; eventDispatcher.addEventListener(new StoreEventListener<String, String>() { @Override public void onEvent(StoreEvent<String, String> event) { if (event.getType().equals(EventType.EVICTED)) { assertThat(event.getKey(), is(firstKey)); } } }); OnHeapStore<String, String> store = new OnHeapStore<String, String>(configuration, timeSource, new IdentityCopier<String>(), new IdentityCopier<String>(), new NoopSizeOfEngine(), eventDispatcher); timeSource.advanceTime(10000L); store.put(firstKey, "daValue"); timeSource.advanceTime(10000L); store.put("other", "otherValue"); } protected <K, V> OnHeapStoreForTests<K, V> newStore(final TimeSource timeSource, final EvictionAdvisor<? super K, ? super V> evictionAdvisor) { return new OnHeapStoreForTests<K, V>(new Store.Configuration<K, V>() { @SuppressWarnings("unchecked") @Override public Class<K> getKeyType() { return (Class<K>) String.class; } @SuppressWarnings("unchecked") @Override public Class<V> getValueType() { return (Class<V>) Serializable.class; } @Override public EvictionAdvisor<? super K, ? super V> getEvictionAdvisor() { return evictionAdvisor; } @Override public ClassLoader getClassLoader() { return getClass().getClassLoader(); } @Override public Expiry<? super K, ? super V> getExpiry() { return Expirations.noExpiration(); } @Override public ResourcePools getResourcePools() { return newResourcePoolsBuilder().heap(1, EntryUnit.ENTRIES).build(); } @Override public Serializer<K> getKeySerializer() { throw new AssertionError(); } @Override public Serializer<V> getValueSerializer() { throw new AssertionError(); } @Override public int getDispatcherConcurrency() { return 1; } }, timeSource); } public static class OnHeapStoreForTests<K, V> extends OnHeapStore<K, V> { private static final Copier DEFAULT_COPIER = new IdentityCopier(); @SuppressWarnings("unchecked") public OnHeapStoreForTests(final Configuration<K, V> config, final TimeSource timeSource) { super(config, timeSource, DEFAULT_COPIER, DEFAULT_COPIER, new NoopSizeOfEngine(), NullStoreEventDispatcher.<K, V>nullStoreEventDispatcher()); } @SuppressWarnings("unchecked") public OnHeapStoreForTests(final Configuration<K, V> config, final TimeSource timeSource, final SizeOfEngine engine) { super(config, timeSource, DEFAULT_COPIER, DEFAULT_COPIER, engine, NullStoreEventDispatcher.<K, V>nullStoreEventDispatcher()); } private boolean enforceCapacityWasCalled = false; @Override protected void enforceCapacity() { enforceCapacityWasCalled = true; super.enforceCapacity(); } boolean enforceCapacityWasCalled() { return enforceCapacityWasCalled; } } }