HHH-17073 Add PrePartialAutoFlush listeners
This commit is contained in:
parent
fe77bcfee9
commit
e4e26ee989
|
@ -53,6 +53,9 @@ public interface SessionEventListener extends Serializable {
|
|||
default void flushStart() {}
|
||||
default void flushEnd(int numberOfEntities, int numberOfCollections) {}
|
||||
|
||||
default void prePartialFlushStart(){}
|
||||
default void prePartialFlushEnd(){}
|
||||
|
||||
default void partialFlushStart() {}
|
||||
default void partialFlushEnd(int numberOfEntities, int numberOfCollections) {}
|
||||
|
||||
|
|
|
@ -229,6 +229,28 @@ public class SessionEventListenerManagerImpl implements SessionEventListenerMana
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prePartialFlushStart() {
|
||||
if ( listeners == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( SessionEventListener listener : listeners ) {
|
||||
listener.prePartialFlushStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prePartialFlushEnd() {
|
||||
if ( listeners == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( SessionEventListener listener : listeners ) {
|
||||
listener.prePartialFlushEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void partialFlushStart() {
|
||||
if ( listeners == null ) {
|
||||
|
|
|
@ -63,6 +63,9 @@ public class StatisticalLoggingSessionEventListener extends BaseSessionEventList
|
|||
private long partialFlushCollectionCount;
|
||||
private long partialFlushTime;
|
||||
|
||||
private int prePartialFlushCount;
|
||||
private long prePartialFlushTime;
|
||||
|
||||
|
||||
// JDBC Connection acquisition ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -237,6 +240,22 @@ public class StatisticalLoggingSessionEventListener extends BaseSessionEventList
|
|||
// Partial-flushing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
private long partialFlushStart = -1;
|
||||
private long prePartialFlushStart = -1;
|
||||
|
||||
@Override
|
||||
public void prePartialFlushStart() {
|
||||
assert prePartialFlushStart < 0 : "Nested calls to prePartialFlushStart";
|
||||
prePartialFlushStart = System.nanoTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prePartialFlushEnd() {
|
||||
assert prePartialFlushStart > 0 : "Unexpected call to partialFlushEnd; expecting partialFlushStart";
|
||||
|
||||
prePartialFlushCount++;
|
||||
prePartialFlushTime += ( System.nanoTime() - partialFlushStart );
|
||||
prePartialFlushStart = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void partialFlushStart() {
|
||||
|
@ -269,6 +288,7 @@ public class StatisticalLoggingSessionEventListener extends BaseSessionEventList
|
|||
" %s nanoseconds spent performing %s L2C hits;\n" +
|
||||
" %s nanoseconds spent performing %s L2C misses;\n" +
|
||||
" %s nanoseconds spent executing %s flushes (flushing a total of %s entities and %s collections);\n" +
|
||||
" %s nanoseconds spent executing %s pre-partial-flushes;\n" +
|
||||
" %s nanoseconds spent executing %s partial-flushes (flushing a total of %s entities and %s collections)\n" +
|
||||
"}",
|
||||
jdbcConnectionAcquisitionTime,
|
||||
|
@ -291,6 +311,8 @@ public class StatisticalLoggingSessionEventListener extends BaseSessionEventList
|
|||
flushCount,
|
||||
flushEntityCount,
|
||||
flushCollectionCount,
|
||||
prePartialFlushTime,
|
||||
prePartialFlushCount,
|
||||
partialFlushTime,
|
||||
partialFlushCount,
|
||||
partialFlushEntityCount,
|
||||
|
|
|
@ -93,11 +93,21 @@ public class DefaultAutoFlushEventListener extends AbstractFlushingEventListener
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onAutoPreFlush(EventSource source) {
|
||||
public void onAutoPreFlush(EventSource source) throws HibernateException {
|
||||
final SessionEventListenerManager eventListenerManager = source.getEventListenerManager();
|
||||
eventListenerManager.prePartialFlushStart();
|
||||
final EventManager eventManager = source.getEventManager();
|
||||
HibernateMonitoringEvent hibernateMonitoringEvent = eventManager.beginPrePartialFlush();
|
||||
try {
|
||||
if ( flushMightBeNeeded( source ) ) {
|
||||
preFlush( source, source.getPersistenceContextInternal() );
|
||||
}
|
||||
}
|
||||
finally {
|
||||
eventManager.completePrePartialFlush( hibernateMonitoringEvent, source );
|
||||
eventListenerManager.prePartialFlushEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean flushIsReallyNeeded(AutoFlushEvent event, final EventSource source) {
|
||||
return source.getHibernateFlushMode() == FlushMode.ALWAYS
|
||||
|
|
|
@ -224,4 +224,16 @@ public final class EmptyEventManager implements EventManager {
|
|||
int[] dirtyProperties) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public HibernateMonitoringEvent beginPrePartialFlush() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completePrePartialFlush(
|
||||
HibernateMonitoringEvent prePartialFlush,
|
||||
SharedSessionContractImplementor session) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,6 @@ public interface AutoFlushEventListener {
|
|||
*/
|
||||
void onAutoFlush(AutoFlushEvent event) throws HibernateException;
|
||||
|
||||
default void onAutoPreFlush(EventSource source) {
|
||||
default void onAutoPreFlush(EventSource source) throws HibernateException {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,6 +155,12 @@ public interface EventManager {
|
|||
EntityEntry entry,
|
||||
int[] dirtyProperties);
|
||||
|
||||
HibernateMonitoringEvent beginPrePartialFlush();
|
||||
|
||||
void completePrePartialFlush(
|
||||
HibernateMonitoringEvent prePartialFlush,
|
||||
SharedSessionContractImplementor session
|
||||
);
|
||||
|
||||
enum CacheActionDescription {
|
||||
ENTITY_INSERT( "Entity Insert" ),
|
||||
|
|
|
@ -43,6 +43,7 @@ public class JfrEventManager implements EventManager {
|
|||
private static final EventType flushEventType = EventType.getEventType( FlushEvent.class );
|
||||
private static final EventType partialFlushEventType = EventType.getEventType( PartialFlushEvent.class );
|
||||
private static final EventType dirtyCalculationEventType = EventType.getEventType( DirtyCalculationEvent.class );
|
||||
private static final EventType prePartialFlushEventType = EventType.getEventType( PrePartialFlushEvent.class );
|
||||
|
||||
@Override
|
||||
public SessionOpenEvent beginSessionOpenEvent() {
|
||||
|
@ -528,6 +529,34 @@ public class JfrEventManager implements EventManager {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrePartialFlushEvent beginPrePartialFlush() {
|
||||
if ( prePartialFlushEventType.isEnabled() ) {
|
||||
final PrePartialFlushEvent partialFlushEvent = new PrePartialFlushEvent();
|
||||
partialFlushEvent.startedAt = System.nanoTime();
|
||||
partialFlushEvent.begin();
|
||||
return partialFlushEvent;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void completePrePartialFlush(
|
||||
HibernateMonitoringEvent event,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( event != null ) {
|
||||
final PrePartialFlushEvent prePartialFlushEvent = (PrePartialFlushEvent) event;
|
||||
prePartialFlushEvent.end();
|
||||
if ( prePartialFlushEvent.shouldCommit() ) {
|
||||
prePartialFlushEvent.executionTime = getExecutionTime( prePartialFlushEvent.startedAt );
|
||||
prePartialFlushEvent.sessionIdentifier = getSessionIdentifier( session );
|
||||
prePartialFlushEvent.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long getExecutionTime(Long startTime) {
|
||||
return NANOSECONDS.convert( System.nanoTime() - startTime, NANOSECONDS );
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.event.jfr.internal;
|
||||
|
||||
import org.hibernate.event.spi.HibernateMonitoringEvent;
|
||||
import org.hibernate.internal.build.AllowNonPortable;
|
||||
|
||||
import jdk.jfr.Category;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.StackTrace;
|
||||
|
||||
@Name( PrePartialFlushEvent.NAME )
|
||||
@Label( "PrePartialFlushEvent Execution" )
|
||||
@Category( "Hibernate ORM" )
|
||||
@Description( "PrePartialFlushEvent Execution" )
|
||||
@StackTrace(false)
|
||||
@AllowNonPortable
|
||||
public class PrePartialFlushEvent extends Event implements HibernateMonitoringEvent {
|
||||
public static final String NAME = "org.hibernate.orm.PrePartialFlushEvent";
|
||||
|
||||
@Label("Session Identifier")
|
||||
public String sessionIdentifier;
|
||||
|
||||
@Label("PrePartialFlushEvent time")
|
||||
public long executionTime;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
public transient long startedAt;
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package org.hibernate.event.jfr.flush;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.event.jfr.internal.FlushEvent;
|
||||
import org.hibernate.event.jfr.internal.PrePartialFlushEvent;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import org.moditect.jfrunit.EnableEvent;
|
||||
import org.moditect.jfrunit.JfrEventTest;
|
||||
import org.moditect.jfrunit.JfrEvents;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@JfrEventTest
|
||||
@DomainModel(annotatedClasses = {
|
||||
PrePartialFlushTest.TestEntity.class,
|
||||
})
|
||||
@SessionFactory
|
||||
public class PrePartialFlushTest {
|
||||
public JfrEvents jfrEvents = new JfrEvents();
|
||||
|
||||
@AfterEach
|
||||
public void tearDown(SessionFactoryScope scope){
|
||||
scope.inTransaction(
|
||||
session -> {
|
||||
session.createMutationQuery( "delete from TestEntity " ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableEvent(PrePartialFlushEvent.NAME)
|
||||
public void testPrePartialFlushEvent(SessionFactoryScope scope) {
|
||||
jfrEvents.reset();
|
||||
String sessionId = scope.fromTransaction(
|
||||
session -> {
|
||||
TestEntity entity = new TestEntity( 1, "name_1" );
|
||||
session.persist( entity );
|
||||
|
||||
session.createQuery( "select t from TestEntity t" ).list();
|
||||
return session.getSessionIdentifier().toString();
|
||||
}
|
||||
);
|
||||
|
||||
List<RecordedEvent> events = jfrEvents.events()
|
||||
.filter(
|
||||
recordedEvent ->
|
||||
{
|
||||
String eventName = recordedEvent.getEventType().getName();
|
||||
return eventName.equals( PrePartialFlushEvent.NAME );
|
||||
}
|
||||
).toList();
|
||||
assertThat( events ).hasSize( 1 );
|
||||
|
||||
RecordedEvent event = events.get( 0 );
|
||||
assertThat( event.getEventType().getName() )
|
||||
.isEqualTo( PrePartialFlushEvent.NAME );
|
||||
assertThat( event.getLong( "executionTime" ) ).isGreaterThan( 0 );
|
||||
assertThat( event.getString( "sessionIdentifier" ) )
|
||||
.isEqualTo( sessionId );
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableEvent(PrePartialFlushEvent.NAME)
|
||||
public void testPrePartialFlushEventNotTriggered(SessionFactoryScope scope) {
|
||||
jfrEvents.reset();
|
||||
String sessionId = scope.fromTransaction(
|
||||
session -> {
|
||||
TestEntity entity = new TestEntity( 1, "name_1" );
|
||||
session.persist( entity );
|
||||
return session.getSessionIdentifier().toString();
|
||||
}
|
||||
);
|
||||
|
||||
List<RecordedEvent> events = jfrEvents.events()
|
||||
.filter(
|
||||
recordedEvent ->
|
||||
{
|
||||
String eventName = recordedEvent.getEventType().getName();
|
||||
return eventName.equals( PrePartialFlushEvent.NAME );
|
||||
}
|
||||
).toList();
|
||||
assertThat( events ).hasSize( 0 );
|
||||
}
|
||||
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
public static class TestEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public TestEntity() {
|
||||
}
|
||||
|
||||
public TestEntity(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue