<div dir="ltr">So, in my opinion, juju/retry is pretty good. I think with some tweaking and some intelligent use, it can feel every bit as lightweight as we'd like.<div><br></div><div>If we have common use cases, we just need to write up an API to encapsulate them. If we want a "retry N times or until this channel is closed" then write an api for that. Having preconstructed call args with prepopulated best practices for delays, backoffs, etc. can make it a lot easier.</div><div><br></div><div>There's no reason running a complex retry can't look like this:</div><div><br></div><div>// closure to encapsulate local variables</div><div>f := func() error { return foo.bar(a, b, c) }</div><div>err := retry.WithCancel(<span style="line-height:1.5">f, </span><span style="line-height:1.5">juju.StdRetry, doneCh)</span></div><div><span style="line-height:1.5"><br></span></div><div><div>Where StdRetry is the juju standard retry scheme (standard timeout, backoff strategy, etc), and doneCh is the channel you expect to get closed to cancel this action.</div><div><br></div><div>That's doable with the current API if we add cancellation (though it would be nice if we extracted the function-to-run from the args struct, since it makes it more obvious that the retry args can be reused for different functions).</div><div><br></div></div><div>I think goroutines should be controlled by the caller. If the caller wants to put retry.Foo() in a goroutine, it can... but I don't think it's useful to have that be retry's responsibility, it complicates the package too much.</div></div><br><div class="gmail_quote"><div dir="ltr">On Tue, Aug 9, 2016 at 11:26 AM Katherine Cox-Buday <<a href="mailto:katherine.cox-buday@canonical.com">katherine.cox-buday@canonical.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Andrew's queue is certainly nice, but I agree with the points Roger is raising:<br>
<br>
1. Go's idiom for coordinating concurrent operations are goroutines and channels.<br>
2. Sticking to these idioms makes it much easier to compose parts into a whole (if only because the language strongly bends code that way).<br>
<br>
As a matter of opinion, I also dislike having to instantiate structs to encapsulate logic; I would rather write the logic inline, or in a closure -- possibly in a goroutine.<br>
<br>
William Reade <<a href="mailto:william.reade@canonical.com" target="_blank">william.reade@canonical.com</a>> writes:<br>
<br>
> On Tue, Aug 9, 2016 at 10:17 AM, roger peppe<br>
> <<a href="mailto:roger.peppe@canonical.com" target="_blank">roger.peppe@canonical.com</a>> wrote:<br>
><br>
> BTW I'm not saying that a timer queue is never the correct answer.<br>
> In some<br>
> circumstances, it can be the exactly the right thing to use.<br>
><br>
><br>
> Yeah -- the context here is that katco has been looking at the<br>
> provisioner, and I think a timer queue is sensible there. Firing off<br>
> goroutines for every machine is not necessarily bad by any means, but<br>
> it *is* somewhat harder to get right in every circumstance (e.g. what<br>
> happens when a machine is removed while that other goroutine is<br>
> working on provisioning it?).<br>
><br>
> There are certainly many places where a single retryable op *is* the<br>
> only important thing and there's no call for a control loop. I'm not<br>
> suggesting we should always use a queue; but I'd tend to encourage it<br>
> whenever we have a component dealing with many similar retryable<br>
> tasks. (I definitely should be using it in dependency.Engine, for<br>
> example.)<br>
><br>
> Cheers<br>
> William<br>
><br>
<br>
--<br>
Katherine<br>
<br>
--<br>
Juju-dev mailing list<br>
<a href="mailto:Juju-dev@lists.ubuntu.com" target="_blank">Juju-dev@lists.ubuntu.com</a><br>
Modify settings or unsubscribe at: <a href="https://lists.ubuntu.com/mailman/listinfo/juju-dev" rel="noreferrer" target="_blank">https://lists.ubuntu.com/mailman/listinfo/juju-dev</a><br>
</blockquote></div>