Hibernate Locking Patterns – How do PESSIMISTIC_READ and PESSIMISTIC_WRITE work

Introduction

Java Persistence API comes with a thorough concurrency control mechanism, supporting both implicit and explicit locking. The implicit locking mechanism is straightforward and it relies on:

  • Optimistic locking: Entity state changes can trigger a version incrementation
  • Row-level locking: Based on the current running transaction isolation level, the INSERT/UPDATE/DELETE statements may acquire exclusive row locks

While implicit locking is suitable for many scenarios, an explicit locking mechanism can leverage a finer-grained concurrency control.

In my previous posts, I covered the explicit optimistic lock modes:

In this post, I am going to unravel the explicit pessimistic lock modes:

Readers–writer lock

A database system is a highly concurrent environment, therefore many concurrency theory idioms apply to database access as well. Concurrent changes must be serialized to preserve data integrity, so most database systems use a two-phase locking strategy, even if it’s usually supplemented by a Multiversion concurrency control mechanism.

Because a mutual exclusion locking would hinder scalability (treating reads and writes equally), most database systems use a readers-writer locking synchronization scheme, so that:

  • A shared (read) lock blocks writers, allowing multiple readers to proceed
  • An exclusive (write) lock blocks both readers and writers, making all write operations be applied sequentially

Because the locking syntax is not part of the SQL Standard, each RDBMS has opted for a different syntax:

Database name Shared lock statement Exclusive lock statement
Oracle FOR UPDATE FOR UPDATE
MySQL LOCK IN SHARE MODE FOR UPDATE
Microsoft SQL Server WITH (HOLDLOCK, ROWLOCK) WITH (UPDLOCK, ROWLOCK)
PostgreSQL FOR SHARE FOR UPDATE
DB2 FOR READ ONLY WITH RS FOR UPDATE WITH RS

Java Persistence abstraction layer hides the database specific locking semantics, offering a common API that only requires two Lock Modes. The shared/read lock is acquired using the PESSIMISTIC_READ Lock Mode Type, and the exclusive/write lock is requested using PESSIMISTIC_WRITE instead.

PostgreSQL row-level lock modes

For the next test cases, we are going to use PostgreSQL for it supports both exclusive and share explicit locking.

All the following tests will use the same concurrency utility, emulating two users: Alice and Bob. Each test scenario will verify a specific read/write locking combination.

private void testPessimisticLocking(ProductLockRequestCallable primaryLockRequestCallable, ProductLockRequestCallable secondaryLockRequestCallable) {
	doInTransaction(session -> {
		try {
			Product product = (Product) session.get(Product.class, 1L);
			primaryLockRequestCallable.lock(session, product);
			executeAsync(
				() -> {
					doInTransaction(_session -> {
						Product _product = (Product) _session.get(Product.class, 1L);
						secondaryLockRequestCallable.lock(_session, _product);
					});
				},
				endLatch::countDown
			);
			sleep(WAIT_MILLIS);
		} catch (StaleObjectStateException e) {
			LOGGER.info("Optimistic locking failure: ", e);
		}
	});
	awaitOnLatch(endLatch);
}

Case 1: PESSIMISTIC_READ doesn’t block PESSIMISTIC_READ lock requests

The first test will check how two concurrent PESSIMISTIC_READ lock requests interact:

@Test
public void testPessimisticReadDoesNotBlockPessimisticRead() throws InterruptedException {
	LOGGER.info("Test PESSIMISTIC_READ doesn't block PESSIMISTIC_READ");
	testPessimisticLocking(
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
			LOGGER.info("PESSIMISTIC_READ acquired");
		},
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
			LOGGER.info("PESSIMISTIC_READ acquired");
		}
	);
}

Running this test, we get the following output:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ doesn't block PESSIMISTIC_READ

#Alice selects the Product entity
[Alice]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]} 

#Alice acquires a SHARED lock on the Product entity
[Alice]: Time:1 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR share 
][1,0]} 
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired

#Alice waits for 500ms
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!

#Bob selects the Product entity
[Bob]: Time:1 Query:{[
 SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]}

#Bob acquires a SHARED lock on the Product entity
[Bob]: Time:1 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR share 
][1,0]} 
[Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired

#Bob's transactions is committed
[Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Alice's transactions is committed
[Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

In this scenario, there is no contention whatsoever. Both Alice and Bob can acquire a shared lock without running into any conflict.

Case 2: PESSIMISTIC_READ blocks UPDATE implicit lock requests

The second scenario will demonstrate how the shared lock prevents a concurrent modification. Alice will acquire a shared lock and Bob will attempt to modify the locked entity:

@Test
public void testPessimisticReadBlocksUpdate() throws InterruptedException {
	LOGGER.info("Test PESSIMISTIC_READ blocks UPDATE");
	testPessimisticLocking(
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
			LOGGER.info("PESSIMISTIC_READ acquired");
		},
		(session, product) -> {
			product.setDescription("USB Flash Memory Stick");
			session.flush();
			LOGGER.info("Implicit lock acquired");
		}
	);
}

The test generates this output:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ blocks UPDATE

#Alice selects the Product entity
[Alice]: Time:0 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]} 

#Alice acquires a SHARED lock on the Product entity
[Alice]: Time:0 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR share 
][1,0]} 
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired

#Alice waits for 500ms
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!

#Bob selects the Product entity
[Bob]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]} 

#Alice's transactions is committed
[Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Bob can acquire the Product entity lock, only after Alice's transaction is committed
[Bob]: Time:427 Query:{[
UPDATE product
SET    description = ?,
       price = ?,
       version = ?
WHERE  id = ?
       AND version = ?
][USB Flash Memory Stick,12.99,1,1,0]} 
[Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Implicit lock acquired

#Bob's transactions is committed
[Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

While Bob could select the Product entity, the UPDATE is delayed up until Alice transaction is committed (that’s why the UPDATE took 427ms to run).

Case 3: PESSIMISTIC_READ blocks PESSIMISTIC_WRITE lock requests

The same behaviour is exhibited by a secondary PESSIMISTIC_WRITE lock request:

@Test
public void testPessimisticReadBlocksPessimisticWrite() throws InterruptedException {
	LOGGER.info("Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE");
	testPessimisticLocking(
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
			LOGGER.info("PESSIMISTIC_READ acquired");
		},
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);
			LOGGER.info("PESSIMISTIC_WRITE acquired");
		}
	);
}

Giving the following output:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE

#Alice selects the Product entity
[Alice]: Time:0 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]}

#Alice acquires a SHARED lock on the Product entity
[Alice]: Time:1 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR share 
][1,0]} 
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired

#Alice waits for 500ms
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!

#Bob selects the Product entity
[Bob]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]} 

#Alice's transactions is committed
[Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Bob can acquire the Product entity lock, only after Alice's transaction is committed
[Bob]: Time:428 Query:{[
SELECT id
FROM   product
WHERE  id = ?
       AND version = ?
FOR UPDATE  
][1,0]} 
[Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired

#Bob's transactions is committed
[Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Bob’s exclusive lock request waits for Alice’s shared lock to be released.

Case 4: PESSIMISTIC_READ blocks PESSIMISTIC_WRITE lock requests, NO WAIT fails fast

Hibernate provides a PESSIMISTIC_NO_WAIT timeout directive, which translates to a database specific NO_WAIT lock acquire policy.

The PostgreSQL NO WAIT directive is described as follows:

To prevent the operation from waiting for other transactions to commit, use the NOWAIT option. With NOWAIT, the statement reports an error, rather than waiting, if a selected row cannot be locked immediately. Note that NOWAIT applies only to the row-level lock(s) — the required ROW SHARE table-level lock is still taken in the ordinary way (see Chapter 13). You can use LOCK with the NOWAIT option first, if you need to acquire the table-level lock without waiting.

@Test
public void testPessimisticReadWithPessimisticWriteNoWait() throws InterruptedException {
	LOGGER.info("Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE, NO WAIT fails fast");
	testPessimisticLocking(
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
			LOGGER.info("PESSIMISTIC_READ acquired");
		},
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).setTimeOut(Session.LockRequest.PESSIMISTIC_NO_WAIT).lock(product);
			LOGGER.info("PESSIMISTIC_WRITE acquired");
		}
	);
}

