clock

package
v1.2.11-prerelease2 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2024 License: MIT Imports: 6 Imported by: 8

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrCannotWait is used as the common base for two internal reasons why Ratelimiter.Wait call cannot
	// succeed, without directly exposing the reason except as an explanatory string.
	//
	// Callers should only ever care about ErrCannotWait to be resistant to racing behavior.
	// Or perhaps ideally: not care about the reason at all beyond "not a ctx.Err()".
	ErrCannotWait = fmt.Errorf("ratelimiter.Wait() cannot be satisfied")
)

err constants to keep allocations low

Functions

This section is empty.

Types

type MockedTimeSource added in v1.2.7

type MockedTimeSource interface {
	TimeSource
	// Advance advances the FakeClock to a new point in time, ensuring any existing
	// waiters are notified appropriately before returning.
	Advance(d time.Duration)
	// BlockUntil blocks until the FakeClock has the given number of waiters
	// running at the same time.
	//
	// Waiters are either time.Sleep, time.After[Func], time.Ticker, or time.Timer,
	// and they decrement the counter when they complete or are stopped.
	BlockUntil(waiters int)
}

MockedTimeSource provides an interface for a clock which can be manually advanced through time.

MockedTimeSource maintains a list of "waiters," which consists of all callers waiting on the underlying clock (i.e. Tickers and Timers including callers of Sleep or After). Users can call BlockUntil to block until the clock has an expected number of waiters.

func NewMockedTimeSource added in v1.2.7

func NewMockedTimeSource() MockedTimeSource

NewMockedTimeSource returns a time source that servers fake controlled time

func NewMockedTimeSourceAt added in v1.2.7

func NewMockedTimeSourceAt(t time.Time) MockedTimeSource

NewMockedTimeSourceAt returns a time source that servers fake controlled time. The initial time of the MockedTimeSource will be the given time.

type Ratelimiter added in v1.2.11

type Ratelimiter interface {
	// Allow returns true if an event can be performed now, i.e. there
	// are burst-able tokens available to use.
	//
	// To perform events at a steady rate, use Wait instead.
	Allow() bool
	// Burst returns the maximum burst size allowed by this Ratelimiter.
	// A Burst of zero returns false from Allow, unless its Limit is infinity.
	//
	// To see the number of burstable tokens available now, use Tokens.
	Burst() int
	// Limit returns the maximum overall rate of events that are allowed.
	Limit() rate.Limit
	// Reserve claims a single Allow() call that can be canceled if not used.
	// Check Reservation.Allow() to see if the event is allowed.
	//
	// This Reservation does not support waiting, and needs to have
	// Reservation.Used(true/false) called as soon as possible to have the
	// best chance of returning its unused token.
	Reserve() Reservation
	// SetBurst sets the Burst value
	SetBurst(newBurst int)
	// SetLimit sets the Limit value
	SetLimit(newLimit rate.Limit)
	// Tokens returns the number of immediately-allowable events when called.
	// Values >= 1 will lead to Allow returning true.
	//
	// These values are NOT able to guarantee that a later call will succeed
	// or fail, as other calls to Allow or Reserve may occur before you are
	// able to act on the returned value.
	// It is essentially only suitable for monitoring or test use, and calling
	// it may lead to failing to cancel reserved tokens (as it advances time).
	Tokens() float64
	// Wait blocks until one token is available (and consumes it), or it returns
	// an error and does not consume any tokens if:
	//   - the context is canceled
	//   - a token will never become available (burst == 0)
	//   - a token is not expected to become available in time
	//     (short deadline and/or many pending reservations or waiters)
	//
	// If an error is returned, regardless of the reason, you may NOT proceed
	// with the limited event you were waiting for.
	Wait(ctx context.Context) error
}

