Class: Wurk::Limiter::Bucket

Inherits:
Base
  • Object
show all
Defined in:
lib/wurk/limiter/bucket.rb

Overview

Cardinal-boundary counter. Reset at the top of the unit (00:00 of minute/hour/day). On exhaustion sleep until next boundary, retry, OverLimit if that exceeds wait_timeout.

Instance Attribute Summary

Attributes inherited from Base

#name, #options

Instance Method Summary collapse

Methods inherited from Base

#delete, #fingerprint, #reset

Constructor Details

#initialize(name, **options) ⇒ Bucket

Returns a new instance of Bucket.



13
14
15
16
17
18
# File 'lib/wurk/limiter/bucket.rb', line 13

def initialize(name, **options)
  # Eager interval validation so a typo in `:fortnight` blows up at boot,
  # not at first call. Lazy validation defers failures to runtime.
  Limiter.interval_seconds(options[:interval], allow_integer: false)
  super
end

Instance Method Details

#sizeObject



20
21
22
# File 'lib/wurk/limiter/bucket.rb', line 20

def size
  Wurk::Limiter.redis { |c| (c.call('GET', epoch_key) || '0').to_i }
end

#statusObject

used = this period’s count; limit = count; reset_at = the next cardinal boundary, when the counter rolls back to zero (#16).



26
27
28
# File 'lib/wurk/limiter/bucket.rb', line 26

def status
  build_status(used: size, limit: @options[:count], reset_at: next_boundary)
end

#typeObject



11
# File 'lib/wurk/limiter/bucket.rb', line 11

def type = :bucket

#within_limit(used: 1, &block) ⇒ Object

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/wurk/limiter/bucket.rb', line 30

def within_limit(used: 1, &block)
  raise ArgumentError, 'block required' unless block

  deadline = ::Time.now.to_f + @options[:wait_timeout]
  loop do
    ok, _current, secs_to_next = acquire(used)
    return block.call if ok.to_i == 1

    remaining = deadline - ::Time.now.to_f
    raise OverLimit, self if remaining <= 0

    sleep [remaining, secs_to_next.to_f, 0.05].compact.min.clamp(0.0, remaining)
  end
end