Use the block-mysql driver - #1219
Conversation
strata links github.com/block/mysql — Block's fork of go-sql-driver/mysql — for capabilities upstream does not carry, and that fork is moving from a `replace` directive to its own module path (block/mysql#3), because `replace` is not inherited across module boundaries and so cannot reach consumers of a library. Once the path differs the two packages declare distinct types, and strata hands spirit a `*mysql.Config` directly (applier.Target.Config), so this is not optional: without it strata does not compile. A binary linking both would also carry two `*mysql.MySQLError` types, across which `errors.As` silently returns false. Mechanical: the import path, and `sql.Open("mysql", ...)` -> `"block-mysql"`. The DSN format, Config, and the rest of the API are unchanged, since the fork tracks upstream. Comments describing upstream driver behaviour still say go-sql-driver, which remains accurate — the fork inherits it. gtid_test.go also gets a sadscan:disable for a pre-existing false positive: the GTID source id 11111111-2222-3333-4444-555555555555 contains a 16-digit run starting with 4, which the Visa PAN rule matches. It is a MySQL server UUID, not card data. The line is unrelated to this change but the file is staged by it, which is what surfaced the finding. The dependency is pinned to the block/mysql PR branch and must be re-pointed at its master commit before this merges. Verified: build, vet and gofmt clean; full test suite passes against MySQL 8.0.44 (33 packages, 0 failures).
|
🤖 Adversarial correctness review — A 123-file find/replace is only reviewable by inspection if you can show it is one, so I checked that before reading anything. Every added line in the diff outside an import and a So nothing was swept up by the replace — no schema name, config key or What I'd want before this merges is a consumer-facing break that doesn't fail at compile time — and it happens to be the one that hits SchemaBot.
1 — the TLS registry crosses the module boundary, and this is the exit the compiler doesn't guard (med)The body's argument is about type identity, and for the surfaces it names that's right: strata stops compiling, loudly, which is the good failure.
It is the only exported function in The live consumer is The consumer-side fix is also not as simple as "move to the fork", which is the part most worth writing down: Two things would close this, both small on your side:
The mirror direction deserves a sentence too, since it's the same split running the other way: a consumer that registers its own TLS config with upstream and hands a 2 — one production call site has no test behind it (low)// pkg/lint/load.go
func LoadSchemaFromDSN(ctx context.Context, dsn string) ([]*statement.CreateTable, error) {
db, err := sql.Open("block-mysql", dsn)I mutated this one line back to The site itself is correct in this PR, so this isn't a defect in the diff. It's a statement about what the diff's correctness rests on: for every other call site a miss would have been caught by a test, and for this one it wouldn't. Either a small test that calls 3 — the "Why" section undercounts the compile-time surface (low)The body names Both break loudly, so this isn't a safety problem — it's that a consumer reading the body to size its own migration will size it wrong, and Same category, not its own row: the sadscan directive is in the canonical form used elsewhere in the org ( Verified — the suite, the probes, the pin, and three attacks that dissolvedLocal: A missed call site fails loudly, module-wide. I probed two test binaries rather than one, because Attack that dissolved: a second driver registering Attack that dissolved: go-sql-driver is still in Attack that dissolved: the pin. Leak check: clean. The sadscan directive puts an internal scanner's rule id in a public repo, which is the only thing in the diff that reads internal — but Both probe files were moved out of the tree and the mutation restored from backup; the worktree is clean at This review was generated by Claude Code (claude-opus-5). |
aparajon
left a comment
There was a problem hiding this comment.
🤖 Approving. The find/replace is provably exhaustive and the suite is green (33 packages, 0 failures locally). The one thing worth acting on before merge is in the review above: EnhanceDSNWithTLS now hands back a tls=rds DSN registered in block/mysql's registry, which breaks block/schemabot's mysqlconn at connect time when it bumps — reproduced with a control against main. Pulling dbconn.DriverName forward from the follow-up section is the cheap fix.
This stamp was left by Claude Code (claude-opus-5).
From review of #1219. The type-identity argument the PR body makes covers the surfaces the compiler guards; EnhanceDSNWithTLS is the one that escapes it, because it takes a string and returns a string. initRDSTLS registers "rds" in block/mysql's package-global TLS registry, and EnhanceDSNWithTLS hands back a DSN that names that entry. A consumer that opens the result with upstream go-sql-driver gets: invalid value / unknown config name: rds at connect time, with nothing in it about drivers. block/schemabot is the live case: pkg/mysqlconn.ConnectionDSN calls EnhanceDSNWithTLS and then opens with sql.Open("mysql", ...) — and it imports none of the driver-typed API, so the compile-time canary never fires for it. It would build clean, deploy, and fail on the first RDS dial after bumping spirit. const DriverName = "block-mysql" in pkg/dbconn makes the consumer-side fix sql.Open(dbconn.DriverName, ...), which stays correct through any future move without every consumer knowing the name. The doc comments on both DriverName and EnhanceDSNWithTLS state the coupling. Production call sites now use it, including pkg/lint/load.go — the one production sql.Open in the module with no test behind it (the reviewer mutated it back to "mysql" and all 33 packages still passed). A constant cannot be half-replaced. pkg/testutils keeps a private copy: dbconn's own tests import testutils, so importing dbconn there is a cycle. It is one const instead of nine literals. Also confirmed the sadscan directive rather than assuming it: `sadscan pkg/change/gtid_test.go` with the comment removed reports rule_id sq.pii.cc.visa at line 283, and reports nothing with it in place. 33 packages, 0 failures against MySQL 8.0.44.
… fork block/spirit#1219 squash-merged as dc3d4c9f, so the pin moves off the PR branch and onto a commit that is an ancestor of spirit main. The Dependencies section still named go-sql-driver, which after this change is not in polt's module graph at all.
* Use the block-mysql driver block/mysql is Block's fork of go-sql-driver/mysql, moving to its own module path (block/mysql#3) because a `replace` directive is not inherited across module boundaries. polt reaches the driver through spirit's dbconn, and spirit is moving with it (block/spirit#1219), so polt follows to keep one driver — and one set of driver types — in the binary. Mechanical: the import path, and `sql.Open("mysql", ...)` -> `"block-mysql"`. Nothing crosses a type boundary here; pkg/test's SetupDB reads fields off *mysql.Config to build a DSN string rather than handing the struct to spirit, so no API had to change. go-sql-driver leaves go.mod entirely, including as an indirect dependency. The block/mysql and block/spirit pins point at unmerged PR branches and must be re-pointed at merged commits before this merges. Verified: build, vet and gofmt clean; all 8 packages pass against MySQL 8.0.44. * Pin block/mysql at merged master (block/mysql#3) * Pin block/spirit at merged main (block/spirit#1219); README: name the fork block/spirit#1219 squash-merged as dc3d4c9f, so the pin moves off the PR branch and onto a commit that is an ancestor of spirit main. The Dependencies section still named go-sql-driver, which after this change is not in polt's module graph at all.
* feat(move): add host-aware autoscaling for sharded moves * fix(move): gate queued copy writes and cap host concurrency * refactor(move): rely on per-shard pools and shared load feedback * refactor(move): preserve copier ownership of throttling * refactor: share applier workers and move connection budgeting * fix(move): align progress and isolate reverse worker counts Signed-off-by: Morgan Tocker <mtocker@squareup.com> * refactor: split connection budgets and progress into separate PRs Signed-off-by: Morgan Tocker <mtocker@squareup.com> * fix: use block/mysql in new host and move autoscale files The merge of main brought in the single-driver switch (#1219, #1222) but this branch's new files still imported go-sql-driver/mysql, which no longer has a go.sum entry and is denied by depguard. * review: address adversarial review findings on #1216 1. Pin workerPool's seal-must-not-retire invariant with a test that fails when seal() calls resizeLocked(0). TestWorkerPoolLifecycle cannot see that mutation because it cancels the context immediately after seal. 2. Route every r.throttler read through currentThrottler(). The five direct reads were safe by call-graph ordering, not by anything visible. 3. Split the Aurora probe failure from the plainly-not-Aurora case, so an ordinary MySQL target no longer warns with "error": nil and a genuinely broken probe is distinguishable in the logs. 4. Record the port-or-extract decision for datasync autoscaling in AGENTS.md's drift list (declined, with the reason). 5. Fix the stale Stats() comment describing a deleted counter. --------- Signed-off-by: Morgan Tocker <mtocker@squareup.com>
Part of the block/mysql#3 wave.
Why
strata links
github.com/block/mysql, Block's fork of go-sql-driver/mysql, for capabilities upstream doesn't carry (QueryResultContext,Warnings()). That fork is moving from areplacedirective to its own module path, becausereplaceisn't inherited across module boundaries and so can't reach consumers of a library built on it.Once the module path differs, the two packages declare distinct types. For spirit that is not a subtle problem — strata hands spirit a
*mysql.Configdirectly:so without this change strata simply does not compile:
applier.Target.Configis the one strata hits, but it is not the only exported surface carrying a driver type — a consumer sizing its own migration should also expect:applier.Target.Config*mysql.Configcheck.SourceResource.Config(pkg/move/check)*mysql.Configdbconn.UnsafeWarningError.Warning*mysql.MySQLErrorAll three break loudly at compile time. Separately, a binary linking both packages carries two
*mysql.MySQLErrortypes, across whicherrors.Asreturns false silently rather than failing loudly —UnsafeWarningError.Warningis reachable by anyone classifying spirit's warnings witherrors.As.One break the compiler does not catch
EnhanceDSNWithTLStakes a string and returns a string, so nothing about it is typed.initRDSTLSregisters"rds"in block/mysql's package-global TLS registry, and the returned DSN names that entry. A consumer that opens the result with upstream go-sql-driver now fails at connect time:with nothing in the message about drivers:
block/schemabotis the live case.pkg/mysqlconn.ConnectionDSNcallsEnhanceDSNWithTLS, andmysqlconn.Openthen opens the result withsql.Open("mysql", …). It importsgithub.com/go-sql-driver/mysqlonly forParseDSN, and none of the three driver-typed declarations above, so it would build clean, deploy, and fail on the first RDS dial after bumping spirit past this commit.Consumers that call
EnhanceDSNWithTLSand open the result themselves must open it withblock-mysql.pkg/dbconnnow exportsDriverNamefor exactly this (sql.Open(dbconn.DriverName, …)), which stays correct through any future driver move.What's in it
Mechanical, 123 files: the import path, and
sql.Open("mysql", …)→sql.Open("block-mysql", …). The DSN format,Config, and the rest of the API are unchanged — the fork tracks upstream. Comments describing upstream driver behaviour still say go-sql-driver, which stays accurate since the fork inherits it.One unrelated line:
pkg/change/gtid_test.gogets a// sadscan:disable sq.pii.cc.visa. The GTID source id11111111-2222-3333-4444-555555555555contains a 16-digit run starting with4, which the Visa PAN rule matches. It's a MySQL server UUID, not card data. The line is pre-existing and untouched by this change, but staging the file surfaced the finding.dbconn.DriverNameThe bulk of this diff is a literal string repeated at 259 call sites, so
pkg/dbconnnow exports the name and the production opens use it. Tests still carry the literal (or, inpkg/testutils, a private copy — testutils cannot import dbconn, since dbconn's own tests import testutils), which keeps the diff reviewable by inspection while giving external consumers something stable to open with.Verification
go build ./...,go vet ./...,gofmt -lall clean🤖 Generated with Claude Code