diff --git a/example/server/docker-compose.yml b/example/server/docker-compose.yml index 01b8d266..77ed1896 100644 --- a/example/server/docker-compose.yml +++ b/example/server/docker-compose.yml @@ -18,7 +18,10 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + # Use 127.0.0.1 (TCP) instead of localhost (Unix socket). MySQL treats + # "localhost" as a socket connection, which can be ready before the TCP + # listener — causing dependent services that connect over TCP to fail. + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 @@ -33,7 +36,7 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 diff --git a/example/server/gateway/docker-compose.yml b/example/server/gateway/docker-compose.yml index 292f65d1..7c9e84ec 100644 --- a/example/server/gateway/docker-compose.yml +++ b/example/server/gateway/docker-compose.yml @@ -18,7 +18,10 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + # Use 127.0.0.1 (TCP) instead of localhost (Unix socket). MySQL treats + # "localhost" as a socket connection, which can be ready before the TCP + # listener — causing dependent services that connect over TCP to fail. + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 @@ -33,7 +36,7 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 diff --git a/example/server/orchestrator/docker-compose.yml b/example/server/orchestrator/docker-compose.yml index 36dd44d1..0f5b3505 100644 --- a/example/server/orchestrator/docker-compose.yml +++ b/example/server/orchestrator/docker-compose.yml @@ -18,7 +18,10 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + # Use 127.0.0.1 (TCP) instead of localhost (Unix socket). MySQL treats + # "localhost" as a socket connection, which can be ready before the TCP + # listener — causing dependent services that connect over TCP to fail. + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 @@ -33,7 +36,7 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 diff --git a/test/integration/extension/counter/mysql/docker-compose.yml b/test/integration/extension/counter/mysql/docker-compose.yml index 82be3b01..2f129aeb 100644 --- a/test/integration/extension/counter/mysql/docker-compose.yml +++ b/test/integration/extension/counter/mysql/docker-compose.yml @@ -11,7 +11,10 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + # Use 127.0.0.1 (TCP) instead of localhost (Unix socket). MySQL treats + # "localhost" as a socket connection, which can be ready before the TCP + # listener — causing dependent services that connect over TCP to fail. + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 diff --git a/test/integration/extension/queue/mysql/docker-compose.yml b/test/integration/extension/queue/mysql/docker-compose.yml index 487587b9..1b17364b 100644 --- a/test/integration/extension/queue/mysql/docker-compose.yml +++ b/test/integration/extension/queue/mysql/docker-compose.yml @@ -11,7 +11,10 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + # Use 127.0.0.1 (TCP) instead of localhost (Unix socket). MySQL treats + # "localhost" as a socket connection, which can be ready before the TCP + # listener — causing dependent services that connect over TCP to fail. + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 diff --git a/test/integration/extension/storage/mysql/docker-compose.yml b/test/integration/extension/storage/mysql/docker-compose.yml index 79be09e0..9a6c3baa 100644 --- a/test/integration/extension/storage/mysql/docker-compose.yml +++ b/test/integration/extension/storage/mysql/docker-compose.yml @@ -11,7 +11,10 @@ services: ports: - "3306" # Random ephemeral port to avoid conflicts healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-proot"] + # Use 127.0.0.1 (TCP) instead of localhost (Unix socket). MySQL treats + # "localhost" as a socket connection, which can be ready before the TCP + # listener — causing dependent services that connect over TCP to fail. + test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-proot"] interval: 5s timeout: 5s retries: 10 diff --git a/test/testutil/compose.go b/test/testutil/compose.go index 84aac5cf..1e423408 100644 --- a/test/testutil/compose.go +++ b/test/testutil/compose.go @@ -190,7 +190,8 @@ func (s *ComposeStack) ServiceHost(serviceName string, containerPort int) (strin } // ConnectMySQLService connects to a MySQL service by name in the compose stack. -// Retries the connection and registers cleanup automatically. +// Requires that Up() has been called first — the TCP-based healthcheck in +// docker-compose ensures MySQL is accepting TCP connections before Up() returns. func (s *ComposeStack) ConnectMySQLService(serviceName string) (*sql.DB, error) { s.t.Helper() @@ -199,24 +200,14 @@ func (s *ComposeStack) ConnectMySQLService(serviceName string) (*sql.DB, error) return nil, err } - // Retry connection a few times as MySQL might still be initializing - var db *sql.DB - for i := 0; i < 10; i++ { - db, err = sql.Open("mysql", dsn) - if err != nil { - return nil, fmt.Errorf("failed to open mysql connection: %w", err) - } - - if err = db.Ping(); err == nil { - break - } - - db.Close() - time.Sleep(1 * time.Second) + db, err := sql.Open("mysql", dsn) + if err != nil { + return nil, fmt.Errorf("failed to open mysql connection: %w", err) } - if err != nil { - return nil, fmt.Errorf("failed to connect to %s mysql after retries: %w", serviceName, err) + if err = db.Ping(); err != nil { + db.Close() + return nil, fmt.Errorf("failed to ping %s mysql: %w", serviceName, err) } port, _ := s.ServicePort(serviceName, 3306) // We already got the port successfully