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/
35 lines
3.2 KiB
Markdown
35 lines
3.2 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to this module will be documented in this file.
|
|
|
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
|
## [0.9.0] - 2026-03-18
|
|
|
|
### Added
|
|
|
|
- `Executor` interface: `ExecContext`, `QueryContext`, `QueryRowContext` using `database/sql` types (`sql.Result`, `*sql.Rows`, `*sql.Row`).
|
|
- `Tx` interface: embeds `Executor` and adds `Commit() error` and `Rollback() error` (no context argument, matching `database/sql` semantics).
|
|
- `Client` interface: `GetExecutor(ctx context.Context) Executor`, `Begin(ctx context.Context) (Tx, error)`, `Ping(ctx context.Context) error`, `HandleError(err error) error`.
|
|
- `Component` interface: composes `launcher.Component`, `health.Checkable`, and `Client`.
|
|
- `UnitOfWork` interface: `Do(ctx context.Context, fn func(ctx context.Context) error) error`.
|
|
- `Config` struct: fields `Host`, `Port`, `User`, `Password`, `Name`, `MaxConns`, `MinConns`, `MaxConnLifetime`, `MaxConnIdleTime`; settable via `MYSQL_*` environment variables with defaults (port `3306`, max conns `5`, idle conns `2`, lifetime `1h`, idle time `30m`).
|
|
- `Config.DSN() string`: constructs a `go-sql-driver` DSN in `user:pass@tcp(host:port)/db?parseTime=true&loc=UTC` format.
|
|
- `New(logger logz.Logger, cfg Config) Component`: returns a `*sql.DB`-backed component; the connection is opened lazily in `OnInit`.
|
|
- Lifecycle hooks: `OnInit` calls `sql.Open`, sets pool limits, and parses duration config fields; `OnStart` pings with a 5-second timeout; `OnStop` closes the `*sql.DB`.
|
|
- `health.Checkable` implementation: `HealthCheck` delegates to `Ping`; `Name()` returns `"mysql"`; `Priority()` returns `health.LevelCritical`.
|
|
- `NewUnitOfWork(logger logz.Logger, client Client) UnitOfWork`: wraps a `Client` to provide transactional `Do` semantics; rolls back and logs on error, commits on success.
|
|
- `HandleError(err error) error` (package-level function): maps `*mysqldrv.MySQLError` error 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.Tx` is stored under an unexported `ctxTxKey{}` context key; `GetExecutor` returns it when found, otherwise returns `*sql.DB`.
|
|
- All `*sql.DB` reads guarded by `sync.RWMutex` for safe concurrent access.
|
|
- `go-sql-driver/mysql` is imported with a blank identifier in `mysql.go` for driver side-effect registration, and as `mysqldrv` in `errors.go` to avoid the package name collision.
|
|
|
|
### Design Notes
|
|
|
|
- `Tx.Commit()` and `Tx.Rollback()` intentionally omit a `context.Context` argument, honestly reflecting the `database/sql` limitation rather than accepting and ignoring one.
|
|
- The module is structurally parallel to `postgres` but uses `database/sql` types 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.
|
|
|
|
[0.9.0]: https://code.nochebuena.dev/go/mysql/releases/tag/v0.9.0
|