2022-02-08 10:53:25 +00:00
|
|
|
package ratelimit
|
|
|
|
|
|
|
|
import "time"
|
|
|
|
|
|
|
|
// Window is a time windows for counting events within a span of time. The
|
|
|
|
// windows slides forward in time so that it spans from the most recent event
|
|
|
|
// to size time in the past.
|
|
|
|
type Window struct {
|
|
|
|
q *queue
|
|
|
|
size int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewWindow creates a new Window that limits the number of events to maximum
|
2022-02-10 19:25:30 +00:00
|
|
|
// count of events within a duration of time. The capacity sets the maximum
|
2022-02-08 10:53:25 +00:00
|
|
|
// number of events, and size sets the span of time over which the events are
|
|
|
|
// counted.
|
|
|
|
func NewWindow(capacity int, size time.Duration) *Window {
|
|
|
|
return &Window{
|
|
|
|
q: &queue{
|
|
|
|
buf: make([]int64, capacity),
|
|
|
|
},
|
|
|
|
size: int64(size),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add attempts to append a new timestamp into the current window. Previously
|
|
|
|
// added values that are not not within `size` difference from the value being
|
|
|
|
// added are first removed. Add fails if adding the value would cause the
|
|
|
|
// window to exceed capacity.
|
|
|
|
func (w *Window) Add() error {
|
|
|
|
now := time.Now().UnixNano()
|
|
|
|
if w.Len() != 0 {
|
|
|
|
w.q.truncate(now - w.size)
|
|
|
|
}
|
|
|
|
return w.q.push(now)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cap returns the maximum number of items the window can hold.
|
|
|
|
func (w *Window) Cap() int {
|
|
|
|
return w.q.cap()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Len returns the number of elements currently in the window.
|
|
|
|
func (w *Window) Len() int {
|
|
|
|
return w.q.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Span returns the distance from the first to the last item in the window.
|
|
|
|
func (w *Window) Span() time.Duration {
|
|
|
|
if w.q.len() < 2 {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return time.Duration(w.q.back() - w.q.front())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Oldest returns the oldest timestamp in the window.
|
|
|
|
func (w *Window) Oldest() time.Time {
|
|
|
|
if w.q.len() == 0 {
|
|
|
|
return time.Time{}
|
|
|
|
}
|
|
|
|
return time.Unix(0, w.q.front())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Newest returns the newest timestamp in the window.
|
|
|
|
func (w *Window) Newest() time.Time {
|
|
|
|
if w.q.len() == 0 {
|
|
|
|
return time.Time{}
|
|
|
|
}
|
|
|
|
return time.Unix(0, w.q.back())
|
|
|
|
}
|