Improving the performance of custom injectors in automated unit tests? | Community
Skip to main content
Level 4
October 10, 2023

Improving the performance of custom injectors in automated unit tests?

  • October 10, 2023
  • 1 reply
  • 1149 views

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? 

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.

1 reply

EstebanBustamante
Community Advisor and Adobe Champion
Community Advisor and Adobe Champion
October 11, 2023

Hi,

 

Correct. If you are using the @586265 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 @586265.

 

In your situation, the ideal scenario would be to not have the @586265 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 @586265 to use the specific annotations helps and improves your performance issue? If so, this would be preferred because having the @586265 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 

Esteban Bustamante
Preston-3Author
Level 4
October 11, 2023

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 @586265 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 @586265 to use the specific annotations helps and improves your performance issue? If so, this would be preferred because having the @586265 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. 

Preston-3Author
Level 4
October 12, 2023

Interestingly, I was not able to reproduce what you are seeing, this is what I did:

  1. 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.

  2. I created a DummyModel that utilizes my custom annotation.

  3. I ran the test both before registering the new annotation and after registering the annotation, but I didn't observe any differences.

  4. 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:

@8220494(service = {Injector.class, StaticInjectAnnotationProcessorFactory.class}, property = { Constants.SERVICE_RANKING + ":Integer=" + 2500 }) public class MyCustomLinkInjector implements Injector, StaticInjectAnnotationProcessorFactory { private final Logger logger = LoggerFactory.getLogger(getClass()); @9944223 public String getName() { return "my-custom-link-value"; } @9944223 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() @9944223 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.