Class: RateLimit::BucketBased

Inherits:
Object
  • Object
show all
Defined in:
lib/ratelimit/bucketbased.rb,
lib/ratelimit/bucketbased/version.rb

Overview

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

Constant Summary collapse

VERSION =
"0.0.1"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(storage, bucket_configs, default_bucket_config = 'default') ⇒ BucketBased

Returns a new instance of BucketBased.



208
209
210
211
212
213
214
215
216
217
# 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

Instance Attribute Details

#storageObject (readonly)

Returns the value of attribute storage.



207
208
209
# File 'lib/ratelimit/bucketbased.rb', line 207

def storage
  @storage
end

Instance Method Details

#create_bucket(name, config_name = @default_bucket_config) ⇒ Object

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



223
224
225
226
227
228
# 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) ⇒ Object

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)



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# 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