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/
2.3 KiB
ADR-002: No Context on Tx.Commit and Tx.Rollback
Status: Accepted Date: 2026-03-18
Context
The postgres module's Tx interface defines Commit(ctx context.Context) error and Rollback(ctx context.Context) error because pgx.Tx supports context-aware commit and rollback — the operation can be cancelled if the context is already done.
The database/sql standard library's *sql.Tx does not support this. Its Commit() and Rollback() methods have no context parameter:
// from database/sql:
func (tx *Tx) Commit() error
func (tx *Tx) Rollback() error
Adding a ctx parameter to the mysql.Tx interface would be a lie: the implementation would have to ignore the context, potentially masking cancellations or deadlines that callers believe they are enforcing.
Decision
The mysql.Tx interface is defined with context-free commit and rollback, matching the database/sql contract honestly:
type Tx interface {
Executor
Commit() error
Rollback() error
}
The UnitOfWork.Do implementation calls tx.Rollback() and tx.Commit() without passing a context. This is consistent with the interface definition and with database/sql semantics.
This means mysql.Tx and postgres.Tx are not structurally compatible — this is intentional. Callers must not attempt to use one in place of the other.
Consequences
- Positive: The interface is an honest representation of what
database/sqlprovides. No silent context-ignore bugs. - Positive:
mysqlTx.Commit()andmysqlTx.Rollback()delegate directly to*sql.Tx.Commit()and*sql.Tx.Rollback()with no wrapper complexity. - Negative: A commit or rollback cannot be cancelled mid-flight via context in MySQL. If the database is slow or unreachable during commit, the goroutine blocks until the driver-level timeout fires.
- Negative: The API asymmetry between
mysql.Txandpostgres.Txmeans that generic code written against one cannot be used against the other. This is a known limitation of the design and the reason the two modules are separate. - Note: Go 1.15 added
*sql.Tx.Commit()and*sql.Tx.Rollback()that honour the context passed toBeginTx— but still do not accept a per-call context. The limitation is inherent todatabase/sql.