Disallow dynamic creation JCache Cache instances

Description

The JCacheRegionFactory will create anonymous, poorly configured caches if one wasn't registered with the JCache provider. Perhaps this was chosen to be similar to JCache's annotation support that does the same thing. By quietly doing bad things the initial experience might be nice in a demo, but production quality is harmed due to poor performance and memory leaks.

The default configuration requires that the cache serialize the key/value when it crosses the API boundary. Unless configured separately, the provider is most likely to use Java serialization for best compatibility and warn users to prefer a more appropriate (faster) mechanism in their documentation. As well documented, Java serialization is very slow.

The default configuration does not evict or expire entries, and does not enable any statistic monitoring. Therefore the unbounded cache may be hidden until discovered when investigating memory leaks.

Ehcache contributed the JCache adapter. In their samples they now explicitly disallow anonymous caches. Perhaps @henri_tremblay can speak on their behalf.

This request is to minimally add a logging statement warning the user when an anonymous cache is created. Preferably this behavior would not be allowed, like in the Ehcache sample, to avoid code that only benefits presenters and is harmful to actual users.

Activity

Show:

Sebastian Schneider June 12, 2018 at 2:50 PM

Sorry i was talking about 5.3.1 .FINAL, I just mixed something up.

MutualConfiguration was a fast test top check if it is working. For all undefined caches there ist the default config in jcache.xml but this isn't used with the original Code.
I Hope you can understand my Problem

Henri Tremblay June 12, 2018 at 2:35 PM

Are you talking about Hibernate 5.2.1.FINAL? Which is before the issue you are commenting in?

The quick answer is: Do not use new MutableConfiguration() because it gives default that you almost never want. Give some thoughts to that you need: "What size? Which storage? Which expiration"

Sebastian Schneider June 12, 2018 at 11:27 AM

Hi,
we have updated to Hibernate 2.5.1.FINAL and are facing the same problem as @Selaron.

In JCacheRegionFactory the method getOrCreateCache is only returning caches defined in jcache.xml but there is no call to cacheManager.createCache to create caches with the default template?
If you overwrite line 79 it is possible to start the application and there are caches created with the default template, for example:

final Cache<Object, Object> cache = cacheManager.getCache( qualifiedRegionName ); if ( cache == null ) { return cacheManager.createCache(qualifiedRegionName, new MutableConfiguration<>()); } return cache;

But what is the right way? the jcache.xml is found by hibernate (testet by renaming it) so there must be another problem or am I getting somthing wrong?

Jim Richards May 24, 2018 at 12:24 PM

Hi @Selaron,

So, I have the same versions of Hibernate and EHCache.

Things that probably matter are making sure jcache.xml is on the path. I have the file in src/main/resources and

pom.xml

<resources> <resource> <filtering>true</filtering> <directory>src/main/resources</directory> </resource> </resources>

And then in persistence.xml I have

persistence.xml

<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jcache.JCacheRegionFactory"/> <property name="hibernate.javax.cache.provider" value="org.ehcache.jsr107.EhcacheCachingProvider"/> <property name="hibernate.javax.cache.uri" value="classpath:jcache.xml"/>

I'm not sure about the creation of a cache for each object. In my testing I couldn't see any SQL queries hitting the database, and the cache statistics were returning mostly hits for the cache. I don't think there were explicit caches in EHCache 2.0 either.

Selaron May 24, 2018 at 10:43 AM

Jim Richards, thanks for your example - however I'm not able to get it to work and keep getting that new CacheException( "On-the-fly creation of JCache Cache objects is not supported...").

Hibernate creates one cacheRegion for each and every entity class and OneToMany relationship property.
Do I have to statically configure (and maintain) a <cache> for each region?

It seemes that the only reference to the default cache template is held by the ConfigurationMerger which is consulted in org.ehcache.jsr107.Eh107CacheManager.createCache(String, C) only.
But there is no actual reference to org.ehcache.jsr107.Eh107CacheManager.createCache(String, C) nor javax.cache.CacheManager.createCache(String, C) and thus the configured templaed is never used.

I'm testing this with Hibernate 5.3.0.Final and EHCache 3.5.2.

hibernate.cache.region.factory_class = cache
hibernate.javax.cache.uri = classpath:ehcache.xml

Jim Richards April 29, 2018 at 11:24 AM

As an example of what's need to get it running again, you can use something like this to avoid startup breaking, as documented in issue HHH-12531 - JCache existing cache not detected.

<?xml version="1.0" encoding="UTF-8"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.ehcache.org/v3" xmlns:jsr107="http://www.ehcache.org/v3/jsr107" xsi:schemaLocation=" http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext.xsd"> <!-- https://hibernate.atlassian.net/browse/HHH-12531 --> <service> <jsr107:defaults enable-management="true" enable-statistics="true" default-template="default" /> </service> <cache alias="org.hibernate.cache.spi.QueryResultsRegion"> <expiry> <tti unit="seconds">300</tti> </expiry> <heap>1024</heap> </cache> <cache alias="org.hibernate.cache.spi.TimestampsRegion"> <expiry> <none /> </expiry> <heap>4096</heap> </cache> <cache-template name="default"> <expiry> <tti unit="seconds">300</tti> </expiry> <heap>1024</heap> </cache-template> </config>

Jim Richards April 28, 2018 at 7:17 AM

This change now breaks current code with

Caused by: org.hibernate.cache.CacheException: On-the-fly creation of JCache Cache objects is not supported [org.hibernate.cache.spi.TimestampsRegion]

It would be good to have documentation on how to configure ehcache.xml, etc. to provide a good set of defaults for the cache.

Ben Manes March 15, 2018 at 3:43 PM

I don't recall the implementation here, but I do know that the spec's annotation support is prone to race conditions when creating anonymous caches. When it fails to find a cache and then creates one, this may fail when a concurrent call creates one first. The result is is an unhandled exception being thrown due to CacheManager lacking a getOrCreate(...) method for this purpose. It requires more care to make anonymous caches work, despite always being undesirable, then to disallow them entirely.

Sorry for not replying on the earlier ticket. I also don't think the JSR should have defined any configuration and hadn't thought it would be helpful to only contribute negatively.

Steve Ebersole March 15, 2018 at 3:42 PM

Yep, I agree we should support extending - we'd just error by default.

Henri Tremblay March 15, 2018 at 3:24 PM
Edited

Fine with me. Spring-cache does something similar. AbstractCacheResolver will throw an exception when a cache is not found. And they provide a way to correctly create the cache on request with AbstractCacheManager.getMissingCache. The default returns null. It is the equivalent of JCacheRegionFactory.createCache.

Steve Ebersole March 15, 2018 at 3:08 PM

I will make that change in hibernate-jcache to error when an existing Cache cannot be obtained from the CacheManager.

Good?

Fixed

Details

Assignee

Reporter

Fix versions

Priority

Created August 30, 2017 at 12:48 AM
Updated July 4, 2018 at 1:59 PM
Resolved March 23, 2018 at 3:54 AM