This test generates the following output:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE, NO WAIT fails fast

#Alice selects the Product entity
[Alice]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]}

#Alice acquires a SHARED lock on the Product entity
[Alice]: Time:1 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR share 
][1,0]} 
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired

#Alice waits for 500ms
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!

#Bob selects the Product entity
[Bob]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]}

#Bob tries to acquire an EXCLUSIVE lock on the Product entity and fails because of the NO WAIT policy
[Bob]: Time:0 Query:{[
SELECT id
FROM   product
WHERE  id = ?
       AND version = ?
FOR UPDATE nowait
][1,0]} 
[Bob]: o.h.e.j.s.SqlExceptionHelper - SQL Error: 0, SQLState: 55P03
[Bob]: o.h.e.j.s.SqlExceptionHelper - ERROR: could not obtain lock on row in relation "product"

#Bob's transactions is rolled back
[Bob]: o.h.e.t.i.j.JdbcTransaction - rolled JDBC Connection

#Alice's transactions is committed
[Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Since Alice already holds a shared lock on the Product entity associated database row, Bob’s exclusive lock request fails immediately.

Case 5: PESSIMISTIC_WRITE blocks PESSIMISTIC_READ lock requests

The next test proves that an exclusive lock will always blocks a shared lock acquire attempt:

@Test
public void testPessimisticWriteBlocksPessimisticRead() throws InterruptedException {
	LOGGER.info("Test PESSIMISTIC_WRITE blocks PESSIMISTIC_READ");
	testPessimisticLocking(
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);
			LOGGER.info("PESSIMISTIC_WRITE acquired");
		},
		(session, product) -> {
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
			LOGGER.info("PESSIMISTIC_WRITE acquired");
		}
	);
}

Generating the following output:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_WRITE blocks PESSIMISTIC_READ

#Alice selects the Product entity
[Alice]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]} 

#Alice acquires an EXCLUSIVE lock on the Product entity
[Alice]: Time:0 Query:{[
SELECT id
FROM   product
WHERE  id = ?
       AND version = ?
FOR UPDATE  
][1,0]} 
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired

#Alice waits for 500ms
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!

#Bob selects the Product entity
[Bob]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ? 
][1]}

#Alice's transactions is committed
[Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Bob can acquire the Product entity SHARED lock, only after Alice's transaction is committed
[Bob]: Time:428 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR share 
][1,0]}
[Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired

#Bob's transactions is committed
[Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Bob’s shared lock request waits for Alice’s transaction to end, so that all acquired locks are released.

Case 6: PESSIMISTIC_WRITE blocks PESSIMISTIC_WRITE lock requests

An exclusive lock blocks an exclusive lock as well:

@Test
public void testPessimisticWriteBlocksPessimisticWrite() throws InterruptedException {
	LOGGER.info("Test PESSIMISTIC_WRITE blocks PESSIMISTIC_WRITE");
	testPessimisticLocking(
			(session, product) -> {
				session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);
				LOGGER.info("PESSIMISTIC_WRITE acquired");
			},
			(session, product) -> {
				session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);
				LOGGER.info("PESSIMISTIC_WRITE acquired");
			}
	);
}

The test generates this output:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_WRITE blocks PESSIMISTIC_WRITE

#Alice selects the Product entity
[Alice]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ?  
][1]} 

#Alice acquires an EXCLUSIVE lock on the Product entity
[Alice]: Time:0 Query:{[
SELECT id
FROM   product
WHERE  id = ?
       AND version = ?
FOR UPDATE  
][1,0]} 
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired

#Alice waits for 500ms
[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!

#Bob selects the Product entity
[Bob]: Time:1 Query:{[
SELECT lockmodepe0_.id          AS id1_0_0_,
       lockmodepe0_.description AS descript2_0_0_,
       lockmodepe0_.price       AS price3_0_0_,
       lockmodepe0_.version     AS version4_0_0_
FROM   product lockmodepe0_
WHERE  lockmodepe0_.id = ? 
][1]}

#Alice's transactions is committed
[Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Bob can acquire the Product entity SHARED lock, only after Alice's transaction is committed
[Bob]: Time:428 Query:{[
SELECT id
FROM   product
WHERE  id =?
AND    version =? FOR update 
][1,0]}
[Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired

#Bob's transactions is committed
[Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Bob’s exclusive lock request has to wait for Alice to release its lock.

Conclusion

Relational database systems use locks for preserving ACID guarantees, so it’s important to understand how shared and exclusive row-level locks inter-operate. An explicit pessimistic lock is a very powerful database concurrency control mechanism and you might even use it for fixing an optimistic locking race condition.

Code available on GitHub.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

Hibernate locking patterns – How does PESSIMISTIC_FORCE_INCREMENT Lock Mode work

Introduction

In my previous post, I introduced the OPTIMISTIC_FORCE_INCREMENT Lock Mode and we applied it for propagating a child entity version change to a locked parent entity. In this post, I am going to reveal the PESSIMISTIC_FORCE_INCREMENT Lock Mode and compare it with its optimistic counterpart.

More alike than different

As we already found out, the OPTIMISTIC_FORCE_INCREMENT Lock Mode can increment an entity version, even when the current transaction doesn’t modify the locked entity state. For each lock mode, Hibernate defines an associated LockingStrategy and the OPTIMISTIC_FORCE_INCREMENT Lock Mode event is handled by the OptimisticForceIncrementLockingStrategy:

public class OptimisticForceIncrementLockingStrategy implements LockingStrategy {

	//code omitted for brevity

	@Override
	public void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session) {
		if ( !lockable.isVersioned() ) {
			throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
		}
		final EntityEntry entry = session.getPersistenceContext().getEntry( object );
		// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.
		( (EventSource) session ).getActionQueue().registerProcess( new EntityIncrementVersionProcess( object, entry ) );
	}
}

This strategy registers an EntityIncrementVersionProcess in the current Persistence Context action queue. The locked entity version gets incremented just before completing the current running transaction.

public class EntityIncrementVersionProcess implements BeforeTransactionCompletionProcess {
	
	//code omitted for brevity
	
	@Override
	public void doBeforeTransactionCompletion(SessionImplementor session) {
		final EntityPersister persister = entry.getPersister();
		final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );
		entry.forceLocked( object, nextVersion );
	}
}

Analogous to OPTIMISTIC_FORCE_INCREMENT, the PESSIMISTIC_FORCE_INCREMENT Lock Mode is handled by the PessimisticForceIncrementLockingStrategy:

public class PessimisticForceIncrementLockingStrategy implements LockingStrategy {

	//code omitted for brevity

	@Override
	public void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session) {
		if ( !lockable.isVersioned() ) {
			throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );
		}
		final EntityEntry entry = session.getPersistenceContext().getEntry( object );
		final EntityPersister persister = entry.getPersister();
		final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );
		entry.forceLocked( object, nextVersion );
	}
}

