database/sql-backed MySQL client with launcher lifecycle, health check, unit-of-work via context injection, and structured error mapping. What's included: - Executor / Tx / Client / Component interfaces using database/sql native types (sql.Result, *sql.Rows, *sql.Row) - Tx.Commit() / Tx.Rollback() without ctx, matching the honest database/sql contract - New(logger, cfg) constructor; *sql.DB opened in OnInit - Config struct with env-tag support for all pool tuning parameters - UnitOfWork via context injection; GetExecutor(ctx) returns active *sql.Tx or *sql.DB - HandleError mapping MySQLError.Number to xerrors codes (1062 → AlreadyExists, 1216/1217/1451/1452 → InvalidInput, ErrNoRows → NotFound) - Driver imported as mysqldrv alias to avoid package name collision - health.Checkable at LevelCritical; HealthCheck delegates to db.PingContext Tested-via: todo-api POC integration Reviewed-against: docs/adr/
3.2 KiB
3.2 KiB
Changelog
All notable changes to this module will be documented in this file.
The format is based on Keep a Changelog, and this module adheres to Semantic Versioning.
0.9.0 - 2026-03-18
Added
Executorinterface:ExecContext,QueryContext,QueryRowContextusingdatabase/sqltypes (sql.Result,*sql.Rows,*sql.Row).Txinterface: embedsExecutorand addsCommit() errorandRollback() error(no context argument, matchingdatabase/sqlsemantics).Clientinterface:GetExecutor(ctx context.Context) Executor,Begin(ctx context.Context) (Tx, error),Ping(ctx context.Context) error,HandleError(err error) error.Componentinterface: composeslauncher.Component,health.Checkable, andClient.UnitOfWorkinterface:Do(ctx context.Context, fn func(ctx context.Context) error) error.Configstruct: fieldsHost,Port,User,Password,Name,MaxConns,MinConns,MaxConnLifetime,MaxConnIdleTime; settable viaMYSQL_*environment variables with defaults (port3306, max conns5, idle conns2, lifetime1h, idle time30m).Config.DSN() string: constructs ago-sql-driverDSN inuser:pass@tcp(host:port)/db?parseTime=true&loc=UTCformat.New(logger logz.Logger, cfg Config) Component: returns a*sql.DB-backed component; the connection is opened lazily inOnInit.- Lifecycle hooks:
OnInitcallssql.Open, sets pool limits, and parses duration config fields;OnStartpings with a 5-second timeout;OnStopcloses the*sql.DB. health.Checkableimplementation:HealthCheckdelegates toPing;Name()returns"mysql";Priority()returnshealth.LevelCritical.NewUnitOfWork(logger logz.Logger, client Client) UnitOfWork: wraps aClientto provide transactionalDosemantics; rolls back and logs on error, commits on success.HandleError(err error) error(package-level function): maps*mysqldrv.MySQLErrorerror numbers to xerrors —1062(duplicate entry) →ErrAlreadyExists;1216,1217,1451,1452(foreign key violations) →ErrInvalidInput;sql.ErrNoRows→ErrNotFound; all other errors →ErrInternal.- Transaction context injection: the active
*sql.Txis stored under an unexportedctxTxKey{}context key;GetExecutorreturns it when found, otherwise returns*sql.DB. - All
*sql.DBreads guarded bysync.RWMutexfor safe concurrent access. go-sql-driver/mysqlis imported with a blank identifier inmysql.gofor driver side-effect registration, and asmysqldrvinerrors.goto avoid the package name collision.
Design Notes
Tx.Commit()andTx.Rollback()intentionally omit acontext.Contextargument, honestly reflecting thedatabase/sqllimitation rather than accepting and ignoring one.- The module is structurally parallel to
postgresbut usesdatabase/sqltypes throughout; the two modules are intentionally type-incompatible. - MySQL error codes are matched by numeric constant via
MySQLError.Number, not by string parsing, for stability across MySQL and MariaDB versions.