From caed6737201f161b216f5a13012c34b7dda0491a Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sun, 15 Dec 2024 17:09:07 +0100 Subject: [PATCH] HHH-18942 JFR events for collection recreate/update/remove --- .../internal/CollectionRecreateAction.java | 18 +++- .../internal/CollectionRemoveAction.java | 30 +++--- .../internal/CollectionUpdateAction.java | 73 +++++++------- .../event/internal/EmptyEventManager.java | 33 +++++++ .../org/hibernate/event/spi/EventManager.java | 12 +++ .../jfr/internal/CollectionRecreateEvent.java | 42 ++++++++ .../jfr/internal/CollectionRemoveEvent.java | 42 ++++++++ .../jfr/internal/CollectionUpdateEvent.java | 42 ++++++++ .../event/jfr/internal/JfrEventManager.java | 96 +++++++++++++++++++ 9 files changed, 339 insertions(+), 49 deletions(-) create mode 100644 hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRecreateEvent.java create mode 100644 hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRemoveEvent.java create mode 100644 hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionUpdateEvent.java diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRecreateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRecreateAction.java index 8778ef0010..d6fa2201e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRecreateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRecreateAction.java @@ -7,7 +7,9 @@ package org.hibernate.action.internal; import org.hibernate.HibernateException; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.spi.EventManager; import org.hibernate.event.spi.EventSource; +import org.hibernate.event.spi.HibernateMonitoringEvent; import org.hibernate.event.spi.PostCollectionRecreateEvent; import org.hibernate.event.spi.PostCollectionRecreateEventListener; import org.hibernate.event.spi.PreCollectionRecreateEvent; @@ -42,14 +44,26 @@ public final class CollectionRecreateAction extends CollectionAction { final PersistentCollection collection = getCollection(); preRecreate(); final SharedSessionContractImplementor session = getSession(); - getPersister().recreate( collection, getKey(), session); + final CollectionPersister persister = getPersister(); + final Object key = getKey(); + final EventManager eventManager = session.getEventManager(); + final HibernateMonitoringEvent event = eventManager.beginCollectionRecreateEvent(); + boolean success = false; + try { + persister.recreate( collection, key, session ); + success = true; + } + finally { + eventManager.completeCollectionRecreateEvent( event, key, persister.getRole(), success, session ); + } + session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); evict(); postRecreate(); final StatisticsImplementor statistics = session.getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { - statistics.recreateCollection( getPersister().getRole() ); + statistics.recreateCollection( persister.getRole() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRemoveAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRemoveAction.java index 4496a8e19e..fbb4dcb9e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRemoveAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionRemoveAction.java @@ -8,7 +8,9 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.spi.EventManager; import org.hibernate.event.spi.EventSource; +import org.hibernate.event.spi.HibernateMonitoringEvent; import org.hibernate.event.spi.PostCollectionRemoveEvent; import org.hibernate.event.spi.PostCollectionRemoveEventListener; import org.hibernate.event.spi.PreCollectionRemoveEvent; @@ -108,8 +110,20 @@ public final class CollectionRemoveAction extends CollectionAction { // is replaced by null or a different collection // (if the collection is uninitialized, Hibernate has no way of // knowing if the collection is actually empty without querying the db) - getPersister().remove( getKey(), session ); + final CollectionPersister persister = getPersister(); + final Object key = getKey(); + final EventManager eventManager = session.getEventManager(); + final HibernateMonitoringEvent event = eventManager.beginCollectionRemoveEvent(); + boolean success = false; + try { + persister.remove( key, session ); + success = true; + } + finally { + eventManager.completeCollectionRemoveEvent( event, key, persister.getRole(), success, session ); + } } + final PersistentCollection collection = getCollection(); if ( collection != null ) { session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); @@ -130,12 +144,7 @@ public final class CollectionRemoveAction extends CollectionAction { } private PreCollectionRemoveEvent newPreCollectionRemoveEvent() { - return new PreCollectionRemoveEvent( - getPersister(), - getCollection(), - eventSource(), - affectedOwner - ); + return new PreCollectionRemoveEvent( getPersister(), getCollection(), eventSource(), affectedOwner ); } private void postRemove() { @@ -145,12 +154,7 @@ public final class CollectionRemoveAction extends CollectionAction { } private PostCollectionRemoveEvent newPostCollectionRemoveEvent() { - return new PostCollectionRemoveEvent( - getPersister(), - getCollection(), - eventSource(), - affectedOwner - ); + return new PostCollectionRemoveEvent( getPersister(), getCollection(), eventSource(), affectedOwner ); } public Object getAffectedOwner() { diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java index 25ca115ed2..350416d68c 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/CollectionUpdateAction.java @@ -8,7 +8,9 @@ import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.event.spi.EventManager; import org.hibernate.event.spi.EventSource; +import org.hibernate.event.spi.HibernateMonitoringEvent; import org.hibernate.event.spi.PostCollectionUpdateEvent; import org.hibernate.event.spi.PostCollectionUpdateEventListener; import org.hibernate.event.spi.PreCollectionUpdateEvent; @@ -45,7 +47,7 @@ public final class CollectionUpdateAction extends CollectionAction { @Override public void execute() throws HibernateException { - final Object id = getKey(); + final Object key = getKey(); final SharedSessionContractImplementor session = getSession(); final CollectionPersister persister = getPersister(); final PersistentCollection collection = getCollection(); @@ -54,34 +56,45 @@ public final class CollectionUpdateAction extends CollectionAction { preUpdate(); if ( !collection.wasInitialized() ) { - // If there were queued operations, they would have been processed - // and cleared by now. - // The collection should still be dirty. + // If there were queued operations, they would have + // been processed and cleared by now. if ( !collection.isDirty() ) { + // The collection should still be dirty. throw new AssertionFailure( "collection is not dirty" ); } - //do nothing - we only need to notify the cache... - } - else if ( !affectedByFilters && collection.empty() ) { - if ( !emptySnapshot ) { - persister.remove( id, session ); - } - } - else if ( collection.needsRecreate( persister ) ) { - if ( affectedByFilters ) { - throw new HibernateException( "cannot recreate collection while filter is enabled: " - + collectionInfoString( persister, collection, id, session ) - ); - } - if ( !emptySnapshot ) { - persister.remove( id, session ); - } - persister.recreate( collection, id, session ); + // Do nothing - we only need to notify the cache } else { - persister.deleteRows( collection, id, session ); - persister.updateRows( collection, id, session ); - persister.insertRows( collection, id, session ); + final EventManager eventManager = session.getEventManager(); + final HibernateMonitoringEvent event = eventManager.beginCollectionUpdateEvent(); + boolean success = false; + try { + if ( !affectedByFilters && collection.empty() ) { + if ( !emptySnapshot ) { + persister.remove( key, session ); + } + //TODO: else we really shouldn't have sent an update event to JFR + } + else if ( collection.needsRecreate( persister ) ) { + if ( affectedByFilters ) { + throw new HibernateException( "cannot recreate collection while filter is enabled: " + + collectionInfoString( persister, collection, key, session ) ); + } + if ( !emptySnapshot ) { + persister.remove( key, session ); + } + persister.recreate( collection, key, session ); + } + else { + persister.deleteRows( collection, key, session ); + persister.updateRows( collection, key, session ); + persister.insertRows( collection, key, session ); + } + success = true; + } + finally { + eventManager.completeCollectionUpdateEvent( event, key, persister.getRole(), success, session ); + } } session.getPersistenceContextInternal().getCollectionEntry( collection ).afterAction( collection ); @@ -101,11 +114,7 @@ public final class CollectionUpdateAction extends CollectionAction { } private PreCollectionUpdateEvent newPreCollectionUpdateEvent() { - return new PreCollectionUpdateEvent( - getPersister(), - getCollection(), - eventSource() - ); + return new PreCollectionUpdateEvent( getPersister(), getCollection(), eventSource() ); } private void postUpdate() { @@ -115,11 +124,7 @@ public final class CollectionUpdateAction extends CollectionAction { } private PostCollectionUpdateEvent newPostCollectionUpdateEvent() { - return new PostCollectionUpdateEvent( - getPersister(), - getCollection(), - eventSource() - ); + return new PostCollectionUpdateEvent( getPersister(), getCollection(), eventSource() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/EmptyEventManager.java b/hibernate-core/src/main/java/org/hibernate/event/internal/EmptyEventManager.java index 22a42ba50b..137276e7ce 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/EmptyEventManager.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/EmptyEventManager.java @@ -15,6 +15,9 @@ import org.hibernate.event.spi.FlushEvent; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; +/** + * An {@link EventManager} that ignores all events. + */ public final class EmptyEventManager implements EventManager { @Override @@ -276,4 +279,34 @@ public final class EmptyEventManager implements EventManager { SharedSessionContractImplementor session) { } + + @Override + public HibernateMonitoringEvent beginCollectionRecreateEvent() { + return null; + } + + @Override + public void completeCollectionRecreateEvent(HibernateMonitoringEvent event, Object id, String role, boolean success, SharedSessionContractImplementor session) { + + } + + @Override + public HibernateMonitoringEvent beginCollectionUpdateEvent() { + return null; + } + + @Override + public void completeCollectionUpdateEvent(HibernateMonitoringEvent event, Object id, String role, boolean success, SharedSessionContractImplementor session) { + + } + + @Override + public HibernateMonitoringEvent beginCollectionRemoveEvent() { + return null; + } + + @Override + public void completeCollectionRemoveEvent(HibernateMonitoringEvent event, Object id, String role, boolean success, SharedSessionContractImplementor session) { + + } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/EventManager.java b/hibernate-core/src/main/java/org/hibernate/event/spi/EventManager.java index 1ec50b61c3..58fa24e7be 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/EventManager.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/EventManager.java @@ -169,6 +169,18 @@ public interface EventManager { void completeEntityDeleteEvent(HibernateMonitoringEvent event, Object id, String entityName, boolean success, SharedSessionContractImplementor session); + HibernateMonitoringEvent beginCollectionRecreateEvent(); + + void completeCollectionRecreateEvent(HibernateMonitoringEvent event, Object id, String role, boolean success, SharedSessionContractImplementor session); + + HibernateMonitoringEvent beginCollectionUpdateEvent(); + + void completeCollectionUpdateEvent(HibernateMonitoringEvent event, Object id, String role, boolean success, SharedSessionContractImplementor session); + + HibernateMonitoringEvent beginCollectionRemoveEvent(); + + void completeCollectionRemoveEvent(HibernateMonitoringEvent event, Object id, String role, boolean success, SharedSessionContractImplementor session); + enum CacheActionDescription { ENTITY_INSERT( "Entity Insert" ), ENTITY_AFTER_INSERT( "Entity After Insert" ), diff --git a/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRecreateEvent.java b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRecreateEvent.java new file mode 100644 index 0000000000..7f17536e23 --- /dev/null +++ b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRecreateEvent.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.event.jfr.internal; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; +import org.hibernate.event.spi.HibernateMonitoringEvent; +import org.hibernate.internal.build.AllowNonPortable; + +@Name(CollectionRecreateEvent.NAME) +@Label("Collection Recreate") +@Category("Hibernate ORM") +@Description("Collection Recreate") +@StackTrace +@AllowNonPortable +public class CollectionRecreateEvent extends Event implements HibernateMonitoringEvent { + public static final String NAME = "org.hibernate.orm.CollectionRecreateEvent"; + + @Label("Session Identifier") + public String sessionIdentifier; + + @Label("Entity Identifier") + public String id; + + @Label("Collection Role") + public String role; + + @Label("Success") + public boolean success; + + @Override + public String toString() { + return NAME; + } + +} diff --git a/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRemoveEvent.java b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRemoveEvent.java new file mode 100644 index 0000000000..96e0b0846c --- /dev/null +++ b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionRemoveEvent.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.event.jfr.internal; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; +import org.hibernate.event.spi.HibernateMonitoringEvent; +import org.hibernate.internal.build.AllowNonPortable; + +@Name(CollectionRemoveEvent.NAME) +@Label("Collection Remove") +@Category("Hibernate ORM") +@Description("Collection Remove") +@StackTrace +@AllowNonPortable +public class CollectionRemoveEvent extends Event implements HibernateMonitoringEvent { + public static final String NAME = "org.hibernate.orm.CollectionRemoveEvent"; + + @Label("Session Identifier") + public String sessionIdentifier; + + @Label("Entity Identifier") + public String id; + + @Label("Collection Role") + public String role; + + @Label("Success") + public boolean success; + + @Override + public String toString() { + return NAME; + } + +} diff --git a/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionUpdateEvent.java b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionUpdateEvent.java new file mode 100644 index 0000000000..1bd1be07ea --- /dev/null +++ b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/CollectionUpdateEvent.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.event.jfr.internal; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; +import org.hibernate.event.spi.HibernateMonitoringEvent; +import org.hibernate.internal.build.AllowNonPortable; + +@Name(CollectionUpdateEvent.NAME) +@Label("Collection Update") +@Category("Hibernate ORM") +@Description("Collection Update") +@StackTrace +@AllowNonPortable +public class CollectionUpdateEvent extends Event implements HibernateMonitoringEvent { + public static final String NAME = "org.hibernate.orm.CollectionUpdateEvent"; + + @Label("Session Identifier") + public String sessionIdentifier; + + @Label("Entity Identifier") + public String id; + + @Label("Collection Role") + public String role; + + @Label("Success") + public boolean success; + + @Override + public String toString() { + return NAME; + } + +} diff --git a/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java index c34cf996c9..3d026f0c49 100644 --- a/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java +++ b/hibernate-jfr/src/main/java/org/hibernate/event/jfr/internal/JfrEventManager.java @@ -44,6 +44,9 @@ public class JfrEventManager implements EventManager { private static final EventType entityInsertEventType = EventType.getEventType( EntityInsertEvent.class ); private static final EventType entityUpdateEventType = EventType.getEventType( EntityUpdateEvent.class ); private static final EventType entityDeleteEventType = EventType.getEventType( EntityDeleteEvent.class ); + private static final EventType collectionRecreateEventType = EventType.getEventType( CollectionRecreateEvent.class ); + private static final EventType collectionUpdateEventType = EventType.getEventType( CollectionUpdateEvent.class ); + private static final EventType collectionRemoveEventType = EventType.getEventType( CollectionRemoveEvent.class ); @Override public SessionOpenEvent beginSessionOpenEvent() { @@ -621,6 +624,99 @@ public class JfrEventManager implements EventManager { } } + @Override + public HibernateMonitoringEvent beginCollectionRecreateEvent() { + if ( collectionRecreateEventType.isEnabled() ) { + final CollectionRecreateEvent event = new CollectionRecreateEvent(); + event.begin(); + return event; + } + else { + return null; + } + } + + @Override + public void completeCollectionRecreateEvent( + HibernateMonitoringEvent event, + Object id, String role, + boolean success, + SharedSessionContractImplementor session) { + if ( event != null ) { + final CollectionRecreateEvent entityInsertEvent = (CollectionRecreateEvent) event; + entityInsertEvent.end(); + if ( entityInsertEvent.shouldCommit() ) { + entityInsertEvent.sessionIdentifier = getSessionIdentifier( session ); + entityInsertEvent.role = role; + entityInsertEvent.id = Objects.toString(id); + entityInsertEvent.success = success; + entityInsertEvent.commit(); + } + } + } + + @Override + public HibernateMonitoringEvent beginCollectionUpdateEvent() { + if ( collectionUpdateEventType.isEnabled() ) { + final CollectionUpdateEvent event = new CollectionUpdateEvent(); + event.begin(); + return event; + } + else { + return null; + } + } + + @Override + public void completeCollectionUpdateEvent( + HibernateMonitoringEvent event, + Object id, String role, + boolean success, + SharedSessionContractImplementor session) { + if ( event != null ) { + final CollectionUpdateEvent entityUpdateEvent = (CollectionUpdateEvent) event; + entityUpdateEvent.end(); + if ( entityUpdateEvent.shouldCommit() ) { + entityUpdateEvent.sessionIdentifier = getSessionIdentifier( session ); + entityUpdateEvent.role = role; + entityUpdateEvent.id = Objects.toString(id); + entityUpdateEvent.success = success; + entityUpdateEvent.commit(); + } + } + } + + @Override + public HibernateMonitoringEvent beginCollectionRemoveEvent() { + if ( collectionRemoveEventType.isEnabled() ) { + final CollectionRemoveEvent event = new CollectionRemoveEvent(); + event.begin(); + return event; + } + else { + return null; + } + } + + @Override + public void completeCollectionRemoveEvent( + HibernateMonitoringEvent event, + Object id, String role, + boolean success, + SharedSessionContractImplementor session) { + if ( event != null ) { + final CollectionRemoveEvent entityDeleteEvent = (CollectionRemoveEvent) event; + entityDeleteEvent.end(); + if ( entityDeleteEvent.shouldCommit() ) { + entityDeleteEvent.sessionIdentifier = getSessionIdentifier( session ); + entityDeleteEvent.role = role; + entityDeleteEvent.id = Objects.toString(id); + entityDeleteEvent.success = success; + entityDeleteEvent.commit(); + } + } + } + private String getSessionIdentifier(SharedSessionContractImplementor session) { if ( session == null ) { return null;