arbiter-core-0.1.0.0: Core types, queue registry, and SQL for a PostgreSQL job queue
Safe HaskellNone
LanguageGHC2024

Arbiter.RateLimit

Description

Per-job rate limiting.

Declare which policy (if any) limits each job with a HasRateLimit instance, building the selector from noLimit / limitBy / globalLimit / chooseWhen / limitByCase. The migration statically collects every policy a selector can reach and seeds it.

Synopsis

Declaring a payload's limit

class HasRateLimit payload where Source #

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

Minimal complete definition

Nothing

Methods

rateLimitFor :: RateLimitFor payload Source #

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

rateLimitCost :: payload -> Double Source #

How many tokens this job spends. Defaults to 1.

Instances

Instances details
HasRateLimit payload Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Methods

rateLimitFor :: RateLimitFor payload Source #

rateLimitCost :: payload -> Double Source #

type RateLimitFor payload = Selector Policy 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.

noLimit :: RateLimitFor payload Source #

This payload is unlimited.

limitBy :: Policy -> (payload -> Text) -> RateLimitFor payload Source #

Limit by a fixed policy, keyed by a per-job suffix (e.g. a tenant id).

globalLimit :: Policy -> Text -> RateLimitFor payload Source #

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 a Source #

Select between two selectors with a job predicate. Policy collection inspects both branches.

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

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.

Policies

data Policy Source #

A token-bucket policy. Burst policyMax, refilling policyRefill every policyInterval. A policyRefill of 0 is a manually-refilled bucket.

Instances

Instances details
AdmissionPolicy Policy Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Show Policy Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Eq Policy Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Methods

(==) :: Policy -> Policy -> Bool #

(/=) :: Policy -> Policy -> Bool #

Ord Policy Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

HasRateLimit payload => CollectFor (payload :: Type) Policy Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

tokenBucket :: Text -> Double -> NominalDiffTime -> Policy Source #

"N per period" with burst N (max = refill = n). The period is floored to a tiny positive value. The prefix must not contain :, the key separator. The migration enforces this.

Bucket durability

data Durability Source #

Whether the rate-limit bucket table is WAL-logged. Set at migration time.

Constructors

Durable 
Unlogged 

Instances

Instances details
Show Durability Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Eq Durability Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Spec

Management and observability views

data RateLimitPolicyView Source #

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

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

Generic RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Associated Types

type Rep RateLimitPolicyView 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Show RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

Eq RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

type Rep RateLimitPolicyView Source # 
Instance details

Defined in Arbiter.Core.RateLimit.Stats

data RateLimitBucketView Source #

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

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

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-Kcm43gPyDGYL5vaZ4cZIJF" 'False) (C1 ('MetaCons "RateLimitBucketView" 'PrefixI 'True) ((S1 ('MetaSel ('Just "rateLimitKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "policyPrefix") '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

Eq 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-Kcm43gPyDGYL5vaZ4cZIJF" 'False) (C1 ('MetaCons "RateLimitBucketView" 'PrefixI 'True) ((S1 ('MetaSel ('Just "rateLimitKey") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 Text) :*: (S1 ('MetaSel ('Just "policyPrefix") '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 RateLimitPolicyUpdate Source #

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.

Instances

Instances details
FromJSON RateLimitPolicyUpdate Source #

Hand-written. 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

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-Kcm43gPyDGYL5vaZ4cZIJF" '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

Eq 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-Kcm43gPyDGYL5vaZ4cZIJF" '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 () Source #

Add tokens to a key's bucket, capped at max, and wake any of its jobs parked mid-wait. A no-op without a policy.

pruneRateLimitBuckets :: MonadArbiter m => NominalDiffTime -> m Int64 Source #

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

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

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 #

List a prefix's buckets with fill levels, paginated.

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

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

setRateLimit :: (MonadArbiter m, RegistryTables (RegistryOf m)) => Policy -> m Int64 Source #

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

clearRateLimit :: (MonadArbiter m, RegistryTables (RegistryOf m)) => Policy -> m Int64 Source #

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

Keys

data RateLimitKey Source #

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

Constructors

RateLimitKey 

Fields