[JIRA] (HHH-16827) Avoid useless locks in NaturalId second level cache region
by Guenther Demetz (JIRA)
Guenther Demetz ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiNDhmODI4ZDdk... ) / Improvement ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiNDhmOD... ) HHH-16827 ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiNDhmOD... ) Avoid useless locks in NaturalId second level cache region ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiNDhmOD... )
Change By: Guenther Demetz ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
When populating second-level-cache by concurrent threads and several threads are reading accidentally the same entities annotated with NaturalIdCache it easy comes to a lock escalation. This scenario is described in ticket [https://hibernate.atlassian.net/browse/HHH-16726|https://hibernate.atlass...].
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 ) {
// prevent identical re-cachings
return;
}{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 #[6834|https://github. com/hibernate/hibernate-orm/pull/6834]
( https://hibernate.atlassian.net/browse/HHH-16827#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16827#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#100227- sha1:8ffa416 )
1 year, 3 months
[JIRA] (HHH-16827) Avoid useless locks in NaturalId second level cache region
by Guenther Demetz (JIRA)
Guenther Demetz ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZGMzYWJjNTQ0... ) / Improvement ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiZGMzYW... ) HHH-16827 ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiZGMzYW... ) Avoid useless locks in NaturalId second level cache region ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiZGMzYW... )
Change By: Guenther Demetz ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
When populating second-level-cache by concurrent threads and several threads are reading accidentally the same entities annotated with NaturalIdCache it easy comes to a lock escalation. This scenario is described in ticket [https://hibernate.atlassian.net/browse/HHH-16726|https://hibernate.atlass...].
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 ) {
// prevent identical re-cachings
return;
} {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.
( https://hibernate.atlassian.net/browse/HHH-16827#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16827#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#100227- sha1:8ffa416 )
1 year, 3 months
[JIRA] (HHH-16827) Avoid useless locks in NaturalId second level cache region
by Guenther Demetz (JIRA)
Guenther Demetz ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiYjYyNDViODQx... ) / Improvement ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiYjYyND... ) HHH-16827 ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiYjYyND... ) Avoid useless locks in NaturalId second level cache region ( https://hibernate.atlassian.net/browse/HHH-16827?atlOrigin=eyJpIjoiYjYyND... )
Change By: Guenther Demetz ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
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.atlass...].
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.
( https://hibernate.atlassian.net/browse/HHH-16827#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16827#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#100227- sha1:8ffa416 )
1 year, 3 months
[JIRA] (HHH-16745) TransientObjectException when loading versioned entity from second-level cache with @OneToOne LAZY mapping
by Koen De Jaeger (JIRA)
Koen De Jaeger ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *commented* on HHH-16745 ( https://hibernate.atlassian.net/browse/HHH-16745?atlOrigin=eyJpIjoiZjZhZG... )
Re: TransientObjectException when loading versioned entity from second-level cache with @OneToOne LAZY mapping ( https://hibernate.atlassian.net/browse/HHH-16745?atlOrigin=eyJpIjoiZjZhZG... )
I tried but can’t reproduce it in a unit test
*unit test can't reproduce*
package org.hibernate.bugs;
import jakarta.persistence.*;
import org.hibernate.annotations.Generated;
import org.hibernate.generator.EventType;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.After;
import org.junit.Before;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import java.io.Serializable;
import java.util.Date;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
/**
* This template demonstrates how to develop a test case for Hibernate ORM, using the Java Persistence API.
*/
@DomainModel(annotatedClasses = {
JPAUnitTestCase.User.class,
JPAUnitTestCase.PAI.class,
})
@SessionFactory
public class JPAUnitTestCase {
private EntityManagerFactory entityManagerFactory;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory("templatePU");
}
@After
public void destroy() {
entityManagerFactory.close();
}
@BeforeAll
public void setUp(SessionFactoryScope scope) {
scope.inTransaction(session -> {
User user1 = new User("user 1");
session.persist(user1);
user1.setSavedBy(user1);
session.merge(user1);
var user2 = new User("user 2");
user2.setSavedBy(user1);
session.persist(user2);
var user3 = new User("user 3");
user3.setSavedBy(user2);
session.persist(user3);
PAI pai1 = new PAI();
pai1.setCreatedBy(user1);
pai1.setLastModifiedBy(user2);
session.persist(pai1);
PAI pai2 = new PAI();
pai2.setCreatedBy(user2);
pai2.setLastModifiedBy(user3);
session.persist(pai2);
});
}
@AfterAll
public void tearDown(SessionFactoryScope scope) {
scope.inTransaction(session -> {
session.createMutationQuery("delete from PAI").executeUpdate();
session.createMutationQuery("delete from User").executeUpdate();
});
}
@org.junit.jupiter.api.Test
public void testSelectNormalUser(SessionFactoryScope scope) {
scope.inTransaction(session -> {
var users = session.createQuery("select p from User p", User.class).getResultList();
assertThat(users.size()).isEqualTo(3);
});
}
@org.junit.jupiter.api.Test
public void testSelectNormalUserasd(SessionFactoryScope scope) {
scope.inTransaction(session -> {
var users = session.createQuery("select p from PAI p", User.class).getResultList();
assertThat(users.size()).isEqualTo(2);
});
}
@Table(name = "users")
@Entity(name = "User")
public static class User implements Serializable {
@Id
@SequenceGenerator(name = "user_seq", sequenceName = "user_seq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
private Long id;
@Column(updatable = false, insertable = false, columnDefinition = "varchar")
private String uuid;
@Column(unique = true)
private String username;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "saved_by")
private User savedBy;
@Version
private Date modified;
public User() {
}
public User(String data) {
this.username = data;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public User getSavedBy() {
return savedBy;
}
public void setSavedBy(User savedBy) {
this.savedBy = savedBy;
}
public Date getModified() {
return modified;
}
public void setModified(Date modified) {
this.modified = modified;
}
}
@Table(name = "pais")
@Entity(name = "PAI")
public static class PAI implements Serializable {
@Id
@SequenceGenerator(name = "procedure_allocation_instance_seq", sequenceName = "procedure_allocation_instance_seq", allocationSize = 1)
@GeneratedValue(generator = "procedure_allocation_instance_seq", strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne
private User createdBy;
@ManyToOne
private User lastModifiedBy;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public User getCreatedBy() {
return createdBy;
}
public void setCreatedBy(User createdBy) {
this.createdBy = createdBy;
}
public User getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(User lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
}
}
( https://hibernate.atlassian.net/browse/HHH-16745#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16745#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#100227- sha1:8ffa416 )
1 year, 3 months