snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
100
server/store/adapters/rdbms/tx.go
Normal file
100
server/store/adapters/rdbms/tx.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package rdbms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type (
|
||||
dbTransactionMaker interface {
|
||||
BeginTxx(ctx context.Context, opts *sql.TxOptions) (*sqlx.Tx, error)
|
||||
}
|
||||
)
|
||||
|
||||
func (s *Store) Tx(ctx context.Context, fn func(context.Context, store.Storer) error) error {
|
||||
if s.TxRetryLimit < 0 {
|
||||
return fn(ctx, s)
|
||||
}
|
||||
|
||||
return txHandler(ctx, s.DB, s.TxRetryLimit, s.TxRetryErrHandler, func(ctx context.Context, tx sqlx.ExtContext) error {
|
||||
return fn(ctx, s.withTx(tx))
|
||||
})
|
||||
}
|
||||
|
||||
// tx begins a new db transaction and handles retries when possible
|
||||
//
|
||||
// It utilizes configured transaction error handlers and max-retry limits
|
||||
// to determine if and how many times transaction should be retried
|
||||
//
|
||||
func txHandler(ctx context.Context, dbc interface{}, max int, reh txRetryOnErrHandler, task func(context.Context, sqlx.ExtContext) error) error {
|
||||
var (
|
||||
lastTaskErr error
|
||||
err error
|
||||
db *sqlx.DB
|
||||
tx *sqlx.Tx
|
||||
try = 1
|
||||
)
|
||||
|
||||
switch dbc.(type) {
|
||||
case dbTransactionMaker:
|
||||
// we can make a transaction!
|
||||
db = dbc.(*sqlx.DB)
|
||||
case sqlx.ExtContext:
|
||||
// Already in a transaction, run the given task and finish
|
||||
return task(ctx, dbc.(sqlx.ExtContext))
|
||||
default:
|
||||
return fmt.Errorf("could not use the db connection for transaction")
|
||||
}
|
||||
|
||||
for {
|
||||
try++
|
||||
|
||||
// Start transaction
|
||||
tx, err = db.BeginTxx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if lastTaskErr = task(ctx, tx); lastTaskErr == nil {
|
||||
// Task completed successfully
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// No matter the cause of the error, rollback the transaction
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
return fmt.Errorf("failed to rollback transaction (tries: %d) on error %v: %w", try, lastTaskErr, rollbackErr)
|
||||
}
|
||||
|
||||
if errors.IsAny(lastTaskErr) {
|
||||
// not a store-internal/transaction error and can be returned right away,
|
||||
// no need to re-run the transaction
|
||||
return lastTaskErr
|
||||
}
|
||||
|
||||
// Call the configured transaction retry error handler
|
||||
// if this particular error should be retried or not
|
||||
//
|
||||
// We cannot generalize here because different store implementation have
|
||||
// different errors; we need to act accordingly
|
||||
if reh != nil && !reh(try, lastTaskErr) {
|
||||
return fmt.Errorf("failed to complete transaction: %w", lastTaskErr)
|
||||
}
|
||||
|
||||
// tx error handlers can take current number of tries into account and
|
||||
// break the retry-loop earlier, but that might not be always the case
|
||||
//
|
||||
// We'll check the configured and hard-limit maximums
|
||||
if try >= max || try >= TxRetryHardLimit {
|
||||
return fmt.Errorf("failed to perform transaction (tries: %d), last error: %w", try, lastTaskErr)
|
||||
}
|
||||
|
||||
// Sleep (with a bit of kickback) before doing next retry
|
||||
time.Sleep(50 * time.Duration(try*50))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user