Class PreStartableTCIFactory<C extends org.testcontainers.containers.GenericContainer<C>,I extends TCI<C>>
- All Implemented Interfaces:
AutoCloseable,TCIFactory<C,I>
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
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 connectis 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
- Due to a bug the host-ports of the container must be fixated. See
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprotected static final recordprotected static final recordstatic class -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected final ThreadPoolExecutorprotected final StringName (used for Thread-names and Logging)protected final AtomicIntegerprotected final AtomicIntegerprotected final LinkedBlockingQueue<PreStartableTCIFactory.StartingInfra<I>>protected final PreStartableTCIFactory.Timeoutsprotected final booleanHas the following effects:true(default) - Directly attaches the Container to the network during startup if possiblefalse- Performs a Network#connect as if PreStarting is active.Fields inherited from class software.xdev.tci.factory.BaseTCIFactory
containerBaseName, containerBuilder, containerLoggerName, getNewTryCount, infraBuilder, returnedAndInUse, tracer, warmedUp -
Constructor Summary
ConstructorsConstructorDescriptionPreStartableTCIFactory(BiFunction<C, String, I> infraBuilder, Supplier<C> containerBuilder, String containerBaseName, String containerLoggerName, String name) PreStartableTCIFactory(BiFunction<C, String, I> infraBuilder, Supplier<C> containerBuilder, String containerBaseName, String containerLoggerName, String name, PreStartConfig config, PreStartableTCIFactory.Timeouts timeouts) -
Method Summary
Modifier and TypeMethodDescriptionprotected PreStartableTCIFactory.StartingInfra<I>acquireNew(PreStartableTCIFactory.DirectNetworkAttachInfo directAttachNetwork) protected PreStartableTCIFactory.StartingInfra<I>bootNew(PreStartableTCIFactory.DirectNetworkAttachInfo directAttachNetwork) protected PreStartableTCIFactory.StartingInfra<I>bootNew(PreStartableTCIFactory.DirectNetworkAttachInfo directAttachNetwork, boolean preStarted) voidclose()protected voidconnectContainerToNetwork(org.testcontainers.containers.GenericContainer<?> container, org.testcontainers.containers.Network network, List<String> aliases) protected voidprotected booleanprotected InewInternal(org.testcontainers.containers.Network network, String... aliases) protected voidpostProcessNew(I infra) This method can be used for post-processing after new infra was acquired.protected voidvoidMethods inherited from class software.xdev.tci.factory.BaseTCIFactory
buildContainer, getLogConsumer, getReturnedAndInUse, getTracer, handleInfraStartFail, log, registerReturned, setGetNewTryCount, warmUp, warmUpInternal, warmUpSyncMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface software.xdev.tci.factory.TCIFactory
getFactoryName, register, unregister
-
Field Details
-
name
Name (used for Thread-names and Logging) -
useDirectNetworkAttachIfPossible
protected final boolean useDirectNetworkAttachIfPossibleHas the following effects:true(default) - Directly attaches the Container to the network during startup if possiblefalse- Performs a Network#connect as if PreStarting is active. This is slower however it emulates PreStarting better and may help finding bugs.
-
preStartQueue
protected final LinkedBlockingQueue<PreStartableTCIFactory.StartingInfra<I extends TCI<C>>> preStartQueue -
nextThreadId
-
executorService
-
preStartCounter
-
timeouts
-
-
Constructor Details
-
PreStartableTCIFactory
-
PreStartableTCIFactory
public PreStartableTCIFactory(BiFunction<C, String, I> infraBuilder, Supplier<C> containerBuilder, String containerBaseName, String containerLoggerName, String name, PreStartConfig config, PreStartableTCIFactory.Timeouts timeouts)
-
-
Method Details
-
registerToPreStartCoordinator
protected void registerToPreStartCoordinator() -
schedulePreStart
public void schedulePreStart() -
bootNew
protected PreStartableTCIFactory.StartingInfra<I> bootNew(PreStartableTCIFactory.DirectNetworkAttachInfo directAttachNetwork) -
bootNew
protected PreStartableTCIFactory.StartingInfra<I> bootNew(PreStartableTCIFactory.DirectNetworkAttachInfo directAttachNetwork, boolean preStarted) -
acquireNew
protected PreStartableTCIFactory.StartingInfra<I> acquireNew(PreStartableTCIFactory.DirectNetworkAttachInfo directAttachNetwork) -
newInternal
-
connectContainerToNetwork
-
getNew
-
postProcessNew
This method can be used for post-processing after new infra was acquired.Example:
Docker needs a few milliseconds (usually less than 100) to reconfigure its networks.
In the meantime existing connections might fail.
This method can be used to validate these connections. -
isPreStartingDisabled
protected boolean isPreStartingDisabled() -
close
public void close() -
handleInterrupt
-