merge v0.23.0-rc changes
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
)
|
||||
|
||||
// RunInTransaction wraps fn into a transaction for the regular app database.
|
||||
//
|
||||
// It is safe to nest RunInTransaction calls as long as you use the callback's txApp.
|
||||
func (app *BaseApp) RunInTransaction(fn func(txApp App) error) error {
|
||||
return app.runInTransaction(app.NonconcurrentDB(), fn, false)
|
||||
}
|
||||
|
||||
// AuxRunInTransaction wraps fn into a transaction for the auxiliary app database.
|
||||
//
|
||||
// It is safe to nest RunInTransaction calls as long as you use the callback's txApp.
|
||||
func (app *BaseApp) AuxRunInTransaction(fn func(txApp App) error) error {
|
||||
return app.runInTransaction(app.AuxNonconcurrentDB(), fn, true)
|
||||
}
|
||||
|
||||
func (app *BaseApp) runInTransaction(db dbx.Builder, fn func(txApp App) error, isForAuxDB bool) error {
|
||||
switch txOrDB := db.(type) {
|
||||
case *dbx.Tx:
|
||||
// run as part of the already existing transaction
|
||||
return fn(app)
|
||||
case *dbx.DB:
|
||||
var txApp *BaseApp
|
||||
txErr := txOrDB.Transactional(func(tx *dbx.Tx) error {
|
||||
txApp = app.createTxApp(tx, isForAuxDB)
|
||||
return fn(txApp)
|
||||
})
|
||||
|
||||
// execute all after event calls on transaction complete
|
||||
if txApp != nil && txApp.txInfo != nil {
|
||||
afterFuncErr := txApp.txInfo.runAfterFuncs(txErr)
|
||||
if afterFuncErr != nil {
|
||||
return errors.Join(txErr, afterFuncErr)
|
||||
}
|
||||
}
|
||||
|
||||
return txErr
|
||||
default:
|
||||
return errors.New("failed to start transaction (unknown db type)")
|
||||
}
|
||||
}
|
||||
|
||||
// createTxApp shallow clones the current app and assigns a new tx state.
|
||||
func (app *BaseApp) createTxApp(tx *dbx.Tx, isForAuxDB bool) *BaseApp {
|
||||
clone := *app
|
||||
|
||||
if isForAuxDB {
|
||||
clone.auxConcurrentDB = tx
|
||||
clone.auxNonconcurrentDB = tx
|
||||
} else {
|
||||
clone.concurrentDB = tx
|
||||
clone.nonconcurrentDB = tx
|
||||
}
|
||||
|
||||
clone.txInfo = &txAppInfo{
|
||||
parent: app,
|
||||
isForAuxDB: isForAuxDB,
|
||||
}
|
||||
|
||||
return &clone
|
||||
}
|
||||
|
||||
type txAppInfo struct {
|
||||
parent *BaseApp
|
||||
afterFuncs []func(txErr error) error
|
||||
mu sync.Mutex
|
||||
isForAuxDB bool
|
||||
}
|
||||
|
||||
func (a *txAppInfo) onAfterFunc(fn func(txErr error) error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
a.afterFuncs = append(a.afterFuncs, fn)
|
||||
}
|
||||
|
||||
// note: can be called only once because txAppInfo is cleared
|
||||
func (a *txAppInfo) runAfterFuncs(txErr error) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
var errs []error
|
||||
|
||||
for _, call := range a.afterFuncs {
|
||||
if err := call(txErr); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
a.afterFuncs = nil
|
||||
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("transaction afterFunc errors: %w", errors.Join(errs...))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user