When populating second-level-cache by concurrent threads and several threads read are reading accidentally the same entities annotated with NaturalIdCache it easy comes to a locking-nightmare lock escalation . This scenario is described in ticket [https://hibernate.atlassian.net/browse/HHH-16726|https://hibernate.atlassian.net/browse/HHH-16726|smart-link]. One of the reasons for the bad performance, is that when verifying if the shared cache already contains the key for the natural-id resolution, the current code calls
{noformat}if ( CacheHelper.fromSharedCache( s, cacheKey, cacheAccess ) != null ) {{noformat}
in order to prevent identical re-cachings (NaturalIdResolutionsImpl.java line 251) This is suboptimal and an overshoot for multiple reasons:
# {{fromSharedCache}} call does not only verify if the shared cache contains the cacheKey, it also tests if the item is readable by comparing session timestamps. If the item is found but considered ‘unreadable', then identical re-caching isn’t prevented. Even more bad: the re-caching attempted is done in vain: NaturaId items aren’t versioned so existing entries get never overriden.
{noformat}public boolean isWriteable(long txTimestamp, Object newVersion, Comparator versionComparator) { ... return version != null && versionComparator.compare( version, newVersion ) < 0;{noformat}
2. fromSharedCache call does return the item if found and 'readable', but we just need the information if the key is cached or not
3. fromSharedCache call does produce unnecessary read-locks in NaturalId-region and as conseguence of the useless re-caching attempts (point 1) also useless write-locks are done with putFromLoad
All this contributes to a huge amount of useless locks in NaturalId cache when it’s get populated. It can avoided by simply calling cacheAccess.contains(cacheKey), see linked PR. |
|