[#1187] added Dao query semaphore and base fail/retry

This commit is contained in:
Gani Georgiev
2022-12-08 10:40:42 +02:00
parent 355f7053fd
commit 693954cdcd
38 changed files with 356 additions and 101 deletions

View File

@@ -1,6 +1,7 @@
package daos
import (
"context"
"errors"
"fmt"
"strings"
@@ -348,50 +349,27 @@ func (dao *Dao) SaveRecord(record *models.Record) error {
// The delete operation may fail if the record is part of a required
// reference in another record (aka. cannot be deleted or set to NULL).
func (dao *Dao) DeleteRecord(record *models.Record) error {
const maxAttempts = 6
attempts := 1
Retry:
err := dao.deleteRecord(record, attempts)
if err != nil &&
attempts <= maxAttempts &&
// note: we are checking the error msg so that we can handle both the cgo and noncgo errors
strings.Contains(err.Error(), "database is locked") {
time.Sleep(time.Duration(300*attempts) * time.Millisecond)
attempts++
goto Retry
// fetch rel references (if any)
//
// note: the select is outside of the transaction to minimize
// SQLITE_BUSY errors when mixing read&write in a single transaction
refs, err := dao.FindCollectionReferences(record.Collection())
if err != nil {
return err
}
return err
}
func (dao *Dao) deleteRecord(record *models.Record, attempts int) error {
return dao.RunInTransaction(func(txDao *Dao) error {
// unset transaction dao before hook on retry to avoid
// triggering the same before callbacks multiple times
if attempts > 1 {
oldBeforeCreateFunc := txDao.BeforeCreateFunc
oldBeforeUpdateFunc := txDao.BeforeUpdateFunc
oldBeforeDeleteFunc := txDao.BeforeDeleteFunc
txDao.BeforeCreateFunc = nil
txDao.BeforeUpdateFunc = nil
txDao.BeforeDeleteFunc = nil
defer func() {
if txDao != nil {
txDao.BeforeCreateFunc = oldBeforeCreateFunc
txDao.BeforeUpdateFunc = oldBeforeUpdateFunc
txDao.BeforeDeleteFunc = oldBeforeDeleteFunc
}
}()
}
// check for references
refs, err := txDao.FindCollectionReferences(record.Collection())
if err != nil {
// run all consequent DeleteRecord requests synchroniously
// to minimize SQLITE_BUSY errors
if len(refs) > 0 {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := dao.Block(ctx); err != nil {
return err
}
defer dao.Continue()
}
return dao.RunInTransaction(func(txDao *Dao) error {
// check if related records has to be deleted (if `CascadeDelete` is set)
// OR
// just unset the record id from any relation field values (if they are not required)