Documentation
¶
Overview ¶
Package erl provides Edge Rate Limiting functionality.
This package includes a RateCounter type that can be used to increment an event counter, and to examine the rate of events per second within a POP over 1, 10, and 60 second windows. It can also estimate the number of events seen over the past minute within a POP in 10 second buckets.
The PenaltyBox type can be used to track entries that should be penalized for a certain amount of time.
The RateLimiter type combines a rate counter and a penalty box to determine whether a given entry should be rate limited based on whether it exceeds a maximum threshold of events per second over a given rate window. Most users can simply use RateLimiter.CheckRate rather than methods on RateCounter and PenaltyBox.
Rate counters and penalty boxes are combined and synchronized within a POP. However, Edge Rate Limiting is not intended to compute counts or rates with high precision and may under count by up to 10%.
Both rate counters and penalty boxes have a fixed capacity for entries. Once a rate counter is full, each new entry evicts the entry that was least recently incremented. Once a penalty box is full, each new entry will evict the entry with the shortest remaining time-to-live (TTL).
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidArgument indicates that an invalid argument was passed // to one of the edge rate limiter methods. // // Most functions and methods have limited ranges for their // parameters. See the documentation for each call for more // details. ErrInvalidArgument = errors.New("invalid argument") // ErrUnexpected indicates that an unexpected error occurred. ErrUnexpected = errors.New("unexpected error") )
var ( // RateWindow1s incidates the rate of events per second over the past // second. RateWindow1s = fastly.RateWindow1s // RateWindow10s indicates the rate of events per second over the // past 10 seconds. RateWindow10s = fastly.RateWindow10s // RateWindow60s indicates the rate of events per second over the // past 60 seconds. RateWindow60s = fastly.RateWindow60s )
var ( // CounterDuration10s indicates the estimated number of events in // the most recent 10 second bucket. CounterDuration10s = fastly.CounterDuration10s // CounterDuration20s indicates the estimated number of events in // the most recent two 10 second buckets. CounterDuration20s = fastly.CounterDuration20s // CounterDuration30s indicates the estimated number of events in // the most recent three 10 second buckets. CounterDuration30s = fastly.CounterDuration30s // CounterDuration40s indicates the estimated number of events in // the most recent four 10 second buckets. CounterDuration40s = fastly.CounterDuration40s // CounterDuration50s indicates the estimated number of events in // the most recent five 10 second buckets. CounterDuration50s = fastly.CounterDuration50s // CounterDuration60s indicates the estimated number of events in // the most recent six 10 second buckets. CounterDuration60s = fastly.CounterDuration60s )
Functions ¶
This section is empty.
Types ¶
type CounterDuration ¶
type CounterDuration = fastly.CounterDuration
CounterDuration indicates the estimated number of events in this duration in the current POP. Counts are divided into 10 second buckets, and each bucket represents the estimated number of requests received up to and including that 10 second window.
Buckets are not continuous. For example, if the current time is 12:01:03, then the 10 second bucket represents events received between 12:01:00 and 12:01:10, not between 12:00:53 and 12:01:03. This means that, in each minute at the ten second mark (:00, :10, :20, etc.) the window represented by each bucket will shift.
Estimated counts are not precise and should not be used as counters.
See CounterDuration10s, CounterDuration20s, CounterDuration30s, CounterDuration40s, CounterDuration50s, and CounterDuration60s.
type PenaltyBox ¶
type PenaltyBox struct {
// contains filtered or unexported fields
}
PenaltyBox is a type that allows entries to be penalized for a given number of minutes in the future.
func OpenPenaltyBox ¶
func OpenPenaltyBox(name string) *PenaltyBox
OpenPenaltyBox opens a penalty box with the given name, creating it if it doesn't already exist. The penalty box name may be up to 64 characters long. Entry names in this penalty box are also limited to 64 characters.
func (*PenaltyBox) Add ¶
func (pb *PenaltyBox) Add(entry string, ttl time.Duration) error
Add adds an entry to the penalty box for the given time-to-live (TTL) duration. The minimum value is 1 minute and the maximum is 60 minutes. If an entry is already in the penalty box, its TTL is replaced with the new value. Entries are automatically evicted from the penalty box when the TTL expires.
type Policy ¶
type Policy struct {
// RateWindow is the window of time to consider when checking the
// rate of events per second.
RateWindow RateWindow
// MaxRate is the maximum number of events per second to allow over
// the rate window. The minimum value is 10 and the maximum is
// 10000.
MaxRate uint32
// PenaltyBoxDuration is the duration to penalize entries that
// exceed the maximum rate. As with PenaltyBox.Add, the minimum
// value is 1 minute and the maximum is 60 minutes.
PenaltyBoxDuration time.Duration
}
Policy contains the rules for applying a RateLimiter.
type RateCounter ¶
type RateCounter struct {
// contains filtered or unexported fields
}
RateCounter is a named counter that can be incremented and queried.
func OpenRateCounter ¶
func OpenRateCounter(name string) *RateCounter
OpenRateCounter opens a rate counter with the given name, creating it if it doesn't already exist. The rate counter name may be up to 64 characters long. Entry names in this counter are also limited to 64 characters.
func (*RateCounter) Increment ¶
func (rc *RateCounter) Increment(entry string, delta uint32) error
Increment increments the rate counter for this entry by the given delta value. The minimum value is 0 and the maximum is 1000.
func (*RateCounter) LookupCount ¶
func (rc *RateCounter) LookupCount(entry string, duration CounterDuration) (uint32, error)
LookupCount returns the estimated number of events in the given duration for this entry. The duration represents a discrete window, not a continuous one. See CounterDuration for more details.
Example ¶
package main
import (
"context"
"fmt"
"github.com/fastly/compute-sdk-go/erl"
"github.com/fastly/compute-sdk-go/fsthttp"
)
func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
rc := erl.OpenRateCounter("requests")
// Increment the request counter by 1
rc.Increment(r.RemoteAddr, 1)
// Get an estimated count of total number of requests over the
// past 60 seconds
count, err := rc.LookupCount(r.RemoteAddr, erl.CounterDuration60s)
if err != nil {
w.WriteHeader(fsthttp.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Estimated count over the past 60 seconds: %d requests\n", count)
})
}
func (*RateCounter) LookupRate ¶
func (rc *RateCounter) LookupRate(entry string, window RateWindow) (uint32, error)
LookupRate returns the rate of events per second over the given rate window for this entry.
Example ¶
package main
import (
"context"
"fmt"
"github.com/fastly/compute-sdk-go/erl"
"github.com/fastly/compute-sdk-go/fsthttp"
)
func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
rc := erl.OpenRateCounter("requests")
// Increment the request counter by 1
rc.Increment(r.RemoteAddr, 1)
// Get the current rate of requests per second over the past 60
// seconds
rate, err := rc.LookupRate(r.RemoteAddr, erl.RateWindow60s)
if err != nil {
w.WriteHeader(fsthttp.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Rate over the past 60 seconds: %d requests per second\n", rate)
})
}
type RateLimiter ¶
type RateLimiter struct {
RateCounter *RateCounter
PenaltyBox *PenaltyBox
}
RateLimiter combines a RateCounter and a PenaltyBox to provide an easy way to check whether a given entry should be rate limited given a rate window and upper limit.
func NewRateLimiter ¶
func NewRateLimiter(rc *RateCounter, pb *PenaltyBox) *RateLimiter
NewRateLimiter creates a new rate limiter.
func (*RateLimiter) CheckRate ¶
CheckRate increments an entry's rate counter by the delta value (RateCounter.Increment), and checks it against the provided Policy. If the count after increment exceeds the policy's MaxRate over the RateWindow, it will add the entry to the penalty box for the policy's PenaltyBoxDuration. It returns true if the entry is in the penalty box.
The limits for the delta value are the same as RateCounter.Increment.
Example ¶
package main
import (
"context"
"time"
"github.com/fastly/compute-sdk-go/erl"
"github.com/fastly/compute-sdk-go/fsthttp"
)
func main() {
fsthttp.ServeFunc(func(ctx context.Context, w fsthttp.ResponseWriter, r *fsthttp.Request) {
limiter := erl.NewRateLimiter(
erl.OpenRateCounter("requests"),
erl.OpenPenaltyBox("bad_ips"),
)
block, err := limiter.CheckRate(
r.RemoteAddr, // Use the IP address of the client as the entry
1, // Increment the request counter by 1
&erl.Policy{
erl.RateWindow10s, // Check the rate of requests per second over the past 10 seconds
100, // Allow up to 100 requests per second
time.Minute, // Put offenders into the penalty box for 1 minute
},
)
if err != nil {
// It's probably better to fail open. Consider logging the
// error but continuing to handle the request.
} else if block {
// The rate limit has been exceeded. Return a 429 Too Many
// Requests response.
w.WriteHeader(fsthttp.StatusTooManyRequests)
return
}
// Otherwise, continue processing the request.
})
}
type RateWindow ¶
type RateWindow = fastly.RateWindow
RateWindow indicates the rate of events per second in the current POP over one of a few predefined time windows. See RateWindow1s, RateWindow10s, and RateWindow60s.