/* * 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.disk.factories; import org.ehcache.config.EvictionAdvisor; import org.ehcache.impl.internal.store.disk.factories.EhcachePersistentSegmentFactory.EhcachePersistentSegment; import org.ehcache.impl.internal.store.offheap.SwitchableEvictionAdvisor; import org.ehcache.impl.internal.store.offheap.HeuristicConfiguration; import org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory; import org.ehcache.impl.internal.store.offheap.factories.EhcacheSegmentFactory.EhcacheSegment.EvictionListener; import org.ehcache.impl.internal.store.offheap.portability.SerializerPortability; import org.ehcache.impl.internal.spi.serialization.DefaultSerializationProvider; import org.ehcache.spi.serialization.SerializationProvider; import org.ehcache.spi.serialization.Serializer; import org.ehcache.spi.serialization.UnsupportedTypeException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.terracotta.offheapstore.disk.paging.MappedPageSource; import org.terracotta.offheapstore.disk.persistent.PersistentPortability; import org.terracotta.offheapstore.disk.storage.FileBackedStorageEngine; import org.terracotta.offheapstore.util.Factory; import java.io.IOException; import static org.ehcache.config.Eviction.noAdvice; import static org.ehcache.impl.internal.store.disk.OffHeapDiskStore.persistent; import static org.ehcache.impl.internal.spi.TestServiceProvider.providerContaining; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.terracotta.offheapstore.util.MemoryUnit.BYTES; public class EhcachePersistentSegmentTest { @Rule public final TemporaryFolder folder = new TemporaryFolder(); @SuppressWarnings("unchecked") private EhcachePersistentSegmentFactory.EhcachePersistentSegment<String, String> createTestSegment() throws IOException { return createTestSegment(noAdvice(), mock(EvictionListener.class)); } @SuppressWarnings("unchecked") private EhcachePersistentSegmentFactory.EhcachePersistentSegment<String, String> createTestSegment(EvictionAdvisor<String, String> evictionPredicate) throws IOException { return createTestSegment(evictionPredicate, mock(EvictionListener.class)); } private EhcachePersistentSegmentFactory.EhcachePersistentSegment<String, String> createTestSegment(EvictionListener<String, String> evictionListener) throws IOException { return createTestSegment(noAdvice(), evictionListener); } private EhcachePersistentSegmentFactory.EhcachePersistentSegment<String, String> createTestSegment(final EvictionAdvisor<? super String, ? super String> evictionPredicate, EvictionListener<String, String> evictionListener) throws IOException { try { HeuristicConfiguration configuration = new HeuristicConfiguration(1024 * 1024); SerializationProvider serializationProvider = new DefaultSerializationProvider(null); serializationProvider.start(providerContaining()); MappedPageSource pageSource = new MappedPageSource(folder.newFile(), true, configuration.getMaximumSize()); Serializer<String> keySerializer = serializationProvider.createKeySerializer(String.class, EhcachePersistentSegmentTest.class.getClassLoader()); Serializer<String> valueSerializer = serializationProvider.createValueSerializer(String.class, EhcachePersistentSegmentTest.class.getClassLoader()); PersistentPortability<String> keyPortability = persistent(new SerializerPortability<String>(keySerializer)); PersistentPortability<String> elementPortability = persistent(new SerializerPortability<String>(valueSerializer)); Factory<FileBackedStorageEngine<String, String>> storageEngineFactory = FileBackedStorageEngine.createFactory(pageSource, configuration.getMaximumSize() / 10, BYTES, keyPortability, elementPortability); SwitchableEvictionAdvisor<String, String> wrappedEvictionAdvisor = new SwitchableEvictionAdvisor<String, String>() { private volatile boolean enabled = true; @Override public boolean adviseAgainstEviction(String key, String value) { return evictionPredicate.adviseAgainstEviction(key, value); } @Override public boolean isSwitchedOn() { return enabled; } @Override public void setSwitchedOn(boolean switchedOn) { this.enabled = switchedOn; } }; return new EhcachePersistentSegmentFactory.EhcachePersistentSegment<String, String>(pageSource, storageEngineFactory.newInstance(), 1, true, wrappedEvictionAdvisor, evictionListener); } catch (UnsupportedTypeException e) { throw new AssertionError(e); } } @Test public void testPutAdvisedAgainstEvictionComputesMetadata() throws IOException { EhcachePersistentSegment<String, String> segment = createTestSegment(new EvictionAdvisor<String, String>() { @Override public boolean adviseAgainstEviction(String key, String value) { return "please-do-not-evict-me".equals(key); } }); try { segment.put("please-do-not-evict-me", "value"); assertThat(segment.getMetadata("please-do-not-evict-me", EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION), is(EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION)); } finally { segment.destroy(); } } @Test public void testPutPinnedAdvisedAgainstEvictionComputesMetadata() throws IOException { EhcachePersistentSegment<String, String> segment = createTestSegment(new EvictionAdvisor<String, String>() { @Override public boolean adviseAgainstEviction(String key, String value) { return "please-do-not-evict-me".equals(key); } }); try { segment.putPinned("please-do-not-evict-me", "value"); assertThat(segment.getMetadata("please-do-not-evict-me", EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION), is(EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION)); } finally { segment.destroy(); } } @Test public void testAdviceAgainstEvictionPreventsEviction() throws IOException { EhcachePersistentSegment<String, String> segment = createTestSegment(); try { assertThat(segment.evictable(1), is(true)); assertThat(segment.evictable(EhcacheSegmentFactory.EhcacheSegment.ADVISED_AGAINST_EVICTION | 1), is(false)); } finally { segment.destroy(); } } @Test public void testEvictionFiresEvent() throws IOException { @SuppressWarnings("unchecked") EvictionListener<String, String> evictionListener = mock(EvictionListener.class); EhcachePersistentSegment<String, String> segment = createTestSegment(evictionListener); try { segment.put("key", "value"); segment.evict(segment.getEvictionIndex(), false); verify(evictionListener).onEviction("key", "value"); } finally { segment.destroy(); } } }