refactored for maintainability
This commit is contained in:
118
internal/db/txhelpers.go
Normal file
118
internal/db/txhelpers.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/oslstats/internal/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/throw"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// TxFunc is a function that runs within a database transaction
|
||||
type (
|
||||
TxFunc func(ctx context.Context, tx bun.Tx) (bool, error)
|
||||
TxFuncSilent func(ctx context.Context, tx bun.Tx) error
|
||||
)
|
||||
|
||||
var timeout = 15 * time.Second
|
||||
|
||||
// WithReadTx executes a read-only transaction with automatic rollback
|
||||
// Returns true if successful, false if error was thrown to client
|
||||
func WithReadTx(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
conn *bun.DB,
|
||||
fn TxFunc,
|
||||
) bool {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
||||
defer cancel()
|
||||
ok, err := withTx(ctx, conn, fn, false)
|
||||
if err != nil {
|
||||
throw.InternalServiceError(s, w, r, "Database error", err)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// WithTxFailSilently executes a transaction with automatic rollback
|
||||
// Returns true if successful, false if error occured.
|
||||
// Does not throw any errors to the client.
|
||||
func WithTxFailSilently(
|
||||
ctx context.Context,
|
||||
conn *bun.DB,
|
||||
fn TxFuncSilent,
|
||||
) error {
|
||||
fnc := func(ctx context.Context, tx bun.Tx) (bool, error) {
|
||||
err := fn(ctx, tx)
|
||||
return err != nil, err
|
||||
}
|
||||
_, err := withTx(ctx, conn, fnc, true)
|
||||
return err
|
||||
}
|
||||
|
||||
// WithWriteTx executes a write transaction with automatic rollback on error
|
||||
// Commits only if fn returns nil. Returns true if successful.
|
||||
func WithWriteTx(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
conn *bun.DB,
|
||||
fn TxFunc,
|
||||
) bool {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
||||
defer cancel()
|
||||
ok, err := withTx(ctx, conn, fn, true)
|
||||
if err != nil {
|
||||
throw.InternalServiceError(s, w, r, "Database error", err)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// WithNotifyTx executes a transaction with notification-based error handling
|
||||
// Uses notifyInternalServiceError instead of throwInternalServiceError
|
||||
func WithNotifyTx(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
conn *bun.DB,
|
||||
fn TxFunc,
|
||||
) bool {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
||||
defer cancel()
|
||||
ok, err := withTx(ctx, conn, fn, true)
|
||||
if err != nil {
|
||||
notify.InternalServiceError(s, w, r, "Database error", err)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// withTx executes a transaction with automatic rollback on error
|
||||
func withTx(
|
||||
ctx context.Context,
|
||||
conn *bun.DB,
|
||||
fn TxFunc,
|
||||
write bool,
|
||||
) (bool, error) {
|
||||
tx, err := conn.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "conn.BeginTx")
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
ok, err := fn(ctx, tx)
|
||||
if err != nil || !ok {
|
||||
return false, err
|
||||
}
|
||||
if write {
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "tx.Commit")
|
||||
}
|
||||
} else {
|
||||
_ = tx.Commit()
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user