服务随机失败:”运行时错误:无效的内存地址或空指针解引用”

huangapple go评论70阅读模式
英文:

Service fails randomly: "runtime error: invalid memory address or nil pointer dereference"

问题

我正在处理一个 Restful API 服务器,并且我需要从 MongoDB 迁移到 MySQL。团队选择的 ORM 是 upper/db。我的问题是我的服务有时会失败,有时不会。

错误本身是:runtime error: invalid memory address or nil pointer dereference

这是导致我有时出错的服务函数:

func (service *UserService) UpdateUser(req UpdateUserRequest) (response UpdateUserResponse, err error) {
	var user models.User
	defer service.db.Close()
	collection := service.db.Collection("users")

	result := collection.Find(req.ID)

	err = result.One(&user) // 这是第 104 行
	if err != nil {
		return
	}

	count, err := result.Count()
	if err != nil {
		return
	}
	if count == 0 {
		err = errors.New("找不到用户")
		return
	}

	if req.FirstName != "" && req.FirstName != user.FirstName {
		user.FirstName = req.FirstName
	}
	if req.LastName != "" && req.LastName != user.LastName {
		user.LastName = req.LastName
	}
	if req.UserType != "" && req.UserType != user.UserType {
		user.UserType = req.UserType
	}
	if req.Status != "" && req.Status != user.Status {
		user.Status = req.Status
	}

	user.UpdatedAt = time.Now()

	if err = result.Update(user); err != nil {
		return
	}

	user.Password = ""

	response.User = user
	return
}

我正在遵循官方文档。奇怪的是第一次(服务器启动后)它成功更新用户,但下一次就不行了。

编辑:添加错误转储:

runtime error: invalid memory address or nil pointer dereference
C:/Program Files/Go/src/runtime/panic.go:221 (0xfe993c)
        panicmem: panic(memoryError)
C:/Program Files/Go/src/runtime/signal_windows.go:254 (0xfe990c)
        sigpanic: panicmem()
C:/Program Files/Go/src/database/sql/sql.go:1260 (0x14452d3)
        (*DB).conn: db.mu.Lock()
C:/Program Files/Go/src/database/sql/sql.go:1695 (0x144831c)
        (*DB).query: dc, err := db.conn(ctx, strategy)
C:/Program Files/Go/src/database/sql/sql.go:1674 (0x144807e)
        (*DB).QueryContext: rows, err = db.query(ctx, query, args, cachedOrNewConn)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqladapter/compat/query_go18.go:39 (0x1486730)
        QueryContext: return p.QueryContext(ctx, query, args...)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqladapter/session.go:823 (0x14866eb)
        (*session).StatementQuery: rows, err = compat.QueryContext(sess.sqlDB, ctx, query, args)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqlbuilder/select.go:480 (0x147304f)
        (*selector).IteratorContext: rows, err := sess.StatementQuery(ctx, sq.statement(), sq.arguments()...)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqlbuilder/select.go:470 (0x1472d9a)
        (*selector).Iterator: return sel.IteratorContext(sel.SQL().sess.Context())
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqlbuilder/paginate.go:176 (0x146d932)
        (*paginator).Iterator: return pq.sel.Iterator()
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqladapter/result.go:243 (0x14809b5)
        (*Result).One: err = query.Iterator().One(dst)
C:/Users/user/go/src/github.com/myrepo/service/services/user_service.go:104 (0x15135be)
        (*UserService).UpdateUser: err = result.One(&user)
