Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Variable transaction isolation levels by request

I am writing a little auction app, and it is very important that my bids are recorded with certainty. After all, the last couple seconds of the auction are critical moments for the buyers, and I can't risk them simultaneously bidding and having a race condition.

And of course, that's what transaction isolation is for. I can set my isolation level to serializeable, and we're all set.

But what about all the other requests? If people are viewing profiles, or sending messages, these requests don't need anywhere near that kind of transaction isolation. A read committed isolation level is perfectly acceptable for those requests.

I'm setting my transaction level as part of my hibernate property hibernate.connection.isolation, but I'd really like to be able to do something like session.setTransactionIsolation(newIsolation) per request.

like image 984
corsiKa Avatar asked Apr 14 '15 17:04

corsiKa


People also ask

How do you determine transaction isolation level?

To check the isolation level(s) present in the statement, the simplest way is to look at the T-SQL itself and see if any hints are present. If not, it is operating at the isolation level of the connection.

What are the four transaction isolation levels?

InnoDB offers all four transaction isolation levels described by the SQL:1992 standard: READ UNCOMMITTED , READ COMMITTED , REPEATABLE READ , and SERIALIZABLE .

How do I change the transaction isolation level?

To set the transaction isolation level, use an ISOLATION LEVEL level clause. It is not permitted to specify multiple ISOLATION LEVEL clauses in the same SET TRANSACTION statement. The default isolation level is REPEATABLE READ . Other permitted values are READ COMMITTED , READ UNCOMMITTED , and SERIALIZABLE .

Which transaction isolation level is the strictest?

Repeatable Read isolation (ANSI Serializable and ANSI Repeatable Read) is the strictest isolation level.


3 Answers

If you're using Spring you can use something like this:

@Transactional(isolation = Isolation.SERIALIZABLE) 

and it works for the JpaTransactionManager. If you are using JtaTransactionManager the request-scope transaction isolation is not propagated, as this is the default JTA behavior.

Because JTA doesn’t support transaction-scoped isolation levels, Spring offers the IsolationLevelDataSourceRouter to overcome this shortcoming when using application server JTA DataSources.

Because most DataSource implementations can only take a default transaction isolation level, we can have multiple such DataSources, each one serving connections for a specific transaction isolation level.

The logical transaction (e.g. @Transactional) isolation level setting is introspected by the IsolationLevelDataSourceRouter and the connection acquire request is therefore delegated to a specific DataSource implementation that can serve a JDBC Connection with the same transaction isolation level setting.

So, even in JTA environments, the transaction isolation router can offer a vendor-independent solution for overriding the default database isolation level on a per transaction basis.

Java EE doesn't support method-level transaction isolation configuration.

The SERIALIZABLE isolation level will protect you against non-repeatable reads and phantom reads, and even SERIALIZABLE doesn't protect you against lost updates across multiple-request logical transactions.

Optimistic locking6 scales better when using the detached entities (as of they were loaded when the logical transaction has started).

like image 196
Vlad Mihalcea Avatar answered Sep 25 '22 15:09

Vlad Mihalcea


Session session = getSession(dataSource, sessionFactory, Connection.TRANSACTION_SERIALIZABLE);

public Session getSession(DataSource dataSource, SessionFactory sessionFactory, int isolationLevel){

  // Get connection from current dataSource and set new isolation
  Connection connectionWithNewIsolation = dataSource.getConnection();
  connectionWithNewIsolation.setTransactionIsolation(isolationLevel);

  // Get session from current sessionFactory with the new isolation
  Session session = sessionFactory.openSession(connectionWithNewIsolation);

  // Hibernate 4.3
  //SessionFactory.openStatelessSession(Connection connection)
  // Hibernate 3.6
  //SessionFactory.openSession(Connection connection)
  //SessionFactory.openStatelessSession(Connection connection)

  return session;
}
like image 20
jjcosare Avatar answered Sep 26 '22 15:09

jjcosare


For this case, i will use Optimistic lock in your bids objects... the race condition will still occurs, but it will be detected when the transaction tries to commit the changes at your domain objects (throwing an exception if the version readed was updated by another thread).

So any change on any bid object, will be almost serializable (i say "almost" because in order to be serializable, the failed transactions will need to be catched and retried somehow).

like image 37
Carlitos Way Avatar answered Sep 25 '22 15:09

Carlitos Way