Class PreStartableTCIFactory<C extends org.testcontainers.containers.GenericContainer<C>,I extends TCI<C>>

java.lang.Object
software.xdev.tci.factory.BaseTCIFactory<C,I>
software.xdev.tci.factory.prestart.PreStartableTCIFactory<C,I>
All Implemented Interfaces:
AutoCloseable, TCIFactory<C,I>

public class PreStartableTCIFactory<C extends org.testcontainers.containers.GenericContainer<C>,I extends TCI<C>> extends BaseTCIFactory<C,I>
A PreStarting-able implementation of TCIFactory.

What is PreStarting?

When running tests usually there are certain times when the available resources are barely utilized:

PreStarting uses a "cached" pool of infrastructure and tries to utilizes these idle times to fill/replenish this pool.
So that when new infrastructure is requested there is no need to wait for the creation of it and use the already started infrastructure from this pool - if it's available.

Requirements

Infrastructure needs to be dependency- and stateless:

This means that e.g. a container can be started without relying on another container.
If another infrastructure/container is needed when doing e.g. a certain request during testing it's advised to use DNS names inside the initial configuration.
The infrastructure may also be configured in a way that test specific data can be created before the test e.g. using a client and not during PreStarting.

Important: PreStarting is disabled by default! So when executing test manually, one can solely focus on the test.
For more information have a look at PreStartConfig and it's implementation(s).

In which situation has this the greatest advantage?

and "Why not just use parallel test execution?"
  • Sometimes parallel test execution is not possible
  • When starts of containers/infrastructure and/or test execution takes a long time and uses not that many resources.
    Note however that this is highly situational and depends on used hardware and infrastructure
  • The general design principle of dependency- and statelessness allows multiple containers to be started in parallel during test initialization even WITHOUT enabled PreStarting.
    Example:
      // Szenario 1: Conventionally starting infrastructures
      dbInfra = DB_INFRA_FACTORY.getNew(network, DNS_NAME_DB); // Starts in 10s
      authInfra = AUTH_INFRA_FACTORY.getNew(network, DNS_NAME_AUTH); // Starts in 10s
    
      appInfra = APP_INFRA_FACTORY.getNew(network, DNS_NAME_APP); // Starts in 20s
    
      // Maximum total start time would be ~40s
    
    
      // Szenario 2 (better): Starting infrastructures in parallel
      var cfDBInfra = CompletableFuture.supplyAsync(() -> DB_INFRA_FACTORY.getNew(network, DNS_NAME_DB));
      var cfAuthInfra = CompletableFuture.supplyAsync(() -> AUTH_INFRA_FACTORY.getNew(network, DNS_NAME_AUTH));
    
      appInfra = APP_INFRA_FACTORY.getNew(network, DNS_NAME_APP);
    
      dbInfra = cfDBInfra.join();
      authInfra = cfAuthInfra.join();
    
      // Maximum total start time would be ~20s
             
Therefore, it's highly recommended to try multiple options.
E.g. enabling/disabling PreStarting and using a different amount of JUnit parallelization.

Caveats of PreStarting

Currently, PreStarting has the following trade-offs:
  • As PreStarted containers/infrastructure are kept in a "cache" pool additional RAM/memory is required
  • To connect PreStarted containers to a network where they can communicate with each other docker network connect is used. This command is not quite optimal:
    • Due to a bug the host-ports of the container must be fixated. See PortFixation
    • When lots of containers and networks are active the command can get quite slow and may needs a few seconds to execute