1 Reply Latest reply on Jun 16, 2008 8:42 PM by jason.greene

    PojoCache(1.4.1SP9) is corrupted when tx modify a map and ro

    oferunipier

      In my cluster i have 2 PojoCache (REPL_SYNC,inactive on startup,transcational,replication version 1.4.0.GA). First i create one cache and fill it with data, then i create the second cache and force full state transfer using region activation. On the second cache, after it received the state, in a new transaction, i fecth an object from the cache and modify a map it contains and then rollback the transaction. After the rollback, *all* the objects in the seconds cache are corrupted: all properties values are now null!, (the other cache is not corrupted). Note, when modifing a primitive property and then rollback, the cache is not corrupted. What i found is that in the corrupted objects, the instanceAdvisor appendedInterceptors list is missing the CacheInterceptor.

      attached a unit test demonstrating this bug. The test is based on the distribution org.jboss.cache.aop.statetransfer.StateTransferAopTestBase unit test, to run it, add it to the distribution org.jboss.cache.aop.statetransfer tests package and run the tests.

      
      package org.jboss.cache.aop.statetransfer;
      
      import java.util.HashMap;
      import java.util.HashSet;
      import java.util.Map;
      import java.util.Set;
      
      import javax.transaction.NotSupportedException;
      import javax.transaction.SystemException;
      import javax.transaction.Transaction;
      import javax.transaction.TransactionManager;
      
      import junit.framework.TestCase;
      
      import org.jboss.cache.DummyTransactionManagerLookup;
      import org.jboss.cache.Fqn;
      import org.jboss.cache.PropertyConfigurator;
      import org.jboss.cache.aop.PojoCache;
      import org.jboss.cache.aop.test.Address;
      import org.jboss.cache.aop.test.Person;
      import org.jboss.cache.misc.TestingUtil;
      import org.jboss.cache.transaction.DummyTransactionManager;
      
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      
      public class StateTransferFollowedByRollback extends TestCase {
       private static Log log = LogFactory.getLog(StateTransferFollowedByRollback.class);
      
       public static final Fqn A_B_1 = Fqn.fromString("/a/b/1");
       public static final Fqn A_B_2 = Fqn.fromString("/a/b/2");
      
       private Person joe;
       private Person jane;
       private Address addr1;
      
       PojoCache cache1;
       PojoCache cache2;
      
       public static final Integer TWENTY = new Integer(20);
       public static final Integer TWENTYFIVE = new Integer(25);
      
       public void testInitialStateTransferFollowedByMapModificationAndRollback() throws Exception {
       log.info("Enter testInitialStateTransferFollowedByMapModificationAndRollback");
      
       //fill the the first cache
       cache1 = createCache( true, true, false);
       cache1.setTransactionManagerLookup(new DummyTransactionManagerLookup());
       log.info("\n\nload the first cache\n\n");
       Transaction tx = startTransaction();
       cache1.activateRegion("/a");
       cache1.putObject(A_B_1, joe);
       cache1.putObject(A_B_2, jane);
       tx.commit();
      
       cache2 = createCache( true, true, false);
       cache2.setTransactionManagerLookup(new DummyTransactionManagerLookup());
       // Pause to give caches time to see each other
       TestingUtil.blockUntilViewsReceived(new PojoCache[]
       {cache1, cache2}, 60000);
       //recieve the state
       log.info("\n\njoin the first cache\n\n");
       tx = startTransaction();
       cache2.activateRegion("/a");
       tx.commit();
      
       tx = startTransaction();
       Person ab1 = (Person) cache2.getObject(A_B_1);
       Person ab2 = (Person) cache2.getObject(A_B_2);
       assertEquals("Name for /a/b/1 is Joe", joe.getName(), ab1.getName());
       assertEquals("City for /a/b/1 is Anytown", addr1.getCity(), ab1.getAddress().getCity());
       assertEquals("Hobbies for /a/b/1 are Biking and Jazz", joe.getHobbies(), ab1.getHobbies());
       assertEquals("Name for /a/b/2 is Jane", jane.getName(), ab2.getName());
       assertEquals("City for /a/b/2 is Anytown", addr1.getCity(), ab2.getAddress().getCity());
       assertEquals("Hobbies for /a/b/2 are Bridge and Country", jane.getHobbies(), ab2.getHobbies());
       assertTrue("Joe and Jane have same Address", ab1.getAddress() == ab2.getAddress());
       tx.commit();
      
       //modify a map in the cached object and rollback
       log.info("\n\nrollback on second cache\n\n");
       tx = startTransaction();
       ab1 = (Person)cache2.getObject(A_B_1);
       ab1.getHobbies().put("Sport", "Tennis");
       tx.rollback();
      
       tx = startTransaction();
       ab1 = (Person) cache2.getObject(A_B_1);
       ab2 = (Person) cache2.getObject(A_B_2);
       assertEquals("Name for /a/b/1 is Joe", joe.getName(), ab1.getName());
       assertEquals("City for /a/b/1 is Anytown", addr1.getCity(), ab1.getAddress().getCity());
       assertEquals("Hobbies for /a/b/1 are Biking and Jazz", joe.getHobbies(), ab1.getHobbies());
       assertEquals("Name for /a/b/2 is Jane", jane.getName(), ab2.getName());
       assertEquals("City for /a/b/2 is Anytown", addr1.getCity(), ab2.getAddress().getCity());
       assertEquals("Hobbies for /a/b/2 are Bridge and Country", jane.getHobbies(), ab2.getHobbies());
       assertTrue("Joe and Jane have same Address", ab1.getAddress() == ab2.getAddress());
       tx.commit();
       }
      
       public void testInitialStateTransferFollowedByPrimitiveModificationAndRollback() throws Exception {
       log.info("Enter testInitialStateTransferFollowedByPrimitiveModificationAndRollback");
      
       //fill the the first cache
       cache1 = createCache(true, true, false);
       cache1.setTransactionManagerLookup(new DummyTransactionManagerLookup());
       log.info("\n\nload the first cache\n\n");
       Transaction tx = startTransaction();
       cache1.activateRegion("/a");
       cache1.putObject(A_B_1, joe);
       cache1.putObject(A_B_2, jane);
       tx.commit();
      
       cache2 = createCache(true, true, false);
       cache2.setTransactionManagerLookup(new DummyTransactionManagerLookup());
       // Pause to give caches time to see each other
       TestingUtil.blockUntilViewsReceived(new PojoCache[]
       {cache1, cache2}, 60000);
       //recieve the state
       log.info("\n\njoin the first cache\n\n");
       tx = startTransaction();
       cache2.activateRegion("/a");
       tx.commit();
      
       tx = startTransaction();
       Person ab1 = (Person) cache2.getObject(A_B_1);
       Person ab2 = (Person) cache2.getObject(A_B_2);
       assertEquals("Name for /a/b/1 is Joe", joe.getName(), ab1.getName());
       assertEquals("City for /a/b/1 is Anytown", addr1.getCity(), ab1.getAddress().getCity());
       assertEquals("Hobbies for /a/b/1 are Biking and Jazz", joe.getHobbies(), ab1.getHobbies());
       assertEquals("Name for /a/b/2 is Jane", jane.getName(), ab2.getName());
       assertEquals("City for /a/b/2 is Anytown", addr1.getCity(), ab2.getAddress().getCity());
       assertEquals("Hobbies for /a/b/2 are Bridge and Country", jane.getHobbies(), ab2.getHobbies());
       assertTrue("Joe and Jane have same Address", ab1.getAddress() == ab2.getAddress());
       tx.commit();
      
       //modify a string in the cached object and rollback
       log.info("\n\nrollback on second cache\n\n");
       tx = startTransaction();
       ab1 = (Person)cache2.getObject(A_B_1);
       ab1.setName("blabla");
       tx.rollback();
      
       tx = startTransaction();
       ab1 = (Person) cache2.getObject(A_B_1);
       ab2 = (Person) cache2.getObject(A_B_2);
       assertEquals("Name for /a/b/1 is Joe", joe.getName(), ab1.getName());
       assertEquals("City for /a/b/1 is Anytown", addr1.getCity(), ab1.getAddress().getCity());
       assertEquals("Hobbies for /a/b/1 are Biking and Jazz", joe.getHobbies(), ab1.getHobbies());
       assertEquals("Name for /a/b/2 is Jane", jane.getName(), ab2.getName());
       assertEquals("City for /a/b/2 is Anytown", addr1.getCity(), ab2.getAddress().getCity());
       assertEquals("Hobbies for /a/b/2 are Bridge and Country", jane.getHobbies(), ab2.getHobbies());
       assertTrue("Joe and Jane have same Address", ab1.getAddress() == ab2.getAddress());
       tx.commit();
       }
      
       private Transaction startTransaction() throws SystemException, NotSupportedException {
       DummyTransactionManager mgr=DummyTransactionManager.getInstance();
       mgr.begin();
       return mgr.getTransaction();
       }
      
       protected String getReplicationVersion() {
       return "1.4.0.GA";
       }
      
       protected void setUp() throws Exception {
       super.setUp();
      
       addr1 = new Address();
       addr1.setStreet("101 Oakview Dr");
       addr1.setCity("Anytown");
       addr1.setZip(11111);
      
       joe = new Person();
       joe.setName("Joe");
       joe.setAge(TWENTY.intValue());
       joe.setAddress(addr1);
       Set skills = new HashSet();
       skills.add("TENNIS");
       skills.add("CARPENTRY");
       joe.setSkills(skills);
       Map hobbies = new HashMap();
       hobbies.put("Sport", "Biking");
       hobbies.put("Music", "Jazz");
       joe.setHobbies(hobbies);
      
       jane = new Person();
       jane.setName("Jane");
       jane.setAge(TWENTYFIVE.intValue());
       jane.setAddress(addr1);
       skills = new HashSet();
       skills.add("JUJITSU");
       skills.add("MACRAME");
       jane.setSkills(skills);
       hobbies = new HashMap();
       hobbies.put("Sport", "Bridge");
       hobbies.put("Music", "Country");
       jane.setHobbies(hobbies);
       }
      
       protected void tearDown() throws Exception {
       super.tearDown();
       stopCache(cache1);
       stopCache(cache2);
       //TODO what about cleanFile?
       }
      
       protected PojoCache createCache(boolean sync, boolean useMarshalling, boolean useCacheLoader)
       throws Exception {
       return createCache( sync, useMarshalling, useCacheLoader, true);
       }
      
       protected PojoCache createCache(boolean sync,
       boolean useMarshalling,
       boolean useCacheLoader,
       boolean inactiveOnStartup) throws Exception {
      
       PojoCache tree = new PojoCache();
       PropertyConfigurator config = new PropertyConfigurator();
       String configFile = sync ? "META-INF/replSync-service.xml" : "META-INF/replAsync-service.xml";
       config.configure(tree, configFile); // read in generic replAsync xml
       //tree.setDeadlockDetection(sync);
       tree.setClusterName("StateTransferTestBase");
       tree.setReplicationVersion(getReplicationVersion());
       // Use a long timeout to facilitate setting debugger breakpoints
       tree.setInitialStateRetrievalTimeout(60000);
       if (useMarshalling)
       {
       tree.setUseMarshalling(true);
       tree.setInactiveOnStartup(inactiveOnStartup);
       }
       if (useCacheLoader)
       {
       //TODO configureCacheLoader(tree, useMarshalling);
       }
      
      
       tree.createService();
       tree.startService();
      
       return tree;
       }
      
       protected void stopCache(PojoCache cache) {
       if (cache != null)
       {
       try
       {
       TransactionManager m = cache.getTransactionManager();
       try
       {
       if (m!= null && m.getTransaction() != null)
       {
       m.rollback();
       }
       }
       catch (Exception e)
       {
       }
       cache.stopService();
       cache.destroyService();
       }
       catch (Exception e)
       {
       log.error("Exception stopping cache " + e.getMessage(), e);
       }
       }
       }
      }