I have a service that is deployed asynchronously and I need to wait a specified amount of time for it to come online. If the specified amount of time elapses and we still aren't able to find the service, then we error out. What is the most optimal way of writing this in go? I was looking into using context.WithTimeout
but not sure exactly how this would work. Thanks for the help!
func (c *Client) WaitForServiceToComeAlive(ctx context.Context, name string, timeout time.Duration) error {
var mysvc *Service
var err error
endtime := time.Now().Add(timeout)
for time.Now().Before(endtime) {
mysvc, err = c.GetService(ctx, name)
if err != nil {
return err
}
if mysvc != nil {
break
}
time.Sleep(time.Second * 10)
}
if mysvc == nil {
return fmt.Errorf("svc %s did not register", name)
}
return nil
}
CodePudding user response:
Never use time.Sleep
especially if it's a long period - as it's uninterruptible. Why does this matter? If its in a goroutine and that task is not going to complete (i.e. context was canceled), then you would rather abort immediately.
So to create a polling-wait with cancelation:
select {
case <-ctx.Done(): // cancel early if context is canceled
return ctx.Err()
case <-time.After(pollInterval): // wait for pollInterval duration
}
Put your larger timeout within the input context:
ctx := context.TODO() // <- outer request context goes here or context.Background()
// wrap context with a timeout
ctx, cancel := context.WithTimeout(ctx, 1 * time.Minute)
defer cancel() // avoid leaks
err := c.WaitForServiceToComeAlive(ctx, "job", 10*time.Second /* poll interval */)
then your service-wait function simplifies to:
func (c *Client) WaitForServiceToComeAlive(ctx context.Context, name string, pollInterval time.Duration) error {
var mysvc *Service
var err error
for {
mysvc, err = c.GetService(name) // <- this should take a ctx too - if possible for early cancelation
if err != nil {
return err
}
if mysvc != nil {
return nil
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(pollInterval):
}
}
}