class RateLimit::BucketBased
BucketBased
is the star of the show. It takes a storage, a set of configurations, and the name of the default configuration.
storage
-
a method for saving and retrieving buckets (memory, mysql, sqlite3, memcache)
bucket_configs
-
a hash of name =>
Config
pairs default_bucket_config
-
the name of the default config to choose when automatically creating buckets for items that don’t have buckets
Constants
- VERSION
Attributes
Public Class Methods
# File lib/ratelimit/bucketbased.rb, line 208 def initialize(storage, bucket_configs, default_bucket_config='default') @storage = storage @bucket_configs = bucket_configs if @bucket_configs.keys.length == 1 @default_bucket_config = @bucket_configs.keys[0] else @default_bucket_config = default_bucket_config end raise "Cannot find default config" unless @bucket_configs[@default_bucket_config] end
Public Instance Methods
Used primarily to preallocate buckets that need an alternate configuration from the default so that they aren’t automatically created with default configurations
name
-
the name of the item to track
config_name
-
the name of the config to use as a template for creating the bucket
The new bucket will be saved into the storage for this instance of RateLimiter
# File lib/ratelimit/bucketbased.rb, line 223 def create_bucket(name, config_name=@default_bucket_config) config = @bucket_configs[config_name] raise "Config is nil" unless config bucket = Bucket.new(name, config.start, config.max, config.min, config.cost, config.refill_amount, config.refill_epoch, Time.now.to_f, 0.0) @storage.set(bucket) end
Returns true if the item name
has enough credits, false otherwise It will automatically create buckets for items that don’t already have buckets and it will do all the bookkeeping to deduct credits, regenerate credits, and track all the credits used.
name
-
the name of the item to track
cost
-
the cost of the transaction (defaults to the cost set in the
Bucket
if nil)
# File lib/ratelimit/bucketbased.rb, line 234 def use(name, cost=nil) # create a bucket using the default config if it doesn't already exist bkt = @storage.get(name) unless bkt create_bucket(name) bkt = @storage.get(name) end unless bkt raise Exception, "Could not find bucket" end # first credit the bucket for the time that has elapsed epochs_elapsed = ((Time.now.to_f - bkt.last_refill)/bkt.refill_epoch).to_i bkt.current += epochs_elapsed * bkt.refill_amount bkt.current = bkt.max if bkt.current > bkt.max bkt.last_refill += epochs_elapsed*bkt.refill_epoch # now see if the bkt has enough to provide service cost ||= bkt.cost # if the cost isn't provided, use the default cost raise "Invalid cost: #{cost}" if cost < 0 enough = bkt.current >= cost # true if sufficient, false if insufficient # track the total costs, but only if service will be rendered bkt.total_used += cost if enough # now deduct the cost, capping at the minimum bkt.current -= cost bkt.current = bkt.min if bkt.current < bkt.min # now save the changes into the storage (if memory, then no changes are needed, we updated the object in memory) @storage.update(bkt) # return the verdict, did they have enough credits to pay the toll? enough end