[ 
https://issues.apache.org/jira/browse/TOMEE-4293?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Nicolas Ternisien updated TOMEE-4293:
-------------------------------------
    Description: 
We're currently experimenting the migration of an EAR project from Weblogic 
application server to TomEE.

While conduction this experiment, we also tried to see if we could rely on 
"Collapsed EAR" approach, ie, simplifying the application by providing a single 
WAR file with EJBs embedded into it, instead of multiple ejb and web modules 
inside the EAR file.


The project is quite big with:
 * 1992 EJBs in EJB Module (+ CDI beans)
 * 1193 EJBs in Web Module (+ CDI beans for JAX RS resources)
 * (and a total of 8569 injection points via @EJB)

But still, with the EAR approach, total loading time is about 1min, with 3.1 GB 
JVM memory usage.

 

When trying the alternative approach via a WAR file (including the EJB module 
as JAR), the startup takes huge time (30 to 40 minutes) and JVM memory 
consumption goes up to 8 GB.

 

We tried to investigate this difference and we have identified the bottleneck 
(via TomEE startup debugging) at JndiEncInfoBuilder, more especially at 
JndiEncInfoBuilder.buildEjbRefs() where each EJB has the *whole* list of 
ejbLocalRef of all the EJBs of the entire application.

 

I've setup a dedicated project (in attachment), with 2 different deployable 
binaries:
 * collapsedear-bug.war
 * collapsedear-bug-ear.ear

Note the deployment is performed via the "tomee/apps" folder.

 

Both contains an EJB module, named "collapsedear-bug-ejb", which contains the 
following EJB NumberService.java:

{code:java}
package org.lastnico.collapsedear.bug.ejb;

import java.util.Random;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
@Startup

public class NumberService {

    private static Logger log = LoggerFactory.getLogger(NumberFinder.class);

    @PostConstruct
    public void start() {
        log.info("Starting {}", this.getClass());
    }

    @EJB
    private NumberFinder userFinder;

    public long save() {
        return new Random().nextLong();
    }
}
 {code}
 

To compare the two processes at loading time. Of course, this project is much 
more simple (less than 10 EJBs in total), so we cannot notice it from memory 
consumption or loading time, but here are the debugging output:

 
h4. collapsedear-bug.war

At startup, with a debugging breakpoint, watching for "NumberService" EJB 
registration

at

JndiEncInfoBuilder.build(final JndiConsumer jndiConsumer, final String ejbName, 
final String moduleId, final URI moduleUri, final JndiEncInfo moduleJndiEnc, 
final JndiEncInfo compJndiEnc) throws OpenEJBException {
{code:java}
 moduleId = collapsedear-bug-ejb

JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"

JndiConsumer.ejbLocalRef = 
{java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}, 
java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}, 
java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront',
 local=org.lastnico.collapsedear.bug.war.CalculatorFront, link='null', 
mappedName='null', lookupName='null'}, 
java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}}{code}
 

You can see that ejbLocalRef contains 8 different injection points, while 
NumberService has only a single injection point (NumberFinder)

h4. collapsedear-bug-ear.ear

At the opposite, when  debugging the startup of the equivalent EAR file

Here are the debugging info extracted:
{code}

moduleId = collapsedear-bug-ejb

JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"

JndiConsumer.ejbLocalRef = 
{java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}}
{code}

This time, as expected, the NumberService EJB only has 1 single EJB local ref, 
matching its single injection point.

I suspect the reason for this is that MergeWebappJndiContext indistinctely 
copies all possible EJB local refs, and hence each EJB receives the whole list 
of injection points.

In the scenario of our application described earlier, we end up with (1992 +  
1193) * 8569 declared EJB local refs, which slows down the whole startup and 
waste huge quantity of memory.




  was:
We're currently experimenting the migration of an EAR project from Weblogic 
application server to TomEE.

While conduction this experiment, we also tried to see if we could rely on 
"Collapsed EAR" approach, ie, simplifying the application by providing a single 
WAR file with EJBs embedded into it, instead of multiple ejb and web modules 
inside the EAR file.


