Skip to content

Instantly share code, notes, and snippets.

@mumoshu
Created January 17, 2021 03:12
Show Gist options
  • Save mumoshu/6f13097f3b77758dbeaa958958468f64 to your computer and use it in GitHub Desktop.
Save mumoshu/6f13097f3b77758dbeaa958958468f64 to your computer and use it in GitHub Desktop.
errgroup.Groupの亜種(一時的なエラーの場合は部分的に再試行し、そうでなければ全て中止する)
package main
import (
"context"
"errors"
"fmt"
"os"
"strings"
"sync"
"time"
)
type errorTransientFailure struct {
}
func (e errorTransientFailure) Error() string {
return "transient failure"
}
type errorPermanentFailure struct {
}
func (e errorPermanentFailure) Error() string {
return "permanent failure"
}
func main() {
fmt.Println("Hello, playground")
var errOnce sync.Once
var cause error
var wg sync.WaitGroup
ch1 := make(chan string)
ch2 := make(chan string)
defer close(ch1)
defer close(ch2)
go func() {
ch1 <- "to1: a"
ch2 <- "to2: a"
ch1 <- "to1: b"
ch1 <- "warn"
ch2 <- "warn"
ch1 <- "transient failure"
ch2 <- "permanent failure"
}()
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
wg.Add(1)
go func() {
defer wg.Done()
for {
if err := procCh(ctx, "proc1", ch1); err != nil {
if errorAny(err, errorTransientFailure{}) {
continue
}
errOnce.Do(func() {
cause = err
cancel()
})
break
}
if ctx.Err() != nil {
break
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for {
if err := procCh(ctx, "proc2", ch2); err != nil {
if errorAny(err, errorTransientFailure{}) {
continue
}
errOnce.Do(func() {
cause = err
cancel()
})
break
}
if ctx.Err() != nil {
break
}
}
}()
wg.Wait()
if cause != nil {
println("main", cause.Error())
os.Exit(1)
}
}
func errorAny(err error, options ...error) bool {
for _, e := range options {
if errors.Is(err, e) {
return true
}
}
return false
}
func procCh(ctx context.Context, text string, ch chan string) error {
println(text, "starting")
defer println(text, "stopping")
for {
select {
case <-ctx.Done():
return nil
case t := <-ch:
if strings.HasSuffix(t, " failure") {
println(text, "failed with", t)
if t == "transient failure" {
return errorTransientFailure{}
} else if t == "permanent failure" {
return errorPermanentFailure{}
}
return errors.New(t)
}
println(text, "received", t)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment