circuitgen

command module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2019 License: Apache-2.0 Imports: 16 Imported by: 0

README

circuitgen

circuitgen generates a circuit wrapper around an interface or struct that encapsulates calling circuits. A wrapper struct matching the interface or struct method set is generated, and each method call that is context-aware and returning an error is wrapped by a circuit. These wrapper structs have no outside Go dependencies besides the interface or struct's dependencies.

It's important to provide a IsBadRequest to not count user errors against the circuit. For example, a spike in HTTP 4xx errors (ex. DynamoDB ConditionalCheckedFailException) should not open the circuit.

Method Wrapping Requirements

When deciding if making a circuit wrapper is right for your interface or struct, consider that methods will only be wrapped if:

  • The method accepts a context as the first argument
  • The method returns an error as the last value

Example

type Publisher interface {
	// Method is wrapped
	Publish(ctx context.Context, message string) error
	// Method is *not* wrapped
	Close() error
}

Installation

go get github.com/twitchtv/circuitgen

Usage

circuitgen --pkg <package path> --name <type name> --out <output path> [--alias <alias>]

Add ./vendor/ to package path if the dependency is vendored.

Example

Generating the DynamoDB client into the wrappers directory with circuits aliased as "DynamoDB"

circuitgen --pkg github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface --name DynamoDBAPI --alias DynamoDB --out internal/wrappers

This generates a circuit wrapper that satifies the github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface.DynamoDBAPI interface.

// Code generated by circuitgen tool. DO NOT EDIT

package wrappers

import (
	"context"

	"github.com/aws/aws-sdk-go/aws/request"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbiface"
	"github.com/cep21/circuit"
)

// CircuitWrapperDynamoDBConfig contains configuration for CircuitWrapperDynamoDB. All fields are optional
type CircuitWrapperDynamoDBConfig struct {
	// IsBadRequest is an optional bad request checker. It is useful to not count user errors as faults
	IsBadRequest func(error) bool

	// Prefix is prepended to all circuit names
	Prefix string

	// Defaults are used for all created circuits. Per-circuit configs override this
	Defaults circuit.Config

	// CircuitBatchGetItemPagesWithContext is the configuration used for the BatchGetItemPagesWithContext circuit. This overrides values set by Defaults
	CircuitBatchGetItemPagesWithContext circuit.Config
	// CircuitBatchGetItemWithContext is the configuration used for the BatchGetItemWithContext circuit. This overrides values set by Defaults
	CircuitBatchGetItemWithContext circuit.Config

	// ... Rest omitted
}

// CircuitWrapperDynamoDB is a circuit wrapper for dynamodbiface.DynamoDBAPI
type CircuitWrapperDynamoDB struct {
	dynamodbiface.DynamoDBAPI

	// IsBadRequest checks whether to count a user error against the circuit. It is recommended to set this
	IsBadRequest func(error) bool

	// CircuitBatchGetItemPagesWithContext is the circuit for method BatchGetItemPagesWithContext
	CircuitBatchGetItemPagesWithContext *circuit.Circuit
	// CircuitBatchGetItemWithContext is the circuit for method BatchGetItemWithContext
	CircuitBatchGetItemWithContext *circuit.Circuit

	// ... Rest omitted
}

// NewCircuitWrapperDynamoDB creates a new circuit wrapper and initializes circuits
func NewCircuitWrapperDynamoDB(
	manager *circuit.Manager,
	embedded dynamodbiface.DynamoDBAPI,
	conf CircuitWrapperDynamoDBConfig,
) (*CircuitWrapperDynamoDB, error) {
	if conf.IsBadRequest == nil {
		conf.IsBadRequest = func(err error) bool {
			return false
		}
	}

	w := &CircuitWrapperDynamoDB{
		DynamoDBAPI:  embedded,
		IsBadRequest: conf.IsBadRequest,
	}

	var err error

	w.CircuitBatchGetItemPagesWithContext, err = manager.CreateCircuit(conf.Prefix+"DynamoDB.BatchGetItemPagesWithContext", conf.CircuitBatchGetItemPagesWithContext, conf.Defaults)
	if err != nil {
		return nil, err
	}

	w.CircuitBatchGetItemWithContext, err = manager.CreateCircuit(conf.Prefix+"DynamoDB.BatchGetItemWithContext", conf.CircuitBatchGetItemWithContext, conf.Defaults)
	if err != nil {
		return nil, err
	}

	// ... Rest omitted

	return w, nil
}

// BatchGetItemPagesWithContext calls the embedded dynamodbiface.DynamoDBAPI's method BatchGetItemPagesWithContext with CircuitBatchGetItemPagesWithContext
func (w *CircuitWrapperDynamoDB) BatchGetItemPagesWithContext(ctx context.Context, p1 *dynamodb.BatchGetItemInput, p2 func(*dynamodb.BatchGetItemOutput, bool) bool, p3 ...request.Option) error {
	err := w.CircuitBatchGetItemPagesWithContext.Run(ctx, func(ctx context.Context) error {
		err := w.DynamoDBAPI.BatchGetItemPagesWithContext(ctx, p1, p2, p3...)

		if w.IsBadRequest(err) {
			return &circuit.SimpleBadRequest{Err: err}
		}
		return err
	})

	if berr, ok := err.(*circuit.SimpleBadRequest); ok {
		err = berr.Err
	}

	return err
}

// BatchGetItemWithContext calls the embedded dynamodbiface.DynamoDBAPI's method BatchGetItemWithContext with CircuitBatchGetItemWithContext
func (w *CircuitWrapperDynamoDB) BatchGetItemWithContext(ctx context.Context, p1 *dynamodb.BatchGetItemInput, p2 ...request.Option) (*dynamodb.BatchGetItemOutput, error) {
	var r0 *dynamodb.BatchGetItemOutput
	err := w.CircuitBatchGetItemWithContext.Run(ctx, func(ctx context.Context) error {
		var err error
		r0, err = w.DynamoDBAPI.BatchGetItemWithContext(ctx, p1, p2...)

		if w.IsBadRequest(err) {
			return &circuit.SimpleBadRequest{Err: err}
		}
		return err
	})

	if berr, ok := err.(*circuit.SimpleBadRequest); ok {
		err = berr.Err
	}

	return r0, err
}

// ... Rest of methods omitted

var _ dynamodbiface.DynamoDBAPI = (*CircuitWrapperDynamoDB)(nil)

The wrapper can be used like such

func createWrappedClient() (dynamodbiface.DynamoDBAPI, error) {
	m := &circuit.Manager{} // Simplest manager

	// Create embedded client
	sess := session.Must(session.NewSession(&aws.Config{}))
	client := dynamodb.New(sess)

	// Create circuit wrapped client
	wrappedClient, err := wrappers.NewCircuitWrapperDynamoDB(m, client, wrappers.CircuitWrapperDynamoDBConfig{
		// Custom check for bad request. This is important to not count user errors as faults.
		// See https://github.com/cep21/circuit#not-counting-user-error-as-a-fault
		IsBadRequest: func(err error) bool {
			rerr, ok := err.(awserr.RequestFailure)
			if ok {
				return rerr.StatusCode() >= 400 && rerr.StatusCode() < 500
			}
			return false
		},
		// Override defaults for the GetItemWithContext circuit
		CircuitGetItemWithContext: circuit.Config{
			Execution: circuit.ExecutionConfig{
				Timeout: 200 * time.Millisecond, // Override default timeout
			},
		},
	})
	if err != nil {
		return nil, err
	}

	return wrappedClient, nil
}

Development

Go version 1.12 or beyond is recommended for development.

Run make test to run Go tests.

License

This library is licensed under the Apache 2.0 License.

Contributing

Any pull requests are extremely welcome! If you run into problems or have questions, please raise a github issue!

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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