Scenario: put data (K1/V1) into the cache with persistence and indexing enabled - after cache restart put the same data under same key (K1,V1) to the cache - new (same) indexes are added instead of update therefore if data are queried after cache restart for K1 the query contains 2 results instead of one. There is different behaviour of the bug in 5.0.1 and 5.1.0:
- in 5.0.1 the bug is present always regardless whether the put is done in or outside transaction
- in 5.1.0 only in case there is transaction manager defined or batching enabled
UT:
package org.y4n.jlibs.jboss.ifspn5.bugs;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.ProvidedId;
import org.hibernate.search.annotations.Store;
import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.query.CacheQuery;
import org.infinispan.query.Search;
import org.infinispan.query.SearchManager;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import static org.testng.Assert.assertEquals;
public class InconsistentIndexesAfterRestart {
private static Log log = LogFactory.getLog(InconsistentIndexesAfterRestart.class);
@Test
public void testPutSearchablePersistent() throws Exception
{
Cache c = getSearchablePersistentCache();
// c.getAdvancedCache().getTransactionManager().begin();
c.put("key1", new SEntity(1, "name1", "surname1"));
// c.getAdvancedCache().getTransactionManager().commit();
assertEquals(searchByName("name1", c).size(), 1, "should be 1");
c.getCacheManager().stop();
c.stop();
Thread.sleep(5000);
c = getSearchablePersistentCache();
// c.getAdvancedCache().getTransactionManager().begin();
// if in sequence get, put (non tx) - there is no idx problem
// c.get("key1");
c.put("key1", new SEntity(1, "name1", "surname1"));
// c.getAdvancedCache().getTransactionManager().commit();
assertEquals(searchByName("name1", c).size(), 1, "should be again 1");
c.getCacheManager().stop();
c.stop();
}
@BeforeMethod
public void init()
{
deleteDir(new File("/tmp/ifspn5tests/"));
}
private Cache getSearchablePersistentCache() throws Exception
{
EmbeddedCacheManager manager = new DefaultCacheManager("searchable_caches.xml");
manager.start();
Cache c = manager.getCache("searchable-persistent");
c.start();
return c;
}
private List searchByName(String name, Cache c)
{
SearchManager sm = Search.getSearchManager(c);
CacheQuery q = sm.getQuery(SEntity.searchByName(name), SEntity.class);
List l = q.list();
return q.list();
}
private static boolean deleteDir(File dir)
{
log.info("Deleting dir recursively: " + dir);
return deleteDirRecursively(dir);
}
private static boolean deleteDirRecursively(File dir) {
boolean success = true;
if (dir.exists()) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (String aChildren : children)
{
success = deleteDirRecursively(new File(dir, aChildren));
}
} else {
success = dir.delete();
if (!success)
{
log.error("Can't delete file: " + dir.getAbsolutePath());
}
}
} else
{
log.debug("Directory doesn't exist: " + dir.getName());
}
return success;
}
@ProvidedId
@Indexed
public static class SEntity implements Serializable {
public static final String IDX_NAME = "name";
public static final String IDX_SURNAME = "surname";
private final long id;
@Field(store = Store.YES)
private final String name;
@Field (store = Store.YES)
private final String surname;
public SEntity(long id, String name, String surname)
{
this.id = id;
this.name = name;
this.surname = surname;
}
public long getId()
{
return id;
}
public String getName()
{
return name;
}
public String getSurname()
{
return surname;
}
@Override
public String toString() {
return "SEntity
{" +
"id=" + id +
", name='" + name + '\'' +
", surname='" + surname + '\'' +
'}
';
}
public static Query searchByName(String name)
{
BooleanQuery query = new BooleanQuery();
query.add(new TermQuery(
new Term(SEntity.IDX_NAME, name.toLowerCase())), BooleanClause.Occur.MUST);
return query;
}
}
}
XML config:
<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd"
xmlns="urn:infinispan:config:5.1">
<default>
<indexing enabled="true" indexLocalOnly="true">
<properties>
<property name="hibernate.search.default.directory_provider" value="filesystem" />
<property name="hibernate.search.default.indexBase" value="/tmp/ifspn5tests/idx" />
</properties>
</indexing>
<!-- idx problem if enabled in 5.1.x (not in 5.0.1) without transactions -->
<invocationBatching enabled="true"/>
</default>
<namedCache name="searchable-persistent">
<loaders passivation="false">
<loader class="org.infinispan.loaders.jdbm.JdbmCacheStore"
fetchPersistentState="true"
ignoreModifications="false"
purgeOnStartup="false">
<properties>
<property name="location" value="/tmp/ifspn5tests/"/>
</properties>
<async enabled="false"/>
</loader>
</loaders>
</namedCache>
</infinispan>