[jboss-user] [JBoss Cache: Core Edition] - Re: on child insert parent, child collection updated in DB b

dukehoops do-not-reply at jboss.com
Fri Mar 6 21:10:57 EST 2009


Here's my first take on a unit test that builds/runs as part of org.hibernate.test.cache.jbc2 maven project.

The problem is that this test (since it tests concurrency) requires JTATransactionManager impl that can handle multiple concurrent Transactions. And SimpleJtaTransactionManagerImpl is only suitable for single thread tests. So testManyUsers() fails for all the wrong reasons.

Does anyone know which TX Manager impl I should be using in this test environment? If one isn't available (then how is concurrency under JTA being tested?) - any hints on the simplest way to plug in a real impl?





  | package org.hibernate.test.cache.jbc2.functional;
  | 
  | import java.util.HashSet;
  | import java.util.Set;
  | import java.util.concurrent.ExecutorService;
  | import java.util.concurrent.Executors;
  | import java.util.concurrent.TimeUnit;
  | import junit.framework.Test;
  | import org.hibernate.FlushMode;
  | import org.hibernate.Session;
  | import org.hibernate.exception.ExceptionUtils;
  | import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
  | import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
  | import org.hibernate.transaction.CMTTransactionFactory;
  | 
  | /**
  |  *
  |  * @author nikita_tovstoles at mba.berkeley.edu
  |  */
  | public class MVCCConcurrentWriteTest extends MVCCJBossCacheTest {
  | 
  |     /**
  |      * when USER_COUNT==1, tests pass, when >4 tests fail
  |      */
  |     final private int USER_COUNT = 5;
  |     final private int ITERATION_COUNT = 50;
  |     final private long THINK_TIME_MILLIS = 10;
  |     final private long LAUNCH_INTERVAL_MILLIS = 10;
  |     /**
  |      * collection of IDs of all customers participating in this test
  |      */
  |     private Set<Integer> customerIDs = new HashSet<Integer>();
  | 
  |     public MVCCConcurrentWriteTest(String x) {
  |         super(x);
  |     }
  | 
  |     @Override
  |     protected Class getTransactionFactoryClass() {
  |         return CMTTransactionFactory.class; //really want JTATransactionFactory here but it's not supported by this test harness
  |     //       return JTATransactionFactory.class;
  |     }
  | 
  |     @Override
  |     public void testEmptySecondLevelCacheEntry() throws Exception {
  |         //do nothing
  |     }
  | 
  |     @Override
  |     public void testQueryCacheInvalidation() {
  |         //do nothing
  |     }
  | 
  |     @Override
  |     public void testStaleWritesLeaveCacheConsistent() {
  |         //do nothing
  |     }
  | 
  |     public void testSingleUser() throws Exception {
  |         try {
  |             //setup
  |             Customer customer = createCustomer(0);
  |             final Integer customerId = customer.getId();
  |             getCustomerIDs().add(customerId);
  | 
  |             assertNull("contact exists despite not being added", getFirstContact(customerId));
  | 
  |             final Contact contact = addContact(customerId);
  |             assertNotNull("contact returned by addContact is null", contact);
  |             assertNotNull("Contact missing after successful add call", getFirstContact(customerId));
  | 
  |             //read everyone's contacts
  |             readEveryonesFirstContact();
  | 
  |             removeContact(customerId);
  |             assertNull("contact still exists after successful remove call", getFirstContact(customerId));
  |         } finally {
  |             //cleanup
  |             cleanup();
  |         }
  |     }
  | 
  |     public void testManyUsers() throws Exception {
  | 
  |         try {
  |             //setup - create users
  |             for (int i = 0; i < USER_COUNT; i++) {
  |                 Customer customer = createCustomer(0);
  |                 getCustomerIDs().add(customer.getId());
  |             }
  | 
  |             final ExecutorService pool = Executors.newFixedThreadPool(USER_COUNT);
  | 
  |             Set<UserRunner> runners = new HashSet<UserRunner>();
  |             for (Integer customerId : getCustomerIDs()) {
  |                 UserRunner r = new UserRunner(customerId);
  |                 runners.add(r);
  |                 pool.execute(r);
  |                 Thread.sleep(LAUNCH_INTERVAL_MILLIS); //rampup
  |             }
  | 
  |             assertEquals("not all user threads launched", USER_COUNT, runners.size());
  | 
  |             pool.shutdown();
  |             boolean finishedInTime = pool.awaitTermination(2, TimeUnit.MINUTES);
  |             assertTrue("test took too long", finishedInTime);
  | 
  |             //check whether all runners suceeded
  |             for (UserRunner r : runners) {
  |                 assertEquals("runner for customerId=" + r.getCustomerId() + " did not complete all iterations; cause=" +
  |                         ExceptionUtils.getFullStackTrace(r.getCauseOfFailure()),
  |                         ITERATION_COUNT,
  |                         r.getCompletedIterations());
  |             }
  | 
  |         } finally {
  |             //TODO - move to onTearDown or similar
  |             //cleanup - remove users and contacts
  |             cleanup();
  |         }
  |     }
  | 
  |     public void cleanup() throws Exception {
  | 
  |         getCustomerIDs().clear();
  | 
  |         String deleteContactHQL = "delete from Contact";
  |         String deleteCustomerHQL = "delete from Customer";
  | 
  |         beginTx();
  |         try {
  | 
  |             Session session = getSessions().getCurrentSession();
  |             session.createQuery(deleteContactHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
  |             session.createQuery(deleteCustomerHQL).setFlushMode(FlushMode.AUTO).executeUpdate();
  |             commitTx();
  |         } catch (Exception e) {
  |             rollbackTx();
  |             throw e;
  |         }
  | 
  |     }
  | 
  |     private Customer createCustomer(int nameSuffix) throws Exception {
  |         beginTx();
  |         try {
  |             Customer customer = new Customer();
  |             customer.setName("customer_" + nameSuffix);
  |             customer.setContacts(new HashSet<Contact>());
  | 
  |             getSessions().getCurrentSession().persist(customer);
  |             commitTx();
  |             return customer;
  |         } catch (Exception e) {
  |             rollbackTx();
  |             throw e;
  |         }
  |     }
  | 
  |     /**
  |      * delegate method since I'm trying to figure out which txManager to use
  |      * given that this test runs multiple threads (SimpleJtaTxMgrImpl isn't suited for that).
  |      *
  |      * What is needed is a thread-safe JTATransactionManager impl that can handle concurrent TXs
  |      * 
  |      * @throws java.lang.Exception
  |      */
  |     private void beginTx() throws Exception {
  |         SimpleJtaTransactionManagerImpl.getInstance().begin();
  |     //getSessions().getCurrentSession().beginTransaction();
  |     }
  | 
  |     /**
  |      * @see #beginTx()
  |      * @throws java.lang.Exception
  |      */
  |     private void commitTx() throws Exception {
  |         SimpleJtaTransactionManagerImpl.getInstance().commit();
  |     //getSessions().getCurrentSession().getTransaction().commit();
  |     }
  | 
  |     /**
  |      * @see #beginTx()
  |      * @throws java.lang.Exception
  |      */
  |     private void rollbackTx() throws Exception {
  |         SimpleJtaTransactionManagerImpl.getInstance().rollback();
  |     //getSessions().getCurrentSession().getTransaction().rollback();
  |     }
  | 
  |     /**
  |      * read first contact of every Customer participating in this test.
  |      * this forces concurrent cache writes of Customer.contacts Collection cache node
  |      * 
  |      * @return who cares
  |      * @throws java.lang.Exception
  |      */
  |     private void readEveryonesFirstContact() throws Exception {
  |         beginTx();
  |         try {
  |             for (Integer customerId : getCustomerIDs()) {
  |                 final Customer customer = (Customer) getSessions().getCurrentSession().load(Customer.class, customerId);
  |                 Set<Contact> contacts = customer.getContacts();
  |                 Contact firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
  |             }
  |             commitTx();
  |         } catch (Exception e) {
  |             rollbackTx();
  |             throw e;
  |         }
  |     }
  | 
  |     /**
  |      * -load existing Customer
  |      * -get customer's contacts; return 1st one
  |      * 
  |      * @param customerId
  |      * @return first Contact or null if customer has none
  |      */
  |     private Contact getFirstContact(Integer customerId) throws Exception {
  |         assert customerId != null;
  | 
  |         beginTx();
  |         try {
  |             final Customer customer = (Customer) getSessions().getCurrentSession().load(Customer.class, customerId);
  |             Set<Contact> contacts = customer.getContacts();
  |             Contact firstContact = contacts.isEmpty() ? null : contacts.iterator().next();
  |             commitTx();
  |             return firstContact;
  |         } catch (Exception e) {
  |             rollbackTx();
  |             throw e;
  |         }
  |     }
  | 
  |     /**
  |      * -load existing Customer     
  |      * -create a new Contact and add to customer's contacts
  |      * 
  |      * @param customerId
  |      * @return added Contact
  |      */
  |     private Contact addContact(Integer customerId) throws Exception {
  |         assert customerId != null;
  | 
  |         beginTx();
  |         try {
  |             final Customer customer = (Customer) getSessions().getCurrentSession().load(Customer.class, customerId);
  | 
  |             Contact contact = new Contact();
  |             contact.setName("contact name");
  |             contact.setTlf("wtf is tlf?");
  | 
  |             contact.setCustomer(customer);
  |             customer.getContacts().add(contact);
  | 
  |             //assuming contact is persisted via cascade from customer
  |             commitTx();
  | 
  |             return contact;
  |         } catch (Exception e) {
  |             rollbackTx();
  |             throw e;
  |         }
  |     }
  | 
  |     /**
  |      * remove existing 'contact' from customer's list of contacts
  |      * 
  |      * @param contact contact to remove from customer's contacts
  |      * @param customerId
  |      * @throws IllegalStateException if customer does not own a contact
  |      */
  |     private void removeContact(Integer customerId) throws Exception {
  |         assert customerId != null;
  | 
  |         beginTx();
  |         try {
  |             Customer customer = (Customer) getSessions().getCurrentSession().load(Customer.class, customerId);
  |             Set<Contact> contacts = customer.getContacts();
  |             if (contacts.size() != 1) {
  |                 throw new IllegalStateException("can't remove contact: customer id=" + customerId + " expected exactly 1 contact, " +
  |                         "actual count=" + contacts.size());
  |             }
  | 
  |             Contact contact = contacts.iterator().next();
  |             contacts.remove(contact);
  |             contact.setCustomer(null);
  | 
  |             //explicitly delete Contact because hbm has no 'DELETE_ORPHAN' cascade?
  |             //getSessions().getCurrentSession().delete(contact); //appears to not be needed
  | 
  |             //assuming contact is persisted via cascade from customer
  |             commitTx();
  |         } catch (Exception e) {
  |             rollbackTx();
  |             throw e;
  |         }
  |     }
  | 
  |     /**
  |      * @return the customerIDs
  |      */
  |     public Set<Integer> getCustomerIDs() {
  |         return customerIDs;
  |     }
  | 
  |     class UserRunner implements Runnable {
  | 
  |         final private Integer customerId;
  |         private int completedIterations = 0;
  |         private Throwable causeOfFailure;
  | 
  |         public UserRunner(final Integer cId) {
  |             assert cId != null;
  |             this.customerId = cId;
  |         }
  | 
  |         private boolean contactExists() throws Exception {
  |             return getFirstContact(customerId) != null;
  |         }
  | 
  |         public void run() {
  | 
  |             //name this thread for easier log tracing
  |             Thread.currentThread().setName("UserRunnerThread-" + getCustomerId());
  |             try {
  |                 for (int i = 0; i < ITERATION_COUNT; i++) {
  | 
  |                     if (contactExists()) {
  |                         throw new IllegalStateException("contact already exists before add, customerId=" + customerId);
  |                     }
  | 
  |                     addContact(customerId);
  | 
  |                     if (!contactExists()) {
  |                         throw new IllegalStateException("contact missing after successful add, customerId=" + customerId);
  |                     }
  | 
  |                     think();
  | 
  |                     //read everyone's contacts
  |                     readEveryonesFirstContact();
  | 
  |                     think();
  | 
  |                     removeContact(customerId);
  | 
  |                     if (contactExists()) {
  |                         throw new IllegalStateException("contact still exists after successful remove call, customerId=" + customerId);
  |                     }
  | 
  |                     think();
  | 
  |                     ++completedIterations;
  |                 }
  | 
  |             } catch (Throwable t) {
  |                 this.causeOfFailure = t;
  |             }
  |         }
  | 
  |         public boolean isSuccess() {
  |             return ITERATION_COUNT == getCompletedIterations();
  |         }
  | 
  |         public int getCompletedIterations() {
  |             return completedIterations;
  |         }
  | 
  |         public Throwable getCauseOfFailure() {
  |             return causeOfFailure;
  |         }
  | 
  |         public Integer getCustomerId() {
  |             return customerId;
  |         }
  |     }
  | 
  |     private void think() {
  |         try {
  |             Thread.sleep(THINK_TIME_MILLIS);
  |         } catch (InterruptedException ex) {
  |             throw new RuntimeException("sleep interrupted", ex);
  |         }
  |     }
  | 
  |     public static Test suite() {
  |         return new FunctionalTestClassTestSuite(MVCCConcurrentWriteTest.class);
  |     }
  | }
  | 

anonymous wrote : 
  | c:\code\hibernate\core\trunk\cache-jbosscache2>mvn test -Dtest=MVCCConcurrentWriteTest
  | [INFO] Scanning for projects...
  | WAGON_VERSION: 1.0-beta-2
  | [INFO] ------------------------------------------------------------------------
  | [INFO] Building Hibernate JBossCache3.x Integration
  | [INFO]    task-segment: [test]
  | [INFO] ------------------------------------------------------------------------
  | [INFO] [enforcer:enforce {execution: enforce-java}]
  | [INFO] [resources:resources]
  | [INFO] Using default encoding to copy filtered resources.
  | [INFO] [compiler:compile]
  | [INFO] Nothing to compile - all classes are up to date
  | [INFO] [resources:testResources]
  | [INFO] Using default encoding to copy filtered resources.
  | [INFO] [compiler:testCompile]
  | [INFO] Nothing to compile - all classes are up to date
  | [INFO] [surefire:test]
  | [INFO] Surefire report directory: c:\code\hibernate\core\trunk\cache-jbosscache2\target\surefire-reports
  | 
  | -------------------------------------------------------
  |  T E S T S
  | -------------------------------------------------------
  | Running org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest
  | Tests run: 5, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 31.557 sec <<< FAILURE!
  | 
  | Results :
  | 
  | Tests in error:
  |   testManyUsers(org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest)
  | 
  | Tests run: 5, Failures: 0, Errors: 1, Skipped: 0
  | 
  | [INFO] ------------------------------------------------------------------------
  | [ERROR] BUILD FAILURE
  | [INFO] ------------------------------------------------------------------------
  | [INFO] There are test failures.
  | 
  | Please refer to c:\code\hibernate\core\trunk\cache-jbosscache2\target\surefire-reports for the individual test results.
  | [INFO] ------------------------------------------------------------------------
  | [INFO] For more information, run Maven with the -e switch
  | [INFO] ------------------------------------------------------------------------
  | [INFO] Total time: 34 seconds
  | [INFO] Finished at: Fri Mar 06 18:04:48 PST 2009
  | [INFO] Final Memory: 11M/20M
  | [INFO] ------------------------------------------------------------------------
  | 
  | c:\code\hibernate\core\trunk\cache-jbosscache2>

anonymous wrote : -------------------------------------------------------------------------------
  | Test set: org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest
  | -------------------------------------------------------------------------------
  | Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 3.222 sec <<< FAILURE!
  | testManyUsers(org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest)  Time elapsed: 1.953 sec  <<< FAILURE!
  | junit.framework.AssertionFailedError: runner for customerId=6 did not complete all iterations; cause=java.lang.IllegalStateException: no current transaction
  | 	at org.hibernate.test.tm.SimpleJtaTransactionManagerImpl.rollback(SimpleJtaTransactionManagerImpl.java:90)
  | 	at org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest.rollbackTx(MVCCConcurrentWriteTest.java:188)
  | 	at org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest.addContact(MVCCConcurrentWriteTest.java:263)
  | 	at org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest.access$100(MVCCConcurrentWriteTest.java:24)
  | 	at org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest$UserRunner.run(MVCCConcurrentWriteTest.java:335)
  | 	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
  | 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
  | 	at java.lang.Thread.run(Thread.java:619)
  |  expected:<50> but was:<20>
  | 	at junit.framework.Assert.fail(Assert.java:47)
  | 	at junit.framework.Assert.failNotEquals(Assert.java:282)
  | 	at junit.framework.Assert.assertEquals(Assert.java:64)
  | 	at junit.framework.Assert.assertEquals(Assert.java:201)
  | 	at org.hibernate.test.cache.jbc2.functional.MVCCConcurrentWriteTest.testManyUsers(MVCCConcurrentWriteTest.java:111)
  | 

View the original post : http://www.jboss.org/index.html?module=bb&op=viewtopic&p=4215913#4215913

Reply to the post : http://www.jboss.org/index.html?module=bb&op=posting&mode=reply&p=4215913



More information about the jboss-user mailing list