From 6aa7788fd034da1ac8afa9055e3cc92c7a2e70a6 Mon Sep 17 00:00:00 2001 From: Colin Henry Date: Mon, 10 Mar 2025 22:59:03 -0700 Subject: [PATCH] added transactional wrapper, and support for it --- database/actor.go | 6 ++++-- database/transactor.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 database/transactor.go 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 + } +}