The project is quite big with:
 * 1992 EJBs in EJB Module (+ CDI beans)
 * 1193 EJBs in Web Module (+ CDI beans for JAX RS resources)
 * (and a total of 8569 injection points via @EJB)

But still, with the EAR approach, total loading time is about 1min, with 3.1 GB 
JVM memory usage.

 

When trying the alternative approach via a WAR file (including the EJB module 
as JAR), the startup takes huge time (30 to 40 minutes) and JVM memory 
consumption goes up to 8 GB.

 

We tried to investigate this difference and we have identified the bottleneck 
(via TomEE startup debugging) at JndiEncInfoBuilder, more especially at 
JndiEncInfoBuilder.buildEjbRefs() where each EJB has the *whole* list of 
ejbLocalRef of all the EJBs of the entire application.

 

I've setup a dedicated project (in attachment), with 2 different deployable 
binaries:
 * collapsedear-bug.war
 * collapsedear-bug-ear.ear

Note the deployment is performed via the "tomee/apps" folder.

 

Both contains an EJB module, named "collapsedear-bug-ejb", which contains the 
following EJB NumberService.java:

{code:java}
package org.lastnico.collapsedear.bug.ejb;

import java.util.Random;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
@Startup

public class NumberService {

    private static Logger log = LoggerFactory.getLogger(NumberFinder.class);

    @PostConstruct
    public void start() {
        log.info("Starting {}", this.getClass());
    }

    @EJB
    private NumberFinder userFinder;

    public long save() {
        return new Random().nextLong();
    }
}
 {code}
 

To compare the two processes at loading time. Of course, this project is much 
more simple (less than 10 EJBs in total), so we cannot notice it from memory 
consumption or loading time, but here are the debugging output:

 
h4. collapsedear-bug.war

At startup, with a debugging breakpoint, watching for "NumberService" EJB 
registration

at

JndiEncInfoBuilder.build(final JndiConsumer jndiConsumer, final String ejbName, 
final String moduleId, final URI moduleUri, final JndiEncInfo moduleJndiEnc, 
final JndiEncInfo compJndiEnc) throws OpenEJBException {
{code:java}
 moduleId = collapsedear-bug-ejb

JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"

JndiConsumer.ejbLocalRef = 
{java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}, 
java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}, 
java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront',
 local=org.lastnico.collapsedear.bug.war.CalculatorFront, link='null', 
mappedName='null', lookupName='null'}, 
java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}}{code}
 

You can see that ejbLocalRef contains 8 different injection points, while 
NumberService has only a single injection point (NumberFinder)

 h4. collapsedear-bug-ear.ear

At the opposite, when  debugging the startup of the equivalent EAR file

Here are the debugging info extracted:
{code}

moduleId = collapsedear-bug-ejb

JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"

JndiConsumer.ejbLocalRef = 
{java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
 local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
mappedName='null', lookupName='null'}}
{code}

This time, as expected, the NumberService EJB only has 1 single EJB local ref, 
matching its single injection point.

I suspect the reason for this is that MergeWebappJndiContext indistinctely 
copies all possible EJB local refs, and hence each EJB receives the whole list 
of injection points.

In the scenario of our application described earlier, we end up with (1992 +  
1193) * 8569 declared EJB local refs, which slows down the whole startup and 
waste huge quantity of memory.





