arbiter-core-0.1.0.0
arbiter-core
Safe HaskellNone
LanguageGHC2024

Arbiter.RateLimit

Description

Per-job rate limiting.

This is the user-facing rate-limit module. The Arbiter.Core.RateLimit.* modules are internal.

Declare which policy (if any) limits each job with a HasRateLimit instance. The migration seeds every policy a selector can reach.

Synopsis

Declaring a payload's policy

class HasRateLimit payload whereSource #

A payload's per-job policy selection. Defaults to unlimited. Only limited payloads need an instance.

Minimal complete definition

Nothing

Methods

rateLimitFor :: RateLimitFor payloadSource #

The selector deciding which policy (if any) limits a given job.

rateLimitCost :: payload -> DoubleSource #

How many tokens this job spends, clamped to the bucket's range. Defaults to 1.

Instances

Instances details
HasRateLimit payload Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

type RateLimitFor payload = Selector RateLimitPolicy payload (Maybe RateLimitKey)Source #

A selective description of the rate-limit key for a payload. Evaluation returns the job key. Static inspection returns the reachable policies.

type Selector policy payload = Select (Prim policy payload)Source #

A selective description over a payload. Evaluation for a job returns an a. Static inspection returns the reachable policies.

noLimit :: RateLimitFor payloadSource #

This payload is unlimited.

limitBy :: RateLimitPolicy -> (payload -> Text) -> RateLimitFor payloadSource #

Limit by a fixed policy, keyed by a per-job suffix, such as a tenant id.

globalLimit :: RateLimitPolicy -> Text -> RateLimitFor payloadSource #

Limit by a fixed policy under one shared key (a single global bucket).

chooseWhen :: (payload -> Bool) -> Selector policy payload a -> Selector policy payload a -> Selector policy payload aSource #

The first selector when the predicate holds, else the second. Policy collection inspects both branches.

limitByCase :: (Bounded k, Enum k, Eq k) => (payload -> k) -> (k -> RateLimitFor payload) -> RateLimitFor payloadSource #

N-way chooseWhen. Maps the job to a finite tag, then each tag to its selector. Policy collection evaluates every tag in [minBound..maxBound]. The tag's Bounded/Enum and the selector must be total over k.

type RegistryRateLimitPolicies (registry :: JobPayloadRegistry) = RegistryPolicies registry RateLimitPolicySource #

The constraint that lets the migration collect a registry's declared policies.

Policies

data RateLimitPolicySource #

A token-bucket policy. Burst policyMax, refilling continuously at policyRefill per policyInterval. A policyRefill of 0 is a manually-refilled bucket. Fields are non-negative and the interval is positive. The migration rejects other values.

Constructors

RateLimitPolicy 

Fields

tokenBucket :: Text -> Double -> NominalDiffTime -> RateLimitPolicySource #

count per period, with a burst of count. The period is floored to a tiny positive value. The prefix must not contain :, the key separator. The migration enforces this.

class Ord p => AdmissionPolicy p whereSource #

A policy that admits jobs under a named prefix.

Methods

policyPrefixOf :: p -> TextSource #

The prefix the policy's keys start with.

Bucket durability

data DurabilitySource #

Whether the rate-limit bucket table is WAL-logged. Set by the migration config.

Constructors

Durable

WAL-logged. Buckets survive a crash.

Unlogged

Not WAL-logged. A crash resets the buckets. The migrations use this by default.

Instances

Instances details
Eq Durability Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Show Durability Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Keys

data RateLimitKeySource #

A resolved key with a prefix and per-key suffix. The stored form is prefix:suffix.

Constructors

RateLimitKey 

Fields

Management and observability views

data RateLimitPolicyViewSource #

A policy with its default and override params plus live bucket and throttle stats. Each effective param is its override* field when set, else its default* field.

Constructors

RateLimitPolicyView 

Fields

Instances

Instances details
FromJSON RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

ToJSON RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Eq RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Generic RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Associated Types

type Rep RateLimitPolicyView 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyView = D1 ('MetaData "RateLimitPolicyView" "Arbiter.Core.RateLimit.Stats" "arbiter-core-0.1.0.0-inplace" 'False) (C1 ('MetaCons "RateLimitPolicyView" 'PrefixI 'True) (((S1 ('MetaSel ('Just "prefix") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "defaultMaxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double) :*: S1 ('MetaSel ('Just "defaultRefillAmount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double))) :*: (S1 ('MetaSel ('Just "defaultInterval") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double) :*: (S1 ('MetaSel ('Just "overrideMaxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: S1 ('MetaSel ('Just "overrideRefillAmount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double))))) :*: ((S1 ('MetaSel ('Just "overrideInterval") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: (S1 ('MetaSel ('Just "bucketCount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int64) :*: S1 ('MetaSel ('Just "throttledCount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int64))) :*: (S1 ('MetaSel ('Just "throttledQueues") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Text]) :*: (S1 ('MetaSel ('Just "minTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: S1 ('MetaSel ('Just "avgTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)))))))
Show RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyView = D1 ('MetaData "RateLimitPolicyView" "Arbiter.Core.RateLimit.Stats" "arbiter-core-0.1.0.0-inplace" 'False) (C1 ('MetaCons "RateLimitPolicyView" 'PrefixI 'True) (((S1 ('MetaSel ('Just "prefix") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "defaultMaxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double) :*: S1 ('MetaSel ('Just "defaultRefillAmount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double))) :*: (S1 ('MetaSel ('Just "defaultInterval") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double) :*: (S1 ('MetaSel ('Just "overrideMaxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: S1 ('MetaSel ('Just "overrideRefillAmount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double))))) :*: ((S1 ('MetaSel ('Just "overrideInterval") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: (S1 ('MetaSel ('Just "bucketCount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int64) :*: S1 ('MetaSel ('Just "throttledCount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Int64))) :*: (S1 ('MetaSel ('Just "throttledQueues") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 [Text]) :*: (S1 ('MetaSel ('Just "minTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: S1 ('MetaSel ('Just "avgTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)))))))

data RateLimitBucketViewSource #

A single key's bucket: current tokens, effective max, and fill fraction.

Constructors

RateLimitBucketView 

Fields

Instances

Instances details
FromJSON RateLimitBucketView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

ToJSON RateLimitBucketView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Eq RateLimitBucketView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Generic RateLimitBucketView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Associated Types

type Rep RateLimitBucketView 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitBucketView = D1 ('MetaData "RateLimitBucketView" "Arbiter.Core.RateLimit.Stats" "arbiter-core-0.1.0.0-inplace" 'False) (C1 ('MetaCons "RateLimitBucketView" 'PrefixI 'True) ((S1 ('MetaSel ('Just "rateLimitKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "rateLimitPrefix") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: S1 ('MetaSel ('Just "tokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double))) :*: (S1 ('MetaSel ('Just "maxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double) :*: (S1 ('MetaSel ('Just "fillFraction") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: S1 ('MetaSel ('Just "lastRefill") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 UTCTime)))))
Show RateLimitBucketView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitBucketView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitBucketView = D1 ('MetaData "RateLimitBucketView" "Arbiter.Core.RateLimit.Stats" "arbiter-core-0.1.0.0-inplace" 'False) (C1 ('MetaCons "RateLimitBucketView" 'PrefixI 'True) ((S1 ('MetaSel ('Just "rateLimitKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "rateLimitPrefix") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: S1 ('MetaSel ('Just "tokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double))) :*: (S1 ('MetaSel ('Just "maxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Double) :*: (S1 ('MetaSel ('Just "fillFraction") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe Double)) :*: S1 ('MetaSel ('Just "lastRefill") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 UTCTime)))))

data RateLimitPolicyUpdateSource #

A patch over a policy's override params. Per field: Nothing leaves it unchanged, Just Nothing clears the override (reverts to the default), and Just (Just v) sets it.

Constructors

RateLimitPolicyUpdate 

Fields

Instances

Instances details
FromJSON RateLimitPolicyUpdate Source #

A missing key leaves the field unchanged. An explicit null clears the override.

Instance details

Defined in Arbiter.Core.RateLimit.Stats

ToJSON RateLimitPolicyUpdate Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Eq RateLimitPolicyUpdate Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Generic RateLimitPolicyUpdate Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Associated Types

type Rep RateLimitPolicyUpdate 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyUpdate = D1 ('MetaData "RateLimitPolicyUpdate" "Arbiter.Core.RateLimit.Stats" "arbiter-core-0.1.0.0-inplace" 'False) (C1 ('MetaCons "RateLimitPolicyUpdate" 'PrefixI 'True) (S1 ('MetaSel ('Just "overrideMaxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe (Maybe Double))) :*: (S1 ('MetaSel ('Just "overrideRefillAmount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe (Maybe Double))) :*: S1 ('MetaSel ('Just "overrideInterval") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe (Maybe Double))))))
Show RateLimitPolicyUpdate Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyUpdate Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyUpdate = D1 ('MetaData "RateLimitPolicyUpdate" "Arbiter.Core.RateLimit.Stats" "arbiter-core-0.1.0.0-inplace" 'False) (C1 ('MetaCons "RateLimitPolicyUpdate" 'PrefixI 'True) (S1 ('MetaSel ('Just "overrideMaxTokens") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe (Maybe Double))) :*: (S1 ('MetaSel ('Just "overrideRefillAmount") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe (Maybe Double))) :*: S1 ('MetaSel ('Just "overrideInterval") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Maybe (Maybe Double))))))

Operations

addRateLimitTokens :: (MonadArbiter m, RegistryTables (RegistryOf m)) => RateLimitKey -> Double -> m Int64Source #

Add tokens to a key's bucket, clamped to [0, max], and wake any of its throttled jobs. A key without a bucket gets a full bucket and the amount has no effect. Without a policy the bucket stays unchanged and the wake still runs. Returns the number of jobs woken.

pruneRateLimitBuckets :: MonadArbiter m => NominalDiffTime -> m Int64Source #

Delete reclaimable idle (full) buckets. Returns the number pruned. The worker pool reaper runs this. A full bucket re-seeds at full on next use.

resetRateLimitBuckets :: (MonadArbiter m, RegistryTables (RegistryOf m)) => Text -> m Int64Source #

Reset every bucket for a prefix to full and wake its throttled jobs. Returns the number of buckets reset. A manual (0-refill) policy plus a cron calling this at the boundary is a fixed window.

listRateLimitPolicies :: (MonadArbiter m, RegistryTables (RegistryOf m)) => m [RateLimitPolicyView]Source #

List every rate-limit policy with its params, bucket stats, and a live count of currently-throttled jobs per prefix across the registry's queues.

listRateLimitBuckets :: MonadArbiter m => Text -> Int -> Int -> m [RateLimitBucketView]Source #

Arguments

:: MonadArbiter m 
=> Text

Prefix

-> Int

Limit

-> Int

Offset

-> m [RateLimitBucketView] 

List a prefix's buckets with fill levels, emptiest first.

updateRateLimitPolicyOverrides :: (MonadArbiter m, RegistryTables (RegistryOf m)) => Text -> RateLimitPolicyUpdate -> m Int64Source #

Set or clear a policy's override params and wake the prefix's throttled jobs. Returns rows affected (0 if absent).

setRateLimit :: (MonadArbiter m, RegistryTables (RegistryOf m)) => RateLimitPolicy -> m Int64Source #

Override a policy with this shape, until it is cleared. Returns rows affected.

clearRateLimit :: (MonadArbiter m, RegistryTables (RegistryOf m)) => RateLimitPolicy -> m Int64Source #

Drop a policy's overrides. Its declared params apply again. Returns rows affected.

getRateLimitPolicy :: (MonadArbiter m, RegistryTables (RegistryOf m)) => Text -> m (Maybe RateLimitPolicyView)Source #

One prefix's rate-limit policy with its params, bucket stats, and live throttled count. Nothing when the prefix has no policy.

rateLimitPolicyExists :: MonadArbiter m => Text -> m BoolSource #

Whether a rate-limit policy exists for a prefix.