The locked entity is incremented right away, so these two lock modes execute the same logic but at different times. The PESSIMISTIC_FORCE_INCREMENT naming might lead you into thinking that you are using a pessimistic locking strategy, while in reality this Lock Mode is just an optimistic locking variation.

Pessimistic locking entails explicit physical locks (shared or exclusive), while optimistic locking relies on current transaction isolation level implicit locking instead.

The Repository Use Case

I am going to reuse the previous post exercise and switch to using the PESSIMISTIC_FORCE_INCREMENT Lock Mode. To recap a little bit, our domain model contains:

  • a Repository entity, whose version is increased with every new Commit
  • a Commit entity, encapsulating a single atomical Repository state transition
  • a CommitChange component, encapsulating a single Repository resource change

Concurrent modification prevention

Our system is simultaneously accessed by both Alice and Bob. The Repository entity is always locked, right after it’s being fetched from the database:

private final CountDownLatch startLatch = new CountDownLatch(1);
private final CountDownLatch endLatch = new CountDownLatch(1);

@Test
public void testConcurrentPessimisticForceIncrementLockingWithLockWaiting() throws InterruptedException {
	LOGGER.info("Test Concurrent PESSIMISTIC_FORCE_INCREMENT Lock Mode With Lock Waiting");
	doInTransaction(session -> {
		try {
			Repository repository = (Repository) session.get(Repository.class, 1L);
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(repository);

			executeAsync(() -> doInTransaction(_session -> {
				LOGGER.info("Try to get the Repository row");
				startLatch.countDown();
				Repository _repository = (Repository) _session.get(Repository.class, 1L);
				_session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(_repository);
				Commit _commit = new Commit(_repository);
				_commit.getChanges().add(new Change("index.html", "0a1,2..."));
				_session.persist(_commit);
				_session.flush();
				endLatch.countDown();
			}));
			startLatch.await();
			LOGGER.info("Sleep for 500ms to delay the other transaction PESSIMISTIC_FORCE_INCREMENT Lock Mode acquisition");
			Thread.sleep(500);
			Commit commit = new Commit(repository);
			commit.getChanges().add(new Change("README.txt", "0a1,5..."));
			commit.getChanges().add(new Change("web.xml", "17c17..."));
			session.persist(commit);
		} catch (InterruptedException e) {
			fail("Unexpected failure");
		}
	});
	endLatch.await();
}

This test case generates the following output:

#Alice selects the Repository
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} 

#Alice locks the Repository using a PESSIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[update repository set version=? where id=? and version=?][1,1,0]} 

#Bob tries to get the Repository but the SELECT is blocked by Alice lock 
INFO  [pool-1-thread-1]: c.v.h.m.l.c.LockModePessimisticForceIncrementTest - Try to get the Repository row

#Alice sleeps for 500ms to prove that Bob is waiting for her to release the acquired lock
Sleep for 500ms to delay the other transaction PESSIMISTIC_FORCE_INCREMENT Lock Mode acquisition

#Alice makes two changes and inserts a new Commit<a href="https://vladmihalcea.files.wordpress.com/2015/02/explicitlockingpessimisticforceincrementfailfast.png"><img src="https://vladmihalcea.files.wordpress.com/2015/02/explicitlockingpessimisticforceincrementfailfast.png?w=585" alt="ExplicitLockingPessimisticForceIncrementFailFast" width="585" height="224" class="alignnone size-large wp-image-3955" /></a>
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,5...,README.txt]#The Repository version is bumped up to version 1 and a conflict is raised
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,17c17...,web.xml]} Query:{[update repository set version=? where id=? and version=?][1,1,0]}

#Alice commits the transaction, therefore releasing all locks
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Bob Repository SELECT can proceed 
Query:{[select lockmodepe0_.id as id1_2_0_, lockmodepe0_.name as name2_2_0_, lockmodepe0_.version as version3_2_0_ from repository lockmodepe0_ where lockmodepe0_.id=?][1]} 

#Bob can insert his changes
Query:{[update repository set version=? where id=? and version=?][2,1,1]} 
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][2,0a1,2...,index.html]} 

This locking process can be easily visualize din the following diagram:

ExplicitLockingPessimisticForceIncrement

The HSQLDB test database Two-Phase Locking implementation uses course grain table locks whenever a database row is modified.

That’s the reason why Bob is unable to get the read lock on the Repository database row that Alice has just updated. Other databases (e.g. Oracle, PostgreSQL) use MVCC, therefore allowing a SELECT to proceed (using the current modifying transaction undo logs for recreating the previous row state) while blocking conflicting data modifying statements (e.g. updating the Repository row, when other concurrent transaction hasn’t yet committed the locked entity state change).

Fail fast

The instantaneous version incrementation has some interesting benefits:

  • If the version UPDATE succeeds (the exclusive row level lock is acquired), no other concurrent transaction can modify the locked database row. This is the moment when the logical lock (the version incrementation) is upgraded to a physical lock (the database exclusive lock).
  • If the version UPDATE fails (because some other concurrent transaction already committed a version change), our current running transaction can be rolled back at once (as opposed to waiting for the transaction to fail during commit)

The latter use case can be visualized as follows:

ExplicitLockingPessimisticForceIncrementFailFast

For this scenario, we are going to use the following test case:

@Test
public void testConcurrentPessimisticForceIncrementLockingFailFast() throws InterruptedException {
	LOGGER.info("Test Concurrent PESSIMISTIC_FORCE_INCREMENT Lock Mode fail fast");
	doInTransaction(session -> {
		try {
			Repository repository = (Repository) session.get(Repository.class, 1L);

			executeSync(() -> {
				doInTransaction(_session -> {
					Repository _repository = (Repository) _session.get(Repository.class, 1L);
					_session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(_repository);
					Commit _commit = new Commit(_repository);
					_commit.getChanges().add(new Change("index.html", "0a1,2..."));
					_session.persist(_commit);
					_session.flush();
				});
			});
			session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(repository);
			fail("Should have thrown StaleObjectStateException!");
		} catch (StaleObjectStateException expected) {
			LOGGER.info("Failure: ", expected);
		}
	});
}

Generating the following output:

#Alice selects the Repository
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} 

#Bob selects the Repository too
Query:{[select lockmodepe0_.id as id1_2_0_, lockmodepe0_.name as name2_2_0_, lockmodepe0_.version as version3_2_0_ from repository lockmodepe0_ where lockmodepe0_.id=?][1]} 

#Bob locks the Repository using a PESSIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[update repository set version=? where id=? and version=?][1,1,0]} 

#Bob makes a change and inserts a new Commit
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,2...,index.html]} 

#Bob commits the transaction
DEBUG [pool-3-thread-1]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Alice tries to lock the Repository
Query:{[update repository set version=? where id=? and version=?][1,1,0]} 

#Alice cannot lock the Repository, because the version has changed
INFO  [main]: c.v.h.m.l.c.LockModePessimisticForceIncrementTest - Failure: 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.LockModePessimisticForceIncrementTest$Repository#1]

Conclusion

Like OPTIMISTIC_FORCE_INCREMENT, the PESSIMISTIC_FORCE_INCREMENT Lock Mode is useful for propagating an entity state change to a parent entity.

While the locking mechanism is similar, the PESSIMISTIC_FORCE_INCREMENT is applied on the spot, allowing the current running transaction to instantaneously evaluate the locking outcome.

Code available on GitHub.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

Hibernate locking patterns – How does OPTIMISTIC_FORCE_INCREMENT Lock Mode work