> WAR-wide EJB local refs inserted into each EJB at startup
> ---------------------------------------------------------
>
>                 Key: TOMEE-4293
>                 URL: https://issues.apache.org/jira/browse/TOMEE-4293
>             Project: TomEE
>          Issue Type: Bug
>          Components: TomEE Core Server
>    Affects Versions: 8.0.16
>            Reporter: Nicolas Ternisien
>            Priority: Major
>
> We're currently experimenting the migration of an EAR project from Weblogic 
> application server to TomEE.
> While conduction this experiment, we also tried to see if we could rely on 
> "Collapsed EAR" approach, ie, simplifying the application by providing a 
> single WAR file with EJBs embedded into it, instead of multiple ejb and web 
> modules inside the EAR file.
> The project is quite big with:
>  * 1992 EJBs in EJB Module (+ CDI beans)
>  * 1193 EJBs in Web Module (+ CDI beans for JAX RS resources)
>  * (and a total of 8569 injection points via @EJB)
> But still, with the EAR approach, total loading time is about 1min, with 3.1 
> GB JVM memory usage.
>  
> When trying the alternative approach via a WAR file (including the EJB module 
> as JAR), the startup takes huge time (30 to 40 minutes) and JVM memory 
> consumption goes up to 8 GB.
>  
> We tried to investigate this difference and we have identified the bottleneck 
> (via TomEE startup debugging) at JndiEncInfoBuilder, more especially at 
> JndiEncInfoBuilder.buildEjbRefs() where each EJB has the *whole* list of 
> ejbLocalRef of all the EJBs of the entire application.
>  
> I've setup a dedicated project (in attachment), with 2 different deployable 
> binaries:
>  * collapsedear-bug.war
>  * collapsedear-bug-ear.ear
> Note the deployment is performed via the "tomee/apps" folder.
>  
> Both contains an EJB module, named "collapsedear-bug-ejb", which contains the 
> following EJB NumberService.java:
> {code:java}
> package org.lastnico.collapsedear.bug.ejb;
> import java.util.Random;
> import javax.annotation.PostConstruct;
> import javax.ejb.EJB;
> import javax.ejb.Singleton;
> import javax.ejb.Startup;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> @Singleton
> @Startup
> public class NumberService {
>     private static Logger log = LoggerFactory.getLogger(NumberFinder.class);
>     @PostConstruct
>     public void start() {
>         log.info("Starting {}", this.getClass());
>     }
>     @EJB
>     private NumberFinder userFinder;
>     public long save() {
>         return new Random().nextLong();
>     }
> }
>  {code}
>  
> To compare the two processes at loading time. Of course, this project is much 
> more simple (less than 10 EJBs in total), so we cannot notice it from memory 
> consumption or loading time, but here are the debugging output:
>  
> h4. collapsedear-bug.war
> At startup, with a debugging breakpoint, watching for "NumberService" EJB 
> registration
> at
> JndiEncInfoBuilder.build(final JndiConsumer jndiConsumer, final String 
> ejbName, final String moduleId, final URI moduleUri, final JndiEncInfo 
> moduleJndiEnc, final JndiEncInfo compJndiEnc) throws OpenEJBException {
> {code:java}
>  moduleId = collapsedear-bug-ejb
> JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"
> JndiConsumer.ejbLocalRef = 
> {java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
>  local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
> mappedName='null', lookupName='null'}, 
> java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/numberFinder',
>  local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
> mappedName='null', lookupName='null'}, 
> java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.GreetingResource/calculatorFront',
>  local=org.lastnico.collapsedear.bug.war.CalculatorFront, link='null', 
> mappedName='null', lookupName='null'}, 
> java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.war.CalculatorFront/numberFinder',
>  local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
> mappedName='null', lookupName='null'}}{code}
>  
> You can see that ejbLocalRef contains 8 different injection points, while 
> NumberService has only a single injection point (NumberFinder)
> h4. collapsedear-bug-ear.ear
> At the opposite, when  debugging the startup of the equivalent EAR file
> Here are the debugging info extracted:
> {code}
> moduleId = collapsedear-bug-ejb
> JndiConsumer.ejbClass = "org.lastnico.collapsedear.bug.ejb.NumberService"
> JndiConsumer.ejbLocalRef = 
> {java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder=EjbLocalRef{name='java:comp/env/org.lastnico.collapsedear.bug.ejb.NumberService/userFinder',
>  local=org.lastnico.collapsedear.bug.ejb.NumberFinder, link='null', 
> mappedName='null', lookupName='null'}}
> {code}
> This time, as expected, the NumberService EJB only has 1 single EJB local 
> ref, matching its single injection point.
> I suspect the reason for this is that MergeWebappJndiContext indistinctely 
> copies all possible EJB local refs, and hence each EJB receives the whole 
> list of injection points.
> In the scenario of our application described earlier, we end up with (1992 +  
> 1193) * 8569 declared EJB local refs, which slows down the whole startup and 
> waste huge quantity of memory.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to