web服务有个接口,service层开启事务,调用dao层两个接口。发现第二个dao层接口返回Error,在service层事务回滚时panic,该panic无法被recover。
代码详情事务中有两条sql,第一条调用Rows查询,第二条普通查询,代码如下:
func TestGorm(db *gorm.DB) ([]*User, error) {
tx := db.Begin()
//第1条SQL
rows, err := tx.Table("user").Select("max(age) as max_age").Rows()
if err != nil {
tx.Rollback()
return nil, err
}
var maxAge int8
if rows.Next() {
err = rows.Scan(&maxAge)
if err != nil {
tx.Rollback()
return nil, err
}
}
//TAG rows没有close
var users []*User
//第2条SQL
tx = tx.Table("user").Where("age = ?", maxAge).Find(&users)
if tx.Error != nil {
//此处回滚
tx.Rollback()
return nil, tx.Error
}
db.Commit()
return users, nil
}
日志
1. 第二条SQL执行失败,tx.Error为driver: bad connection,并打印如下日志
[mysql] 2021/12/30 13:59:22 packets.go:445: busy buffer
2. 执行tx.Rollback()时panic,日志如下:
原因panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x78 pc=0x16f7f3f]goroutine 100 [running]:
code.byted.org/gopkg/mysql-driver.sqlAfter(0xc0004ea340, 0x0, 0x0)
/Users/bytedance/go/pkg/mod/code.byted.org/gopkg/mysql-driver@v1.2.1/middleware.go:66 +0x3f
code.byted.org/gopkg/mysql-driver.(*mysqlStmt).query.func1.1()
/Users/bytedance/go/pkg/mod/code.byted.org/gopkg/mysql-driver@v1.2.1/statement.go:130 +0x3b
code.byted.org/gopkg/mysql-driver.(*mysqlStmt).QueryContext.func1()
/Users/bytedance/go/pkg/mod/code.byted.org/gopkg/mysql-driver@v1.2.1/connection_go18.go:152 +0x4b
code.byted.org/gopkg/mysql-driver.(*mysqlRows).Close(0xc0004f2780, 0x0, 0x0)
/Users/bytedance/go/pkg/mod/code.byted.org/gopkg/mysql-driver@v1.2.1/rows.go:70 +0x134
database/sql.(*Rows).close.func1()
/usr/local/go/src/database/sql/sql.go:3178 +0x3c
database/sql.withLock(0x19912c0, 0xc0001c6000, 0xc000502700)
/usr/local/go/src/database/sql/sql.go:3294 +0x69
database/sql.(*Rows).close(0xc000167300, 0x0, 0x0, 0x0, 0x0)
/usr/local/go/src/database/sql/sql.go:3177 +0x114
database/sql.(*Rows).awaitDone(0xc000167300, 0x19994a8, 0xc0004e8700, 0x19994a8, 0xc000334040)
/usr/local/go/src/database/sql/sql.go:2831 +0xd4
created by database/sql.(*Rows).initContextClose
/usr/local/go/src/database/sql/sql.go:2815 +0xc9
rows会维护这个数据库连接,当 tx 想再次调用当前连接进行数据库操作的时候,因为连接还没有断开,没有调用 rows.Close(),tx 无法再从连接池里获取当前连接。参考:Golang 报错 | 操作mysql提示busy buffer_MyBlog-CSDN博客
解决方法在“//TAG rows没有close”行增加“rows.Close()”



