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()
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 用verilog实现tanh函数和softplus函数
  • ¥15 求京东批量付款能替代天诚
  • ¥15 slaris 系统断电后,重新开机后一直自动重启
  • ¥15 51寻迹小车定点寻迹
  • ¥15 谁能帮我看看这拒稿理由啥意思啊阿啊
  • ¥15 关于vue2中methods使用call修改this指向的问题
  • ¥15 idea自动补全键位冲突
  • ¥15 请教一下写代码,代码好难
  • ¥15 iis10中如何阻止别人网站重定向到我的网站
  • ¥15 滑块验证码移动速度不一致问题