Doing some heavy load-tests using Locust we discovered that performance goes down when multiple threads are filling 2ndLevel cache simultaneously with the same persistent objects. Although all threads have more or less the same transaction start timestamp, when it comes to checking the readability of a read-write cache item, the current logic makes following comparison:
{noformat}public boolean isReadable(long txTimestamp) { return txTimestamp > timestamp; }{noformat}
This implementation considers items unReadable also when the timestamp of current transaction *equals* to the timestamp of the cached item. *Important Update:* so this condidion considers even unReadable items which were previously cached by the current session ({{txTimestamp == timestamp}}) ! Why distrust ourself !?
My proposal is to change the condition to
{{ return txTimestamp >= timestamp;}}
so that a transaction which started simultaneously with the one that cached the item, can consider valid the puts of other transactions of same age. This has to be modified in class org.hibernate.cache.spi.support.AbstractReadWriteAccess.java
By overriding the used region factory the user can choose which granularity he wants for the timestamp comparison, for instance in our use-case we don’t want to go beyond fractions of seconds.
{noformat}public class MyJCacheRegionFactory extends JCacheRegionFactory {
// UNIT SECONDS public long nextTimestamp() { return System.currentTimeMillis() / 1000; } ... }{noformat}
Applying these 2 tweaks on hibernate5.6.15 I was able to enhance the performance in remarkable way: 100 threads accessing fresh (empty 2ndLevelCache) persistence at same time.
With
{{<property name="hibernate.cache.region.factory_class" value="JCacheRegionFactory"/>}}
Threads ran for 14.231 ms with over 247.000 {{isReadable}} calls returning false.
{{<property name="hibernate.cache.region.factory_class" value="MyJCacheRegionFactory"/>}}
Threads ran for 7.365 ms with 0 {{isReadable}} calls returning false. |
|