2017-02-07 21:33:23 +00:00
|
|
|
package cbreaker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
2018-01-22 11:16:03 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
2017-02-07 21:33:23 +00:00
|
|
|
"github.com/vulcand/predicate"
|
|
|
|
)
|
|
|
|
|
|
|
|
type hpredicate func(*CircuitBreaker) bool
|
|
|
|
|
|
|
|
// parseExpression parses expression in the go language into predicates.
|
|
|
|
func parseExpression(in string) (hpredicate, error) {
|
|
|
|
p, err := predicate.NewParser(predicate.Def{
|
|
|
|
Operators: predicate.Operators{
|
|
|
|
AND: and,
|
|
|
|
OR: or,
|
|
|
|
EQ: eq,
|
|
|
|
NEQ: neq,
|
|
|
|
LT: lt,
|
|
|
|
LE: le,
|
|
|
|
GT: gt,
|
|
|
|
GE: ge,
|
|
|
|
},
|
|
|
|
Functions: map[string]interface{}{
|
|
|
|
"LatencyAtQuantileMS": latencyAtQuantile,
|
|
|
|
"NetworkErrorRatio": networkErrorRatio,
|
|
|
|
"ResponseCodeRatio": responseCodeRatio,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
out, err := p.Parse(in)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pr, ok := out.(hpredicate)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected predicate, got %T", out)
|
|
|
|
}
|
|
|
|
return pr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type toInt func(c *CircuitBreaker) int
|
|
|
|
type toFloat64 func(c *CircuitBreaker) float64
|
|
|
|
|
|
|
|
func latencyAtQuantile(quantile float64) toInt {
|
|
|
|
return func(c *CircuitBreaker) int {
|
|
|
|
h, err := c.metrics.LatencyHistogram()
|
|
|
|
if err != nil {
|
2017-11-22 17:20:03 +00:00
|
|
|
log.Errorf("Failed to get latency histogram, for %v error: %v", c, err)
|
2017-02-07 21:33:23 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return int(h.LatencyAtQuantile(quantile) / time.Millisecond)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func networkErrorRatio() toFloat64 {
|
|
|
|
return func(c *CircuitBreaker) float64 {
|
|
|
|
return c.metrics.NetworkErrorRatio()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func responseCodeRatio(startA, endA, startB, endB int) toFloat64 {
|
|
|
|
return func(c *CircuitBreaker) float64 {
|
|
|
|
return c.metrics.ResponseCodeRatio(startA, endA, startB, endB)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// or returns predicate by joining the passed predicates with logical 'or'
|
|
|
|
func or(fns ...hpredicate) hpredicate {
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
for _, fn := range fns {
|
|
|
|
if fn(c) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// and returns predicate by joining the passed predicates with logical 'and'
|
|
|
|
func and(fns ...hpredicate) hpredicate {
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
for _, fn := range fns {
|
|
|
|
if !fn(c) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// not creates negation of the passed predicate
|
|
|
|
func not(p hpredicate) hpredicate {
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return !p(c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// eq returns predicate that tests for equality of the value of the mapper and the constant
|
|
|
|
func eq(m interface{}, value interface{}) (hpredicate, error) {
|
|
|
|
switch mapper := m.(type) {
|
|
|
|
case toInt:
|
|
|
|
return intEQ(mapper, value)
|
|
|
|
case toFloat64:
|
|
|
|
return float64EQ(mapper, value)
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("eq: unsupported argument: %T", m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// neq returns predicate that tests for inequality of the value of the mapper and the constant
|
|
|
|
func neq(m interface{}, value interface{}) (hpredicate, error) {
|
|
|
|
p, err := eq(m, value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return not(p), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// lt returns predicate that tests that value of the mapper function is less than the constant
|
|
|
|
func lt(m interface{}, value interface{}) (hpredicate, error) {
|
|
|
|
switch mapper := m.(type) {
|
|
|
|
case toInt:
|
|
|
|
return intLT(mapper, value)
|
|
|
|
case toFloat64:
|
|
|
|
return float64LT(mapper, value)
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("lt: unsupported argument: %T", m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// le returns predicate that tests that value of the mapper function is less or equal than the constant
|
|
|
|
func le(m interface{}, value interface{}) (hpredicate, error) {
|
|
|
|
l, err := lt(m, value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
e, err := eq(m, value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return l(c) || e(c)
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// gt returns predicate that tests that value of the mapper function is greater than the constant
|
|
|
|
func gt(m interface{}, value interface{}) (hpredicate, error) {
|
|
|
|
switch mapper := m.(type) {
|
|
|
|
case toInt:
|
|
|
|
return intGT(mapper, value)
|
|
|
|
case toFloat64:
|
|
|
|
return float64GT(mapper, value)
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("gt: unsupported argument: %T", m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ge returns predicate that tests that value of the mapper function is less or equal than the constant
|
|
|
|
func ge(m interface{}, value interface{}) (hpredicate, error) {
|
|
|
|
g, err := gt(m, value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
e, err := eq(m, value)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return g(c) || e(c)
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func intEQ(m toInt, val interface{}) (hpredicate, error) {
|
|
|
|
value, ok := val.(int)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected int, got %T", val)
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return m(c) == value
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func float64EQ(m toFloat64, val interface{}) (hpredicate, error) {
|
|
|
|
value, ok := val.(float64)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected float64, got %T", val)
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return m(c) == value
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func intLT(m toInt, val interface{}) (hpredicate, error) {
|
|
|
|
value, ok := val.(int)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected int, got %T", val)
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return m(c) < value
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func intGT(m toInt, val interface{}) (hpredicate, error) {
|
|
|
|
value, ok := val.(int)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected int, got %T", val)
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return m(c) > value
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func float64LT(m toFloat64, val interface{}) (hpredicate, error) {
|
|
|
|
value, ok := val.(float64)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected int, got %T", val)
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return m(c) < value
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func float64GT(m toFloat64, val interface{}) (hpredicate, error) {
|
|
|
|
value, ok := val.(float64)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("expected int, got %T", val)
|
|
|
|
}
|
|
|
|
return func(c *CircuitBreaker) bool {
|
|
|
|
return m(c) > value
|
|
|
|
}, nil
|
|
|
|
}
|