Skip to content

fix: retrospective review fixes for merged PR #97#115

Merged
ikeikeikeike merged 1 commit into
mainfrom
fix/retro-review-pr97
Jul 16, 2026
Merged

fix: retrospective review fixes for merged PR #97#115
ikeikeikeike merged 1 commit into
mainfrom
fix/retro-review-pr97

Conversation

@ikeikeikeike

Copy link
Copy Markdown
Member

Retrospective /code-review sweep of merged PR #97 (kind: compose — wrap an existing docker-compose.yml as an engine), which shipped without pre-merge review.

Finding: the only mysql-usable readiness probe is a bare TCP dial

kind: compose's ReadyCheck picks a probe via extras["compose.ready_probe"], supporting tcp (default) / redis / postgres / http. There is no mysql option — so a compose-wrapped mysql service (the exact motivating use case in the sibling, unmerged PR #96) has no way to get a real readiness check; it is stuck on the bare-dial tcp default.

That reintroduces, for kind: compose, the exact race plugins/engine/mysql/docker.go's dockerReadyCheck already had to fix for the bundled Docker backend (see its TestDockerReadyCheck_NoRaceWithTemporaryServer regression test): a Docker host port's forwarding accepts the TCP handshake the instant the container starts, regardless of whether the process inside is listening yet. The official mysql:8.x image runs a --skip-networking "temporary server" to bootstrap the datadir before restarting as the final, network-enabled server — a dial-only probe goes ready during that window.

Confirmed empirically, not just by reasoning: brought up a fresh mysql:8.4 container via docker compose up -d and polled the actual probeOnce function from this package every 100ms. Results:

16:52:41.424 tcp=true(<nil>) mysql=false(EOF)
...  (~1.6s of tcp=true / mysql=false)
16:52:43.058 tcp=true(<nil>) mysql=true(<nil>)

For ~1.6 seconds, the current tcp probe (bare dial+close) reported the service ready while mysqld was still bootstrapping and any real connection attempt got EOF — i.e., bough create would declare a compose-wrapped mysql engine ready before it can actually serve a query, breaking any post_create hook (e.g. a migration) that connects immediately after Up returns.

Fix

Add a mysql compose.ready_probe option that reads the server's unsolicited Initial Handshake Packet and checks its protocol-version byte (0x0a, unchanged since MySQL 4.1) instead of just dialing — the same technique the mysql plugin's own conformance regression test already relies on (plugins/engine/mysql/conformance_test.go's mysqlHandshakeOnce).

  • plugins/engine/compose/probe.go: new mysqlProbe, wired into probeOnce's "mysql" case, error message updated.
  • plugins/engine/compose/compose.go: package doc updated to describe the option and why it matters for mysql specifically.
  • README.md: compose.ready_probe documentation updated to list mysql and call out the risk of leaving it unset for a wrapped mysql service.
  • plugins/engine/compose/probe_test.go: TestProbeOnce_MySQL (valid handshake), TestProbeOnce_MySQL_WrongProtocolVersion, and TestProbeOnce_MySQL_AcceptThenClose (the regression guard for the accept-then-close docker-proxy race a bare TCP probe cannot see).

Default behavior for existing tcp/redis/postgres/http users is unchanged — this only adds the missing, correct option for mysql.

Test plan

  • go build ./...
  • go test ./... (full repo green, including new plugins/engine/compose tests)
  • go test ./plugins/engine/compose/... -race -v green
  • golangci-lint run ./... — 0 issues
  • Empirically verified against a real mysql:8.4 container via docker compose up -d (see finding above) — not just unit tests against a fake TCP server

kind: compose's ReadyCheck only offered tcp/redis/postgres/http as
extras["compose.ready_probe"] values, with "tcp" (a bare dial+close)
as the default and the ONLY option available for a wrapped mysql
service. That reintroduces, for any compose-wrapped mysql, the exact
race plugins/engine/mysql/docker.go's dockerReadyCheck already had to
fix for the bundled Docker backend: a Docker host port's forwarding
accepts the TCP handshake the instant the container starts, regardless
of whether mysqld is listening yet, so a dial-only probe goes ready
during the mysql:8.x image's --skip-networking "temporary server"
bootstrap phase.

Confirmed empirically against a real, freshly started mysql:8.4
container: the bare "tcp" probe reported ready for ~1.6s while mysqld
was still bootstrapping and any real connection attempt got EOF.

Adds a "mysql" ready_probe that reads the server's unsolicited Initial
Handshake Packet and checks its protocol-version byte (0x0a since
MySQL 4.1) instead of just dialing — mirroring the handshake check the
mysql plugin's own regression test already relies on. Default behavior
for existing tcp/redis/postgres/http users is unchanged; this only
adds the missing, correct option for mysql.
@ikeikeikeike
ikeikeikeike merged commit f3993bd into main Jul 16, 2026
11 checks passed
@ikeikeikeike
ikeikeikeike deleted the fix/retro-review-pr97 branch July 16, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant