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