在延迟函数中重用context.WithTimeout

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

Reusing context.WithTimeout in deferred function

问题

下面的代码片段(为了简洁起见)来自MongoDB的Go快速入门博客文章,它在连接数据库时创建了context.WithTimeout,并在延迟的Disconnect函数中重用了相同的上下文,我认为这是有问题的。

func main() {
    client, _ := mongo.NewClient(options.Client().ApplyURI("<ATLAS_URI_HERE>"))
    ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)

    _ = client.Connect(ctx)
    defer client.Disconnect(ctx)
}

我的思路是:

context.WithTimeout在创建时设置了一个UNIX时间截点。因此,将其传递给Connect是有意义的,因为我们希望在连接建立的过程超过时间限制(即派生的UNIX时间)时取消连接。

现在,将相同的ctx传递给延迟的Disconnect,这很可能会在将来的某个时候调用,将导致ctx的时间已经过去。也就是说,当函数开始执行时,它已经过期了。这不是预期的结果,也破坏了逻辑,因为引用Disconnect的文档中所说:

如果上下文在使用中的连接返回之前通过取消、截止时间或超时而过期,则使用中的连接将被关闭,导致任何正在进行的读取或写入操作失败。

请告诉我是否以及我哪里错了或者遗漏了什么。

英文:

The below code snippet (reduced for brevity) from MongoDB's Go quickstart blog post creates context.WithTimeout at the time of connecting with the database and reuses the same for the deferred Disconnect function, which I think is buggy.

func main() {
	client, _ := mongo.NewClient(options.Client().ApplyURI(&quot;&lt;ATLAS_URI_HERE&gt;&quot;))
	ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)

	_ = client.Connect(ctx)
	defer client.Disconnect(ctx)
}

My train of thought-

context.WithTimeout sets a deadline in UNIX time at the point it is created.
So, passing it to Connect makes sense as we want to cancel the process of establishing the connection if it exceeds the time limit (ie, the derived UNIX time).

Now, passing the same ctx to the deferred Disconnect, which will most probably be called late in the future, will result in the ctx's time being in the past. Meaning, it is already expired when the function starts executing. This is not what is expected and breaks the logic as- quoting the doc for Disconnect-

> If the context expires via cancellation,
deadline, or timeout before the in use connections have returned, the in use
connections will be closed, resulting in the failure of any in flight read
or write operations.

Please tell me if and how I am wrong and/or missing something.

答案1

得分: 1

你的理解是正确的。

在这个例子中,这样做是足够的,因为这个例子只是连接到数据库,执行一些示例操作(例如列出数据库),然后 main() 结束,所以使用相同的上下文运行延迟断开连接不会有问题(这个例子应该在10秒内运行良好)。

在“真实世界”的应用程序中,情况当然不会这样。所以你可能不会在连接和断开连接时使用相同的上下文(除非该上下文没有超时)。

英文:

Your understanding is correct.

In the example it is sufficient because the example just connects to the database, performs some example operation (e.g. lists databases), then main() ends, so running the deferred disconnect with the same context will cause no trouble (the example will/should run well under 10 seconds).

In "real-world" applications this won't be the case of course. So you will likely not use the same context for connecting and disconnecting (unless that context has no timeout).

huangapple
  • 本文由 发表于 2023年1月12日 03:20:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/75088022.html
匿名

发表评论

匿名网友

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

确定