jhipster / generator-jhipster

JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
https://www.jhipster.tech
Apache License 2.0
21.54k stars 4.02k forks source link

OutOfMemoryError running backend Integration Tests #18907

Closed egvimo closed 1 year ago

egvimo commented 2 years ago
Overview of the issue

If there are several entities, the integration tests are failing with an OutOfMemoryError.

Abc7ResourceIT > putNewAbc7() FAILED
    java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
        Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:628
            Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
                Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:658
                    Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:185
                        Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:800
                            Caused by: org.springframework.beans.factory.BeanCreationException at AbstractAutowireCapableBeanFactory.java:628    
                                Caused by: java.lang.OutOfMemoryError

The tests are failing mostly around the 15th entity.

This is also happening in CI. Reproduced in Azure DevOps and GitHub.

And the integration tests are very slow compared to before.

Motivation for or Use Case

Tests should not fail with OutOfMemory error.

Reproduce the error
  1. Generate ~30 Entities
  2. Run backend integration tests: ./gradlew integrationTest -x webapp -x webapp_test
Related issues
Suggest a Fix

Something changed between 7.6.0 and 7.8.1, which causes this error. I'm currently using version 7.6.0 and wanted to Upgrade to the newest version. Earlier I tried to upgrade to 7.7.0 and as far as I recall the problem was not there (but I'm not 100 % sure).

JHipster Version(s)

7.8.1

JHipster configuration
JDL definitions
    application {
        config {
            baseName sample
            packageName sample
            applicationType monolith
            authenticationType oauth2
            buildTool gradle
            cacheProvider ehcache
            enableHibernateCache true
            clientFramework angularX
            clientPackageManager npm
            clientTheme none
            databaseType sql
            prodDatabaseType postgresql
            devDatabaseType postgresql
            messageBroker kafka
            languages [de, en]
            nativeLanguage de
            testFrameworks [cypress]
            websocket spring-websocket
        }
        entities *
    }

    entity Abc0 {
        name String unique required
        otherField String
    }

    entity Abc1 {
        name String unique required
        otherField String
    }

    entity Abc2 {
        name String unique required
        otherField String
    }

    entity Abc3 {
        name String unique required
        otherField String
    }

    entity Abc4 {
        name String unique required
        otherField String
    }

    entity Abc5 {
        name String unique required
        otherField String
    }

    entity Abc6 {
        name String unique required
        otherField String
    }

    entity Abc7 {
        name String unique required
        otherField String
    }

    entity Abc8 {
        name String unique required
        otherField String
    }

    entity Abc9 {
        name String unique required
        otherField String
    }

    entity Abc10 {
        name String unique required
        otherField String
    }

    entity Abc11 {
        name String unique required
        otherField String
    }

    entity Abc12 {
        name String unique required
        otherField String
    }

    entity Abc13 {
        name String unique required
        otherField String
    }

    entity Abc14 {
        name String unique required
        otherField String
    }

    entity Abc15 {
        name String unique required
        otherField String
    }

    entity Abc16 {
        name String unique required
        otherField String
    }

    entity Abc17 {
        name String unique required
        otherField String
    }

    entity Abc18 {
        name String unique required
        otherField String
    }

    entity Abc19 {
        name String unique required
        otherField String
    }

    entity Abc20 {
        name String unique required
        otherField String
    }

    entity Abc21 {
        name String unique required
        otherField String
    }

    entity Abc22 {
        name String unique required
        otherField String
    }

    entity Abc23 {
        name String unique required
        otherField String
    }

    entity Abc24 {
        name String unique required
        otherField String
    }

    entity Abc25 {
        name String unique required
        otherField String
    }

    entity Abc26 {
        name String unique required
        otherField String
    }

    entity Abc27 {
        name String unique required
        otherField String
    }

    entity Abc28 {
        name String unique required
        otherField String
    }

    entity Abc29 {
        name String unique required
        otherField String
    }
  
Entity configuration(s) entityName.json files generated in the .jhipster directory

See my sample app for full example.

Browsers and Operating System
Tcharl commented 2 years ago

you can tweak the memory param in the task integrationTest plugin section of the gradle files (build.gradle or gradle/profile_<env>.gradle. We had to find a tradeoff for memory usage to constrained environments (I.e. GH actions)

egvimo commented 2 years ago

Increasing the memory does not help. I've made some tests:

Version Time Result Remark
7.8.1 >120m (aborted) :x: (OutOfMemory) Memory increaded to 1024m
7.8.1 ~15m :x: (OutOfMemory) With default memory of 256m
7.8.0 ~15m :x: (OutOfMemory) With default memory of 256m
7.7.0 ~2m :heavy_check_mark: With default memory of 256m

The first tests are running quick and fine, but the more tests there are, the slower they are getting. Until finally throwing an OutOfMemoyError.

So something between 7.7.0 and 7.8.0 changed causing this behavior.

mshima commented 2 years ago

It’s related to testcontainer migration, azure doesn’t have docker support at windows. You will need to change embedded kafka to start an embedded version instead of testcontainer: https://github.com/jhipster/generator-jhipster/blob/cfc70f498ad2611f3fe366858753506bf0b01291/generators/server/templates/src/test/java/package/config/TestContainersSpringContextCustomizerFactory.java.ejs#L219-L229

And use dev profile to test with h2 instead of postgresql

There were no kafka test before v7.8.0. So kafka tests and testcontainer can be removed too.

egvimo commented 2 years ago

@mshima, you are right. It is related to Kafka, but testcontainers are working well. We migrated our whole backend integration tests to testcontainers. So it's not related to them. Btw. we are using Azure DevOps CI with Ubuntu (the same is true for Github Actions).

So I think there is a Memory Leak in the TestContainersSpringContextCustomizerFactory class.

Tcharl commented 2 years ago

@egvimo if you ever find this leak please fix! I wasn't 100% sure when implementing this class and there are not that much samples of usage in there...

egvimo commented 2 years ago

@Tcharl it seems that JHipster Kotlin has the same issue. I've fixed it by reusing the ContextCustomizer instead of creating every time a new one in the factory method. At least this worked with Kotlin. Didn't tried with Java yet. Something like:

public class TestContainersSpringContextCustomizerFactory implements ContextCustomizerFactory {

    // ...
    private static ContextCustomizer contextCustomizer;

    @Override
    public ContextCustomizer createContextCustomizer(Class<?> testClass, List<ContextConfigurationAttributes> configAttributes) {
        if (contextCustomizer == null) {
            contextCustomizer = (context, mergedConfig) -> {
                ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
                // ...
            };
        }
        return contextCustomizer;
    }
}