Introduction

In my previous post, I explained how OPTIMISTIC Lock Mode works and how it can help us synchronize external entity state changes. In this post, we are going to unravel the OPTIMISTIC_FORCE_INCREMENT Lock Mode usage patterns.

With LockModeType.OPTIMISTIC, the locked entity version is checked towards the end of the current running transaction, to make sure we don’t use a stale entity state. Because of the application-level validation nature, this strategy is susceptible to race-conditions, therefore requiring an additional pessimistic lock .

The LockModeType.OPTIMISTIC_FORCE_INCREMENT not only it checks the expected locked entity version, but it also increments it. Both the check and the update happen in the same UPDATE statement, therefore making use of the current database transaction isolation level and the associated physical locking guarantees.

It is worth noting that the locked entity version is bumped up even if the entity state hasn’t been changed by the current running transaction.

A Centralized Version Control Use Case

As an exercise, we are going to emulate a centralized Version Control System, modeled as follows:

RepositoryCommitChangeOptimisticForceIncrement

The Repository is our system root entity and each state change is represented by a Commit child entity. Each Commit may contain one or more Change components, which are propagated as a single atomic Unit of Work.

The Repository version is incremented with each new Commit. For simplicity sake, we only verify the Repository entity version, although a more realistic approach would surely check each individual file version instead (to allow non-conflicting commits to proceed concurrently).

Testing time

First, we should check if the OPTIMISTIC_FORCE_INCREMENT Lock Mode suits our use case requirements:

doInTransaction(session -> {
	Repository repository = (Repository) session.get(Repository.class, 1L);
	session.buildLockRequest(new LockOptions(LockMode.OPTIMISTIC_FORCE_INCREMENT)).lock(repository);
	Commit commit = new Commit(repository);
	commit.getChanges().add(new Change("README.txt", "0a1,5..."));
	commit.getChanges().add(new Change("web.xml", "17c17..."));
	session.persist(commit);
});

This code generates the following output:

#Alice selects the Repository and locks it using an OPTIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} 

#Alice makes two changes and inserts a new Commit
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} 
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,5...,README.txt]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,17c17...,web.xml]} 

#The Repository version is bumped up
Query:{[update repository set version=? where id=? and version=?][1,1,0]} 

Our user has selected a Repository and issued a new Commit. At the end of her transaction, the Repository version is incremented as well (therefore recording the new Repository state change).

Conflict detection

In our next example, we are going to have two users (Alice and Bob) to concurrently commit changes. To avoid losing updates, both users acquire an explicit OPTIMISTIC_FORCE_INCREMENT Lock Mode.

Before Alice gets the chance to commit, Bob has just finished his transaction and incremented the Repository version. Alice transaction will be rolled back, throwing an unrecoverable StaleObjectStateException.

ExplicitLockingOptimisticForceIncrement

To emulate the conflict detection mechanism, we are going to use the following test scenario:

doInTransaction(session -> {
	Repository repository = (Repository) session.get(Repository.class, 1L);
	session.buildLockRequest(new LockOptions(LockMode.OPTIMISTIC_FORCE_INCREMENT)).lock(repository);

	executeSync(() -> {
		doInTransaction(_session -> {
			Repository _repository = (Repository) _session.get(Repository.class, 1L);
			_session.buildLockRequest(new LockOptions(LockMode.OPTIMISTIC_FORCE_INCREMENT)).lock(_repository);
			Commit _commit = new Commit(_repository);
			_commit.getChanges().add(new Change("index.html", "0a1,2..."));
			_session.persist(_commit);
		});
	});

	Commit commit = new Commit(repository);
	commit.getChanges().add(new Change("README.txt", "0a1,5..."));
	commit.getChanges().add(new Change("web.xml", "17c17..."));
	session.persist(commit);
});

The following output is generated:

#Alice selects the Repository and locks it using an OPTIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} 

#Bob selects the Repository and locks it using an OPTIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} 

#Bob makes a change and inserts a new Commit
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,2...,index.html]} 

#The Repository version is bumped up to version 1
Query:{[update repository set version=? where id=? and version=?][1,1,0]} 

#Alice makes two changes and inserts a new Commit
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][2,0a1,5...,README.txt]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][2,17c17...,web.xml]} 

#The Repository version is bumped up to version 1 and a conflict is raised
Query:{[update repository set version=? where id=? and version=?][1,1,0]} 
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticForceIncrementTest - Failure: 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : 
[com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.LockModeOptimisticForceIncrementTest$Repository#1]

This example exhibits the same behavior as the typical implicit optimistic locking mechanism. The only difference lies in the version change originator. While implicit locking only works for modifying entities, explicit locking can span to any managed entity instead (disregarding the entity state change requirement).

Conclusion

The OPTIMISTIC_FORCE_INCREMENT is therefore useful for propagating a child entity state change to an unmodified parent entity. This pattern can help us synchronize various entity types, by simply locking a common parent of theirs.

When a child entity state change has to trigger a parent entity version incrementation, the explicit OPTIMISTIC_FORCE_INCREMENT lock mode is probably what you are after.

Code available on GitHub.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

How to fix optimistic locking race conditions with pessimistic locking

Recap

In my previous post, I explained the benefits of using explicit optimistic locking. As we then discovered, there’s a very short time window in which a concurrent transaction can still commit a Product price change right before our current transaction gets committed.

This issue can be depicted as follows:

ExplicitLockingLockModeOptimisticRaceCondition

  • Alice fetches a Product
  • She then decides to order it
  • The Product optimistic lock is acquired
  • The Order is inserted in the current transaction database session
  • The Product version is checked by the Hibernate explicit optimistic locking routine
  • The price engine manages to commit the Product price change
  • Alice transaction is committed without realizing the Product price has just changed

Replicating the issue

So we need a way to inject the Product price change in between the optimistic lock check and the order transaction commit.

After analyzing the Hibernate source code, we discover that the SessionImpl.beforeTransactionCompletion() method is calling the current configured Interceptor.beforeTransactionCompletion() callback, right after the internal actionQueue stage handler (where the explicit optimistic locked entity version is checked):

public void beforeTransactionCompletion(TransactionImplementor hibernateTransaction) {
	LOG.trace( "before transaction completion" );
	actionQueue.beforeTransactionCompletion();
	try {
		interceptor.beforeTransactionCompletion( hibernateTransaction );
	}
	catch (Throwable t) {
		LOG.exceptionInBeforeTransactionCompletionInterceptor( t );
	}
}	

Armed with this info, we can set-up a test to replicate our race condition:

private AtomicBoolean ready = new AtomicBoolean();
private final CountDownLatch endLatch = new CountDownLatch(1);

@Override
protected Interceptor interceptor() {
	return new EmptyInterceptor() {
		@Override
		public void beforeTransactionCompletion(Transaction tx) {
			if(ready.get()) {
				LOGGER.info("Overwrite product price asynchronously");

				executeAsync(() -> {
					Session _session = getSessionFactory().openSession();
					_session.doWork(connection -> {
						try (PreparedStatement ps = connection.prepareStatement("UPDATE product set price = 14.49 WHERE id = 1")) {
							ps.executeUpdate();
						}
					});
					_session.close();
					endLatch.countDown();
				});
				try {
					LOGGER.info("Wait 500 ms for lock to be acquired!");
					Thread.sleep(500);
				} catch (InterruptedException e) {
					throw new IllegalStateException(e);
				}
			}
		}
	};
}

@Test
public void testExplicitOptimisticLocking() throws InterruptedException {
	try {
		doInTransaction(session -> {
			try {
				final Product product = (Product) session.get(Product.class, 1L, new LockOptions(LockMode.OPTIMISTIC));
				OrderLine orderLine = new OrderLine(product);
				session.persist(orderLine);
				lockUpgrade(session, product);
				ready.set(true);
			} catch (Exception e) {
				throw new IllegalStateException(e);
			}
		});
	} catch (OptimisticEntityLockException expected) {
		LOGGER.info("Failure: ", expected);
	}
	endLatch.await();
}

protected void lockUpgrade(Session session, Product product) {}

When running it, the test generates the following output:

#Alice selects a Product
DEBUG [main]: Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} 

