Skip to content

Commit 0044f4d

Browse files
christophstroblmp911de
authored andcommitted
Add repository initialization metrics.
This commit adds initial support for collecting data repository startup metrics using core framework ApplicationStartup and StartupStep. Collected metrics can be stored with Java Flight Recorder when using a FlightRecorderApplicationStartup.
1 parent b1be1cc commit 0044f4d

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.List;
2323
import java.util.Map;
2424
import java.util.Optional;
25+
import java.util.function.Consumer;
2526
import java.util.stream.Collectors;
2627

2728
import org.aopalliance.intercept.MethodInterceptor;
@@ -35,9 +36,13 @@
3536
import org.springframework.beans.factory.BeanClassLoaderAware;
3637
import org.springframework.beans.factory.BeanFactory;
3738
import org.springframework.beans.factory.BeanFactoryAware;
39+
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
40+
import org.springframework.context.ApplicationContext;
3841
import org.springframework.core.convert.support.DefaultConversionService;
3942
import org.springframework.core.convert.support.GenericConversionService;
4043
import org.springframework.core.log.LogMessage;
44+
import org.springframework.core.metrics.ApplicationStartup;
45+
import org.springframework.core.metrics.StartupStep;
4146
import org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor;
4247
import org.springframework.data.projection.ProjectionFactory;
4348
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
@@ -269,12 +274,21 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
269274
logger.debug(LogMessage.format("Initializing repository instance for %s…", repositoryInterface.getName()));
270275
}
271276

277+
ApplicationStartup applicationStartup = getStartup();
278+
272279
Assert.notNull(repositoryInterface, "Repository interface must not be null!");
273280
Assert.notNull(fragments, "RepositoryFragments must not be null!");
274281

282+
StartupStep repositoryInit = applicationStartup.start("spring.data.repository.init");
283+
284+
StartupStep repositoryMetadataStep = applicationStartup.start("spring.data.repository.metadata");
275285
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
286+
repositoryMetadataStep.end();
287+
288+
StartupStep repositoryCompositionStep = applicationStartup.start("spring.data.repository.metadata");
276289
RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
277290
RepositoryInformation information = getRepositoryInformation(metadata, composition);
291+
repositoryCompositionStep.end();
278292

279293
validate(information, composition);
280294

@@ -291,7 +305,9 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
291305

292306
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
293307

308+
StartupStep repositoryPostprocessorsStep = applicationStartup.start("spring.data.repository.postprocessors");
294309
postProcessors.forEach(processor -> processor.postProcess(result, information));
310+
repositoryPostprocessorsStep.end();
295311

296312
if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
297313
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
@@ -313,9 +329,21 @@ public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fra
313329
.debug(LogMessage.format("Finished creation of repository instance for {}.", repositoryInterface.getName()));
314330
}
315331

332+
repositoryInit.end();
316333
return repository;
317334
}
318335

336+
ApplicationStartup getStartup() {
337+
338+
try {
339+
ApplicationStartup applicationStartup = beanFactory != null ? beanFactory.getBean(ApplicationStartup.class) : ApplicationStartup.DEFAULT;
340+
return applicationStartup != null ? applicationStartup : ApplicationStartup.DEFAULT;
341+
}
342+
catch (NoSuchBeanDefinitionException e) {
343+
return ApplicationStartup.DEFAULT;
344+
}
345+
}
346+
319347
/**
320348
* Returns the {@link ProjectionFactory} to be used with the repository instances created.
321349
*

src/test/java/org/springframework/data/repository/core/support/DummyRepositoryFactory.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,40 @@
1919

2020
import java.lang.reflect.Method;
2121
import java.util.Optional;
22+
import java.util.function.Supplier;
2223

24+
import org.mockito.ArgumentMatchers;
2325
import org.mockito.Mockito;
26+
import org.springframework.beans.factory.BeanFactory;
27+
import org.springframework.core.metrics.ApplicationStartup;
28+
import org.springframework.core.metrics.StartupStep;
2429
import org.springframework.data.projection.ProjectionFactory;
2530
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
2631
import org.springframework.data.repository.core.EntityInformation;
2732
import org.springframework.data.repository.core.NamedQueries;
2833
import org.springframework.data.repository.core.RepositoryInformation;
2934
import org.springframework.data.repository.core.RepositoryMetadata;
3035
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
31-
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
3236
import org.springframework.data.repository.query.QueryLookupStrategy;
3337
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
38+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
3439
import org.springframework.data.repository.query.RepositoryQuery;
3540

3641
/**
3742
* Dummy implementation for {@link RepositoryFactorySupport} that is equipped with mocks to simulate behavior for test
3843
* cases.
3944
*
4045
* @author Oliver Gierke
46+
* @author Christoph Strobl
4147
*/
4248
public class DummyRepositoryFactory extends RepositoryFactorySupport {
4349

4450
public final MyRepositoryQuery queryOne = mock(MyRepositoryQuery.class);
4551
public final RepositoryQuery queryTwo = mock(RepositoryQuery.class);
4652
public final QueryLookupStrategy strategy = mock(QueryLookupStrategy.class);
4753

54+
private final ApplicationStartup applicationStartup;
55+
4856
@SuppressWarnings("unchecked") private final QuerydslPredicateExecutor<Object> querydsl = mock(
4957
QuerydslPredicateExecutor.class);
5058
private final Object repository;
@@ -55,6 +63,16 @@ public DummyRepositoryFactory(Object repository) {
5563

5664
when(strategy.resolveQuery(Mockito.any(Method.class), Mockito.any(RepositoryMetadata.class),
5765
Mockito.any(ProjectionFactory.class), Mockito.any(NamedQueries.class))).thenReturn(queryOne);
66+
67+
this.applicationStartup = mock(ApplicationStartup.class);
68+
StartupStep startupStep = mock(StartupStep.class);
69+
when(applicationStartup.start(anyString())).thenReturn(startupStep);
70+
when(startupStep.tag(anyString(), anyString())).thenReturn(startupStep);
71+
when(startupStep.tag(anyString(), ArgumentMatchers.<Supplier<String>> any())).thenReturn(startupStep);
72+
73+
BeanFactory beanFactory = Mockito.mock(BeanFactory.class);
74+
when(beanFactory.getBean(ApplicationStartup.class)).thenReturn(applicationStartup);
75+
setBeanFactory(beanFactory);
5876
}
5977

6078
/*
@@ -109,10 +127,15 @@ protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata
109127
: fragments;
110128
}
111129

130+
ApplicationStartup getApplicationStartup() {
131+
return this.applicationStartup;
132+
}
133+
112134
/**
113135
* @author Mark Paluch
114136
*/
115137
public interface MyRepositoryQuery extends RepositoryQuery {
116138

117139
}
140+
118141
}

src/test/java/org/springframework/data/repository/core/support/RepositoryFactorySupportUnitTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,13 @@ void considersNullabilityForKotlinInterfaceProperties() {
405405
assertThatThrownBy(repository::getFindRouteQuery).isInstanceOf(EmptyResultDataAccessException.class);
406406
}
407407

408+
@Test // DATACMNS-1832
409+
void callsApplicationStartupOnRepositoryIntialization() {
410+
411+
factory.getRepository(ObjectRepository.class, backingRepo);
412+
verify(factory.getApplicationStartup()).start("spring.data.repository.init");
413+
}
414+
408415
private ConvertingRepository prepareConvertingRepository(final Object expectedValue) {
409416

410417
when(factory.queryOne.execute(any(Object[].class))).then(invocation -> {

0 commit comments

Comments
 (0)