Your Question
how to control timeout for every single sql statement,like create,update,query,raw sql
The document you expected this should be explained
i wanna control timeout for every single sql statement,like create,update,query,raw sql
one way is to use context every statement like:
ctx,cancel := context.WithTimeout(ctx,time.Second)
db.WithContext(ctx).Take(xxx_A)
cancel()
ctx2,cancel2 := context.WithTimeout(ctx,time.Second)
db.WithContext(ctx2).Take(xxx_B)
cancel2()
too ugly!!! forget this one
or create a plugin:
db.Callback().Row().Before("gorm:row").Register("c:before_row", func(db *gorm.DB) {
ctx, cancelFn := context.WithTimeout(db.Statement.Context, time.Second)
ctxInfo := &gormCtxInfo{
Origin: db.Statement.Context,
cancelFn: cancelFn,
}
db.Statement.Context = context.WithValue(ctx, gormCtxInfoKey{}, ctxInfo)
})
db.Callback().Row().After("gorm:row").Register("c:after_row", func(db *gorm.DB) {
ctxInfo := db.Statement.Context.Value(gormCtxInfoKey{}).(*gormCtxInfo)
if ctxInfo != nil {
ctxInfo.cancelFn()
}
})
then exec raw sql
sqlStr := "select count(*) as num from user_v2"
err = gdb.Raw(sqlStr).Scan(&res).Error
// output context cancel error
but this will cause an error: context cancel
this is Scan source code
func (db *DB) Scan(dest interface{}) (tx *DB) {
config := *db.Config
currentLogger, newLogger := config.Logger, logger.Recorder.New()
config.Logger = newLogger
tx = db.getInstance()
tx.Config = &config
if rows, err := tx.Rows(); err == nil {
if rows.Next() {
tx.ScanRows(rows, dest)
} else {
tx.RowsAffected = 0
tx.AddError(rows.Err())
}
tx.AddError(rows.Close())
}
currentLogger.Trace(tx.Statement.Context, newLogger.BeginAt, func() (string, int64) {
return newLogger.SQL, tx.RowsAffected
}, tx.Error)
tx.Logger = currentLogger
return
}
notice there are two steps to get result: tx.Rows() then call rows.Next(), but after tx.Rows called, ctx is cancelled by plugin. so the rows.Next() failed.
Rows ctx done related source code:
func (rs *Rows) awaitDone(ctx, txctx, closectx context.Context) {
var txctxDone <-chan struct{}
if txctx != nil {
txctxDone = txctx.Done()
}
select {
case <-ctx.Done():
err := ctx.Err()
rs.contextDone.Store(&err)
case <-txctxDone:
err := txctx.Err()
rs.contextDone.Store(&err)
case <-closectx.Done():
// rs.cancel was called via Close(); don't store this into contextDone
// to ensure Err() is unaffected.
}
rs.close(ctx.Err())
}
Expected answer
Is there a simple way to implement this scenario, which control every sql statement timeout?
Comment From: Tang-RoseChild
could Gorm provider callback for Scan ?
because Gorm' Scan func call Rows and Scans in one function, but only callbacks for Rows, if ctx cancelled in plugin, ScanRows also failed
Comment From: github-actions[bot]
This issue has been automatically marked as stale because it has been open 360 days with no activity. Remove stale label or comment or this will be closed in 180 days