#Alice inserts an OrderLine
DEBUG [main]: Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} 
#Alice transaction verifies the Product version
DEBUG [main]: Query:{[select version from product where id =?][1]} 

#The price engine thread is started
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Overwrite product price asynchronously
#Alice thread sleeps for 500ms to give the price engine a chance to execute its transaction
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Wait 500 ms for lock to be acquired!

#The price engine changes the Product price
DEBUG [pool-1-thread-1]: Query:{[UPDATE product set price = 14.49 WHERE id = 1][]} 

#Alice transaction is committed without realizing the Product price change
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

So, the race condition is real. It’s up to you to decide if your current application demands stronger data integrity requirements, but as rule of thumb, better safe than sorry.

Fixing the issue

To fix this issue, we just need to add a pessimistic lock request just before ending our transactional method.

@Override
protected void lockUpgrade(Session session, Product product) {
	session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);
}

The explicit shared lock will prevent concurrent writes on the entity we’ve previously locked optimistically. With this method, no other concurrent transaction can change the Product prior to releasing this lock (after the current transaction is committed or rolled back).

ExplicitLockingLockModeOptimisticRaceConditionFix

With the new pessimistic lock request in place, the previous test generates the following output:

#Alice selects a Product
DEBUG [main]: Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} 

#Alice inserts an OrderLine
DEBUG [main]: Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} 

#Alice applies an explicit physical lock on the Product entity
DEBUG [main]: Query:{[select id from product where id =? and version =? for update][1,0]} 

#Alice transaction verifies the Product version
DEBUG [main]: Query:{[select version from product where id =?][1]} 

#The price engine thread is started
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Overwrite product price asynchronously
#Alice thread sleeps for 500ms to give the price engine a chance to execute its transaction
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Wait 500 ms for lock to be acquired!

#The price engine cannot proceed because of the Product entity was locked exclusively, so Alice transaction is committed against the ordered Product price
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#The physical lock is released and the price engine can change the Product price
DEBUG [pool-1-thread-1]: Query:{[UPDATE product set price = 14.49 WHERE id = 1][]} 

Even though we asked for a PESSIMISTIC_READ lock, HSQLDB can only execute a FOR UPDATE exclusive lock instead, equivalent to an explicit PESSIMISTIC_WRITE lock mode.

Conclusion

If you wonder why we use both optimistic and pessimistic locking for our current transaction, you must remember that optimistic locking is the only feasible concurrency control mechanism for multi-request conversations.

In our example, The Product entity is loaded by the first request, using a read-only transaction. The Product entity has an associated version, and this read-time entity snapshot is going to be locked optimistically during the write-time transaction.

The pessimistic lock is useful only during the write-time transaction, to prevent any concurrent update from occurring after the Product entity version check. So, both the logical lock and the physical lock are cooperating for ensuring the Order price data integrity.

While I was working on this blog post, the Java Champion Markus Eisele took me an interview about the Hibernate Master Class initiative. During the interview I tried to explain the current post examples, while emphasizing the true importance of knowing your tools beyond the reference documentation.

Code available on GitHub.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

Hibernate locking patterns – How does Optimistic Lock Mode work

Explicit optimistic locking

In my previous post, I introduced the basic concepts of Java Persistence locking.

The implicit locking mechanism prevents lost updates and it’s suitable for entities that we can actively modify. While implicit optimistic locking is a widespread technique, few happen to understand the inner workings of explicit optimistic lock mode.

Explicit optimistic locking may prevent data integrity anomalies, when the locked entities are always modified by some external mechanism.

The product ordering use case

Let’s say we have the following domain model:

ProductOrderLineOptimisticLockMode

Our user, Alice, wants to order a product. The purchase goes through the following steps:

ImplicitLockingLockModeNone

  • Alice loads a Product entity
  • Because the price is convenient, she decides to order the Product
  • the price Engine batch job changes the Product price (taking into consideration currency changes, tax changes and marketing campaigns)
  • Alice issues the Order without noticing the price change

Implicit locking shortcomings

First, we are going to test if the implicit locking mechanism can prevent such anomalies. Our test case looks like this:

doInTransaction(session -> {
	final Product product = (Product) session.get(Product.class, 1L);
	try {
		executeSync(() -> doInTransaction(_session -> {
			Product _product = (Product) _session.get(Product.class, 1L);
			assertNotSame(product, _product);
			_product.setPrice(BigDecimal.valueOf(14.49));
		}));
	} catch (Exception e) {
		fail(e.getMessage());
	}
	OrderLine orderLine = new OrderLine(product);
	session.persist(orderLine);
});

The test generates the following output:

#Alice selects a Product
Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} 

#The price engine selects the Product as well
Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} 
#The price engine changes the Product price
Query:{[update product set description=?, price=?, version=? where id=? and version=?][USB Flash Drive,14.49,1,1,0]}
#The price engine transaction is committed
DEBUG [pool-2-thread-1]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Alice inserts an OrderLine without realizing the Product price change
Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]}
#Alice transaction is committed unaware of the Product state change
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

The implicit optimistic locking mechanism cannot detect external changes, unless the entities are also changed by the current Persistence Context. To protect against issuing an Order for a stale Product state, we need to apply an explicit lock on the Product entity.

Explicit locking to the rescue

The Java Persistence LockModeType.OPTIMISTIC is a suitable candidate for such scenarios, so we are going to put it to a test.

Hibernate comes with a LockModeConverter utility, that’s able to map any Java Persistence LockModeType to its associated Hibernate LockMode.

For simplicity sake, we are going to use the Hibernate specific LockMode.OPTIMISTIC, which is effectively identical to its Java persistence counterpart.

According to Hibernate documentation, the explicit OPTIMISTIC Lock Mode will:

assume that transaction(s) will not experience contention for entities. The entity version will be verified near the transaction end.

I will adjust our test case to use explicit OPTIMISTIC locking instead:

try {
	doInTransaction(session -> {
		final Product product = 
			(Product) session.get(Product.class, 1L, new LockOptions(LockMode.OPTIMISTIC));

		executeSync(() -> {
			doInTransaction(_session -> {
				Product _product = (Product) _session.get(Product.class, 1L);
				assertNotSame(product, _product);
				_product.setPrice(BigDecimal.valueOf(14.49));
			});
		});

		OrderLine orderLine = new OrderLine(product);
		session.persist(orderLine);
	});
	fail("It should have thrown OptimisticEntityLockException!");
} catch (OptimisticEntityLockException expected) {
	LOGGER.info("Failure: ", expected);
}

The new test version generates the following output:

#Alice selects a Product
Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} 

#The price engine selects the Product as well
Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} 
#The price engine changes the Product price
Query:{[update product set description=?, price=?, version=? where id=? and version=?][USB Flash Drive,14.49,1,1,0]} 
#The price engine transaction is committed
DEBUG [pool-1-thread-1]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

#Alice inserts an OrderLine
Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} 
#Alice transaction verifies the Product version
Query:{[select version from product where id =?][1]} 
#Alice transaction is rolled back due to Product version mismatch
INFO  [main]: c.v.h.m.l.c.LockModeOptimisticTest - Failure: 
org.hibernate.OptimisticLockException: Newer version [1] of entity [[com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.AbstractLockModeOptimisticTest$Product#1]] found in database

The operation flow goes like this:

ExplicitLockingLockModeOptimistic

The Product version is checked towards transaction end. Any version mismatch triggers an exception and a transaction rollback.

Race condition risk

Unfortunately, the application-level version check and the transaction commit are not an atomic operation. The check happens in EntityVerifyVersionProcess, during the before-transaction-commit stage:

public class EntityVerifyVersionProcess implements BeforeTransactionCompletionProcess {
	private final Object object;
	private final EntityEntry entry;

	/**
	 * Constructs an EntityVerifyVersionProcess
	 *
	 * @param object The entity instance
	 * @param entry The entity's referenced EntityEntry
	 */
	public EntityVerifyVersionProcess(Object object, EntityEntry entry) {
		this.object = object;
		this.entry = entry;
	}

	@Override
	public void doBeforeTransactionCompletion(SessionImplementor session) {
		final EntityPersister persister = entry.getPersister();

		final Object latestVersion = persister.getCurrentVersion( entry.getId(), session );
		if ( !entry.getVersion().equals( latestVersion ) ) {
			throw new OptimisticLockException(
					object,
					"Newer version [" + latestVersion +
							"] of entity [" + MessageHelper.infoString( entry.getEntityName(), entry.getId() ) +
							"] found in database"
			);
		}
	}
}

The AbstractTransactionImpl.commit() method call, will execute the before-transaction-commit stage and then commit the actual transaction:

@Override
public void commit() throws HibernateException {
	if ( localStatus != LocalStatus.ACTIVE ) {
		throw new TransactionException( "Transaction not successfully started" );
	}

	LOG.debug( "committing" );

	beforeTransactionCommit();

	try {
		doCommit();
		localStatus = LocalStatus.COMMITTED;
		afterTransactionCompletion( Status.STATUS_COMMITTED );
	}
	catch (Exception e) {
		localStatus = LocalStatus.FAILED_COMMIT;
		afterTransactionCompletion( Status.STATUS_UNKNOWN );
		throw new TransactionException( "commit failed", e );
	}
	finally {
		invalidate();
		afterAfterCompletion();
	}
}

Between the check and the actual transaction commit, there is a very short time window for some other transaction to silently commit a Product price change.

Conclusion

The explicit OPTIMISTIC locking strategy offers a limited protection against stale state anomalies. This race condition is a typical case of Time of check to time of use data integrity anomaly.

In my next article, I will explain how we can save this example using an optimistic-to-pessimistic lock upgrade technique.

Code available on GitHub.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

How to get a 10,000 points StackOverflow reputation

How it all started

In spring 2014, I initiated the Hibernate Master Class project, focusing on best practices and well-established usage patterns. I then realized that all my previous Hibernate experience wouldn’t be enough for this task. I needed more than that.

Hibernate has a very steep learning curve and tens of new StackOverflow questions are being asked on a daily basis. With so many problems waiting to be solved, I came to realize this was a great opportunity to prove my current skills, while learning some new tricks.

On 8th of May 2014, I gave my very first StackOverflow answer. After 253 days, on 16th of January 2015, I managed to get a reputation of over 10,000:

meta_10k_stackoverflow

StackOveflow facts

StackExchange offers a data query tool to analyze anything you can possible think of. Next I’m going to run some queries against my own account and four well renowned users:

User Reputation Answers
Jon Skeet 743,416 30,812
Peter Lawrey 251,229 10,663
Tomasz Nurkiewicz 152,139 2,964
Lukas Eder 55,208 1077
Vlad Mihalcea 10,018 581

Accepted answers reputation

The accepted answer ratio tells us how much you can count on the OP (question poster) to accept your answers:

User Average acceptance ratio Average acceptance reputation
[Ratio x 15]
Jon Skeet 60.42% 9.06
Peter Lawrey 28,90% 4.35
Tomasz Nurkiewicz 53,91% 8,08
Lukas Eder 46,69% 7.00
Vlad Mihalcea 37,36% 5.60

The chance of having your answer accepted rarely surpasses the 60% rate, so don’t count too much on this one. Some OP will never accept your answer, even if it’s the right one and it has already generated a high score.

Lesson 1: Don’t get upset if your answer was not accepted, and think of your answer as a contribution to our community rather than a gift to the question author.

Up-votes reputation

Another interesting metric is the answer score graph:

meta-10k-upvote-stackoverflow

The average answer score is a good indicator of your overall answer effectiveness, as viewed by the whole community:

User Average Score Average score reputation
[Ratio x 10]
Jon Skeet 8.16 81.6
Peter Lawrey 2.50 25
Tomasz Nurkiewicz 4.67 46.7
Lukas Eder 4.25 42.5
Vlad Mihalcea 0.75 7.5

While the answer acceptance is a one time event, up-voting can be a recurring action. A good answer can increase your reputation, long after you’ve posted your solution.

Lesson 2: Always strive for getting high quality answers. Even if they don’t get accepted, someone else might find it later and thank you with an up-vote.

Bounty hunting reputation

I’ve been a bounty hunter from the very beginning and the bounty contribution query proves why I happen to favor featured questions over regular ones:

User Bounty Count Total bounty reputation Average bounty reputation
Jon Skeet 67 8025 119
Tomasz Nurkiewicz 2 100 50
Peter Lawrey 4 225 56
Lukas Eder 2 550 275
Vlad Mihalcea 36 2275 63

To place a bounty, you have to be willing to deduct your own reputation, so naturally the question is both challenging and rewarding. The featured questions have a dedicated tab, therefore getting much more traction than regular ones, increasing the up-vote chance as well.

Lesson 3: Always favor bounty questions over regular ones.

Reputation is a means not a goal

The reputation alone is just a community contribution indicator and you should probably care more about tag badges instead. The tag badges prove one’s expertise in a certain technology, and it’s the fairest endorsement system currently available in the software industry.

If you want to become an expert in a particular area, I strongly recommend you trying to get a gold badge on that topic. The effort of earning 1000 up-votes will get you more than a virtual medal on your StackOverflow account. You will get to improve your problem-solving skills and make a name for yourself in the software community.

As I said it before:

When you answer a question you are reiterating your knowledge. Sometimes you only have a clue, so you start investigating that path, which not only provides you the right answer but it also allows you to strengthen your skills. It’s like constant rehearsing.

Conclusion

If you cannot image developing software without the helping hand of the StackOverflow knowledge base, then you should definitely start contributing.

In the end, the occasional “Thank you, it works now!” is much more rewarding than even a 10,000 points reputation.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

A beginner’s guide to Java Persistence locking

Implicit locking

In concurrency theory, locking is used for protecting mutable shared data against hazardous data integrity anomalies. Because lock management is a very complex problem, most applications rely on their data provider implicit locking techniques.

Delegating the whole locking responsibility to the database system can both simplify application development and prevent concurrency issues, such as deadlocking. Deadlocks can still occur, but the database can detect and take safety measures (arbitrarily releasing one of the two competing locks).

Physical locks

Most database systems use shared (read) and exclusive (write) locks, attributed to specific locking elements (rows, tables). While physical locking is demanded by the SQL standard, the pessimistic approach might hinder scalability.

Modern databases have implemented lightweight locking techniques, such as multiversion concurrency control.

The implicit database locking is hidden behind the transaction isolation level configuration. Each isolation level comes with a predefined locking scheme, aimed for preventing a certain set of data integrity anomalies.

READ COMMITTED uses query-level shared locks and exclusive locks for the current transaction modified data. REPEATABLE READ and SERIALIZABLE use transaction-level shared locks when reading and exclusive locks when writing.

Logical locks

If database locking is sufficient for batch processing systems, a multi-request web flow spans over several database transactions. For long conversations, a logical (optimistic) locking mechanism is much more appropriate.

Paired with a conversation-level repeatable read storage, optimistic locking can ensure data integrity without trading scalability.

JPA supports both optimistic locking and persistence context repeatable reads, making it ideal for implementing logical transactions.

Explicit locking

While implicit locking is probably the best choice for most applications concurrency control requirements, there might be times when you want a finer-grained locking strategy.

Most database systems support query-time exclusive locking directives, such as SELECT FOR UPDATE or SELECT FOR SHARE. We can therefore use lower level default isolation levels (READ COMMITTED), while requesting share or exclusive locks for specific transaction scenarios.

Most optimistic locking implementations verify modified data only, but JPA allows explicit optimistic locking as well.

JPA locking

As a database abstraction layer, JPA can benefit from the implicit locking mechanisms offered by the underlying RDBMS. For logical locking, JPA offers an optional automated entity version control mechanism as well.

JPA supports explicit locking for the following operations:

Explicit lock types

The LockModeType contains the following optimistic and pessimistic locking modes:

Lock Mode Type Description
NONE In the absence of explicit locking, the application will use implicit locking (optimistic or pessimistic)
OPTIMISTIC Always issues a version check upon transaction commit, therefore ensuring optimistic locking repeatable reads.
READ Same as OPTIMISTIC.
OPTIMISTIC_FORCE_INCREMENT Always increases the entity version (even when the entity doesn’t change) and issues a version check upon transaction commit, therefore ensuring optimistic locking repeatable reads.
WRITE Same as OPTIMISTIC_FORCE_INCREMENT.
PESSIMISTIC_READ A shared lock is acquired to prevent any other transaction from acquiring a PESSIMISTIC_WRITE lock.
PESSIMISTIC_WRITE An exclusive lock is acquired to prevent any other transaction from acquiring a PESSIMISTIC_READ or a PESSIMISTIC_WRITE lock.
PESSIMISTIC_FORCE_INCREMENT A database lock is acquired to prevent any other transaction from acquiring a PESSIMISTIC_READ or a PESSIMISTIC_WRITE lock and the entity version is incremented upon transaction commit.

Lock scope and timeouts

JPA 2.0 defined the javax.persistence.lock.scope property, taking one of the following values:

  • NORMAL

    Because object graphs can span to multiple tables, an explicit locking request might propagate to more than one table (e.g. joined inheritance, secondary tables).

    Because the entire entity associated row(s) are locked, many-to-one and one-to-one foreign keys will be locked as well but without locking the other side parent associations. This scope doesn’t propagate to children collections.

  • EXTENDED

    The explicit lock is propagated to element collections and junction tables, but it doesn’t lock the actual children entities. The lock is only useful for protecting against removing existing children, while permitting phantom reads or changes to the actual children entity states.

JPA 2.0 also introduced the javax.persistence.lock.timeout property, allowing us to configure the amount of time (milliseconds) a lock request will wait before throwing a PessimisticLockException.

Hibernate locking

Hibernate supports all JPA locking modes and some additional specific locking options. As with JPA, explicit locking can be configured for the following operations:

The LockModeConverter takes care of mapping JPA and Hibernate lock modes as follows:

Hibernate LockMode JPA LockModeType
NONE NONE
OPTIMISTIC
READ
OPTIMISTIC
OPTIMISTIC_FORCE_INCREMENT
WRITE
OPTIMISTIC_FORCE_INCREMENT
PESSIMISTIC_READ PESSIMISTIC_READ
PESSIMISTIC_WRITE
UPGRADE
UPGRADE_NOWAIT
UPGRADE_SKIPLOCKED
PESSIMISTIC_WRITE
PESSIMISTIC_FORCE_INCREMENT
FORCE
PESSIMISTIC_FORCE_INCREMENT

The UPGRADE and FORCE lock modes are deprecated in favor of PESSIMISTIC_WRITE.

UPGRADE_NOWAIT and UPGRADE_SKIPLOCKED use an Oracle-style select for update nowait or select for update skip locked syntax respectively.

Lock scope and timeouts

Hibernate also defines scope and timeout locking options:

  • scope

    The lock scope allows explicit locking cascade to owned associations.

  • timeout

    A timeout interval may prevent a locking request from waiting indefinitely.

In my next articles, I am going to unravel different explicit locking design pasterns, so stay tuned!

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, consider following my blog.

Why you should pay developers to learn

A true story

We were having a meeting with a customer and he had just presented a project idea. He wanted us to give him a draft system architecture, supporting his project technical requirements. At one point, I was telling him that incremental development requires architecture evolution as well.

When I said that finding the right architecture is also a learning process, he cut me off short with the following sentence:

Do you expect me to pay you to learn?

To save the day, I told him I was referring to the business domain, we needed to fully understand in order to provide the right architecture.

Do you want your project be developed by an unskilled team?

Unless you hire a highly expensive consultant, chances are you need a software development team for more than a few months. If the project spans over a year or more, how would you feel about a team that never has time to level up?

Software development is one of the most knowledge-driven industry, yet many expect developers to be readily equipped with everything it takes to solve any given problem.

Languages keep on evolving. Relational databases keep on adding new features. There’s a plethora of NoSQL databases most have never ever worked with. Successful frameworks keep on releasing new versions. New techniques emerge (e.g. reactive programming or micro services), while others keep on getting more and more traction (e.g. functional programming).

To master all these technologies and techniques you need to spend a considerable amount of time.

When are developers supposed to level up?

There are extremely passionate developers dedicating their spare time to reading books or technical articles or studying new technologies, but they are an exception to a rule.

Most developers acquire all knowledge during their job and if you don’t invest in their skills they will never grow within your team.

The right place and time to learn software is during your job.

Unfortunately, not everybody in the software industry shares this vision of mine. Business owners don’t want to spend resources (time and money) on training developers.

I really believe it’s a matter of perspective. If you don’t manage to get any direct or indirect revenue, you might be tempted to think you’re wasting money. But if you plan it properly, you can easily turn it into a very profitable investment.

Learn for profit

High quality software demands solid knowledge and expertize, but accumulating skills requires more than just reading. You need to become an active learner to maximize knowledge acquiring.

I used to be a passive learner myself, only reading books and articles while constantly having the impression that I don’t actually make too much progress.

When I started writing this blog, I realized I was now learning through teaching.

When I became an active StackOverflow user, this feeling was reassured.

When I started an open-source project, I finally realized that learning is only a side effect of hard working.

All these examples are what active learning is all about.

From the business perspective, it’s not difficult to foresee where the return of investment might come from:

  • A more skilled development team can leverage more complex projects with a lower risk of failure.
  • You can master a certain technology and start offering professional training and consultancy services
  • You can write books and sell them through a self-publishing program. Ninja Squad’s AngularJS book (French) was a profitable investment after all.

All in all, expertize always sells.

Investing in developement skills can definitely payoff. Many developers enjoy a working environment where they can grow, so this move can actually be beneficial for employee retention as well.

Starting on this journey is not as difficult as one might think, and I’m going to present some of my favorites active learning activities:

Preparing a training material

Let’s say you want to acquire a certain key technology skill in your company. Some developers should be partially allocated for studying and preparing a training material on this subject.

A workshop is always a better choice than a simple presentation. When the training material is ready, you have accumulated both knowledge and a training base too. You can now start offering training or consultancy services on that particular technology.

A company blog

Every software company accumulates experience, yet few of them actually share it with the rest. A company technical blog can be a great marketing instrument. A high quality blog can prove your domain knowledge and expertize.

You can build strategic partnerships with DZone or JavaCodeGeeks and therefore help promoting your business as well.

Answering StackOverflow questions

Contributing to StackOverflow is totally underrated. If you really want to become an expert into a certain domain, you should start answering question on that particular tag. When you answer a question you are reiterating your knowledge.

Sometimes you only have a clue, so you start investigating that path, which not only provides you the right answer but it also allows you to strengthen your skills. It’s like constant rehearsing.

After all, repetition is the mother of learning.

Contributing to open source projects

If you want to boost your design and coding skills, you should probably start contributing to open source projects. Browsing code can unveil certain patterns you’ve never previously applied.

Most frameworks authors are incredibly craftsmen and their code review can teach you a lot about best programming practices. If your company makes heavy use of a certain open source technology, it’s a great idea to start contributing back. The best way to deal with an annoying framework issue is to actually fix it.

Nobody knows a framework better than its own maintainers.

Writing and selling books

You can summarize all your experience in a book. Writing a book is very intense learning process as well. By the time you are done with it, you can really say you’ve come to master the subject.

Amazon offers self publishing programs and selling books can become an alternative revenue source and an advertisement as well.

Conclusion

Embracing learning can be a competitive advantage for your company. Your products carry your company name, and a software product quality mirrors the development team professionalism.

In the end, you are not only investing in individuals, you are investing in your own company as well.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, then you need to follow my blog.

Book review – How Linux Works 2nd edition

O’Reilly Reader Review Program

I heard about O’Reilly Reader Review Program after reading Thorben Janssen’s Java Performance: The Definitive Guide book review. After being admitted, I got a copy of How Linux Works, 2nd Edition. The book is great for anybody working in this field and you should totally read it. Next I’m going to tell you why.

The book

The book’s author is Brian Ward, who has a Ph.D. in computer science and has written several books about Linux Kernel, Vim and VMware. The book has 17 Chapters and covers many Linux aspects, from the Operating System architecture to Bash scripting and Package Managers.

Chapter 1

The first chapter is a very nice introduction of Linux architecture. You are going to learn about Linux abstraction layers and the clear difference between the kernel and the user space.

Chapter 2

This second chapter is very useful for Linux beginners to accommodate with some basic, yet extremely useful, Linux commands, utilities and shell pipes and filters. One very important aspect of Linux is the directory hierarchy, which you definitely have to know it, if you don’t want to get lost.

Chapter 3

The third chapter is dedicated to Linux devices. You’ll learn about the standard file-based device interface and the most useful dd command. The chapter covers in details all device types, from hard disks to USB and terminals.

Chapter 4

The forth chapter talks about disk partitions and various Linux File Systems. You’ll learn how to mount a device and how to partition it for both data and swap. The inode concept is very well explained too.

Chapter 5 and 6

The fifths and the six chapters are more advanced and so they require more time to understand what’s happening during kernel boot process and the user space initialization.

Chapter 7

This chapters is dedicated to system configuration. You’ll learn about the content of etc/ folder, as well as user management and cron tasks. This chapter is very useful for Linux beginners, since you’ll interact with them on a regular basis.

Chapter 8

This chapter is one of the most important one, since it covers everything you need to know about Linux processes. You will learn to use ps and lsof for both process and thread monitoring. From CPU to memory, you will learn that Linux offers a great variety of resource monitoring tools. Unless you are a .NET developer, there’s a great chance your applications get deployed on a Linux server, so skipping this chapter is not an option.

Chapter 9

This chapter is an introduction into networking and you can skip it if you already know networking basics. You can also learn about Linux routing, but unless you are a system administrator, you are not going to need this on your daily job.

Chapter 10

While the previous chapter was a more theoretical one, the tenth chapter is one you don’t want to miss. You are going to learn about network monitoring, using lsof, tcpdump and
port scanning. The network security is also a good read for every programmer as well as the socket section. The Unix socket domains and the Inter Process Communication (IPC) are very important aspects for every developer working with Linux.

Chapter 11 and 12

The eleventh chapter is dedicated to shell scripting and automating recurring tasks is not only a system administrator job. Learning a little about shell scripting can save you a lot of time and prevent accidental mistakes, so make sure you don’t skip it.

The twelfth chapter talks about file network access and the rsync section is very important, since there’s a great chance you’ll have to use it sooner or later.

Chapter 13

In this chapter you are going to learn about user environment configurations for both login and remote sessions.

Chapter 14

The fourteenth chapter is dedicated to Desktop environments, emphasizing the importance of X server and client utilities. You’ll also learn how to utilize window-based applications on a remote Linux server using X11 forwarding from within a SSH session.

Chapter 15 and 16

These chapters give you an introduction into C programming, from a Linux administration perspective. You will learn how to build a Linux package even without a package manager.

Chapter 17

The last chapter wraps everything up and reiterates the importance of Linux for both servers and embedded devices. Linux might not be easy in other domains of activity, but as a developer you have no excuse but to learn to use it.

Conclusion

I certainly recommend this book for every developer wanting to learn something more about Linux.

To master the command line, I also recommend the The Linux Command Line by William Shotts.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, then you need to follow my blog.

2014 – A year in review

Retrospective

January

In the beginning of 2014, I took the initial version of my time series MongoDB aggregation example and pass it through a multistage optimization process, from indexing to advanced data-modelling:

February

In February, I starting developing FlexyPool, the ultimate connection pool sizing utility. This was a great opportunity to dig into Queuing Theory and the following articles capture some of my findings:

May

After finishing FlexyPool, I decided investing in a Data knowledge stack, and so I started working on my Hibernate Master Class training material.

The Hibernate Master Class allowed me to dig into a great variety of JPA/Hibernate features, some of which are lesser known:

Almost at the time, I started answering Hibernate StackOverfow questions, and I accumulated a reputation on 8918 points.

August

In August, I was elected One of August’s Most Interesting Developers.

If you wonder what happened with my open-source Java Transactions Book, you can take a look on the Concurrency Control section of the Hibernate Master Class:

I decided to include my knowledge about transactions in the Master Class material, since you can’t separate transactions out of the run-time environment anyway.

September

In September my blog’s just turned one.

December

Although I didn’t win the Most interesting developer competition, I’m proud I managed to finish on the 3rd place.

2014 most viewed articles

WordPress has created a wonderful 2014 statistics report, but I promised I’d publish my top 5 posts, so there you have them:

Name Views
Hibernate Identity, Sequence and Table (Sequence) generator 5650
Time to break free from the SQL-92 mindset 4725
MongoDB and the fine art of data modelling 4251
The anatomy of Connection Pooling 3347
MongoDB 2.6 is $out 3297

Plans for 2015

I plan on finishing the Hibernate Master Class training and further complete the Data knowledge stack with other database access related technologies.

I want to get a Hibernate and a JPA gold badge on StackOverflow.

I want to read more books than I did in 2014.

If you have enjoyed reading my article and you’re looking forward to getting instant email notifications of my latest posts, you just need to follow my blog.