package org.infinispan.query.backend; import static org.infinispan.test.TestingUtil.withCacheManager; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import java.io.File; import java.nio.file.Files; import java.util.concurrent.atomic.LongAdder; import org.apache.lucene.index.SegmentInfos; import org.apache.lucene.search.MatchAllDocsQuery; import org.infinispan.Cache; import org.infinispan.commons.util.Util; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.configuration.cache.Index; import org.infinispan.configuration.global.GlobalConfigurationBuilder; import org.infinispan.eviction.EvictionStrategy; import org.infinispan.manager.DefaultCacheManager; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.notifications.Listener; import org.infinispan.notifications.cachelistener.annotation.CacheEntryActivated; import org.infinispan.notifications.cachelistener.annotation.CacheEntryPassivated; import org.infinispan.notifications.cachelistener.event.CacheEntryActivatedEvent; import org.infinispan.notifications.cachelistener.event.CacheEntryPassivatedEvent; import org.infinispan.query.Search; import org.infinispan.query.SearchManager; import org.infinispan.query.queries.faceting.Car; import org.infinispan.query.test.Person; import org.infinispan.test.CacheManagerCallable; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Test for interaction of activation and preload on indexing * * @author gustavonalle * @since 7.0 */ @Test(groups = "functional", testName = "query.backend.QueryInterceptorTest") public class QueryInterceptorTest { private static final int MAX_CACHE_ENTRIES = 1; private File indexDir; private File storeDir; private final Person person1 = new Person("p1", "b1", 12); private final Person person2 = new Person("p2", "b2", 22); private final Car car1 = new Car("subaru", "blue", 200); private final Car car2 = new Car("lamborghini", "yellow", 230); @BeforeMethod protected void setup() throws Exception { indexDir = Files.createTempDirectory("test-").toFile(); storeDir = Files.createTempDirectory("test-").toFile(); } @AfterMethod protected void tearDown() { Util.recursiveFileRemove(indexDir); Util.recursiveFileRemove(storeDir); } @Test public void shouldNotReindexOnActivation() throws Exception { withCacheManager(new CacheManagerCallable(createCacheManager(MAX_CACHE_ENTRIES)) { @Override public void call() { LuceneIndexTracker luceneIndexTracker = new LuceneIndexTracker(new File(indexDir + "/person")); luceneIndexTracker.mark(); Cache<Integer, Person> cache = cm.getCache(); CacheListener cacheListener = new CacheListener(); cache.addListener(cacheListener); cache.put(1, person1); cache.put(2, person2); assertEquals(cacheListener.numberOfPassivations(), 1); assertEquals(cacheListener.numberOfActivations(), 0); assertTrue(luceneIndexTracker.indexChanged()); luceneIndexTracker.mark(); cache.get(1); assertEquals(cacheListener.numberOfActivations(), 1); assertFalse(luceneIndexTracker.indexChanged()); } }); } @Test public void shouldNotReindexOnPreload() throws Exception { final LuceneIndexTracker luceneIndexTracker = new LuceneIndexTracker(new File(indexDir + "/person")); luceneIndexTracker.mark(); withCacheManager(new CacheManagerCallable(createCacheManager(MAX_CACHE_ENTRIES)) { @Override public void call() { Cache<Integer, Person> cache = cm.getCache(); cache.put(1, person1); cache.put(2, person2); assertTrue(luceneIndexTracker.indexChanged()); } }); luceneIndexTracker.mark(); withCacheManager(new CacheManagerCallable(createCacheManager(MAX_CACHE_ENTRIES + 10)) { @Override public void call() { Cache<Integer, Person> cache = cm.getCache(); CacheListener cacheListener = new CacheListener(); cache.addListener(cacheListener); assertTrue(cache.containsKey(1)); assertTrue(cache.containsKey(2)); assertEquals(cacheListener.numberOfPassivations(), 0); assertEquals(cacheListener.numberOfActivations(), 0); assertFalse(luceneIndexTracker.indexChanged()); } }); } @Test public void shouldDeleteSingleIndex() throws Exception { withCacheManager(new CacheManagerCallable(createCacheManager(MAX_CACHE_ENTRIES)) { @Override public void call() { Cache<String, Object> cache = cm.getCache(); cache.put("P1", person1); cache.put("P2", person2); cache.put("C1", car1); cache.put("C2", car2); SearchManager searchManager = Search.getSearchManager(cache); assertEquals(2, countIndex(Car.class, cache)); assertEquals(2, countIndex(Person.class, cache)); searchManager.purge(Car.class); assertEquals(0, countIndex(Car.class, cache)); assertEquals(2, countIndex(Person.class, cache)); searchManager.purge(Person.class); assertEquals(0, countIndex(Car.class, cache)); assertEquals(0, countIndex(Person.class, cache)); } }); } protected EmbeddedCacheManager createCacheManager(int maxEntries) throws Exception { return new DefaultCacheManager( new GlobalConfigurationBuilder().globalJmxStatistics().allowDuplicateDomains(true).build(), new ConfigurationBuilder() .eviction().strategy(EvictionStrategy.LRU).size(maxEntries) .persistence().passivation(true) .addSingleFileStore().location(storeDir.getAbsolutePath()).preload(true) .indexing().index(Index.ALL) .addIndexedEntity(Person.class) .addIndexedEntity(Car.class) .addProperty("default.directory_provider", "filesystem") .addProperty("default.indexBase", indexDir.getAbsolutePath()) .addProperty("lucene_version", "LUCENE_CURRENT") .build() ); } private int countIndex(Class<?> entityType, Cache<?, ?> cache) { return Search.getSearchManager(cache).getQuery(new MatchAllDocsQuery(), entityType).getResultSize(); } private static final class LuceneIndexTracker { private final File indexBase; private long indexVersion; public LuceneIndexTracker(File indexBase) { this.indexBase = indexBase; } public void mark() { indexVersion = getLuceneIndexVersion(); } public boolean indexChanged() { return getLuceneIndexVersion() != indexVersion; } private long getLuceneIndexVersion() { return indexBase.list() == null ? -1 : SegmentInfos.getLastCommitGeneration(indexBase.list()); } } @Listener @SuppressWarnings("unused") private static final class CacheListener { private final LongAdder passivationCount = new LongAdder(); private final LongAdder activationCount = new LongAdder(); @CacheEntryPassivated public void onEvent(CacheEntryPassivatedEvent payload) { if (!payload.isPre()) passivationCount.increment(); } @CacheEntryActivated public void onEvent(CacheEntryActivatedEvent payload) { if (!payload.isPre()) activationCount.increment(); } public int numberOfPassivations() { return passivationCount.intValue(); } public int numberOfActivations() { return activationCount.intValue(); } } }