package org.jboss.cache.api;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.EvictionConfig;
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.eviction.LRUConfiguration;
import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
import org.jboss.cache.misc.TestingUtil;
import static org.testng.Assert.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.transaction.TransactionManager;
/**
* Tester class for Node.isResident functionality.
*
* @author <a href="mailto:mircea.markus@jboss.com">Mircea Markus</a>
* @since 2.1.0
*/
@Test(groups = {"functional"})
public class ResidentNodesTest
{
private CacheSPI<Object, Object> cache;
private final String TEST_NODES_ROOT = "residentNodesTest";
private Cache[] caches = {};
@BeforeMethod(alwaysRun = true)
public void setUp()
{
Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true);
cacheConfig.setCacheMode(Configuration.CacheMode.LOCAL);
cache = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(cacheConfig, false);
cache.getConfiguration().getEvictionConfig().setWakeupIntervalSeconds(1);
createNewRegion();
cache.start();
}
/**
* Setting up a new region for our purposes.
*/
private void createNewRegion()
{
EvictionConfig evConfig = cache.getConfiguration().getEvictionConfig();
EvictionRegionConfig evRegConfig = new EvictionRegionConfig();
evRegConfig.setRegionFqn(Fqn.fromString("/" + TEST_NODES_ROOT));
evRegConfig.setEventQueueSize(100);
LRUConfiguration lruConfig = new LRUConfiguration();
lruConfig.setMaxAgeSeconds(100000);
lruConfig.setTimeToLiveSeconds(100000);
lruConfig.setMaxNodes(3);
evRegConfig.setEvictionPolicyConfig(lruConfig);
evConfig.getEvictionRegionConfigs().add(evRegConfig);
//end setting up region stuff
}
@AfterMethod(alwaysRun = true)
public void tearDown() throws Exception
{
cache.stop();
for (Cache c : caches)
{
if (c != null)
{
c.stop();
}
}
}
/**
* Mark some nodes as resident and show that they won't get evicted,
* even if normally scenario they would
*/
public void testHappyFlow() throws InterruptedException
{
cache.put(getSubFqn("/a"), "k_a", "v_a");
cache.getNode(getSubFqn("/a")).setResident(true);
cache.put(getSubFqn("/b"), "k_b", "v_b");
cache.getNode(getSubFqn("/b")).setResident(true);
cache.put(getSubFqn("/c"), "k_c", "v_c");
cache.put(getSubFqn("/d"), "k_d", "v_d");
cache.put(getSubFqn("/e"), "k_e", "v_e");
cache.put(getSubFqn("/f"), "k_f", "v_f");
cache.put(getSubFqn("/g"), "k_g", "v_g");
cache.put(getSubFqn("/h"), "k_h", "v_h");
cache.put(getSubFqn("/i"), "k_i", "v_i");
Thread.sleep(3000);//so that eviction is activated
assertTrue(cache.exists(getSubFqn("/a")));
assertTrue(cache.exists(getSubFqn("/b")));
assertFalse(cache.exists(getSubFqn("/c")));
assertFalse(cache.exists(getSubFqn("/d")));
assertFalse(cache.exists(getSubFqn("/e")));
assertFalse(cache.exists(getSubFqn("/f")));
//only last three used are not evicted
assertTrue(cache.exists(getSubFqn("/g")));
assertTrue(cache.exists(getSubFqn("/h")));
assertTrue(cache.exists(getSubFqn("/i")));
}
/**
* If a node is marked as resident, and a get is made on that given node then an VISITED event would normally be
* added to the eviction queue. In a LRU scenario, this will cause another node to be evicted given that the size of
* the eviction queue is bounded. This test makes sure that this scenario will not hapen.
*/
public void testNoEvictionEventsForResidentNodes() throws InterruptedException
{
cache.put(getSubFqn("/a"), "k_a", "v_a");
cache.put(getSubFqn("/b"), "k_b", "v_b");
cache.getNode(getSubFqn("/a")).setResident(true);
cache.getNode(getSubFqn("/b")).setResident(true);
cache.put(getSubFqn("/c"), "k_c", "v_c");
cache.put(getSubFqn("/d"), "k_d", "v_d");
cache.put(getSubFqn("/e"), "k_e", "v_e");
cache.put(getSubFqn("/f"), "k_f", "v_f");
cache.put(getSubFqn("/g"), "k_g", "v_g");
cache.put(getSubFqn("/h"), "k_h", "v_h");
//at this point the oldest nodes are /a and /b so. There are eviction events in the queue corresponding
// to those nodes
cache.getNode(getSubFqn("/a"));
cache.getNode(getSubFqn("/b"));
TestingUtil.sleepThread(3000);//so that eviction is activated
//a and b should exist as those were marked resident. Also they shouldn't be caunted as nodes in the eviction
// queue
assertTrue(cache.exists(getSubFqn("/a")));
assertTrue(cache.exists(getSubFqn("/b")));
// c, d and e were the first accessed, they should be evicted
assertFalse(cache.exists(getSubFqn("/c")));
assertFalse(cache.exists(getSubFqn("/d")));
assertFalse(cache.exists(getSubFqn("/e")));
//all of them should be there - even if we re-retrieved a and b at a prev step (cache.get(getSubFqn("/a"))) this
//call shouldn't create an eviction event.
assertTrue(cache.exists(getSubFqn("/f")));
assertTrue(cache.exists(getSubFqn("/g")));
assertTrue(cache.exists(getSubFqn("/h")));
}
/**
* Check the behavior whilst using optimistic locking.
*/
public void testResidencyAndOptimisticLocking() throws Exception
{
Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true);
config.setCacheMode(Configuration.CacheMode.LOCAL);
config.setNodeLockingOptimistic(true);
CacheSPI<Object, Object> cache = (CacheSPI<Object, Object>) new DefaultCacheFactory().createCache(config, true);
cache.put(Fqn.fromString("/a/b"), "key", "value");
TransactionManager txManager = cache.getTransactionManager();
txManager.begin();
cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(true);
cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2");
assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2);
txManager.rollback();
assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
txManager.begin();
cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(false);
cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2");
assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2);
txManager.commit();
assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2);
try
{
if (cache.getTransactionManager().getTransaction() != null)
{
cache.getTransactionManager().rollback();
}
}
finally
{
cache.stop();
}
}
public void testResidencyAndPesimistickLocking() throws Exception
{
cache.put(Fqn.fromString("/a/b"), "key", "value");
TransactionManager txManager = cache.getTransactionManager();
txManager.begin();
cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(true);
cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2");
assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2);
txManager.rollback();
assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
txManager.begin();
cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(false);
cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2");
assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2);
txManager.commit();
assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident());
assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2);
}
private Fqn getSubFqn(String str)
{
return Fqn.fromString("/" + TEST_NODES_ROOT + str);
}
}