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

storage[R]

Public Class Methods

new(storage, bucket_configs, default_bucket_config='default') click to toggle source
# 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

create_bucket(name, config_name=@default_bucket_config) click to toggle source

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
use(name, cost=nil) click to toggle source

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