I have a Spring Boot Application (Version 2.5.3) with enabled SSL using a self signed certificate. One endpoint is used to download a file in the client using a StreamingResponseBody.
The Problem is, when a user requests a file via this endpoint, the connection pool doesn't get cleaned up. Working example showcasing the problem here: https://github.com/smotastic/blocked-connection-pool
@GetMapping("/ping")
public ResponseEntity<StreamingResponseBody> ping() {
// service.findSomeFile(...)
StreamingResponseBody body = new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// outputStream.write(someFile);
}
};
return ResponseEntity.ok(body);
}
After calling this, and turning on the debug logging for the Hikari Connection Pool, the following will be logged.
Pool stats (total=10, active=1, idle=9, waiting=0)
As you can see, one connection is active and will never be released again. If I call this endpoint 9 more times, the connection pool is full and any subsequent requests will run into a timeout.
The combination of having a ssl enabled application, the call to a service or a repository and the return value of a StreamingResponseBody seems to be my problem here. As soon as i turn one of these factors off, the problem disappears and the connection gets released as expected.
// SecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
http.requiresChannel().anyRequest().requiresSecure(); // turning this off 'solves' the problem
}
In the example i provided a simple FooEntity and a FooRepository, where i just call the count method. I guess that this opens a new transaction and provokes the error here.
// Controller.java
@Autowired
FooRepository fooRepo;
@GetMapping("/ping")
public ResponseEntity<StreamingResponseBody> ping() {
fooRepo.count(); // removing this line 'solves' the error
StreamingResponseBody body = new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// outStream.write(...)
}
};
return ResponseEntity.ok(body);
}
The FooRepository is in this case just a simple CrudRepository from Spring, with no extra functionality.
// FooRepository.java
@Repository
public interface FooRepository extends CrudRepository<Foo, Long>{
}
The FooEntity is a simple entity with an id.
// Foo.java
@Entity
public class Foo {
@GeneratedValue
@Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
In this example i'm using a h2 datasource, but i also tried it with postgres. Both are not working.
# application.properties
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
The ssl is enabled via the following configuration.
# https via self signed certificate
server.ssl.key-store-type=${SSL_KEY_STORE_TYPE:PKCS12}
# The path to the keystore containing the certificate
server.ssl.key-store=${SSL_KEY_STORE:classpath:identity.p12}
# The password used to generate the certificate
server.ssl.key-store-password=${SSL_KEY_STORE_PASSWORD:hello}
# The alias mapped to the certificate
server.ssl.key-alias=${SSL_KEY_ALIAS:mykey}
server.ssl.enabled=${SSL_ENABLED:true}
The certificate is a self signed certificate, created on my Windows 10 machine.
Also, the Hikari Connection Pool doesn't seem to be the problem. I also tried the Tomcat JDBC Connection Pool. The Pool still gets blocked.
# application.properties
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
try adding
spring.jpa.open-in-view=false
in properties.
Introducing Open Session in View
Running out of JDBC connections when loading entity in StreamingResponseBody
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With