Shachar
Shachar

Reputation: 943

NHibernate session.flush() fails but makes changes

We have a SQL Server database table that consists of user id, some numeric value, e.g. balance, and a version column.

We have multiple threads updating this table's value column in parallel, each in its own transaction and session (we're using a session-per-thread model). Since we want all logical transaction to occur, each thread does the following:

  1. load the current row (mapped to a type).
  2. make the change to the value, based on old value. (e.g. add 50).
  3. session.update(obj)
  4. session.flush() (since we're optimistic, we want to make sure we had the correct version value prior to the update)
  5. if step 4 (flush) threw StaleStateException, refresh the object (with lockmode.read) and goto step 1

we only do this a certain number of times per logical transaction, if we can't commit it after X attempts, we reject the logical transaction.

each such thread commits periodically, e.g. after 100 successful logical transactions, to keep commit-induced I/O to manageable levels. meaning - we have a single database transaction (per transaction) with multiple flushes, at least once per logical change.

what's the problem here, you ask? well, on commits we see changes to failed logical objects. specifically, if the value was 50 when we went through step 1 (for the first time), and we tried to update it to 100 (but we failed since e.g. another thread changed it to 70), then the value of 50 is committed for this row. obviously this is incorrect.

What are we missing here?

Upvotes: 0

Views: 1283

Answers (4)

cws
cws

Reputation: 158

Here is the documention for exception in the session http://nhibernate.info/doc/nhibernate-reference/best-practices.html

Upvotes: 0

FelixM
FelixM

Reputation: 1506

If you want to make your changes anyway, why do you bother with row versioning? It sounds like you should get the same result if you simply always update the data and let the last transaction win.

As to why the update becomes permanent, it depends on what the SQL statements for the version check/update look like and on your transaction control, which you left out of the code example. If you turn on the Hibernate SQL logging it will probably become obvious how this is happening.

Upvotes: 1

Chris Shaffer
Chris Shaffer

Reputation: 32585

Well, I do not have a ton of experience here, but one thing I remember reading in the documentation is that if an exception occurs, you are supposed to immediately rollback the transaction and dispose of the session. Perhaps your issue is related to the session being in an inconsistent state?

Also, calling update in your code here is not necessary. Since you loaded the object in that session, it is already being tracked by nhibernate.

Upvotes: 2

Arnis Lapsa
Arnis Lapsa

Reputation: 47677

I'm not a nhibernate guru, but answer seems simple.

When nhibernate loads an object, it expects it not to change in db as long as it's in nhibernate session cache.

As you mentioned - you got multi thread app.

This is what happens=>

  • 1st thread loads an entity
  • 2nd thread loads an entity
  • 1st thread changes entity
  • 2nd thread changes entity and => finds out that loaded entity has changed by something else and being afraid that it has screwed up changes 1st thread made - throws an exception to let programmer be aware about that.

You are missing locking mechanism. Can't tell much about how to apply that properly and elegantly. Maybe Transaction would help.

We had similar problems when we used nhibernate and raw ado.net concurrently (luckily - just for querying - at least for production code). All we had to do - force updating db on insert/update so we could actually query something through full-text search for some specific entities.

Had StaleStateException in integration tests when we used raw ado.net to reset db. NHibernate session was alive through bunch of tests, but every test tried to cleanup db without awareness of NHibernate.

Upvotes: 0

Related Questions