circuit

package module
v3.2.2 Latest Latest
Warning

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

Go to latest
Published: Mar 7, 2022 License: Apache-2.0 Imports: 7 Imported by: 26

Documentation

Overview

Package circuit is a Go implementation of the circuit breaker pattern. Most documentation is available on the github README page https://github.com/cep21/circuit/blob/master/README.md

Use case

Netflix describes most use cases on their wiki for Hystrix at https://github.com/Netflix/Hystrix/wiki. Quoting the wiki:

Give protection from and control over latency and failure from dependencies accessed (typically over the network) via third-party client libraries.
Stop cascading failures in a complex distributed system.
Fail fast and rapidly recover.
Fallback and gracefully degrade when possible.
Enable near real-time monitoring, alerting, and operational control.

It is a great library for microservice applications that require a large number of calls to many, small services where any one of these calls could fail, or any of these services could be down or degraded.

Getting started

The godoc contains many examples. Look at them for a good start on how to get started integrated and using the Hystrix library for Go.

Circuit Flowchart

A circuits start Closed. The default logic is to open a circuit if more than 20 requests have come in during a 10 second window, and over 50% of requests during that 10 second window are failing.

Once failed, the circuit waits 10 seconds before allowing a single request. If that request succeeds, then the circuit closes. If it fails, then the circuit waits another 10 seconds before allowing another request (and so on).

Almost every part of this flow can be configured. See the CommandProperties struct for information.

Metric tracking

All circuits record circuit stats that you can fetch out of the Circuit at any time. In addition, you can also inject your own circuit stat trackers by modifying the MetricsCollectors structure.

Example (Http)

This is a full example of using a circuit around HTTP requests.

h := circuit.Manager{}
c := h.MustCreateCircuit("hello-http", circuit.Config{
	Execution: circuit.ExecutionConfig{
		// Timeout after 3 seconds
		Timeout: time.Second * 3,
	},
})

testServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
	_, _ = io.WriteString(rw, "hello world")
}))
defer testServer.Close()

var body bytes.Buffer
runErr := c.Run(context.Background(), func(ctx context.Context) error {
	req, err := http.NewRequest("GET", testServer.URL, nil)
	if err != nil {
		return circuit.SimpleBadRequest{Err: err}
	}
	req = req.WithContext(ctx)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode >= 400 && resp.StatusCode <= 499 {
		return circuit.SimpleBadRequest{Err: errors.New("server found your request invalid")}
	}
	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return fmt.Errorf("invalid status code: %d", resp.StatusCode)
	}
	if _, err := io.Copy(&body, resp.Body); err != nil {
		return err
	}
	return resp.Body.Close()
})
if runErr == nil {
	fmt.Printf("We saw a body\n")
	return
}
fmt.Printf("There was an error with the request: %s\n", runErr)
Output:

We saw a body

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsBadRequest

func IsBadRequest(err error) bool

IsBadRequest returns true if the error is of type BadRequest

Types

type BadRequest

type BadRequest interface {
	BadRequest() bool
}

BadRequest is implemented by an error returned by runFunc if you want to consider the requestor bad, not the circuit bad. See http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/exception/HystrixBadRequestException.html and https://github.com/Netflix/Hystrix/wiki/How-To-Use#error-propagation for information.

Example

This example shows how to return errors in a circuit without considering the circuit at fault. Here, even if someone tries to divide by zero, the circuit will not consider it a failure even if the function returns non nil error.

c := circuit.NewCircuitFromConfig("divider", circuit.Config{})
divideInCircuit := func(numerator, denominator int) (int, error) {
	var result int
	err := c.Run(context.Background(), func(ctx context.Context) error {
		if denominator == 0 {
			// This error type is not counted as a failure of the circuit
			return &circuit.SimpleBadRequest{
				Err: errors.New("someone tried to divide by zero"),
			}
		}
		result = numerator / denominator
		return nil
	})
	return result, err
}
_, err := divideInCircuit(10, 0)
fmt.Println("Result of 10/0 is", err)
Output:

Result of 10/0 is someone tried to divide by zero

type Circuit

type Circuit struct {
	// circuitStats
	CmdMetricCollector      RunMetricsCollection
	FallbackMetricCollector FallbackMetricsCollection
	CircuitMetricsCollector MetricsCollection

	// ClosedToOpen controls when to open a closed circuit
	ClosedToOpen ClosedToOpen
	// openToClosed controls when to close an open circuit
	OpenToClose OpenToClosed
	// contains filtered or unexported fields
}

Circuit is a circuit breaker pattern implementation that can accept commands and open/close on failures

Example (Noearlyterminate)

If the context passed into a circuit function ends, before the circuit can finish, it does not count the circuit as unhealthy. You can disable this behavior with the `IgnoreInterrupts` flag.

This example proves that terminating a circuit call early because the passed in context died does not, by default, count as an error on the circuit. It also demonstrates setting up internal stat collection by default for all circuits

// Inject stat collection to prove these failures don't count
f := rolling.StatFactory{}
manager := circuit.Manager{
	DefaultCircuitProperties: []circuit.CommandPropertiesConstructor{
		f.CreateConfig,
	},
}
c := manager.MustCreateCircuit("don't fail me bro")
// The passed in context times out in one millisecond
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
defer cancel()
errResult := c.Execute(ctx, func(ctx context.Context) error {
	select {
	case <-ctx.Done():
		// This will return early, with an error, since the parent context was canceled after 1 ms
		return ctx.Err()
	case <-time.After(time.Hour):
		panic("We never actually get this far")
	}
}, nil)
rs := f.RunStats("don't fail me bro")
fmt.Println("errResult is", errResult)
fmt.Println("The error and timeout count is", rs.ErrTimeouts.TotalSum()+rs.ErrFailures.TotalSum())
Output:

errResult is context deadline exceeded
The error and timeout count is 0

func NewCircuitFromConfig

func NewCircuitFromConfig(name string, config Config) *Circuit

NewCircuitFromConfig creates an inline circuit. If you want to group all your circuits together, you should probably just use Manager struct instead.

func (*Circuit) CloseCircuit

func (c *Circuit) CloseCircuit()

CloseCircuit closes an open circuit. Usually because we think it's healthy again. Be aware, if the circuit isn't actually healthy, it will just open back up again.

func (*Circuit) ConcurrentCommands

func (c *Circuit) ConcurrentCommands() int64

ConcurrentCommands returns how many commands are currently running

func (*Circuit) ConcurrentFallbacks

func (c *Circuit) ConcurrentFallbacks() int64

ConcurrentFallbacks returns how many fallbacks are currently running

func (*Circuit) Config

func (c *Circuit) Config() Config

Config returns the circuit's configuration. Modifications to this configuration are not reflected by the circuit. In other words, this creates a copy.

func (*Circuit) Execute

func (c *Circuit) Execute(ctx context.Context, runFunc func(context.Context) error, fallbackFunc func(context.Context, error) error) error

Execute the circuit. Prefer this over Go. Similar to http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/HystrixCommand.html#execute-- The returned error will either be the result of runFunc, the result of fallbackFunc, or an internal library error. Internal library errors will match the interface Error and you can use type casting to check this.

Example (Fallback)

This example shows execute failing (marking the circuit with a failure), but not returning an error back to the user since the fallback was able to execute. For this case, we try to load the size of the largest message a user can send, but fall back to 140 if the load fails.

c := circuit.NewCircuitFromConfig("divider", circuit.Config{})
var maximumMessageSize int
err := c.Execute(context.Background(), func(_ context.Context) error {
	return errors.New("your circuit failed")
}, func(ctx context.Context, err2 error) error {
	maximumMessageSize = 140
	return nil
})
fmt.Printf("value=%d err=%v", maximumMessageSize, err)
Output:

value=140 err=<nil>
Example (Fallbackhelloworld)

This example shows how fallbacks execute to return alternate errors or provide logic when the circuit is open.

// You can create circuits without using the manager
c := circuit.NewCircuitFromConfig("hello-world-fallback", circuit.Config{})
errResult := c.Execute(context.Background(), func(ctx context.Context) error {
	return errors.New("this will fail")
}, func(ctx context.Context, err error) error {
	fmt.Println("Circuit failed with error, but fallback returns nil")
	return nil
})
fmt.Println("Execution result:", errResult)
Output:

Circuit failed with error, but fallback returns nil
Execution result: <nil>
Example (Helloworld)

This example shows execute failing (marking the circuit with a failure), but not returning an error back to the user since the fallback was able to execute. For this case, we try to load the size of the largest message a user can send, but fall back to 140 if the load fails.

c := circuit.NewCircuitFromConfig("hello-world", circuit.Config{})
err := c.Execute(context.Background(), func(_ context.Context) error {
	return nil
}, nil)
fmt.Printf("err=%v", err)
Output:

err=<nil>
Example (Panics)

Code executed with `Execute` does not spawn a goroutine and panics naturally go up the call stack to the caller. This is also true for `Go`, where we attempt to recover and throw panics on the same stack that calls Go. This example will panic, and the panic can be caught up the stack.

h := circuit.Manager{}
c := h.MustCreateCircuit("panic_up")

defer func() {
	r := recover()
	if r != nil {
		fmt.Println("I recovered from a panic", r)
	}
}()
_ = c.Execute(context.Background(), func(ctx context.Context) error {
	panic("oh no")
}, nil)
Output:

I recovered from a panic oh no

func (*Circuit) Go

func (c *Circuit) Go(ctx context.Context, runFunc func(context.Context) error, fallbackFunc func(context.Context, error) error) error

Go executes `Execute`, but uses spawned goroutines to end early if the context is canceled. Use this if you don't trust the runFunc to end correctly if context fails. This is a design mirroed in the go-hystrix library, but be warned it is very dangerous and could leave orphaned goroutines hanging around forever doing who knows what.

Example

It is recommended to use `circuit.Execute` and a context aware function. If, however, you want to exit your run function early and leave it hanging (possibly forever), then you can call `circuit.Go`.

h := circuit.Manager{}
c := h.MustCreateCircuit("untrusting-circuit", circuit.Config{
	Execution: circuit.ExecutionConfig{
		// Time out the context after a few ms
		Timeout: time.Millisecond * 30,
	},
})

errResult := c.Go(context.Background(), func(ctx context.Context) error {
	// Sleep 30 seconds, way longer than our timeout
	time.Sleep(time.Second * 30)
	return nil
}, nil)
fmt.Printf("err=%v", errResult)
Output:

err=context deadline exceeded
Example (Panics)

Even though Go executes inside a goroutine, we catch that panic and bubble it up the same call stack that called Go

c := circuit.NewCircuitFromConfig("panic_up", circuit.Config{})

defer func() {
	r := recover()
	if r != nil {
		fmt.Println("I recovered from a panic", r)
	}
}()
_ = c.Go(context.Background(), func(ctx context.Context) error {
	panic("oh no")
}, nil)
Output:

I recovered from a panic oh no

func (*Circuit) IsOpen

func (c *Circuit) IsOpen() bool

IsOpen returns true if the circuit should be considered 'open' (ie not allowing runFunc calls)

func (*Circuit) Name

func (c *Circuit) Name() string

Name of this circuit

func (*Circuit) OpenCircuit

func (c *Circuit) OpenCircuit()

OpenCircuit will open a closed circuit. The circuit will then try to repair itself

func (*Circuit) Run

func (c *Circuit) Run(ctx context.Context, runFunc func(context.Context) error) error

Run will execute the circuit without a fallback. It is the equivalent of calling Execute with a nil fallback function

func (*Circuit) SetConfigNotThreadSafe

func (c *Circuit) SetConfigNotThreadSafe(config Config)

SetConfigNotThreadSafe is only useful during construction before a circuit is being used. It is not thread safe, but will modify all the circuit's internal structs to match what the config wants. It also doe *NOT* use the default configuration parameters.

func (*Circuit) SetConfigThreadSafe

func (c *Circuit) SetConfigThreadSafe(config Config)

SetConfigThreadSafe changes the current configuration of this circuit. Note that many config parameters, specifically those around creating stat tracking buckets, are not modifiable during runtime for efficiency reasons. Those buckets will stay the same.

Example

Many configuration variables can be set at runtime in a thread safe way

h := circuit.Manager{}
c := h.MustCreateCircuit("changes-at-runtime", circuit.Config{})
// ... later on (during live)
c.SetConfigThreadSafe(circuit.Config{
	Execution: circuit.ExecutionConfig{
		MaxConcurrentRequests: int64(12),
	},
})
Output:

func (*Circuit) Var

func (c *Circuit) Var() expvar.Var

Var exports that help diagnose the circuit

type ClosedToOpen

type ClosedToOpen interface {
	RunMetrics
	Metrics
	// AttemptToOpen a circuit that is currently closed, after a bad request comes in.  Only called after bad requests,
	// never called after a successful request
	ShouldOpen(now time.Time) bool
	// Even though the circuit is closed, and we want to allow the circuit to remain closed, we still prevent this
	// command from happening.  The error will return as a short circuit to the caller, as well as trigger fallback
	// logic.  This could be useful if your circuit is closed, but some external force wants you to pretend to be open.
	Prevent(now time.Time) bool
}

ClosedToOpen receives events and controls if the circuit should open or close as a result of those events. Return true if the circuit should open, false if the circuit should close.

type CommandPropertiesConstructor

type CommandPropertiesConstructor func(circuitName string) Config

CommandPropertiesConstructor is a generic function that can create command properties to configure a circuit by name It is safe to leave not configured properties their empty value.

Example

You can use DefaultCircuitProperties to set configuration dynamically for any circuit

myFactory := func(circuitName string) circuit.Config {
	timeoutsByName := map[string]time.Duration{
		"v1": time.Second,
		"v2": time.Second * 2,
	}
	customTimeout := timeoutsByName[circuitName]
	if customTimeout == 0 {
		// Just return empty if you don't want to set any config
		return circuit.Config{}
	}
	return circuit.Config{
		Execution: circuit.ExecutionConfig{
			Timeout: customTimeout,
		},
	}
}

// Manager manages circuits with unique names
h := circuit.Manager{
	DefaultCircuitProperties: []circuit.CommandPropertiesConstructor{myFactory},
}
h.MustCreateCircuit("v1")
fmt.Println("The timeout of v1 is", h.GetCircuit("v1").Config().Execution.Timeout)
Output:

The timeout of v1 is 1s

type Config

type Config struct {
	General   GeneralConfig
	Execution ExecutionConfig
	Fallback  FallbackConfig
	Metrics   MetricsCollectors
}

Config controls how a circuit operates

Example (Custommetrics)

Implement interfaces CmdMetricCollector or FallbackMetricCollector to know what happens with commands or fallbacks.

Then pass those implementations to configure.
config := circuit.Config{
	Metrics: circuit.MetricsCollectors{
		Run: []circuit.RunMetrics{
			// Here is where I would insert my custom metric collector
		},
	},
}
circuit.NewCircuitFromConfig("custom-metrics", config)
Output:

func (*Config) Merge

func (c *Config) Merge(other Config) *Config

Merge these properties with another command's properties. Anything set to the zero value, will takes values from other.

type Configurable

type Configurable interface {
	// SetConfigThreadSafe can be called while the circuit is currently being used and will modify things that are
	// safe to change live.
	SetConfigThreadSafe(props Config)
	// SetConfigNotThreadSafe should only be called when the circuit is not in use: otherwise it will fail -race
	// detection
	SetConfigNotThreadSafe(props Config)
}

Configurable is anything that can receive configuration changes while live

type Error

type Error interface {
	error
	// ConcurrencyLimitReached returns true if this error is because the concurrency limit has been reached.
	ConcurrencyLimitReached() bool
	// CircuitOpen returns true if this error is because the circuit is open.
	CircuitOpen() bool
}

Error is the type of error returned by internal errors using the circuit library.

Example (Checking)

Shows how to check if an error is part of the circuit library.

x := errors.New("an error")
if _, ok := x.(circuit.Error); ok {
	// this error is a circuit library error, not the result of runFunc or fallbackFunc
}
Output:

type ExecutionConfig

type ExecutionConfig struct {
	// ExecutionTimeout is https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.thread.timeoutInMilliseconds
	Timeout time.Duration
	// MaxConcurrentRequests is https://github.com/Netflix/Hystrix/wiki/Configuration#executionisolationsemaphoremaxconcurrentrequests
	MaxConcurrentRequests int64
	// Normally if the parent context is canceled before a timeout is reached, we don't consider the circuit
	// unhealthy.  Set this to true to consider those circuits unhealthy.
	IgnoreInterrupts bool `json:",omitempty"`
	// IsErrInterrupt should return true if the error from the original context should be considered an interrupt error.
	// The error passed in will be a non-nil error returned by calling `Err()` on the context passed into Run.
	// The default behavior is to consider all errors from the original context interrupt caused errors.
	// Default behaviour:
	// 		IsErrInterrupt: function(e err) bool { return true }
	IsErrInterrupt func(originalContextError error) bool `json:"-"`
}

ExecutionConfig is https://github.com/Netflix/Hystrix/wiki/Configuration#execution

type FallbackConfig

type FallbackConfig struct {
	// Enabled is opposite of https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerenabled
	// Note: Java Manager calls this "Enabled".  I call it "Disabled" so the zero struct can fill defaults
	Disabled bool `json:",omitempty"`
	// MaxConcurrentRequests is https://github.com/Netflix/Hystrix/wiki/Configuration#fallback.isolation.semaphore.maxConcurrentRequests
	MaxConcurrentRequests int64
}

FallbackConfig is https://github.com/Netflix/Hystrix/wiki/Configuration#fallback

type FallbackMetrics

type FallbackMetrics interface {
	// All `fallback` calls will implement one of the following metrics
	// Success each time fallback is called and succeeds.
	Success(now time.Time, duration time.Duration)
	// ErrFailure each time fallback callback fails.
	ErrFailure(now time.Time, duration time.Duration)
	// ErrConcurrencyLimitReject each time fallback fails due to concurrency limit
	ErrConcurrencyLimitReject(now time.Time)
}

FallbackMetrics is guaranteed to execute one (and only one) of the following functions each time a fallback is executed. Methods with durations are when the fallback is actually executed. Methods without durations are when the fallback was never called, probably because of some circuit condition.

type FallbackMetricsCollection

type FallbackMetricsCollection []FallbackMetrics

FallbackMetricsCollection sends fallback metrics to all collectors

func (FallbackMetricsCollection) ErrConcurrencyLimitReject

func (r FallbackMetricsCollection) ErrConcurrencyLimitReject(now time.Time)

ErrConcurrencyLimitReject sends ErrConcurrencyLimitReject to all collectors

func (FallbackMetricsCollection) ErrFailure

func (r FallbackMetricsCollection) ErrFailure(now time.Time, duration time.Duration)

ErrFailure sends ErrFailure to all collectors

func (FallbackMetricsCollection) Success

func (r FallbackMetricsCollection) Success(now time.Time, duration time.Duration)

Success sends Success to all collectors

func (FallbackMetricsCollection) Var

Var exposes run collectors as expvar

type GeneralConfig

type GeneralConfig struct {
	// if disabled, Execute functions pass to just calling runFunc and do no tracking or fallbacks
	// Note: Java Manager calls this "Enabled".  I call it "Disabled" so the zero struct can fill defaults
	Disabled bool `json:",omitempty"`
	// ForceOpen is https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerforceopen
	ForceOpen bool `json:",omitempty"`
	// ForcedClosed is https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerforceclosed
	ForcedClosed bool `json:",omitempty"`
	// GoLostErrors can receive errors that would otherwise be lost by `Go` executions.  For example, if Go returns
	// early but some long time later an error or panic eventually happens.
	GoLostErrors func(err error, panics interface{}) `json:"-"`
	// ClosedToOpenFactory creates logic that determines if the circuit should go from Closed to Open state.
	// By default, it never opens
	ClosedToOpenFactory func() ClosedToOpen `json:"-"`
	// OpenToClosedFactory creates logic that determines if the circuit should go from Open to Closed state.
	// By default, it never closes
	OpenToClosedFactory func() OpenToClosed `json:"-"`
	// CustomConfig is anything you want.
	CustomConfig map[interface{}]interface{} `json:"-"`
	// TimeKeeper returns the current way to keep time.  You only want to modify this for testing.
	TimeKeeper TimeKeeper `json:"-"`
}

GeneralConfig controls the non general logic of the circuit. Things specific to metrics, execution, or fallback are in their own configs

type Manager

type Manager struct {
	// DefaultCircuitProperties is a list of Config constructors called, in reverse order,
	// to append or modify configuration for your circuit.
	DefaultCircuitProperties []CommandPropertiesConstructor
	// contains filtered or unexported fields
}

Manager manages circuits with unique names

func (*Manager) AllCircuits

func (h *Manager) AllCircuits() []*Circuit

AllCircuits returns every hystrix circuit tracked

func (*Manager) CreateCircuit

func (h *Manager) CreateCircuit(name string, configs ...Config) (*Circuit, error)

CreateCircuit creates a new circuit, or returns error if a circuit with that name already exists

func (*Manager) GetCircuit

func (h *Manager) GetCircuit(name string) *Circuit

GetCircuit returns the circuit with a given name, or nil if the circuit does not exist. You should not call this in live code. Instead, store the circuit somewhere and use the circuit directly.

func (*Manager) MustCreateCircuit

func (h *Manager) MustCreateCircuit(name string, config ...Config) *Circuit

MustCreateCircuit calls CreateCircuit, but panics if the circuit name already exists

Example (Helloworld)

This example shows how to create a hello-world circuit from the circuit manager

// Manages all our circuits
h := circuit.Manager{}
// Create a circuit with a unique name
c := h.MustCreateCircuit("hello-world")
// Call the circuit
errResult := c.Execute(context.Background(), func(ctx context.Context) error {
	return nil
}, nil)
fmt.Println("Result of execution:", errResult)
Output:

Result of execution: <nil>

func (*Manager) Var

func (h *Manager) Var() expvar.Var

Var allows you to expose all your hystrix circuits on expvar

Example

If you wanted to publish hystrix information on Expvar, you can register your manager.

h := circuit.Manager{}
expvar.Publish("hystrix", h.Var())
Output:

type Metrics

type Metrics interface {
	// Closed is called when the circuit transitions from Open to Closed.
	Closed(now time.Time)
	// Opened is called when the circuit transitions from Closed to Opened.
	Opened(now time.Time)
}

Metrics reports internal circuit metric events

type MetricsCollection

type MetricsCollection []Metrics

MetricsCollection allows reporting multiple circuit metrics at once

func (MetricsCollection) Closed

func (r MetricsCollection) Closed(now time.Time)

Closed sends Closed to all collectors

func (MetricsCollection) Opened

func (r MetricsCollection) Opened(now time.Time)

Opened sends Opened to all collectors

type MetricsCollectors

type MetricsCollectors struct {
	Run      []RunMetrics      `json:"-"`
	Fallback []FallbackMetrics `json:"-"`
	Circuit  []Metrics         `json:"-"`
}

MetricsCollectors can receive metrics during a circuit. They should be fast, as they will block circuit operation during function calls.

type OpenToClosed

type OpenToClosed interface {
	RunMetrics
	Metrics
	// AttemptToOpen a circuit that is currently closed, after a bad request comes in
	ShouldClose(now time.Time) bool
	// Allow a single request while remaining in the closed state
	Allow(now time.Time) bool
}

OpenToClosed controls logic that tries to close an open circuit

type RunMetrics

type RunMetrics interface {
	// Success each time `Execute` does not return an error
	Success(now time.Time, duration time.Duration)
	// ErrFailure each time a runFunc (the circuit part) ran, but failed
	ErrFailure(now time.Time, duration time.Duration)
	// ErrTimeout increments the number of timeouts that occurred in the circuit breaker.
	ErrTimeout(now time.Time, duration time.Duration)
	// ErrBadRequest is counts of http://netflix.github.io/Hystrix/javadoc/com/netflix/hystrix/exception/HystrixBadRequestException.html
	// See https://github.com/Netflix/Hystrix/wiki/How-To-Use#error-propagation
	ErrBadRequest(now time.Time, duration time.Duration)
	// ErrInterrupt means the request ended, not because the runFunc failed, but probably because the original
	// context canceled.  Your circuit returned an error, but it's probably because someone else killed the context,
	// and not that your circuit is broken.  Java Manager doesn't have an equivalent for this, but it would be like if
	// an interrupt was called on the thread.
	//
	// A note on stat tracking: you may or may not consider this duration valid.  Yes, that's how long it executed,
	// but the circuit never finished correctly since it was asked to end early, so the value is smaller than the
	// circuit would have otherwise taken.
	ErrInterrupt(now time.Time, duration time.Duration)

	// If `Execute` returns an error, it will increment one of the following metrics
	// ErrConcurrencyLimitReject each time a circuit is rejected due to concurrency limits
	ErrConcurrencyLimitReject(now time.Time)
	// ErrShortCircuit each time runFunc is not called because the circuit was open.
	ErrShortCircuit(now time.Time)
}

RunMetrics is guaranteed to execute one (and only one) of the following functions each time the circuit attempts to call a run function. Methods with durations are when run was actually executed. Methods without durations never called run, probably because of the circuit.

type RunMetricsCollection

type RunMetricsCollection []RunMetrics

RunMetricsCollection send metrics to multiple RunMetrics

func (RunMetricsCollection) ErrBadRequest

func (r RunMetricsCollection) ErrBadRequest(now time.Time, duration time.Duration)

ErrBadRequest sends ErrBadRequest to all collectors

func (RunMetricsCollection) ErrConcurrencyLimitReject

func (r RunMetricsCollection) ErrConcurrencyLimitReject(now time.Time)

ErrConcurrencyLimitReject sends ErrConcurrencyLimitReject to all collectors

func (RunMetricsCollection) ErrFailure

func (r RunMetricsCollection) ErrFailure(now time.Time, duration time.Duration)

ErrFailure sends ErrFailure to all collectors

func (RunMetricsCollection) ErrInterrupt

func (r RunMetricsCollection) ErrInterrupt(now time.Time, duration time.Duration)

ErrInterrupt sends ErrInterrupt to all collectors

func (RunMetricsCollection) ErrShortCircuit

func (r RunMetricsCollection) ErrShortCircuit(now time.Time)

ErrShortCircuit sends ErrShortCircuit to all collectors

func (RunMetricsCollection) ErrTimeout

func (r RunMetricsCollection) ErrTimeout(now time.Time, duration time.Duration)

ErrTimeout sends ErrTimeout to all collectors

func (RunMetricsCollection) Success

func (r RunMetricsCollection) Success(now time.Time, duration time.Duration)

Success sends Success to all collectors

func (RunMetricsCollection) Var

Var exposes run collectors as expvar

type SimpleBadRequest

type SimpleBadRequest struct {
	Err error
}

SimpleBadRequest is a simple wrapper for an error to mark it as a bad request

func (SimpleBadRequest) BadRequest

func (s SimpleBadRequest) BadRequest() bool

BadRequest always returns true

func (SimpleBadRequest) Cause

func (s SimpleBadRequest) Cause() error

Cause returns the wrapped error

func (SimpleBadRequest) Error

func (s SimpleBadRequest) Error() string

Cause returns the wrapped error

type TimeKeeper

type TimeKeeper struct {
	// Now should simulate time.Now
	Now func() time.Time
	// AfterFunc should simulate time.AfterFunc
	AfterFunc func(time.Duration, func()) *time.Timer
}

TimeKeeper allows overriding time to test the circuit

Directories

Path Synopsis
Package closers contains subpackages that control circuit open and close logic.
Package closers contains subpackages that control circuit open and close logic.
hystrix
Package hystrix is a Go implementation of Netflix's Hystrix logic for circuit breakers.
Package hystrix is a Go implementation of Netflix's Hystrix logic for circuit breakers.
simplelogic
Package simplelogic is a holding place for close and open circuit logic that is otherwise simple in use or complexity.
Package simplelogic is a holding place for close and open circuit logic that is otherwise simple in use or complexity.
Run this simple Go program to see what circuits look like.
Run this simple Go program to see what circuits look like.
Package faststats contains helpers to calculate circuit statistics quickly (usually atomically).
Package faststats contains helpers to calculate circuit statistics quickly (usually atomically).
internal
Package metriceventstream allows exposing your circuit's health as a metric stream that you can visualize with the hystrix dashboard.
Package metriceventstream allows exposing your circuit's health as a metric stream that you can visualize with the hystrix dashboard.
Package metrics contains implementations of MetricsCollectors to aid circuit health detection.
Package metrics contains implementations of MetricsCollectors to aid circuit health detection.
responsetimeslo
Package responsetimeslo contains a MetricsCollector that tracks a SLO metric for circuits.
Package responsetimeslo contains a MetricsCollector that tracks a SLO metric for circuits.
rolling
Package rolling contains a MetricsCollector that tracks in memory rolling stats about a circuit.
Package rolling contains a MetricsCollector that tracks in memory rolling stats about a circuit.
statsdmetrics
Package statsdmetrics contains a MetricsCollector that submits circuit metrics to statsd using the github.com/cactus/go-statsd-client/statsd interface.
Package statsdmetrics contains a MetricsCollector that submits circuit metrics to statsd using the github.com/cactus/go-statsd-client/statsd interface.

Jump to

Keyboard shortcuts

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