[JIRA] (HHH-16218) Natural id cache is extremely slow for entities with compound natural id
by Sylvain Dusart (JIRA)
Sylvain Dusart ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=63f6394... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZTQ4YzI3ZDQ1... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiZTQ4Yz... ) HHH-16218 ( https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiZTQ4Yz... ) Natural id cache is extremely slow for entities with compound natural id ( https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiZTQ4Yz... )
Change By: Sylvain Dusart ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=63f6394... )
Hibernate maintains a cache for id<->naturalId correspondances (pkToNaturalIdMap and naturalIdToPkMap maps in org.hibernate.engine.internal.NaturalIdResolutionsImpl.EntityResolutions#cache).
This cache is updated after creations and loads (at least, I did not test other use-cases) .
In the maps, the natural id is encapsulated in a ResolutionImpl instance and the hashcode is computed in the constructor :
{code:java}final int prime = 31;
int hashCodeCalculation = 1;
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.hashCode();
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.getNaturalIdMapping().calculateHashCode( naturalIdValue, persistenceContext.getSession() );{code}
For entities with a simple natural id, entityDescriptor.getNaturalIdMapping() is an instance of SimpleNaturalIdMapping with this calculateHashCode method :
{code:java}@Override
public int calculateHashCode(Object value) {
//noinspection unchecked
return value == null ? 0 : ( (JavaType<Object>) getJavaType() ).extractHashCode( value );
}{code}
For entities with a compound natural id, entityDescriptor.getNaturalIdMapping() is an instance of CompoundNaturalIdMapping with this calculateHashCode method :
{code:java}@Override
public int calculateHashCode(Object value) {
return 0;
}{code}
As a consequence, for a given entity with a compound natural id, the hashcodes of all ResolutionImpl objects used as keys in naturalIdToPkMap are the same, which creates collisions and consumes a lot of cpu cycles.
The calculateHashCode method in CompoundNaturalIdMapping should be something like
{code:java}@Override
public int calculateHashCode(Object value) {
return Arrays.hashCode((Object[]) value);
}{code}
to align with the areEqual method.
The attached test case creates 20000 objects in database then re-reads these objects.
It is done for two entity classes : EntityWithSimpleNaturalId and EntityWithCompoundNaturalId.
Logs before correction modification :
{code:java}16:33:31,782 INFO CompoundNaturalIdCacheTest:64 - Starting creations
16:33:53,745 INFO CompoundNaturalIdCacheTest:74 - Persisted 20000 EntityWithCompoundNaturalId objects, duration=21957ms
16:33:53,906 INFO CompoundNaturalIdCacheTest:83 - Persisted 20000 EntityWithSimpleNaturalId objects, duration=160ms
16:33:55,140 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId
16:34:12,915 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId, duration=17777ms
16:34:12,921 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId
16:34:13,021 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId, duration=99ms{code}
Logs after correction modification :
{code:java}16:35:37,950 INFO CompoundNaturalIdCacheTest:64 - Starting creations
16:35:38,247 INFO CompoundNaturalIdCacheTest:74 - Persisted 20000 EntityWithCompoundNaturalId objects, duration=292ms
16:35:38,368 INFO CompoundNaturalIdCacheTest:83 - Persisted 20000 EntityWithSimpleNaturalId objects, duration=119ms
16:35:39,679 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId
16:35:40,096 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId, duration=418ms
16:35:40,103 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId
16:35:40,232 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId, duration=129ms{code}
( https://hibernate.atlassian.net/browse/HHH-16218#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16218#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100216- sha1:64a4cf6 )
1 year, 10 months
[JIRA] (HHH-16218) Natural id cache is extremely slow for entities with compound natural id
by Sylvain Dusart (JIRA)
Sylvain Dusart ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=63f6394... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiYjRmNTY1YmY1... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiYjRmNT... ) HHH-16218 ( https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiYjRmNT... ) Natural id cache is extremely slow for entities with compound natural id ( https://hibernate.atlassian.net/browse/HHH-16218?atlOrigin=eyJpIjoiYjRmNT... )
Change By: Sylvain Dusart ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=63f6394... )
Hibernate maintains a cache in the session for id<->naturalId correspondances (pkToNaturalIdMap and naturalIdToPkMap maps in org.hibernate.engine.internal.NaturalIdResolutionsImpl.EntityResolutions#cache).
This cache is updated after creations and loads (at least, I did not test other use-cases) .
In the maps, the natural id is encapsulated in a ResolutionImpl instance and the hashcode is computed in the constructor :
{code :java }
final int prime = 31;
int hashCodeCalculation = 1;
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.hashCode();
hashCodeCalculation = prime * hashCodeCalculation + entityDescriptor.getNaturalIdMapping().calculateHashCode( naturalIdValue, persistenceContext.getSession() );
{code}
For entities with a simple natural id, entityDescriptor.getNaturalIdMapping() is an instance of SimpleNaturalIdMapping with this calculateHashCode method :
{code :java }
@Override
public int calculateHashCode(Object value) {
//noinspection unchecked
return value == null ? 0 : ( (JavaType<Object>) getJavaType() ).extractHashCode( value );
}
{code}
For entities with a compound natural id, entityDescriptor.getNaturalIdMapping() is an instance of CompoundNaturalIdMapping with this calculateHashCode method :
{code :java }
@Override
public int calculateHashCode(Object value) {
return 0;
}
{code}
As a consequence, for a given entity with a compound natural id, the hashcodes of all ResolutionImpl objects used as keys in naturalIdToPkMap are the same, which creates collisions and consumes a lot of cpu cycles.
The calculateHashCode method in CompoundNaturalIdMapping should be
{code :java }
@Override
public int calculateHashCode(Object value) {
return Arrays.hashCode((Object[]) value);
}
{code}
to align with the areEqual method.
The attached test case creates 20000 objects in database then re-reads these objects.
It is done for two entity classes : EntityWithSimpleNaturalId and EntityWithCompoundNaturalId.
Logs before correction :
{code :java }
16:33:31,782 INFO CompoundNaturalIdCacheTest:64 - Starting creations
16:33:53,745 INFO CompoundNaturalIdCacheTest:74 - Persisted 20000 EntityWithCompoundNaturalId objects, duration=21957ms
16:33:53,906 INFO CompoundNaturalIdCacheTest:83 - Persisted 20000 EntityWithSimpleNaturalId objects, duration=160ms
16:33:55,140 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId
16:34:12,915 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId, duration=17777ms
16:34:12,921 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId
16:34:13,021 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId, duration=99ms
{code}
Logs after correction :
{code :java }
16:35:37,950 INFO CompoundNaturalIdCacheTest:64 - Starting creations
16:35:38,247 INFO CompoundNaturalIdCacheTest:74 - Persisted 20000 EntityWithCompoundNaturalId objects, duration=292ms
16:35:38,368 INFO CompoundNaturalIdCacheTest:83 - Persisted 20000 EntityWithSimpleNaturalId objects, duration=119ms
16:35:39,679 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId
16:35:40,096 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithCompoundNaturalId, duration=418ms
16:35:40,103 INFO CompoundNaturalIdCacheTest:115 - Loading at most 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId
16:35:40,232 INFO CompoundNaturalIdCacheTest:123 - Loaded 20000 instances of class org.hibernate.orm.test.mapping.naturalid.compound.CompoundNaturalIdCacheTest$EntityWithSimpleNaturalId, duration=129ms
{code}
( https://hibernate.atlassian.net/browse/HHH-16218#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16218#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100216- sha1:64a4cf6 )
1 year, 10 months