<p>Hey there,
I'm looking for a clean method to reset a timer being used <a href="https://blog.golang.org/go-concurrency-patterns-timing-out-and" rel="nofollow">similar to this example of a timeout with a select statement.</a></p>
<p>My use case is worker goroutines pulling from an sqs queue and performing work, the queue restores a message after a certain length of time so I either need to let the queue know I'm still working or timeout. Currently I have a timeout setup when the worker starts, and it cleans up if the timeout passes and dumps long running tasks to a deadletter queue.</p>
<p>I want to have it renew the visibility window and reset it's timeout,
but I'm curious if there's a nice way to do this without having to manage a timer for every worker and keep a reference elsewhere to call reset on. </p>
<p>This is just for a learning project so I'm open to alternative design decisions than the select statement, if it would be easier. </p>
<hr/>**评论:**<br/><br/>epiris: <pre><p>The way you describe this makes me think you hand your workers these jobs in a fire and forget fashion. However the fact that the work you give your workers has a deadline, makes the work a request. Try not to be "unsure" if the work completed after the handoff to the worker, instead try to have the worker signal when he fails. Give it a context to let him know that if he hasn't finished by the time the context is canceled, it should stop trying so I may try again.</p>
<p>I would change the type you launch your workers, perhaps it looks like this now:</p>
<pre><code>go work(job)
</code></pre>
<p>To:</p>
<pre><code>ch := make(chan error)
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
go func() {
ch <- work(ctx, job)
}()
var err error
// In addition to using it here, you can run this same select inside `work` if you want
// to cancel any context it creates for network resources and such
select {
case ctx.Done():
err = ctx.Err()
cancel() // notify worker immediately so he can stop trying.
case err = <-ch:
}
if err = context.DeadlineExceeded {
// send to your failure queue, or if you return meaningful errors
// from the worker branch here on non-nil
}
// everything was good
</code></pre>
<p>Or maybe this is not any help at all, I'm known for that. Regardless happy coding :- )</p></pre>fallen77: <pre><p>I appreciate the suggestion. Is there a way to add time to the current context's timeout? For example in the context.DeadlineExceeded statement simply giving the timer a longer period?</p>
<p>Currently I'm using a deadletter queue but I'd prefer to allow the work to continue if an error hasn't been thrown and legitimate work is being done. Currently my deadletter queue is identical but has a very long timeout. </p></pre>epiris: <pre><p>If you posted your code I could give a very concise answer, but as it stands I think I'm missing something, or you are. If you are able to know that legitimate work is being done and if an error occurred in the work.. then that is the place to initiate a retry.</p>
<p>I think you may be trying to centralize book keeping in the dispatcher, which I completely understand. As a good programmer you feel in the dispatcher that natural pattern to try the work, and only release ownership if it succeeded. But Go provides you a bit nicer of mechanisms to let <strong>"go"</strong> of that work and trust you will be notified later when it is complete.</p>
<p>So, that said, forget about context, in your dispatcher pass the task to the worker and a second channel for failures. Have your dispatcher in it's current dispatch goroutine have another select to pull and handle failures. Now your workers can run as long as they need to, and if they don't need retried you can trust you won't receive them. Or again, maybe I am way off, hard to know exactly without being in the weeds with ya. Have fun!</p></pre>peterbourgon: <pre><p>One general pattern is to attach a deadline time.Time to each unit of work, signifying when an action needs to take place. Then, have a separate timer, firing at a relatively high rate, which checks all outstanding units of work to see if time.Now().After(work.deadline). If yes, the single timer goroutine can do whatever work needs to be done, serially over all outstanding work items. This can work well when you have some flexibility with the precise time that things need to happen.</p></pre>jerf: <pre><p>That sounds like recreating what the runtime is already doing, only with less efficiency. I'd want to see some benchmarks that show that's faster than just setting and cancelling alarms before I used it. </p></pre>peterbourgon: <pre><p>It's not a speed play, it primarily reduces goroutines and garbage.</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传