yegor256/rultor

`Ec2Client` from `AwsEc2.aws()` is never closed; AWS SDK v2 connections leak in `StartsInstance` and `TerminatesInstance`

Open

#2,322 opened on May 11, 2026

View on GitHub
 (1 comment) (0 reactions) (0 assignees)Java (165 forks)github user discovery
bughelp wanted

Repository metrics

Stars
 (575 stars)
PR merge metrics
 (PR metrics pending)

Description

What happens

In src/main/java/com/rultor/agents/aws/AwsEc2.java, the method aws() builds and returns a fresh Ec2Client on every invocation:

public Ec2Client aws() {
    return Ec2Client.builder()
        .region(Region.of(this.region))
        .credentialsProvider(StaticCredentialsProvider.create(
            AwsBasicCredentials.create(this.key, this.secret)
        ))
        .build();
}

software.amazon.awssdk.services.ec2.Ec2Client (AWS SDK v2) extends SdkAutoCloseable. Each instance owns an Apache or Netty HTTP client, a thread pool, and a connection pool, and the SDK documentation explicitly states clients are expensive and must be closed.

The two call sites in the same package never close the returned client:

  • StartsInstance.java:161-162this.api.aws().runInstances(request)
  • TerminatesInstance.java:47this.api.aws().terminateInstances(...)

Each agent tick that starts or terminates an EC2 instance therefore leaks one full AWS SDK client. Over a long-running rultor deployment that processes many builds, the leaked threads and connection pools accumulate.

What should happen

Either cache a single Ec2Client as a field on AwsEc2 and reuse it across calls (the SDK v2 client is thread-safe), or wrap each call site in a try-with-resources:

try (Ec2Client client = this.api.aws()) {
    client.runInstances(request);
}

The choice depends on how @Immutable is intended to constrain AwsEc2. Either fix eliminates the per-call leak.

Contributor guide