Documentation ¶
Overview ¶
Package memoryless helps repeated calls to a function be distributed across time in a memoryless fashion.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var MakeTicker = NewTicker
MakeTicker is a deprecated alias for NewTicker
Functions ¶
func AfterFunc ¶
AfterFunc constructs a single-shot time.Timer that, if repeatedly used to construct a series of timers, will ensure that the resulting events conform to the memoryless distribution. For more on how this could and should be used, see the comments to Ticker. It is intended to be a drop-in replacement for time.AfterFunc.
func NewTimer ¶
NewTimer constructs a single-shot time.Timer that, if repeatedly used to construct a series of timers, will ensure that the resulting events conform to the memoryless distribution. For more on how this could and should be used, see the comments to Ticker. It is intended to be a drop-in replacement for time.NewTimer.
Types ¶
type Config ¶
type Config struct { // Expected records the expected/mean/average amount of time between runs. Expected time.Duration // Min provides clamping of the randomly produced value. All timers will wait // at least Min time. Min time.Duration // Max provides clamping of the randomly produced value. All timers will take // at most Max time. Max time.Duration // Once is provided as a helper, because frequently for unit testing and // integration testing, you only want the "Forever" loop to run once. // // The zero value of this struct has Once set to false, which means the value // only needs to be set explicitly in codepaths where it might be true. Once bool }
Config represents the time we should wait between runs of the function.
A valid config will have:
0 <= Min <= Expected <= Max (or 0 <= Min <= Expected and Max is 0)
If Max is zero or unset, it will be ignored. If Min is zero or unset, it will be ignored.
type Ticker ¶
type Ticker struct { C <-chan time.Time // The channel on which the ticks are delivered. // contains filtered or unexported fields }
Ticker is a struct that waits a config.Expected amount of time on average between sends down the channel C. It has the same interface and requirements as time.Ticker. Every Ticker created must have its Stop() method called or it will leak a goroutine.
The inter-send time is a random variable governed by the exponential distribution and will generate a memoryless (Poisson) distribution of channel reads over time, ensuring that a measurement scheme using this ticker has the PASTA property (Poisson Arrivals See Time Averages). This statistical guarantee is subject to two caveats:
Caveat 1 is that, in a nod to the realities of systems needing to have guarantees, we allow the random wait time to be clamped both above and below. This means that channel events will be at least config.Min and at most config.Max apart in time. This clamping causes bias in the timing. For use of Ticker to be statistically sensible, the clamping should not be too extreme. The exact mathematical meaning of "too extreme" depends on your situation, but a nice rule of thumb is config.Min should be at most 10% of expected and config.Max should be at least 250% of expected. These values mean that less than 10% of time you will be waiting config.Min and less than 10% of the time you will be waiting config.Max.
Caveat 2 is that this assumes that the actions performed between channel reads take negligible time when compared to the expected wait time. Memoryless sequences have the property that the times between successive event starts has the exponential distribution, and the exponential distribution can generate numbers arbitrarily close to zero (albeit exponentially infrequently). This code will not send on the channel if the other end is not ready to receive, which provides another lower bound on inter-event times. The only other option if the other side of the channel is not ready to receive would be queueing events in the channel, and that has some pathological cases we would like to avoid. In particular, queuing can cause long-term correlations if the queue gets big, which is the exact opposite of what a memoryless system is trying to achieve.