C:/Users/user/go/src/github.com/myrepo/service/handlers/users_handler.go:39 (0x1518e3c)
        handleUpdateUser.func1: user, err := service.UpdateUser(req)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x1515ff6)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/tpkeeper/gin-dump@v1.0.1/gindump.go:98 (0x15153b4)
        DumpWithOptions.func1: ctx.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x14311a6)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/logger.go:241 (0x1431189)
        LoggerWithConfig.func1: c.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x1431f41)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/recovery.go:99 (0x1431f2c)
        CustomRecoveryWithWriter.func1: c.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x1430730)
        (*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:555 (0x1430398)
        (*Engine).handleHTTPRequest: c.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:511 (0x142fed1)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
C:/Program Files/Go/src/net/http/server.go:2879 (0x120959a)
        serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
C:/Program Files/Go/src/net/http/server.go:1930 (0x1204c47)
        (*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
C:/Program Files/Go/src/runtime/asm_amd64.s:1581 (0x1004640)
        goexit: BYTE    $0x90   // NOP

编辑 2:添加数据库连接

func Open(settings db.ConnectionURL) (db.Session, error) {
	db, err := mysql.Open(settings)
	if err != nil {
		return nil, err
	}

	if db.Ping() != nil {
		return nil, errors.New("连接数据库时出错")
	}

	return db, nil
}

我做错了什么?

英文:

I am working on a Restful API server and I had to migrate from MongoDB to MySQL. The ORM the team picked up is upper/db. My issue is that my service fails some times, some times it doesn't.

The error itself: runtime error: invalid memory address or nil pointer dereference

This is the service function that gives me error some times:

func (service *UserService) UpdateUser(req UpdateUserRequest) (response UpdateUserResponse, err error) {
	var user models.User
	defer service.db.Close()
	collection := service.db.Collection("users")

	result := collection.Find(req.ID)

	err = result.One(&user) // this is line 104
	if err != nil {
		return
	}

	count, err := result.Count()
	if err != nil {
		return
	}
	if count == 0 {
		err = errors.New("couldn't find user")
		return
	}

	if req.FirstName != "" && req.FirstName != user.FirstName {
		user.FirstName = req.FirstName
	}
	if req.LastName != "" && req.LastName != user.LastName {
		user.LastName = req.LastName
	}
	if req.UserType != "" && req.UserType != user.UserType {
		user.UserType = req.UserType
	}
	if req.Status != "" && req.Status != user.Status {
		user.Status = req.Status
	}

	user.UpdatedAt = time.Now()

	if err = result.Update(user); err != nil {
		return
	}

	user.Password = ""

	response.User = user
	return
}

I am following the official documentation. Something strange is the first time (after the server starts) it updates the user successfully, the next don't.

Edit: Error dump added:

runtime error: invalid memory address or nil pointer dereference
C:/Program Files/Go/src/runtime/panic.go:221 (0xfe993c)
panicmem: panic(memoryError)
C:/Program Files/Go/src/runtime/signal_windows.go:254 (0xfe990c)
sigpanic: panicmem()
C:/Program Files/Go/src/database/sql/sql.go:1260 (0x14452d3)
(*DB).conn: db.mu.Lock()
C:/Program Files/Go/src/database/sql/sql.go:1695 (0x144831c)
(*DB).query: dc, err := db.conn(ctx, strategy)
C:/Program Files/Go/src/database/sql/sql.go:1674 (0x144807e)
(*DB).QueryContext: rows, err = db.query(ctx, query, args, cachedOrNewConn)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqladapter/compat/query_go18.go:39 (0x1486730)
QueryContext: return p.QueryContext(ctx, query, args...)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqladapter/session.go:823 (0x14866eb)
(*session).StatementQuery: rows, err = compat.QueryContext(sess.sqlDB, ctx, query, args)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqlbuilder/select.go:480 (0x147304f)
(*selector).IteratorContext: rows, err := sess.StatementQuery(ctx, sq.statement(), sq.arguments()...)
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqlbuilder/select.go:470 (0x1472d9a)
(*selector).Iterator: return sel.IteratorContext(sel.SQL().sess.Context())
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqlbuilder/paginate.go:176 (0x146d932)
(*paginator).Iterator: return pq.sel.Iterator()
C:/Users/user/go/pkg/mod/github.com/upper/db/v4@v4.5.0/internal/sqladapter/result.go:243 (0x14809b5)
(*Result).One: err = query.Iterator().One(dst)
C:/Users/user/go/src/github.com/myrepo/service/services/user_service.go:104 (0x15135be)
(*UserService).UpdateUser: err = result.One(&user)
C:/Users/user/go/src/github.com/myrepo/service/handlers/users_handler.go:39 (0x1518e3c)
handleUpdateUser.func1: user, err := service.UpdateUser(req)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x1515ff6)
(*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/tpkeeper/gin-dump@v1.0.1/gindump.go:98 (0x15153b4)
DumpWithOptions.func1: ctx.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x14311a6)
(*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/logger.go:241 (0x1431189)
LoggerWithConfig.func1: c.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x1431f41)
(*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/recovery.go:99 (0x1431f2c)
CustomRecoveryWithWriter.func1: c.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/context.go:168 (0x1430730)
(*Context).Next: c.handlers[c.index](c)
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:555 (0x1430398)
(*Engine).handleHTTPRequest: c.Next()
C:/Users/user/go/pkg/mod/github.com/gin-gonic/gin@v1.7.7/gin.go:511 (0x142fed1)
(*Engine).ServeHTTP: engine.handleHTTPRequest(c)
C:/Program Files/Go/src/net/http/server.go:2879 (0x120959a)
serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
C:/Program Files/Go/src/net/http/server.go:1930 (0x1204c47)
(*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
C:/Program Files/Go/src/runtime/asm_amd64.s:1581 (0x1004640)
goexit: BYTE    $0x90   // NOP

Edit 2: Added database connection

func Open(settings db.ConnectionURL) (db.Session, error) {
	db, err := mysql.Open(settings)
	if err != nil {
		return nil, err
	}

	if db.Ping() != nil {
		return nil, errors.New("error al conectar la base de datos")
	}

	return db, nil
}

What am I doing wrong?

答案1

得分: 4

函数UpdateUser在返回时关闭了数据库。这是一个大问题!后续对UpdateUser的调用会导致恐慌,因为函数在关闭的数据库上调用了方法。

删除这行代码以解决问题。

defer service.db.Close()
英文:

The function UpdateUser closes the database on return. That's big problem! The subsequent calls to UpdateUser panic because the function calls methods on the closed database.

Delete this line of code to fix the issue.

defer service.db.Close()

huangapple
  • 本文由 发表于 2022年2月15日 10:21:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/71120430.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定