Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reliably tracking changes made by Hibernate

Tags:

I'm using a PostUpdateEventListener registered via

registry.appendListeners(EventType.POST_COMMIT_UPDATE, listener)

and a few other listeners in order to track changes made by Hibernate. This works perfectly, however, I see a problem there:

Let's say, for tracking some amount by id, I simply execute

 amountByIdConcurrentMap.put(id, amount);

on every POST_COMMIT_UPDATE (let's ignore other operations). The problem is that this call happens some time after the commit. So with two commits writing the same entity shortly one after the other, I can receive the events in the wrong order, ending up with the older amount stored.

  • Is this really possible or are the operations synchronized somehow?
  • Is there a way how to prevent or at least detect such situation?
like image 867
maaartinus Avatar asked Nov 12 '17 21:11

maaartinus


2 Answers

Two questions and a proposal later

  1. Are you sure, that you need this optimization. Why not fetch the amount as it is written to the database by querying there. What gives you reason to work with caching.

  2. How do you make sure, that the calculation of the amount before writing it to the database is properly synchronized, so that multiple threads or probably nodes do not use old data to calculate the amount and therefore overwrite the result of a later calculation?

I suppose you handle question number 2 right. Then you have to options:

  1. Pessimistic locking, that means that immediatly before the commit you can exclusively update your cache without concurrency issues.
  2. Optimistic locking: In that case you have a kind of timestamp or counter in your database-record you can also put into the cache together with the amount. This value you can use to find out, what the more recent value is.
like image 115
aschoerk Avatar answered Sep 22 '22 13:09

aschoerk


No, there are no ordering guarantees, so you'll have to take care to ensure proper synchronization manually.

If the real problem you are solving is caching of entity state and if it is suitable to use second-level cache for the entity in question, then you would get everything out of the box by enabling the L2 cache.

Otherwise, instead of updating the map from the update listeners directly, you could submit tasks to an Executor or messaging system that would asynchronously start a new transaction and select for update the amount for the given id from the database. Then update the map in the same transaction while holding the corresponding row lock in the db, so that map updates for the same id are done serially.

like image 31
Dragan Bozanovic Avatar answered Sep 19 '22 13:09

Dragan Bozanovic