Public vals often use resources which have some usage limits ("quota"). Publishing such vals carries the risk that these quotas may be exceeded. For this reason, it makes sense to keep track of resource consumption and trigger a relevant response when a preset limit is reached (for example, one could provide the user with a meaningful error message).
The "floatingQuotaTracker" is designed to help monitor resources that may only be used a certain number of times in a given (but floating) time span (e.g., 10 times a minute).
Simply add an invocation of the "floatingQuotaTracker" into your val just before the tracked resource is going to be used and, from then on, that resource will be tracked and protected against excessive consumption.
Using a floatingQuotaTracker
(within the val that requires the resource) is quite simple:
import { floatingQuotaTracker } from 'https://esm.town/v/rozek/floatingQuotaTracker'
const ResourceTable = 'xxx' // enter name of sqlite table that can be used
const ResourceLimit = 10 // max. number of allowed requests per period
const ResourcePeriod = 60*1000 // the period, given in ms
const Granularity = 5*1000 // how precisely should be logged?
;(async () => {
const Tracker = await floatingQuotaTracker.new(
ResourceTable, ResourceLimit,ResourcePeriod, Granularity
)
let exceeded = await Tracker.LimitExceeded() // true if quota exceeded
// can be used to chech the current status
let TimeToWait = await Tracker.TimeToWait() // how long to wait before...
// ...the resource may be used again?
await Tracker.incrementIfAllowed() // increments resource usage if allowed
// or throws a "LimitExceeded" exception otherwise
... now use your resource as usual
})()
Simply provide the following settings:
- ResourceTable - the name prefix of an sqlite table
- ResourceLimit - how many calls are allowed within the given ResourcePeriod
- ResourcePeriod - the time span relevant for usage counting
- Granularity - how precisely should be logged? large values produce less rows in the sqlite tables and are therefore more efficient, but may let you wait longer
The "floatingQuotaTracker" is designed to be accessed simultaneously from multiple vals without interfering with each other.
static async new (Name:string, Limit:number = 10, Period:number = 60*1000, Granularity:number = 5*1000):Promise<floatingQuotaTracker>
creates a new tracker instance.Name
is a prefix for SQLite tables (which must match the RegEx pattern/^[a-z_][0-9a-z_]+$/i
),Limit
is the maximum calls per period,Period
is the time window in ms, andGranularity
is the time resolution in ms (Limit
,Period
andGranularity
are optional and only needed when the tracking tables are created)async Limit(): Promise<number>
returns the current usage limitasync setLimit (newLimit:number):Promise<void>
sets a new usage limit.newLimit
must be a cardinal numberasync Period ():Promise<number>
returns the current time period in millisecondsasync setPeriod (newPeriod:number):Promise<void>
sets a new time period.newPeriod
must be a cardinal number representing millisecondsasync Granularity ():Promise<number>
returns the current time granularity in millisecondsasync setGranularity (newGranularity:number):Promise<void>
sets a new time granularity.newGranularity
must be a cardinal number representing millisecondsasync reset ():Promise<void>
clears all usage dataasync Usage ():Promise<number>
returns the total usage within the current periodasync LimitExceeded ():Promise<boolean>
checks if the usage limit has been exceededasync increment ():Promise<void>
increases the usage count for the current time slotasync incrementIfAllowed ():Promise<void>
increases the usage count if the limit hasn't been exceeded. Throws a 'LimitExceeded' error otherwiseasync TimeToWait ():Promise<number>
returns the time in milliseconds to wait before the next allowed increment, or 0 if increment is currently allowed
Some tests can be found in val floatingQuotaTracker_Test