duanji5746 2016-01-11 21:33
浏览 112
已采纳

在结构sqlx.Close()上延迟在堆栈溢出中结束

I just started learning Go. Lesson of the day was to wrap my database handler in a struct to avoid using global scope variables. Thought I understood it so far and wanted to defer the Close() method as I did before, which ended in an stack overflow.

I couldn't find an explanation why this happen, nor what's the propper way to do this.

Here is the key code:

package exporter

type DB struct {
    *sqlx.DB
    queriesExecuted int
}

func Open(dataSourceName string) *DB {
    connection := sqlx.MustConnect("mysql", dataSourceName)
    db := &DB{connection, 0}
    return db
}

func (db *DB) Close() {
    db.Close() // this is where the stack growth happens
}

func (db *DB) GetArticles() []oxarticle {
  ... 
}

package main

func main() {
    exporter := feedexporter.Open("root:pass@/feedexport")
    defer exporter.Close()

    articles := exporter.GetArticles()
}

Everything works fine without the defer exporter.Close(), including it ends in:

runtime: goroutine stack exceeds 1000000000-byte limit

fatal error: stack overflow

It feels so bad to not close connections ;) What's the propper way to handle this?

  • 写回答

1条回答 默认 最新

  • doulu8341 2016-01-11 21:46
    关注

    You're triggering an infinite recursion in your Close() method:

    func (db *DB) Close() {
        db.Close() // you're currently IN this exact method!
    }
    

    What you're probably meaning to do is calling the Close() method of the sqlx.DB struct that's embedded in your custom DB struct. I'm not that familiar with the sqlx package, but according to the documentation that type does not even have a Close() method. This is most probably because the sqlx.DB does not actually represent a single connection, but a connection pool that creates and closes connections transparently:

    A DB instance is not a connection, but an abstraction representing a Database. This is why creating a DB does not return an error and will not panic. It maintains a connection pool internally, and will attempt to connect when a connection is first needed.

    The documentation explains in-depth how to handle this connection pool (emphasis mine):

    By default, the pool grows unbounded, and connections will be created whenever there isn't a free connection available in the pool. You can use DB.SetMaxOpenConns to set the maximum size of the pool. Connections that are not being used are marked idle and then closed if they aren't required. To avoid making and closing lots of connections, set the maximum idle size with DB.SetMaxIdleConns to a size that is sensible for your query loads.

    It is easy to get into trouble by accidentally holding on to connections. To prevent this:

    1. Ensure you Scan() every Row object
    2. Ensure you either Close() or fully-iterate via Next() every Rows object
    3. Ensure every transaction returns its connection via Commit() or Rollback()
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度