Documentation ¶
Index ¶
- Constants
- Variables
- func DomainsForRateLimiting(names []string) []string
- type Decision
- type Limiter
- func (l *Limiter) BatchRefund(ctx context.Context, txns []Transaction) (*Decision, error)
- func (l *Limiter) BatchSpend(ctx context.Context, txns []Transaction) (*Decision, error)
- func (l *Limiter) Check(ctx context.Context, txn Transaction) (*Decision, error)
- func (l *Limiter) Refund(ctx context.Context, txn Transaction) (*Decision, error)
- func (l *Limiter) Reset(ctx context.Context, bucketKey string) error
- func (l *Limiter) Spend(ctx context.Context, txn Transaction) (*Decision, error)
- type Name
- type RedisSource
- func (r *RedisSource) BatchGet(ctx context.Context, bucketKeys []string) (map[string]time.Time, error)
- func (r *RedisSource) BatchSet(ctx context.Context, buckets map[string]time.Time) error
- func (r *RedisSource) Delete(ctx context.Context, bucketKey string) error
- func (r *RedisSource) Get(ctx context.Context, bucketKey string) (time.Time, error)
- func (r *RedisSource) Ping(ctx context.Context) error
- type Transaction
- type TransactionBuilder
- func (builder *TransactionBuilder) CertificatesPerDomainTransactions(regId int64, orderDomains []string, maxNames int) ([]Transaction, error)
- func (builder *TransactionBuilder) CertificatesPerFQDNSetTransaction(orderNames []string) (Transaction, error)
- func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderDomains []string, maxNames int) ([]Transaction, error)
- func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction(regId int64, orderDomain string) (Transaction, error)
- func (builder *TransactionBuilder) OrdersPerAccountTransaction(regId int64) (Transaction, error)
- func (builder *TransactionBuilder) RegistrationsPerIPAddressTransaction(ip net.IP) (Transaction, error)
- func (builder *TransactionBuilder) RegistrationsPerIPv6RangeTransaction(ip net.IP) (Transaction, error)
Constants ¶
const ( // Allowed is used for rate limit metrics, it's the value of the 'decision' // label when a request was allowed. Allowed = "allowed" // Denied is used for rate limit metrics, it's the value of the 'decision' // label when a request was denied. Denied = "denied" )
Variables ¶
var ErrBucketNotFound = fmt.Errorf("bucket not found")
ErrBucketNotFound indicates that the bucket was not found.
var ErrInvalidCost = fmt.Errorf("invalid cost, must be >= 0")
ErrInvalidCost indicates that the cost specified was < 0.
var ErrInvalidCostOverLimit = fmt.Errorf("invalid cost, must be <= limit.Burst")
ErrInvalidCostOverLimit indicates that the cost specified was > limit.Burst.
Functions ¶
func DomainsForRateLimiting ¶
DomainsForRateLimiting transforms a list of FQDNs into a list of eTLD+1's for the purpose of rate limiting. It also de-duplicates the output domains. Exact public suffix matches are included.
Types ¶
type Decision ¶
type Decision struct { // Allowed is true if the bucket possessed enough capacity to allow the // request given the cost. Allowed bool // Remaining is the number of requests the client is allowed to make before // they're rate limited. Remaining int64 // RetryIn is the duration the client MUST wait before they're allowed to // make a request. RetryIn time.Duration // ResetIn is the duration the bucket will take to refill to its maximum // capacity, assuming no further requests are made. ResetIn time.Duration // contains filtered or unexported fields }
type Limiter ¶
type Limiter struct {
// contains filtered or unexported fields
}
Limiter provides a high-level interface for rate limiting requests by utilizing a leaky bucket-style approach.
func NewLimiter ¶
func NewLimiter(clk clock.Clock, source source, stats prometheus.Registerer) (*Limiter, error)
NewLimiter returns a new *Limiter. The provided source must be safe for concurrent use.
func (*Limiter) BatchRefund ¶
BatchRefund attempts to refund all or some of the costs to the provided buckets' capacities. Non-existent buckets will NOT be initialized. The new bucket state is persisted to the underlying datastore, if applicable, before returning. Spend-only Transactions are assumed to be refundable. Check-only Transactions are never refunded. The following rules are applied to merge the Decisions for each Transaction into a single batch Decision:
- Allowed is true if all Transactions where check is true were allowed,
- RetryIn and ResetIn are the largest values of each across all Decisions,
- Remaining is the smallest value of each across all Decisions, and
- Decisions resulting from spend-only Transactions are never merged.
func (*Limiter) BatchSpend ¶
BatchSpend attempts to deduct the costs from the provided buckets' capacities. If applicable, new bucket states are persisted to the underlying datastore before returning. Non-existent buckets will be initialized WITH the cost factored into the initial state. The following rules are applied to merge the Decisions for each Transaction into a single batch Decision:
- Allowed is true if all Transactions where check is true were allowed,
- RetryIn and ResetIn are the largest values of each across all Decisions,
- Remaining is the smallest value of each across all Decisions, and
- Decisions resulting from spend-only Transactions are never merged.
func (*Limiter) Check ¶
Check DOES NOT deduct the cost of the request from the provided bucket's capacity. The returned *Decision indicates whether the capacity exists to satisfy the cost and represents the hypothetical state of the bucket IF the cost WERE to be deducted. If no bucket exists it will NOT be created. No state is persisted to the underlying datastore.
func (*Limiter) Refund ¶
Refund attempts to refund all of the cost to the capacity of the specified bucket. The returned *Decision indicates whether the refund was successful and represents the current state of the bucket. The new bucket state is persisted to the underlying datastore, if applicable, before returning. If no bucket exists it will NOT be created. Spend-only Transactions are assumed to be refundable. Check-only Transactions are never refunded.
Note: The amount refunded cannot cause the bucket to exceed its maximum capacity. Partial refunds are allowed and are considered successful. For instance, if a bucket has a maximum capacity of 10 and currently has 5 requests remaining, a refund request of 7 will result in the bucket reaching its maximum capacity of 10, not 12.
func (*Limiter) Reset ¶
Reset resets the specified bucket to its maximum capacity. The new bucket state is persisted to the underlying datastore before returning.
func (*Limiter) Spend ¶
Spend attempts to deduct the cost from the provided bucket's capacity. The returned *Decision indicates whether the capacity existed to satisfy the cost and represents the current state of the bucket. If no bucket exists it WILL be created WITH the cost factored into its initial state. The new bucket state is persisted to the underlying datastore, if applicable, before returning.
type Name ¶
type Name int
Name is an enumeration of all rate limit names. It is used to intern rate limit names as strings and to provide a type-safe way to refer to rate limits.
IMPORTANT: If you add a new limit Name, you MUST add:
- it to the nameToString mapping,
- an entry for it in the validateIdForName(), and
- provide the appropriate constructors in bucket.go.
const ( // Unknown is the zero value of Name and is used to indicate an unknown // limit name. Unknown Name = iota // NewRegistrationsPerIPAddress uses bucket key 'enum:ipAddress'. NewRegistrationsPerIPAddress // NewRegistrationsPerIPv6Range uses bucket key 'enum:ipv6rangeCIDR'. The // address range must be a /48. RFC 3177, which was published in 2001, // advised operators to allocate a /48 block of IPv6 addresses for most end // sites. RFC 6177, which was published in 2011 and obsoletes RFC 3177, // advises allocating a smaller /56 block. We've chosen to use the larger // /48 block for our IPv6 rate limiting. See: // 1. https://tools.ietf.org/html/rfc3177#section-3 // 2. https://datatracker.ietf.org/doc/html/rfc6177#section-2 NewRegistrationsPerIPv6Range // NewOrdersPerAccount uses bucket key 'enum:regId'. NewOrdersPerAccount // FailedAuthorizationsPerDomainPerAccount uses two different bucket keys // depending on the context: // - When referenced in an overrides file: uses bucket key 'enum:regId', // where regId is the ACME registration Id of the account. // - When referenced in a transaction: uses bucket key 'enum:regId:domain', // where regId is the ACME registration Id of the account and domain is a // domain name in the certificate. FailedAuthorizationsPerDomainPerAccount // CertificatesPerDomain uses bucket key 'enum:domain', where domain is a // domain name in the certificate. CertificatesPerDomain // CertificatesPerDomainPerAccount uses two different bucket keys depending // on the context: // - When referenced in an overrides file: uses bucket key 'enum:regId', // where regId is the ACME registration Id of the account. // - When referenced in a transaction: uses bucket key 'enum:regId:domain', // where regId is the ACME registration Id of the account and domain is a // domain name in the certificate. // // When overrides to the CertificatesPerDomainPerAccount are configured for a // subscriber, the cost: // - MUST be consumed from each CertificatesPerDomainPerAccount bucket and // - SHOULD be consumed from each CertificatesPerDomain bucket, if possible. CertificatesPerDomainPerAccount // CertificatesPerFQDNSet uses bucket key 'enum:fqdnSet', where fqdnSet is a // hashed set of unique eTLD+1 domain names in the certificate. // // Note: When this is referenced in an overrides file, the fqdnSet MUST be // passed as a comma-separated list of domain names. CertificatesPerFQDNSet )
func (Name) EnumString ¶
EnumString returns the string representation of the Name enumeration.
type RedisSource ¶
type RedisSource struct {
// contains filtered or unexported fields
}
RedisSource is a ratelimits source backed by sharded Redis.
func NewRedisSource ¶
func NewRedisSource(client *redis.Ring, clk clock.Clock, stats prometheus.Registerer) *RedisSource
NewRedisSource returns a new Redis backed source using the provided *redis.Ring client.
func (*RedisSource) BatchGet ¶
func (r *RedisSource) BatchGet(ctx context.Context, bucketKeys []string) (map[string]time.Time, error)
BatchGet retrieves the TATs at the specified bucketKeys using a pipelined Redis Transaction in order to reduce the number of round-trips to each Redis shard. An error is returned if the operation failed and nil otherwise. If a bucketKey does not exist, it WILL NOT be included in the returned map.
func (*RedisSource) BatchSet ¶
BatchSet stores TATs at the specified bucketKeys using a pipelined Redis Transaction in order to reduce the number of round-trips to each Redis shard. An error is returned if the operation failed and nil otherwise.
func (*RedisSource) Delete ¶
func (r *RedisSource) Delete(ctx context.Context, bucketKey string) error
Delete deletes the TAT at the specified bucketKey ('name:id'). It returns an error if the operation failed and nil otherwise. A nil return value does not indicate that the bucketKey existed.
type Transaction ¶
type Transaction struct {
// contains filtered or unexported fields
}
Transaction represents a single rate limit operation. It includes a bucketKey, which combines the specific rate limit enum with a unique identifier to form the key where the state of the "bucket" can be referenced or stored by the Limiter, the rate limit being enforced, a cost which MUST be >= 0, and check/spend fields, which indicate how the Transaction should be processed. The following are acceptable combinations of check/spend:
- check-and-spend: when check and spend are both true, the cost will be checked against the bucket's capacity and spent/refunded, when possible.
- check-only: when only check is true, the cost will be checked against the bucket's capacity, but will never be spent/refunded.
- spend-only: when only spend is true, spending is best-effort. Regardless of the bucket's capacity, the transaction will be considered "allowed".
- allow-only: when neither check nor spend are true, the transaction will be considered "allowed" regardless of the bucket's capacity. This is useful for limits that are disabled.
type TransactionBuilder ¶
type TransactionBuilder struct {
// contains filtered or unexported fields
}
TransactionBuilder is used to build Transactions for various rate limits. Each rate limit has a corresponding method that returns a Transaction for that limit. Call NewTransactionBuilder to create a new *TransactionBuilder.
func NewTransactionBuilder ¶
func NewTransactionBuilder(defaults, overrides string) (*TransactionBuilder, error)
NewTransactionBuilder returns a new *TransactionBuilder. The provided defaults and overrides paths are expected to be paths to YAML files that contain the default and override limits, respectively. Overrides is optional, defaults is required.
func (*TransactionBuilder) CertificatesPerDomainTransactions ¶
func (builder *TransactionBuilder) CertificatesPerDomainTransactions(regId int64, orderDomains []string, maxNames int) ([]Transaction, error)
CertificatesPerDomainTransactions returns a slice of Transactions for the provided order domain names. An error is returned if any of the order domain names are invalid. When a CertificatesPerDomainPerAccount override is configured, two types of Transactions are returned:
- A spend-only Transaction for each per domain bucket. Spend-only transactions will not be denied if the bucket lacks the capacity to satisfy the cost.
- A check-and-spend Transaction for each per account per domain bucket. Check- and-spend transactions will be denied if the bucket lacks the capacity to satisfy the cost.
When a CertificatesPerDomainPerAccount override is not configured, a check- and-spend Transaction is returned for each per domain bucket.
func (*TransactionBuilder) CertificatesPerFQDNSetTransaction ¶
func (builder *TransactionBuilder) CertificatesPerFQDNSetTransaction(orderNames []string) (Transaction, error)
CertificatesPerFQDNSetTransaction returns a Transaction for the provided order domain names.
func (*TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions ¶
func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderDomains []string, maxNames int) ([]Transaction, error)
FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions returns a slice of Transactions for the provided order domain names. An error is returned if any of the order domain names are invalid. This method should be used for checking capacity, before allowing more authorizations to be created.
func (*TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction ¶
func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction(regId int64, orderDomain string) (Transaction, error)
FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction returns a spend- only Transaction for the provided order domain name. An error is returned if the order domain name is invalid. This method should be used for spending capacity, as a result of a failed authorization.
func (*TransactionBuilder) OrdersPerAccountTransaction ¶
func (builder *TransactionBuilder) OrdersPerAccountTransaction(regId int64) (Transaction, error)
OrdersPerAccountTransaction returns a Transaction for the NewOrdersPerAccount limit for the provided ACME registration Id.
func (*TransactionBuilder) RegistrationsPerIPAddressTransaction ¶
func (builder *TransactionBuilder) RegistrationsPerIPAddressTransaction(ip net.IP) (Transaction, error)
RegistrationsPerIPAddressTransaction returns a Transaction for the NewRegistrationsPerIPAddress limit for the provided IP address.
func (*TransactionBuilder) RegistrationsPerIPv6RangeTransaction ¶
func (builder *TransactionBuilder) RegistrationsPerIPv6RangeTransaction(ip net.IP) (Transaction, error)
RegistrationsPerIPv6RangeTransaction returns a Transaction for the NewRegistrationsPerIPv6Range limit for the /48 IPv6 range which contains the provided IPv6 address.