Add repository initialization metrics.
This commit adds support for collecting data repository startup metric (repository scanning, repository initialization) using core framework ApplicationStartup and StartupStep. Collected metrics can be stored with Java Flight Recorder when using a FlightRecorderApplicationStartup. Closes #2247. Original pull request: #2273.
This commit is contained in:
committed by
Mark Paluch
parent
584464807e
commit
7d7f62f703
@@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
@@ -35,6 +36,8 @@ import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.metrics.ApplicationStartup;
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.core.type.StandardAnnotationMetadata;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationDelegate.LazyRepositoryInjectionPointResolver;
|
||||
import org.springframework.data.repository.sample.AddressRepository;
|
||||
@@ -107,6 +110,26 @@ class RepositoryConfigurationDelegateUnitTests {
|
||||
return context.getDefaultListableBeanFactory();
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1832
|
||||
void writesRepositoryScanningMetrics() {
|
||||
|
||||
ApplicationStartup startup = Mockito.spy(ApplicationStartup.DEFAULT);
|
||||
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.setApplicationStartup(startup);
|
||||
|
||||
RepositoryConfigurationSource configSource = new AnnotationRepositoryConfigurationSource(
|
||||
new StandardAnnotationMetadata(TestConfig.class, true), EnableRepositories.class, context, environment,
|
||||
context.getDefaultListableBeanFactory());
|
||||
|
||||
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configSource, context, environment);
|
||||
|
||||
delegate.registerRepositoriesIn(context, extension);
|
||||
|
||||
Mockito.verify(startup).start("spring.data.repository.scanning");
|
||||
}
|
||||
|
||||
@EnableRepositories(basePackageClasses = ProductRepository.class)
|
||||
static class TestConfig {}
|
||||
|
||||
|
||||
@@ -19,8 +19,13 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.mockito.ArgumentMatchers;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.core.metrics.ApplicationStartup;
|
||||
import org.springframework.core.metrics.StartupStep;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
@@ -28,9 +33,9 @@ import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
/**
|
||||
@@ -38,6 +43,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
* cases.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
@@ -45,6 +51,8 @@ public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
public final RepositoryQuery queryTwo = mock(RepositoryQuery.class);
|
||||
public final QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
|
||||
|
||||
private final ApplicationStartup applicationStartup;
|
||||
|
||||
@SuppressWarnings("unchecked") private final QuerydslPredicateExecutor<Object> querydsl = mock(
|
||||
QuerydslPredicateExecutor.class);
|
||||
private final Object repository;
|
||||
@@ -55,6 +63,16 @@ public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
when(strategy.resolveQuery(Mockito.any(Method.class), Mockito.any(RepositoryMetadata.class),
|
||||
Mockito.any(ProjectionFactory.class), Mockito.any(NamedQueries.class))).thenReturn(queryOne);
|
||||
|
||||
this.applicationStartup = mock(ApplicationStartup.class);
|
||||
StartupStep startupStep = mock(StartupStep.class);
|
||||
when(applicationStartup.start(anyString())).thenReturn(startupStep);
|
||||
when(startupStep.tag(anyString(), anyString())).thenReturn(startupStep);
|
||||
when(startupStep.tag(anyString(), ArgumentMatchers.<Supplier<String>> any())).thenReturn(startupStep);
|
||||
|
||||
BeanFactory beanFactory = Mockito.mock(BeanFactory.class);
|
||||
when(beanFactory.getBean(ApplicationStartup.class)).thenReturn(applicationStartup);
|
||||
setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -109,10 +127,15 @@ public class DummyRepositoryFactory extends RepositoryFactorySupport {
|
||||
: fragments;
|
||||
}
|
||||
|
||||
ApplicationStartup getApplicationStartup() {
|
||||
return this.applicationStartup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public interface MyRepositoryQuery extends RepositoryQuery {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@@ -42,6 +43,7 @@ import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.metrics.ApplicationStartup;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
@@ -405,6 +407,22 @@ class RepositoryFactorySupportUnitTests {
|
||||
assertThatThrownBy(repository::getFindRouteQuery).isInstanceOf(EmptyResultDataAccessException.class);
|
||||
}
|
||||
|
||||
@Test // DATACMNS-1832
|
||||
void callsApplicationStartupOnRepositoryInitialization() {
|
||||
|
||||
factory.getRepository(ObjectRepository.class, backingRepo);
|
||||
|
||||
ApplicationStartup startup = factory.getApplicationStartup();
|
||||
|
||||
InOrder orderedInvocation = Mockito.inOrder(startup);
|
||||
orderedInvocation.verify(startup).start("spring.data.repository.init");
|
||||
orderedInvocation.verify(startup).start("spring.data.repository.metadata");
|
||||
orderedInvocation.verify(startup).start("spring.data.repository.composition");
|
||||
orderedInvocation.verify(startup).start("spring.data.repository.target");
|
||||
orderedInvocation.verify(startup).start("spring.data.repository.proxy");
|
||||
orderedInvocation.verify(startup).start("spring.data.repository.postprocessors");
|
||||
}
|
||||
|
||||
private ConvertingRepository prepareConvertingRepository(final Object expectedValue) {
|
||||
|
||||
when(factory.queryOne.execute(any(Object[].class))).then(invocation -> {
|
||||
|
||||
Reference in New Issue
Block a user