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 ¶
- Variables
- type CompositeConfig
- type CompositeLoadLimiter
- type CompositeRuntimeStatistics
- type Config
- type LoadLimiter
- type LoadRequestRejected
- type LoadRequestTimeout
- type Logger
- type RuntimeStatistics
- type SingleTenantCompositeLoadLimiter
- type SingleTenantLoadLimiter
- type SingleTenantStandaloneLoadLimiter
- type StandaloneLoadLimiter
- type SubmitResult
- type SubmitUntilResult
- type SyncAdapter
Constants ¶
This section is empty.
Variables ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.