Can't connect to DB after isListeningNow succeed
kasecato opened this issue · 6 comments
- version: 0.32.0
- db: MySQL 8
- junit: 4.12
- docker: 17.03.1
- os: macOS Sierra
- reproduction repository: https://github.com/k--kato/docker-compose-rule-db-parallel
I cannot connect to MySQL even after isListeningNow
succeeded. How can I wait until JDBC can connect?
private DockerComposeRule dockerDb;
private Connection conn;
@Before
public void before() throws IOException, InterruptedException, SQLException {
dockerDb = DockerComposeRule.builder()
.file("src/test/resources/docker-compose-test.yml")
.waitingForService("db", HealthChecks.toHaveAllPortsOpen())
.addClusterWait(new ClusterWait(
ClusterHealthCheck.transformingHealthCheck(
cluster -> cluster.container("db").port(3306),
target -> SuccessOrFailure.fromBoolean(target.isListeningNow(), target + " was not opened")),
Duration.standardSeconds(30)))
.build();
dockerDb.before();
final MysqlDataSource dataSource = new MysqlDataSource() {{
final String url = dockerDb.containers().container("db").port(3306).inFormat("jdbc:mysql://$HOST:$EXTERNAL_PORT/sampledb");
setURL(url);
setUser("root");
setPassword("sa");
}};
conn = dataSource.getConnection();
// I don't want to use like this codes
/*
while (true) {
try {
conn = dataSource.getConnection();
break;
} catch (Exception e) {
Thread.sleep(1000);
}
}
*/
}
@After
public void tearDown() {
dockerDb.after();
}
@Test
public void test1() throws SQLException {
conn.prepareStatement("SELECT 1").executeQuery();
}
I already wrote a JdbcContainerHealthCheck
for my project, was originally going to submit a PR back here, but was unsure if it would be appropriate for tracking individual Health check types?
Would it be fair to have it under HealthChecks
? Or is it too specific of a use case?
Either way I would be happy to contribute it back.
@dotCipher I'd love to use JdbcContainerHealthCheck
. Is it possible to paste code here?
@iamdanfox / @hpryce for input here, but I think the most scalable solution would be to commit back to the product instead of pasting code around.
I'd be more than happy to open a PR, but what kind of structure should we apply to arbitrary one-off HealthCheck<T>
types? Gradle sub-module like health-checks-<type>
or just in some package that comes with docker-compose-rule-core
?
@dotCipher I 100% agree another gradle project called something like health-checks would be amazing... I literally just wrote a crappy one again for the hundredth time! I personally don't think I have bandwidth to get to this, but I think we should definitely get a PR from someone merged in with one of these.
@ClassRule public static final DockerComposeRule docker = DockerComposeRule.builder()
.file("../docker-compose.yml")
.saveLogsTo(LogDirectory.circleAwareLogDirectory(MyIntegTest.class))
.waitingForService("postgres", new WaitForPostgres())
.build()
import com.palantir.docker.compose.connection.Container;
import com.palantir.docker.compose.connection.waiting.HealthCheck;
import com.palantir.docker.compose.connection.waiting.SuccessOrFailure;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class WaitForPostgres implements HealthCheck<Container> {
@Override
public SuccessOrFailure isHealthy(Container target) {
String uri = target.port(5432)
.inFormat("jdbc:postgresql://localhost:$EXTERNAL_PORT/database");
return SuccessOrFailure.onResultOf(() -> select1(uri));
}
private boolean select1(String uri) throws SQLException {
try (Connection connection = DriverManager.getConnection(uri, "username", "password");
ResultSet result = connection.createStatement().executeQuery("SELECT 1")) {
result.next();
return result.getInt(1) == 1;
}
}
}
Instead of using the healthchecks defined in code, I would suggest implementing your healthchecks in docker-compose
since it is now supported since version 2.1. For your example, you could use the following docker-compose
directive in your file for a postgrs container (use the mysql client instead for your purpose):
healthcheck:
test: ["CMD", "psql", "-h", "localhost", "-c", "SELECT version();"]
interval: 1s
timeout: 5s
retries: 12
This will only pass when a JDBC connection can be made.