We're currently using unit tests with AEM Mock to test our models in our application. We've recently introduced a custom injector and custom annotations and this has been bogging down the tests significantly. Even in tests that don't do any work, merely injecting the custom injector adds a half second or more of startup time. Is there a way to improve the performance this? My understanding based on this question and the answer is that each custom injector is forced to interrogate every Inject annotation of every model registered in the system at startup. Is that understanding correct? It's possible that there's no way to speed this up, but I wanted to make sure we weren't missing something. If this is the case and each new AEM Context will simply have to apply the injector to every model with an Inject annotation, then I can't see a way to improve this outside of shunting these tests off to an Integration test suite or something like that.
We were hoping there was an ability to hint the AEM Context in the mock to only read certain models. Or maybe in the custom injector implementation is there a way to scope down which annotations the custom injector reads. Can someone let me know if I'm missing something?
Views
Replies
Total Likes
Hi,
Correct. If you are using the @inject annotation, it means you don't have a clear idea of which implementation of an "injector" needs to be selected. Consequently, the system will have to iterate through all the available "injectors" to determine which one to use. That's why it is highly recommended to use the most specific injector possible. Please take a look at this great article[1] explaining the behavior and the performance implications of using @inject.
In your situation, the ideal scenario would be to not have the @inject annotation in your Sling Model. This way, when the AEM Context runs, it won't evaluate all the injectors but only the ones specific to your sling model. Can you check if refactoring one Sling model which is using @inject to use the specific annotations helps and improves your performance issue? If so, this would be preferred because having the @inject annotation as explained in the article would potentially impact your application's performance.
On the other hand and AFAIK there is no way to mock the Inject annotation to narrow down the scope of available injectors.
I would love to hear back on your results.
[1]. https://cqdump.joerghoh.de/2022/11/28/sling-model-performance/
Another great article: https://medium.com/ida-mediafoundry/sling-models-the-story-behind-inject-c51615164b11
The behavior we're seeing that's making us most concerned about the performance is that when we breakpoint the injector's createAnnotationProcessor method, we can see references to elements like ImageImpl.pageManager and com.adobe.cq.wcm.core.components.internal.models.v2.ListImpl.linkManager, for example. Meaning that it appears like even models that aren't part of our custom code base are being applied against our custom injector.
So when you say:
In your situation, the ideal scenario would be to not have the @inject annotation in your Sling Model. This way, when the AEM Context runs, it won't evaluate all the injectors but only the ones specific to your sling model. Can you check if refactoring one Sling model which is using @inject to use the specific annotations helps and improves your performance issue? If so, this would be preferred because having the @inject annotation as explained in the article would potentially impact your application's performance.
Won't we still deal with the slow start time, because the Context is trying to apply all of the other models that use Inject against our custom injector? Or am I still missing something here? What we're seeing is that the simple act of adding our custom injector to the context is causing it to be tested against every model in the system, whether custom code, or OOTB or AEM Core Components.
So for now at least we're not concerned about the performance of the models per se. We're more concerned that in the context of trying to do automated testing against our models, that the test suite is slow to spin up because of the fact that our custom injector is being applied against all of these models, which we can't figure out how to avoid or if we even can.
Interestingly, I was not able to reproduce what you are seeing, this is what I did:
I created a custom injector, using this link as a reference: https://aemhints.com/2023/06/01/create-custom-sling-injector-aem-6-5/. I explicitly added a Thread.sleep(5000)
to ensure that the performance degrades when this is invoked.
I created a DummyModel
that utilizes my custom annotation.
I ran the test both before registering the new annotation and after registering the annotation, but I didn't observe any differences.
I added a breakpoint in the createAnnotationProcessor
method as you suggested, but I didn't observe the behavior you described. Could it be possible that I did something wrong?
Could you please share some code related to your annotation? Please pay attention to the ranking value, and ensure that it returns null
if the injector doesn't match your annotation. It should look something like this:
@component(service = {Injector.class, StaticInjectAnnotationProcessorFactory.class}, property = {
Constants.SERVICE_RANKING + ":Integer=" + 2500
})
public class MyCustomLinkInjector implements Injector, StaticInjectAnnotationProcessorFactory {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Override
public String getName() {
return "my-custom-link-value";
}
@Override
public Object getValue(final Object adaptable,
final String name,
final Type type,
final AnnotatedElement element,
final DisposalCallbackRegistry callbackRegistry) {
// Injectors are cycled through, so it is important to get yours otherwise return NULL
final MyCustomLinkAnnotation annotation = element.getAnnotation(MyCustomLinkAnnotation.class);
if (annotation == null) {
return null;
}
//Now should comes your business logic
....
}//End of getValue()
@Override
public InjectAnnotationProcessor2 createAnnotationProcessor(final AnnotatedElement element) {
// Check if the element has the expected annotation
final MyCustomLinkAnnotation annotation = element.getAnnotation(MyCustomLinkAnnotation.class);
if (annotation != null) {
return new MyCustomLinkValueProcessor(annotation);
}
return null;
}
Hope this helps
So for posterity we had something akin to your Thread Sleep going on in our injector, it turns out. We had an I/O operation going on that we didn't notice and once we removed that the tests blazed.
With that, I'm confused about what you're seeing. Everything I've read on these forums, in documentation and even from the folks at AEM Mocks says that the AEM Context will try to test every model with an Inject annotation against all injectors until it finds an injector that can handle the annotation. So that knowledge makes me think what we're seeing is the correct behavior. Especially if we don't specify OOTB or 3rd party injectors in our test, but those models are part of our classpath. We did change the priority level and that doesn't make a difference, which I assume is because we don't have other injectors in place that could handle those models.
Views
Likes
Replies
Views
Likes
Replies