Guaranteeing DB closure. Is this idiomatic Go?

agolangf · · 716 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hello <a href="/r/golang">/r/golang</a> </p> <p>I&#39;m writing a small webapp that does a bit of database IO using <a href="https://github.com/jinzhu/gorm">gorm</a>. I&#39;m trying to write my application such that a sudden/unexpected halt in the application will still gracefully close database connections (hopefully flushing any pending transactions).</p> <p>Here&#39;s my current solution:</p> <pre><code>func ConnectPostgres(connstr string) (*PostgresController, error) { db, e := gorm.Open(&#34;postgres&#34;, connstr) go func() { defer func() { e := db.Close() if e != nil { log.Fatal(e) } }() select {} // block indefinitely }() return &amp;PostgresController{db}, e } </code></pre> <p>Go is still a little bit unfamiliar to me; will this behave as expected, and is this the &#34;Go Way&#34; of solving the problem at hand?</p> <p>Thanks!</p> <h1>Edit: updated snippet</h1> <pre><code>func ConnectPostgres(connstr string) (*PostgresController, error) { db, e := gorm.Open(&#34;postgres&#34;, connstr) c := make(chan os.Signal, 1) // BTW, why must this channel be buffered? signal.Notify(c, os.Interrupt, os.Kill) go func() { select { case &lt;-c: err := db.Close() if err != nil { log.Fatal(err) } } }() return &amp;PostgresController{db}, e } </code></pre> <hr/>**评论:**<br/><br/>cryp7ix: <pre><p>Just add a Close method to your Postgress Controller that calls close on the internal db object.</p> <p>The defer inside the goroutine will never be called because the select, like you commented, blocks indefinitely.</p></pre>cryp7ix: <pre><p>to handle application termination, you need to setup <code>os/signal</code> handlers for the termination signals and then call close on the db handle.</p></pre>elithrar_: <pre><p>This one. Catch the signals you care about (i.e. SIGINT) and then call <code>yourDBtype.Close()</code>.</p> <p>You can never truly guarantee a close (i.e. SIGKILL) but this should be &#34;as good as it gets&#34; under normal conditions.</p></pre>omginternets: <pre><p>Wait, now I&#39;m a bit confused. The call to <code>defer</code> happens before the call to <code>select</code>. I would expect the deferred function to be executed as the goroutine is killed.</p> <p>If this is not the case, then what is <code>defer</code> actually doing?</p></pre>nliadm: <pre><p>I&#39;m the case of unexpected halts, defers will not run. I can&#39;t recall if that defer would run in the case of reaching the end of main(), but the spec should say.</p> <p>In general, there&#39;s no way to ensure that some action is taken regardless of the means of process termination. Searching through golang-nuts for &#34;atexit&#34; should provide some reasoning from the core team as to why.</p></pre>omginternets: <pre><blockquote> <p>I&#39;m the case of unexpected halts, defers will not run. I can&#39;t recall if that defer would run in the case of reaching the end of main()</p> </blockquote> <p>You&#39;re right, those are actually two separate questions!</p> <p>It seems my Python background has bitten me in the ass, here. I was expecting <code>defer</code> to act somewhat like Python&#39;s context managers.</p> <p>That&#39;ll teach me not to bring in baggage from other languages :/</p> <p>Thanks!</p></pre>cryp7ix: <pre><p>Deferred functions get called when the function returns which set them up.</p> <p>In your case that function will never return. (The select is basically a while true {} that doesn&#39;t burn cpu cycles). Execution will never get passed it and so the defer won&#39;t unfold.</p></pre>omginternets: <pre><p>Aha! Makes perfect sense. As stated elsewhere, I come from a Python background and I was expecting behaviour similar to context managers.</p> <p>Thanks for the clarification!</p></pre>hmmdar: <pre><p>I think the important part is that the goroutine would never be killed. because <code>select{}</code> blocks forever. The defer will never be called. Also its worth noting that nothing will break the <code>select</code> allowing the closure function to return and the defer to fire. Even the process exiting will just kill the goroutine bypassing the defer all together.</p> <p>If you&#39;re wanting to perform some kind of processing on process exit you should take a look at handling <code>SIGINT</code>, and propagating the exit command through your app so it can clean up.</p> <p><code>defer</code> only ever gets executed after the function it was created returns. Take a look at <a href="https://golang.org/doc/effective_go.html#defer" rel="nofollow">Effective Go - Defer</a> that should help.</p></pre>RSoreil: <pre><p>Regarding your confusion on why the buffered channel, from the os/signal documentation:</p> <blockquote> <p>Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate. For a channel used for notification of just one signal value, a buffer of size 1 is sufficient.</p> </blockquote> <p>Effective Go also has some probably helpful info on how to handle this. Especially the part about Panic and Recover is interesting although it seems quite heavy handed for your use case. <a href="http://golang.org/doc/effective_go.html#Errors" rel="nofollow">http://golang.org/doc/effective_go.html#Errors</a></p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

716 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传