errgroup

package module
v0.0.0-...-cbcca03 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2021 License: MIT Imports: 4 Imported by: 1

README

Actions Status Go Report Card GoDoc license

StevenACoffman/errgroup

StevenACoffman/errgroup is a drop-in alternative to Go's wonderful sync/errgroup but it converts goroutine panics to errors.

While net/http installs a panic handler with each request-serving goroutine, goroutines do not and cannot inherit panic handlers from parent goroutines, so a panic() in one of the child goroutines will kill the whole program.

So whenever you use an sync.errgroup, with some discipline, you can always remember to add a deferred recover() to every goroutine. This library just avoids that boilerplate and does that for you.

You can see it in use

package main

import (
	"fmt"

	"github.com/StevenACoffman/errgroup"
)

func main() {
	g := new(errgroup.Group)
	var urls = []string{
		"http://www.golang.org/",
		"http://www.google.com/",
		"http://www.somestupidname.com/",
	}
	for i := range urls {
		// Launch a goroutine to fetch the URL.
		i := i // https://golang.org/doc/faq#closures_and_goroutines
		g.Go(func() error {

			// deliberate index out of bounds triggered
			fmt.Println("Fetching:", i, urls[i+1])

			return nil
		})
	}
	// Wait for all HTTP fetches to complete.
	err := g.Wait()
	if err == nil {
		fmt.Println("Successfully fetched all URLs.")
	} else {
		fmt.Println(err)
	}
}

This work was done by my co-worker Ben Kraft, and, with his permission, I lightly modified it to lift it out of our repository for Go community discussion.

Counterpoint

There is an interesting discussion which has an alternative view that, with few exceptions, panics should crash your program.

Prior Art

With only a cursory search, I found a few existing open source examples.

[Kratos](https://github.com/go-kratos/kratos errgroup

Kratos Go framework for microservices has a similar errgroup solution.

PanicGroup by Sergey Alexandrovich

In the article Errors in Go: From denial to acceptance, (which advocates panic based flow control 😱), they have a PanicGroup that's roughly equivalent:

type PanicGroup struct {
  wg      sync.WaitGroup
  errOnce sync.Once
  err     error
}

func (g *PanicGroup) Wait() error {
  g.wg.Wait()
  return g.err
}

func (g *PanicGroup) Go(f func()) {
  g.wg.Add(1)

  go func() {
    defer g.wg.Done()
    defer func(){
      if r := recover(); r != nil {
        if err, ok := r.(error); ok {
          // We need only the first error, sync.Once is useful here.
          g.errOnce.Do(func() {
            g.err = err
          })
        } else {
          panic(r)
        }
      }
    }()

    f()
  }()
}

Documentation

Overview

Package errgroup provides synchronization, error propagation, and Context cancellation for groups of goroutines working on subtasks of a common task.

It wraps, and exposes a similar API to, the upstream package golang.org/x/sync/errgroup. Our version additionally recovers from panics, converting them into errors.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CollectStack

func CollectStack() []byte

func FromPanicValue

func FromPanicValue(i interface{}) error

FromPanicValue takes a value recovered from a panic and converts it into an error, for logging purposes. If the value is nil, it returns nil instead of an error.

Use like:

 defer func() {
		err := FromPanicValue(recover())
		// log or otheriwse use err
	}()

Types

type Group

type Group struct {
	// contains filtered or unexported fields
}

A Group is a collection of goroutines working on subtasks that are part of the same overall task.

A zero Group is valid and does not cancel on error.

func WithContext

func WithContext(ctx context.Context) (*Group, context.Context)

WithContext returns a new Group and an associated Context derived from ctx.

The derived Context is canceled the first time a function passed to Go returns a non-nil error or panics, or the first time Wait returns, whichever occurs first.

func (*Group) Go

func (g *Group) Go(f func() error)

Go calls the given function in a new goroutine.

The first call to return a non-nil error cancels the group; its error will be returned by Wait.

If the function panics, this is treated as if it returned an error.

func (*Group) Wait

func (g *Group) Wait() error

Wait blocks until all function calls from the Go method have returned, then returns the first non-nil error (if any) from them.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL