114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
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 (db *DB) WithReadTx(
|
|
s *hws.Server,
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
fn TxFunc,
|
|
) bool {
|
|
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
|
defer cancel()
|
|
ok, err := db.withTx(ctx, 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 (db *DB) WithTxFailSilently(
|
|
ctx context.Context,
|
|
fn TxFuncSilent,
|
|
) error {
|
|
fnc := func(ctx context.Context, tx bun.Tx) (bool, error) {
|
|
err := fn(ctx, tx)
|
|
return err == nil, err
|
|
}
|
|
_, err := db.withTx(ctx, 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 (db *DB) WithWriteTx(
|
|
s *hws.Server,
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
fn TxFunc,
|
|
) bool {
|
|
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
|
defer cancel()
|
|
ok, err := db.withTx(ctx, 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 (db *DB) WithNotifyTx(
|
|
s *hws.Server,
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
fn TxFunc,
|
|
) bool {
|
|
ctx, cancel := context.WithTimeout(r.Context(), timeout)
|
|
defer cancel()
|
|
ok, err := db.withTx(ctx, 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 (db *DB) withTx(
|
|
ctx context.Context,
|
|
fn TxFunc,
|
|
write bool,
|
|
) (bool, error) {
|
|
tx, err := db.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
|
|
}
|