-
Notifications
You must be signed in to change notification settings - Fork 2
/
command.go
75 lines (63 loc) · 1.52 KB
/
command.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package gobreak
import (
"context"
"errors"
"time"
"github.com/sony/gobreaker"
)
type command struct {
name string
circuit *gobreaker.TwoStepCircuitBreaker
errChan chan error
run runFunc
fall fallbackFunc
start time.Time
}
var (
// errPanic is returned when goroutine panics
errPanic = errors.New("command panics")
)
// errorWithFallback process error and fallback logic, with prometheus metrics
func (c *command) errorWithFallback(ctx context.Context, err error) {
// collect prometheus metrics
elapsed := c.start.Sub(time.Now()).Seconds()
requests.WithLabelValues(c.name, ErrorToEvent(err)).Inc()
requestLatencyHistogram.WithLabelValues(c.name).Observe(elapsed)
// run returns nil means everything is ok
if err == nil {
c.errChan <- nil
return
}
// return err directly when no fallback found
if c.fall == nil {
c.errChan <- err
return
}
// fallback and return err
err = c.fall(ctx, err)
c.errChan <- err
if err != nil {
requests.WithLabelValues(c.name, "fallback-failure").Inc()
return
}
requests.WithLabelValues(c.name, "fallback-success").Inc()
}
// ErrorToEvent converts error to event
func ErrorToEvent(err error) string {
event := "failure"
switch err {
case nil:
event = "success"
case context.DeadlineExceeded:
event = "context-deadline-exceeded"
case context.Canceled:
event = "context-cancled"
case gobreaker.ErrTooManyRequests:
event = "too-many-requests"
case gobreaker.ErrOpenState:
event = "circuit-open"
case errPanic:
event = "panic"
}
return event
}