I'm implementing Non-repeatable read isolation level on WildFly 9.0.2 in a @Stateless
rest resource
Account
entity, prints the balance and then do some other work (sleep).Account
entity, prints the balance and calculates the balance via calculateBalance()
method and then update the entity. It reads the entity again and prints out the balance.According to my understanding of the Non-Repeatable read level Thread B should block until Thread A completely finish (Exit the transaction/Stateless rest resource).
Here is the printout:
From that I can see Thread B is not blocking and is allowed to run even though Thread A is still busy.
Below is the code:
@GET
@Path("/{accountId}/{threadName}")
public Response calculcateBalance(@PathParam("accountId") Long accountId, @PathParam("threadName") String threadName) {
Account account = em.find(Account.class, accountId);
printBalance(account,threadName);
if ("ThreadA".equals(threadName)) {
sleepSeconds(10);
} else if ("ThreadB".equals(threadName)) {
account.calculateBalance();
em.merge(account);
}
account = em.find(Account.class, accountId);
printBalance(account,threadName);
return Response.ok().build();
}
If I change the isolation level to Serializable everything blocks.
Is my understanding of Non-repeatable read wrong? Should Thread B not be blocked until Thread A is finished?
It depends on the underlying database system. If you were using SQL Server, which uses 2PL by default, the Thread A would acquire a shared lock upon reading that row and the Thread B would be preventing from writing that row (until Thread A releases the shared lock).
Oracle, PostgreSQL, and MySQL use MVCC and the Repeatable Read doesn't use locking because readers don't block writers and writers don't block readers. In MVCC, anomalies are detected and if Thread B modifies that row, then Thread A would detect that change and would abort its transactions.
So, in MVCC, anomalies are detected rather than prevented.