We have a Java application that uses MySQL, Hibernate (3.5.1-Final) and EHcache(1.2.3) for our 2nd level cache.
Our hibernate.properties isolation level is Read-committed isolation = 2
# 2-Read committed isolation
hibernate.connection.isolation=2
Under a high number of concurrent transactions, we're seeing an issue where certain collections (DB associations) when loaded will throw an ObjectNotFoundException and it appears that the 2nd level cache is returning an old copy of that collection.
We have many different types of transactions that access this collection (only reading) and only a couple that will add/delete items from it.
We don't see this issue under single transaction load or even moderate transaction load (10 - 20 concurrent connections).
For example we have a Character entity:
@Entity
@Table(name = "CHARACTERS")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Character extends AbstractCharacter implements Serializable {
...
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@OneToMany(mappedBy = "character", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<CharacterItem> items;
We are properly maintaining the object graph when deleting entities by removing them from the collection that they're contained in and calling session.delete().
character.getItems().remove(characterItem);
session.delete(characterItem);
We've tried changing the Set items; CacheConcurrencyStrategy from:
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<CharacterItem> items;
To
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<CharacterItem> items;
With no luck.
We don't use database locks instead we use optimistic concurrency control to catch and retry conflicting transactions.
The only 2 solutions we can see at this point is to:
Try to catch the ObjectNotFoundException and try to intelligently evict the collection (although there doesn't seem to be enough context in the exception)
Use the @NotFound(action=NotFoundAction.IGNORE) annotation on the items collection, which will ignore and not throw the ObjectNotFoundException (but we have concerns as to how this works with the 2nd level cache and ensure that it's looking at the proper data).
I wish there was a @NotFound(action=NotFoundAction.EVICT_2ND_LEVEL_CACHE_RELOAD) where it would evict that object from the cache and attempt to reload the collection.
We could also try changing the FetchyType from LAZY to EAGER but I want to try to understand the problem and choose the best solution that will provide that data in our transactions are consistent under high concurrency.