2

I have moved the java integration tests to use elasticsearch test containers instead of using embedded elasticsearch. The tests have become slower by 1 hour which is a huge productivity hit. I am looking for ways to speed that up.

I tried using params like reuse on Elasticsearch container but that didn't make a dent. My latest configuration is

    private static final String ELASTICSEARCH_VERSION = "7.11.2";
    private static ElasticsearchContainer elasticsearchContainer;
    private static final DockerImageName ELASTICSEARCH_IMAGE =
          DockerImageName
                .parse("docker.elastic.co/elasticsearch/elasticsearch")
                .withTag(ELASTICSEARCH_VERSION);
            elasticsearchContainer = new ElasticsearchContainer(ELASTICSEARCH_IMAGE)
                  .withEnv("foo", "bar").withSharedMemorySize(1000000000L);
            elasticsearchContainer.addExposedPorts(9200, 9300);
            elasticsearchContainer.withStartupTimeout(Duration.of(5, ChronoUnit.MINUTES));
            elasticsearchContainer.start();
    private static RestHighLevelClient getRestHighLevelClient(ElasticsearchContainer container) {
        final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(AuthScope.ANY,
                                           new UsernamePasswordCredentials(ELASTICSEARCH_USERNAME,
                                                                           ELASTICSEARCH_PASSWORD));
        RestClientBuilder restClientBuilder = RestClient.builder(HttpHost.create(container.getHttpHostAddress()))
              .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
                    .setDefaultCredentialsProvider(credentialsProvider)
                    .setKeepAliveStrategy((response, context) -> 3 * 60 * 1000));
        // Try to prevent SocketTimeoutException when fetching larger batch size
        restClientBuilder.setRequestConfigCallback(
              requestConfigBuilder -> requestConfigBuilder.setSocketTimeout(2 * 60 * 1000));

        return new RestHighLevelClient(restClientBuilder);
    }

Gradle config (gradle daemon crashed once so increased to 2g)

org.gradle.jvmargs=-Xms2g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

Any suggestions on making the tests faster?

A_G
  • 2,260
  • 3
  • 23
  • 56

1 Answers1

5

General advice for any performance work or optimizations is to measure before introducing changes. I'd recommend profiling your test runs before you draw conclusions.

There are a few metrics there that you can estimate without extensive use of profilers.

  • How long does the Elastic container start?

For me it takes 8.2 seconds (I used elasticsearch-oss:7.10.2):

14:35:55.803 [main] INFO   [docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2] -
 Container docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2 
 started in PT8.264592S
  • How often do you start new containers? The Docker image will be naturally cached, so it's not a concern, but depending on how you organize your test suite the amount of time you start new containers can grow.

You can read more about the lifecycle of the containers managed by Testcontainers in the docs.

For example, if you use JUnit you can check whether you start new containers in @BeforeEach or @BeforeAll or are using a singleton container throughout the test suite.

  • How fast is the application in the container?

One of the factors is how much resources Docker is allowed to use. By default, Docker is configured to have 2G of memory which could be a bottleneck. If you don't have enough memory available in the container Elastic might behave slower than it should (even slower if it starts swapping, etc). Give it plenty of CPU and memory for fastest results.

If looking at these 3 things doesn't help, then perhaps you could profile the test run to check what are the bottlenecks.

Oleg Šelajev
  • 3,530
  • 1
  • 17
  • 25
  • Its dramatically high for me : 22/01/20 10:12:38 INFO [docker.elastic.co/elasticsearch/elasticsearch:7.11.2]: Container docker.elastic.co/elasticsearch/elasticsearch:7.11.2 started in PT50.984S – A_G Jan 20 '22 at 18:14
  • 1
    for me starting a Elasticsearch 7.16.3 testcontainer takes a little over 20 seconds. But I use only one for all of my integration tests, I think that's the crucial point. – P.J.Meisch Jan 20 '22 at 21:12
  • @AsthaGupta, can you please check how much resources Docker has access to? And does the container start faster if you give more memory/CPU? – Oleg Šelajev Jan 20 '22 at 23:01
  • 2
    This is perfect. I doubled the memory from 2Gb to 4 and increased the CPU from 8 to 10 and the same tests finished in less than a minute which were earlier taking 30 mins. Thanks again for all the helpful links! – A_G Jan 21 '22 at 00:32
  • Hi, (Disclaimer: Co-Founder&CTO @ Thundra) You can also use Thundra Foresight to trace, debug and detect performance bottlenecks in your tests: https://www.thundra.io/foresight – serkan_ozal Jan 21 '22 at 13:14
  • We (https://www.thundra.io/foresight) also have integration with Testcontainers so you can also trace and debug Testcontainers operations with Thundra Foresight (https://foresight.docs.thundra.io/) for detecting the points to be optimized – serkan_ozal Jan 21 '22 at 13:17
  • Elasticsearch 8 is even slower :( – Rob Audenaerde Jan 05 '23 at 11:00