Implement in-application mutex to reduce DB_LOCKED errors

This commit is contained in:
2024-09-16 15:17:20 +02:00
parent 527a659a1b
commit ce641a3ffe
23 changed files with 324 additions and 470 deletions

View File

@@ -10,6 +10,7 @@ import (
"context"
"errors"
"github.com/rs/zerolog/log"
golock "github.com/viney-shih/go-lock"
"gogs.mikescher.com/BlackForestBytes/goext/ginext"
"gogs.mikescher.com/BlackForestBytes/goext/rext"
"gogs.mikescher.com/BlackForestBytes/goext/syncext"
@@ -38,14 +39,16 @@ type Application struct {
Port string
IsRunning *syncext.AtomicBool
RequestLogQueue chan models.RequestLog
MainDatabaseLock golock.RWMutex
}
func NewApp(db *DBPool) *Application {
return &Application{
Database: db,
stopChan: make(chan bool),
IsRunning: syncext.NewAtomicBool(false),
RequestLogQueue: make(chan models.RequestLog, 1024),
Database: db,
stopChan: make(chan bool),
IsRunning: syncext.NewAtomicBool(false),
RequestLogQueue: make(chan models.RequestLog, 1024),
MainDatabaseLock: golock.NewCASMutex(),
}
}

View File

@@ -23,7 +23,7 @@ type RequestOptions struct {
IgnoreWrongContentType bool
}
func (app *Application) DoRequest(gectx *ginext.AppContext, g *gin.Context, fn func(ctx *AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
func (app *Application) DoRequest(gectx *ginext.AppContext, g *gin.Context, lockmode models.TransactionLockMode, fn func(ctx *AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
maxRetry := scn.Conf.RequestMaxRetry
retrySleep := scn.Conf.RequestRetrySleep
@@ -40,6 +40,29 @@ func (app *Application) DoRequest(gectx *ginext.AppContext, g *gin.Context, fn f
wrap, stackTrace, panicObj := callPanicSafe(func(ctx *AppContext, finishSuccess func(r ginext.HTTPResponse) ginext.HTTPResponse) ginext.HTTPResponse {
dl, ok := ctx.Deadline()
if !ok {
dl = time.Now().Add(time.Second * 5)
}
if lockmode == models.TLockRead {
islock := app.MainDatabaseLock.RTryLockWithTimeout(dl.Sub(time.Now()))
if !islock {
return ginresp.APIError(g, 500, apierr.INTERNAL_EXCEPTION, "Failed to lock {MainDatabaseLock} [ro]", nil)
}
defer app.MainDatabaseLock.RUnlock()
} else if lockmode == models.TLockReadWrite {
islock := app.MainDatabaseLock.TryLockWithTimeout(dl.Sub(time.Now()))
if !islock {
return ginresp.APIError(g, 500, apierr.INTERNAL_EXCEPTION, "Failed to lock {MainDatabaseLock} [rw]", nil)
}
defer app.MainDatabaseLock.Unlock()
}
authheader := g.GetHeader("Authorization")
perm, err := app.getPermissions(actx, authheader)