Tuesday, February 26, 2008

Examining Chaos with Hibernate

Alex Miller recently wrote a good introduction to CyclicBarriers. Here's one usage that wouldn't go into production, but can be illuminating.

Background

The Hibernate book has 2 large sections: one on structure; the other, on behaviour. It is easy to come up with a good, isolated example for explorations of mapping structure (e.g. collections). It's not as easy to isolate behaviour with respect to isolation levels and concurrency (e.g. simultaneous inserts to the DB).

However, with the threading tools, it is definitely possible (and fun) to create some homegrown chaos in isolation. Consider this scenario:
  • Several threads each open a transaction against the database
  • Each thread creates a new object with a common value in a field designated as unique in the DB
  • Each thread then attempts to commit its record into the DB: chaos!
One might try writing a small multi-threaded example that might cause the collision, and then run it repeatedly, analyzing the log to see what happened.

CyclicBarrier

However, with the CyclicBarrier, we can precisely hold up the threads until the Moment of Maximum Pathogenesis. In this case, after the creation but before the commit.

It is easy to use Alex's driver program to execute several of worker threads. The gist is as follows:
  • Initialize Hibernate's SessionFactory
  • Create a ThreadPoolExecutor
  • Create a CyclicBarrier of size N
  • Create N workers (see below): each one receives a handle to the
    SessionFactory and CyclicBarrier
  • Pass the workers into the executor
The example uses a simple Owner POJO (from a car example). In the DB, the Owner table simply has a surrogate key and a my_name field which has a unique constraint.

The individual worker looks like this:


class MyTask implements Runnable {

private int taskId;
private CyclicBarrier barrier;
private SessionFactory sessionFactory;

// omitting ctor and emit() method
// for brevity

public void run() {
try {
emit("waiting to start");

// begin Txn
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

// create new persistent object
Owner owner = new Owner();
owner.setName("CodeToJoy");
session.saveOrUpdate(owner);

// barrier: wait for all threads
emit("waiting...");
barrier.await();

// unleash the hounds!

tx.commit();
session.close();

emit("done");
} catch(Exception ex) {
emit("caught : " + ex.toString() );
}
}

}


Here is example output (cleaned-up considerably):


starting....
task 1 : waiting to start
task 2 : waiting to start
task 3 : waiting to start
task 4 : waiting to start
task 5 : waiting to start
task 4 : waiting...
task 3 : waiting...
task 5 : waiting...
task 1 : waiting...
task 2 : waiting...
barrier reached (NOTE: from a println the CyclicBarrier itself)
(NOTE: thread ids may not match primary key)
task 5 : done
task 1 : insert into Owner (my_name, id)
values (CodeToJoy, 1) was aborted.
task 3 : insert into Owner (my_name, id)
values (CodeToJoy, 5) was aborted.
task 4 : insert into Owner (my_name, id)
values (CodeToJoy, 4) was aborted.
task 2 : insert into Owner (my_name, id)
values (CodeToJoy, 2) was aborted.

(NOTE: list all records in the DB. Task 5 won and received id = 3)
owner id = 3
ownername = CodeToJoy

The Upshot

In this way, the threading utilities can be used to come up with many weird and wonderful situations. Implementing these ideas can illuminate the concepts in the Hibernate book (and in the Java concurrency libraries).

1 comment: