goll

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2021 License: MIT Imports: 12 Imported by: 2

README

Goll, a Go Load Limiter

Documentation Go Report Card CircleCI

A highly configurable, feature-packed, load-aware limiter for Go.

Features

  • Sliding window/buckets algorithm for smooth limiting and cooldown
  • Handles multi-tenancy by default
  • Limit amount of 'load' instead of simply limiting the number of requests by allowing load-aware requests
  • Support for automatic retry, delay or timeout on load submissions
  • Automatically compute RetryIn (time-to-availability) to easily give clients an amount of time to wait before resubmissions
  • Configurable penalties for over-max-load requests and for uncompliant clients who do not respect the required delays
  • Synchronization adapters for clustering
  • Composite load limiter to allow for complex load limiting with a single instance (eg. long-time rate limiting together with burst protection)
  • Configurable window fragmentation for optimal smoothness vs performance tuning
  • Dedicated Gin-Gonic middleware
  • Thread safe

Load limiter vs rate limiter

A standard rate limiter will count and limit the amount of requests during a fixed amount of time. While useful, sometimes this is not enough as not all requests are equal.

Load balancing works on the concept of load-weighted requests: different requests get assigned an arbitrary amount of "load score" and the total load gets limited instead of the number of requests.

Think of your REST API: maybe you don't want to limit the GET /ping endpoint in the same way you limit your POST /heavy-operation or your GET /fetch-whole-database[1] endpoint.

By assigning those endpoints a different load score you can get a limiting policy that is effective for defending your system against abuse, but also not too limiting and adapting to different clients' needs.

Note that assigning "1" as load score for all requests you can use it as a standard rate limiter.

[1] put down the gun I was kidding

Quickstart

Get the module with:

go get github.com/fabiofenoglio/goll

Basic usage boils down to creating an instance and calling its Submit method with a tenantKey and an arbitrary amount of load.

Please check out the full quickstart document.

limiter, _ := goll.New(&goll.Config{
    MaxLoad:           1000,
    WindowSize:        20 * time.Second,
})

res := limiter.Submit("tenantKey", 1)

if res.Accepted {
    fmt.Println("yeee")
} else {
    fmt.Println("darn")
}

Note that you don't have to handle multitenancy if you don't need to.

Synchronizing multiple instances

In order to handle heavy loads you will probably be scaling horizontally.

The load limiter is able to quickly work in such a scenario by synchronizing live load data across all the required instances using the channel you prefer.

A sample implementation is provided to synchronize via a Redis instance but you could write your own adapter for memcached, any database, an infinispan cluster and so on.

Please see the synchronization page for a full explanation and some examples.

// provided adapter for synchronizing over a Redis instance
adapter, _ := gollredis.NewRedisSyncAdapter(&gollredis.Config{
    Pool:      goredis.NewPool(goredislib.NewClient(&goredislib.Options{
        Addr:     "localhost:6379",
    })),
    MutexName: "redisAdapterTest",
})

limiter, _ := goll.New(&goll.Config{
    MaxLoad:           1000,
    WindowSize:        20 * time.Second,
    // just plug the adapter here
    SyncAdapter:       adapter,
})

Uncompliance penalties

The limiting policy can be tuned in order to penalize clients for hitting the load limit and/or penalize clients that send an excessive number of requests and do not comply with the required delays.

With a couple parameters you can easily provide a better protection for your system that also encourages compliance and further limits uncompliant clients.

See the penalties page for an in-depth explanation, or the performances page for a visual demonstration of the effect of penalties.

limiter, err := goll.New(&goll.Config{
    MaxLoad:                                  100,
    WindowSize:                               20 * time.Second,

    // apply penalization when the load limit is reached
    OverstepPenaltyFactor:                    0.20,
    OverstepPenaltyDistributionFactor:        0.25,

    // apply penalization when client is not complying with required delays
    RequestOverheadPenaltyFactor:             0.33,
    RequestOverheadPenaltyDistributionFactor: 0.25,
})

Composite limiters

It is often useful to combine multiple constraints on the acceptable load.

For instance you may want to limit the maximum load your system can handle every minute but you'd also like to limit the load per second in order to protect against sudden request bursts.

See the composition page for a full explanation or the performances page for a graphic illustration explaining how composition can be useful.

limiter, _ := goll.NewComposite(&goll.CompositeConfig{
    Limiters: []goll.Config{
        {
            // limit max load for each minute
            MaxLoad:           100,
            WindowSize:        60 * time.Second,
        }, 
        {
            // also limit load per second against request bursts
            MaxLoad:           10,
            WindowSize:        1 * time.Second,
        },
    },
})

Gin-Gonic middleware

A separate module is available to plug the limiter as a Gin middleware.

Please check out the gin-goll repository.

limiter, _ := goll.New(&goll.Config{
    MaxLoad:           100,
    WindowSize:        3 * time.Second,
})

ginLimiter := gingoll.NewLimiterMiddleware(gingoll.Config{
    Limiter: limiter,
    DefaultRouteLoad: 1,
    TenantKeyFunc: func(c *gin.Context) (string, error) {  
        return c.ClientIP(), nil // we'll limit per IP
    },
    AbortHandler: func(c *gin.Context, result goll.SubmitResult) {
        if result.RetryInAvailable {
            c.Header("X-Retry-In", fmt.Sprintf("%v", result.RetryIn.Milliseconds()))
        }
        c.AbortWithStatus(429)
    },
})

r := gin.Default()

// plugin the load limiter middleware for all routes like this:
r.Use(ginLimiter.Default())

// or on single route
r.GET("/something", ginLimiter.Default(), routeHandler)

// specify per-route load
r.POST("/create-something", ginLimiter.WithLoad(5), routeHandler)
r.PUT("/update-something", ginLimiter.WithLoad(3), routeHandler)

// on route group with specific load
r.Group("/intensive-operations/").Use(ginLimiter.WithLoad(10))

Performances

You can check out the performances page for graphics illustrating performances in a variety of common scenarios.

130% vs standard limiter 130% vs limiter with penalties 150% vs standard limiter 150% vs composite limiter
graph graph graph graph

The limiter overhead and its memory requirements are very small:

goos: windows
goarch: amd64
pkg: github.com/fabiofenoglio/goll
cpu: Intel(R) Core(TM) i7-9700KF CPU @ 3.60GHz
BenchmarkSubmit50pc-8          	 7575757	       159.6 ns/op	      81 B/op	       3 allocs/op
BenchmarkSubmitAllAccepted-8   	 8715760	       139.4 ns/op	      73 B/op	       3 allocs/op
BenchmarkSubmitAllRejected-8   	 7199920	       166.3 ns/op	      88 B/op	       4 allocs/op

Examples

You can check out some example programs.

Documentation

Overview

A highly configurable, feature-packed, variable-request-sized load limiter module.

Features:

- Sliding window/buckets algorithm for smooth limiting and cooldown

- Limit amount of 'load' instead of simply limiting the number of requests by allowing load-aware requests

- Support for automatic retry, delay or timeout on load submissions

- Automatically compute RetryIn (time-to-availability) to easily give clients an amount of time to wait before resubmissions

- Configurable penalties for over-max-load requests and for uncompliant clients who do not respect the required delays

- Configurable window fragmentation for optimal smoothness vs performance tuning

- Thread safe

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrLoadRequestTimeout is a sentinel for the error that
	// occurs when autoretrying a submission (ex. with SubmitUntil)
	// failed because the maximum timeout was reached
	ErrLoadRequestTimeout = &LoadRequestTimeout{}

	// ErrLoadRequestRejected is a sentinel for the error that
	// occurs when a submission can't be accepted
	// usually happens when:
	// - the request asks for a load greater than the limiter maximum load
	// - the request gets rejected and the limiter was built with SkipRetryInComputing = true
	ErrLoadRequestRejected = &LoadRequestRejected{}
)

Functions

This section is empty.

Types

type CompositeConfig

type CompositeConfig struct {

	// Limiters is a required parameter holding the configurations
	// of the single limiters you want to compose together.
	Limiters []Config

	// SyncAdapter is an implementation used to synchronize
	// the limiter data in a clustered environment.
	//
	// You can provide your own implementation.
	// You can use as example github.com/fabiofenoglio/goll-redis
	// which synchronizes data for multiple instances over a Redis cluster.
	SyncAdapter SyncAdapter

	// Time-related functions can be overriden to allow for easier testing
	// you should usually not override these.
	TimeFunc  func() time.Time
	SleepFunc func(d time.Duration)

	// you can pass your custom logger if you'd like to
	// but it's not required
	Logger Logger
}

type CompositeLoadLimiter

type CompositeLoadLimiter interface {
	// Probe checks if the given load would be allowed right now.
	// it is a readonly method that does not modify the current window data.
	Probe(tenantKey string, load uint64) (bool, error)

	// Submit asks for the given load to be accepted.
	// The result object contains an Accepted property
	// together with RetryIn information when available.
	Submit(tenantKey string, load uint64) (SubmitResult, error)

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil value is returned.
	// In case of timeout or other errors a non-nil error is returned.
	//
	// You can check the returned error with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntil(tenantKey string, load uint64, timeout time.Duration) error

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil Error field is returned in the output object.
	// In case of timeout or other errors a non-nil Error field is returned in the output object.
	//
	// Unlike SubmitUntil, more information about the request is returned with the output object,
	// like the amount of time waited and the amount of submissions attempt.
	//
	// You can check the returned Error field with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntilWithDetails(tenantKey string, load uint64, timeout time.Duration) SubmitUntilResult

	// IsComposite is "inherited" from LoadLimiter
	// and always returns true for this type.
	IsComposite() bool

	// Stats returns runtime statistics useful to evaluate system status,
	// performance and overhead.
	//
	// In the case of a composite limiter, both statistics about the
	// composite limiter itself and statistics for all the single composed
	// limiters will be returned.
	Stats(tenantKey string) (CompositeRuntimeStatistics, error)

	// ForTenant returns a semplified proxy that applies the load limiting
	// for the specified tenant, dropping the tenantKey input parameter.
	//
	// It's useful to simplify the code when you are acting on a single tenant.
	//
	// Please note that this does not create a new limiter instance,
	// it just proxies the calls to the current limiter adding a fixed tenantKey.
	ForTenant(tenantKey string) SingleTenantCompositeLoadLimiter

	// ForTenant returns a semplified proxy drops the tenantKey input parameter.
	//
	// It's useful to simplify the code when you don't need multitenancy.
	//
	// Please note that this does not create a new limiter instance,
	// it just proxies the calls to the current limiter adding a fixed tenantKey.
	AsSingleTenant() SingleTenantCompositeLoadLimiter
}

CompositeLoadLimiter is the specialized interface for the composite load limiters created with goll.NewComposite(...).

Note that all types implementing CompositeLoadLimiter also implements LoadLimiter: You are encouraged to use this type when storing references to your limiters in order to allow for easier implementations switch.

func NewComposite

func NewComposite(config *CompositeConfig) (CompositeLoadLimiter, error)

NewComposite returns an instance of goll.LoadLimiter built with the specified configuration, combining multiple limiter policies into a single instance.

A non-nil error is returned in case of invalid configuration.

type CompositeRuntimeStatistics

type CompositeRuntimeStatistics struct {

	// LimitersStats holds the statistics for each composed limiter
	LimitersStats []RuntimeStatistics
}

RuntimeStatistics holds runtime statistics for a composite load limiter.

type Config

type Config struct {

	// MaxLoad is the absolute maximum amonut of load
	// that you want to allow in the specified time window.
	MaxLoad uint64

	// WindowSize is the width of the time window.
	// It heavily depends on the kind of limiter that you are setting up.
	//
	// WindowSize should be exactly divisible by WindowSegmentSize if the latter is specified.
	WindowSize time.Duration

	// WindowSegmentSize is the width of the segments that the
	// active window is divided in when shifting.
	//
	// The smaller the segment size, the smoother the limiting will be.
	// However, too small segments will increase memory and CPU overhead.
	//
	// WindowSize should be exactly divisible by WindowSegmentSize.
	//
	// When not specified, it is automatically assumed to be 1/20 of the WindowSize.
	WindowSegmentSize time.Duration

	// OverstepPenaltyFactor represents the multiplier applied to
	// the max load when the load limit gets reached.
	OverstepPenaltyFactor float64

	// OverstepPenaltyDistributionFactor must be in the range 0 - 1.0
	// and determines how widely the penalty configured by OverstepPenaltyFactor
	// is spread against the active time window
	OverstepPenaltyDistributionFactor float64

	// RequestOverheadPenaltyFactor represents the multiplier applied to
	// rejected load that gets applied as penalty load.
	RequestOverheadPenaltyFactor float64

	// RequestOverheadPenaltyDistributionFactor must be in the range 0 - 1.0
	// and determines how widely the penalty configured by RequestOverheadPenaltyFactor
	// is spread against the active time window
	RequestOverheadPenaltyDistributionFactor float64

	// MaxPenaltyCapFactor represents the max multiplier
	// applied for penalties.
	// If MaxPenaltyCapFactor > 0, the current load
	// will never be allowed to get penalized above (MaxLoad * (1.0 + MaxPenaltyCapFactor))
	// A good default value is usually in the range 0.30 - 0.50
	//
	// If not provided, a default value of 0.5 is assumed,
	// meaning the virtual load will cap at 150% of the maximum load
	// when unrestricted penalties are applied.
	MaxPenaltyCapFactor float64

	// if SkipRetryInComputing is true,
	// no RetryIn will be computed and RetryInAvailable will always be false.
	// Enable this if you don't need the RetryIn feature and want a slight
	// performance gain.
	SkipRetryInComputing bool

	// SyncAdapter is an implementation used to synchronize
	// the limiter data in a clustered environment.
	//
	// You can provide your own implementation.
	// You can use as example github.com/fabiofenoglio/goll-redis
	// which synchronizes data for multiple instances over a Redis cluster.
	SyncAdapter SyncAdapter

	// Time-related functions can be overriden to allow for easier testing
	// you should usually not override these.
	TimeFunc  func() time.Time
	SleepFunc func(d time.Duration)

	// you can pass your custom logger if you'd like to
	// but it's not required
	Logger Logger
}

Config holds the basic configuration for a load limiter instance

type LoadLimiter

type LoadLimiter interface {
	// Probe checks if the given load would be allowed right now.
	// it is a readonly method that does not modify the current window data.
	Probe(tenantKey string, load uint64) (bool, error)

	// Submit asks for the given load to be accepted.
	// The result object contains an Accepted property
	// together with RetryIn information when available.
	Submit(tenantKey string, load uint64) (SubmitResult, error)

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil value is returned.
	// In case of timeout or other errors a non-nil error is returned.
	//
	// You can check the returned error with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntil(tenantKey string, load uint64, timeout time.Duration) error

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil Error field is returned in the output object.
	// In case of timeout or other errors a non-nil Error field is returned in the output object.
	//
	// Unlike SubmitUntil, more information about the request is returned with the output object,
	// like the amount of time waited and the amount of submissions attempt.
	//
	// You can check the returned Error field with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntilWithDetails(tenantKey string, load uint64, timeout time.Duration) SubmitUntilResult

	// IsComposite returns true if the limiter is a CompositeLoadLimiter.
	IsComposite() bool
}

LoadLimiter is the parent interface for all kinds of load limiters.

You are encouraged to use this type when storing references to your limiters in order to allow for easier implementations switch.

type LoadRequestRejected

type LoadRequestRejected struct {
	Reason string
}

LoadRequestRejected is returned when a submission can't be accepted usually happens when: - the request asks for a load greater than the limiter maximum load - the request gets rejected and the limiter was built with SkipRetryInComputing = true

func (*LoadRequestRejected) Error

func (e *LoadRequestRejected) Error() string

func (*LoadRequestRejected) Is

func (e *LoadRequestRejected) Is(tgt error) bool

type LoadRequestTimeout

type LoadRequestTimeout struct {
	AttemptsNumber uint64
	WaitedFor      time.Duration
}

LoadRequestTimeout is returned when autoretrying a submission (ex. with SubmitUntil) failed because the maximum timeout was reached

func (*LoadRequestTimeout) Error

func (e *LoadRequestTimeout) Error() string

func (*LoadRequestTimeout) Is

func (e *LoadRequestTimeout) Is(tgt error) bool

type Logger

type Logger interface {
	Debug(string)
	Info(string)
	Warning(string)
	Error(string)
}

Logger interface is provided to allow you to customize the logging internally done by the limiters.

The default implementation logs to the "log" standard module via log.Default().

If you want to disable the default logger you can pass an instance of goll.NewNoOpLogger() to the limiter constructor.

func NewNoOpLogger

func NewNoOpLogger() Logger

type RuntimeStatistics

type RuntimeStatistics struct {
	// WindowTotal holds the current active load in absolute units.
	WindowTotal uint64

	// WindowSegments is a slice holding the amount of absolute load
	// allocated to each segment of the window.
	WindowSegments []uint64
}

RuntimeStatistics holds runtime statistics for a single load limiter.

type SingleTenantCompositeLoadLimiter

type SingleTenantCompositeLoadLimiter interface {
	// Probe checks if the given load would be allowed right now.
	// it is a readonly method that does not modify the current window data.
	Probe(load uint64) (bool, error)

	// Submit asks for the given load to be accepted.
	// The result object contains an Accepted property
	// together with RetryIn information when available.
	Submit(load uint64) (SubmitResult, error)

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil value is returned.
	// In case of timeout or other errors a non-nil error is returned.
	//
	// You can check the returned error with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntil(load uint64, timeout time.Duration) error

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil Error field is returned in the output object.
	// In case of timeout or other errors a non-nil Error field is returned in the output object.
	//
	// Unlike SubmitUntil, more information about the request is returned with the output object,
	// like the amount of time waited and the amount of submissions attempt.
	//
	// You can check the returned Error field with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntilWithDetails(load uint64, timeout time.Duration) SubmitUntilResult

	// Stats returns runtime statistics useful to evaluate system status,
	// performance and overhead.
	//
	// In the case of a composite limiter, both statistics about the
	// composite limiter itself and statistics for all the single composed
	// limiters will be returned.
	Stats() (CompositeRuntimeStatistics, error)

	// IsComposite always returns true for this type.
	IsComposite() bool
}

SingleTenantCompositeLoadLimiter is the specialized interface for composite load limiters that do not need to handle multitenancy.

It works exactly like the standard composite limiter but drops the tenantKey

type SingleTenantLoadLimiter

type SingleTenantLoadLimiter interface {
	// Probe checks if the given load would be allowed right now.
	// it is a readonly method that does not modify the current window data.
	Probe(load uint64) (bool, error)

	// Submit asks for the given load to be accepted.
	// The result object contains an Accepted property
	// together with RetryIn information when available.
	Submit(load uint64) (SubmitResult, error)

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil value is returned.
	// In case of timeout or other errors a non-nil error is returned.
	//
	// You can check the returned error with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntil(load uint64, timeout time.Duration) error

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil Error field is returned in the output object.
	// In case of timeout or other errors a non-nil Error field is returned in the output object.
	//
	// Unlike SubmitUntil, more information about the request is returned with the output object,
	// like the amount of time waited and the amount of submissions attempt.
	//
	// You can check the returned Error field with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntilWithDetails(load uint64, timeout time.Duration) SubmitUntilResult

	// IsComposite returns true if the limiter is a CompositeLoadLimiter.
	IsComposite() bool
}

SingleTenantLoadLimiter is the specialized interface for load limiters that do not need to handle multitenancy.

It works exactly like the standard limiter but drops the tenantKey

type SingleTenantStandaloneLoadLimiter

type SingleTenantStandaloneLoadLimiter interface {
	// Probe checks if the given load would be allowed right now.
	// it is a readonly method that does not modify the current window data.
	Probe(load uint64) (bool, error)

	// Submit asks for the given load to be accepted.
	// The result object contains an Accepted property
	// together with RetryIn information when available.
	Submit(load uint64) (SubmitResult, error)

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil value is returned.
	// In case of timeout or other errors a non-nil error is returned.
	//
	// You can check the returned error with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntil(load uint64, timeout time.Duration) error

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil Error field is returned in the output object.
	// In case of timeout or other errors a non-nil Error field is returned in the output object.
	//
	// Unlike SubmitUntil, more information about the request is returned with the output object,
	// like the amount of time waited and the amount of submissions attempt.
	//
	// You can check the returned Error field with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntilWithDetails(load uint64, timeout time.Duration) SubmitUntilResult

	// Stats returns runtime statistics useful to evaluate system status,
	// performance and overhead.
	Stats() (RuntimeStatistics, error)

	// IsComposite always returns false for this type.
	IsComposite() bool
}

SingleTenantStandaloneLoadLimiter is the specialized interface for standalone (non composite) load limiters that do not need to handle multitenancy.

It works exactly like the standard standalone limiter but drops the tenantKey

type StandaloneLoadLimiter

type StandaloneLoadLimiter interface {
	// Probe checks if the given load would be allowed right now.
	// it is a readonly method that does not modify the current window data.
	Probe(tenantKey string, load uint64) (bool, error)

	// Submit asks for the given load to be accepted.
	// The result object contains an Accepted property
	// together with RetryIn information when available.
	Submit(tenantKey string, load uint64) (SubmitResult, error)

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil value is returned.
	// In case of timeout or other errors a non-nil error is returned.
	//
	// You can check the returned error with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntil(tenantKey string, load uint64, timeout time.Duration) error

	// SubmitUntil asks for the given load to be accepted and,
	// in case of rejection, automatically handles retries and delays.
	// In case of acceptance a nil Error field is returned in the output object.
	// In case of timeout or other errors a non-nil Error field is returned in the output object.
	//
	// Unlike SubmitUntil, more information about the request is returned with the output object,
	// like the amount of time waited and the amount of submissions attempt.
	//
	// You can check the returned Error field with errors.Is against
	// the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected,
	// or you can cast them to the
	// goll.LoadRequestSubmissionTimeout / goll.LoadRequestRejected
	// types if you need additional info.
	SubmitUntilWithDetails(tenantKey string, load uint64, timeout time.Duration) SubmitUntilResult

	// IsComposite is "inherited" from LoadLimiter
	// and always returns false for this type.
	IsComposite() bool

	// Stats returns runtime statistics useful to evaluate system status,
	// performance and overhead.
	Stats(tenantKey string) (RuntimeStatistics, error)

	// ForTenant returns a semplified proxy that applies the load limiting
	// for the specified tenant, dropping the tenantKey input parameter.
	//
	// It's useful to simplify the code when you are acting on a single tenant.
	//
	// Please note that this does not create a new limiter instance,
	// it just proxies the calls to the current limiter adding a fixed tenantKey.
	ForTenant(tenantKey string) SingleTenantStandaloneLoadLimiter

	// ForTenant returns a semplified proxy drops the tenantKey input parameter.
	//
	// It's useful to simplify the code when you don't need multitenancy.
	//
	// Please note that this does not create a new limiter instance,
	// it just proxies the calls to the current limiter adding a fixed tenantKey.
	AsSingleTenant() SingleTenantStandaloneLoadLimiter
}

StandaloneLoadLimiter is the specialized interface for the standard load limiters created with goll.New(...).

Note that all types implementing StandaloneLoadLimiter also implements LoadLimiter: You are encouraged to use this type when storing references to your limiters in order to allow for easier implementations switch.

func New

func New(config *Config) (StandaloneLoadLimiter, error)

New returns an instance of goll.LoadLimiter built with the specified configuration.

A non-nil error is returned in case of invalid configuration.

type SubmitResult

type SubmitResult struct {
	Accepted         bool
	RetryInAvailable bool
	RetryIn          time.Duration
}

SubmitResult holds the result of a load request.

the Accepted field will be true if the request was accepted.

If the request was rejected and RetryIn is enabled, the RetryInAvailable field will be true and the RetryIn field will be the amount the client is required to wait before resubmitting a request for the same load.

func (*SubmitResult) String

func (s *SubmitResult) String() string

type SubmitUntilResult

type SubmitUntilResult struct {
	AttemptsNumber uint64
	WaitedFor      time.Duration
	Error          error
}

SubmitUntilResult holds the result of a load request automatically handled and optionally retried via SubmitUntil.

the Error field will be nil if the request was accepted.

The AttemptsNumber and WaitedFor will provide information about the delay and attempts made by the SubmitUntil handler.

You can check the returned Error field with errors.Is against the sentinels goll.ErrLoadRequestTimeout or goll.ErrLoadRequestRejected, or you can cast them to the gollLoadRequestSubmissionTimeout / goll.LoadRequestRejected types if you need additional info.

type SyncAdapter

type SyncAdapter interface {
	Lock(ctx context.Context, tenantKey string) error
	Fetch(ctx context.Context, tenantKey string) (string, error)
	Write(ctx context.Context, tenantKey string, serializedData string) error
	Unlock(ctx context.Context, tenantKey string) error
}

Jump to

Keyboard shortcuts

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