Ratelimiter is a test-friendly version of golang.org/x/time/rate.Limiter, which can be backed by any TimeSource. The changes summarize as:

  • Time is never allowed to rewind, and does not advance when canceling, fixing core issues with golang.org/x/time/rate.Limiter and greatly improving the chances of successfully canceling a reserved token in our usage (from "almost literally never" to "likely 99%+").
  • Reservations are simplified and do not allow waiting, but are MUCH more likely to return tokens when canceled or not allowed.
  • All MethodAt APIs have been excluded as we do not use them, and they would have to bypass the contained TimeSource, which seems error-prone.
  • All `MethodN` APIs (specifically the ">1 event" parts) have been excluded because we do not use them, but they seem fairly easy to add if needed later.

func NewMockRatelimiter added in v1.2.11

func NewMockRatelimiter(ts TimeSource, lim rate.Limit, burst int) Ratelimiter

func NewRatelimiter added in v1.2.11

func NewRatelimiter(lim rate.Limit, burst int) Ratelimiter

type Reservation added in v1.2.11

type Reservation interface {
	// Allow returns true if a request should be allowed.
	//
	// This may be called at any time and it will return the same value each time,
	// but it becomes less correct as time passes, so you are expected to call it ~immediately.
	//
	// As soon as possible, also call Used(true/false) to consume / return the reserved token.
	//
	// This is equivalent to `OK() == true && DelayFrom(reservationTime) == 0`
	// with [rate.Reservation].
	Allow() bool

	// Used marks this Reservation as either used or not-used, and it must be called
	// once on every Reservation:
	//  - If true, the event is assumed to be allowed, and the Ratelimiter token
	//    will remain consumed.
	//  - If false, the Reservation will be rolled back, possibly restoring a Ratelimiter token.
	//    This is equivalent to calling Cancel() on a [rate.Reservation], but it is more likely
	//    to return the token.
	Used(wasUsed bool)
}

Reservation is a simplified and test-friendly version of golang.org/x/time/rate.Reservation that only allows checking if it is successful or not, and (possibly) returning unused tokens.

This Reservation MUST have Allow() checked and marked as Used(true/false) as soon as possible, or an unused token is unlikely to be returned.

Recommended use is either by using defer:

	func allow() (allowed bool) {
    	r := ratelimiter.Reserve()
		// defer in a closure to read the most-recent value, not the value when deferred
		defer func() { r.Used(allowed) }()
		allowed = r.Allow()
		if !allowed {
			return false // also marks the reservation as not-used
		}
		if somethingElseAllowsIt() {
			// allow the event, and mark the reservation as used so the token is consumed
			return true
		}
		// cancel the reservation and possibly return the token
		return false
	}

Or directly calling Used when appropriate:

	func allow() bool {
    	r := ratelimiter.Reserve()
		allowed := r.Allow()
		if !allowed {
			r.Used(false) // mark the reservation as not-used
			return false
		}
		if somethingElseAllowsIt() {
			// allow the event, and mark the reservation as used
			r.Used(true)
			return true
		}
		r.Used(false) // mark the reservation as not-used, and restore the unused token
		return false
	}

type Ticker added in v1.2.7

type Ticker interface {
	Chan() <-chan time.Time
	Reset(d time.Duration)
	Stop()
}

Ticker provides an interface which can be used instead of directly using time.Ticker. The real-time ticker t provides ticks through t.C which becomes t.Chan() to make this channel requirement definable in this interface.

type TimeSource

type TimeSource interface {
	After(d time.Duration) <-chan time.Time
	Sleep(d time.Duration)
	Now() time.Time
	Since(t time.Time) time.Duration
	NewTicker(d time.Duration) Ticker
	NewTimer(d time.Duration) Timer
	AfterFunc(d time.Duration, f func()) Timer
}

TimeSource provides an interface that packages can use instead of directly using the time module, so that chronology-related behavior can be tested.

func NewRealTimeSource

func NewRealTimeSource() TimeSource

NewRealTimeSource returns a time source that servers real wall clock time

type Timer added in v1.2.7

type Timer interface {
	Chan() <-chan time.Time
	Reset(d time.Duration) bool
	Stop() bool
}

Timer provides an interface which can be used instead of directly using time.Timer. The real-time timer t provides events through t.C which becomes t.Chan() to make this channel requirement definable in this interface.

Jump to

Keyboard shortcuts

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