diff --git a/database/actor.go b/database/actor.go index dad527d..29993a4 100644 --- a/database/actor.go +++ b/database/actor.go @@ -7,7 +7,7 @@ import ( "git.sdf.org/jchenry/x" ) -type Func func(db *sql.DB) +type Func func(ctx context.Context, db *sql.DB) error type Actor struct { DB *sql.DB @@ -19,7 +19,9 @@ func (a *Actor) Run(ctx context.Context) error { for { select { case f := <-a.ActionChan: - f(a.DB) + if err:= f(ctx, a.DB); err != nil{ + return err + } case <-ctx.Done(): return ctx.Err() } diff --git a/database/transactor.go b/database/transactor.go new file mode 100644 index 0000000..49b225f --- /dev/null +++ b/database/transactor.go @@ -0,0 +1,29 @@ +package database + +import ( + "context" + "database/sql" + "fmt" +) + +// WithinTransaction is a functional equivalent of the Transactor interface created by Thibaut Rousseau's +// https://blog.thibaut-rousseau.com/blog/sql-transactions-in-go-the-good-way/ +func WithinTransaction(f Func) Func { + return func(ctx context.Context, db *sql.DB) error { + tx, err := db.BeginTx(ctx, nil) + if err != nil { + return fmt.Errorf("failed to begin transaction: %w", err) + } + + if err := f(ctx, db); err != nil { + _ = tx.Rollback() + return err + } + + if err := tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + + return nil + } +}