OPTIMISTIC_FORCE_INCREMENT does not work on flush()

Hello,
I have noticed strange behavior. Although we load an entity with LockType: OPTIMISTIC_FORCE_INCREMENT which shall update the version even when there are no changes to entity, when we call flush() this is update is not being triggered.
Calling isDirty() on session returns also false when no change is made on entity (but FORCE lock present).

When we change a property, then flush() works as expected - entity gets its version updated.

I have created small reproducible example, please checkout the repo and run code:

stdout logs will show that version is not updated.

Is there something I don’t understand properly?
Looking forward for a discussion!
Regards

The increment happens just before the transaction commits to potentially reduce the time a lock is held on a row to the minimum possible. If you want the increment to happen immediately, use PESSIMISTIC_FORCE_INCREMENT.

Thank you for answering!
But is the lock really held? With OPTIMISTIC_FORCE_INCREMENT there is no db locking (thus optimistic).
By issuing PESSIMISTIC LockType there will be select for update, what I want to avoid with optimistic locking.
Regads

Like I wrote, the OPTIMISTIC_FORCE_INCREMENT lock will be acquired by incrementing the version just right before a transaction commits.

Right, but why isnt session marked Dirty then?
There is a change staged for execution…

You mean Session#isDirty returns false? The javadoc of that method say

In other words, would any DML operations be executed if we flushed this session?

And in fact, the session flushing does not produce any DML statements, so it’s correct. There are of course some pending operations bound to the transaction lifecycle, but isDirty is not the method to understand such operations.

Why is that important in the first place?

Well, before we use direct jdbc query in transaction, we want to flush() and clear() session to avoid any inconsistences in Hibernate’s L1 cache.
But this erases the scheduled version increment on locked objects.
I guess we have to go for manual version increment then

we want to flush() and clear() session to avoid any inconsistences in Hibernate’s L1 cache.

I don’t know what “inconsistencies” you’re referring to here, but Hibernate ORM will ensure that the version on the entity is incremented just like it is updated in the database before the transaction commits.

But this erases the scheduled version increment on locked objects.

I don’t know what that means. Please clarify.

I guess we have to go for manual version increment then

Like I wrote before, if you want the version increment to happen immediately, then use PESSIMISTIC_FORCE_INCREMENT.

Generally, when modifying sql is issued outside of hibernate cache, the cache should be cleared so susequent fetches from hibernate do not return stale data.
Well, even for select queries cache should be flushed at least so db and l1 cache is synchronised.
This is why we issue flush() clear() before running direct sqls in transaction.

I have prepared the reproduction example in my repository just to visualize that. So, in summary:

  1. We fetch entity with force optimistic increment lock
  2. We then do flush() clear()
  3. We comit transaction
  4. The version is not updated! It will get updated if we omit flush() clear()

Well, it is not about happening immediately but i found it strange that this version update was not making session dirty.
I can understand that isDirty does return false because the change is bound to happen on transaction commit exclusively. But then, why session clear() is removing this scheduled change?

Seems like you’re hitting this known issue: HHH-13688

According to the specification, this behavior is “valid”:

If an entity is removed before a deferred version update was to have been applied, the forced version update is omitted.

I understand the desire to have this working, but I’m not sure about the best solution. Maybe it would be better to invoke this force increment during flush time, but that might have a negative performance impact for users that flush without clearing.

If this is important to you, I’d suggest you join our Zulip chat platform and start a discussion about the topic.

1 Like

Thabks for explanation.
I think people may expect that clear() and isdirty() are somewgat synced. So if we say that session is not dirty, then clear does not affect much (whn it comes to losing information).

As I am using spring i am able to simulate wanted behaviour: in those places where forced lock is required, i am registering a TransactionSynchronisation which will apply version increment beforeCommit.

Thanks to our doscussion I’vr understood the problem and think that my proposed workaround is enough for now :wink: