Class: Philiprehberger::RateLimiter::SlidingWindow

Inherits:
Object
  • Object
show all
Includes:
StatsTracking
Defined in:
lib/philiprehberger/rate_limiter/sliding_window.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from StatsTracking

#allow!, #keys, #on_reject, #stats, #throttle

Constructor Details

#initialize(limit:, window:) ⇒ SlidingWindow

Returns a new instance of SlidingWindow.



12
13
14
15
16
17
18
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 12

def initialize(limit:, window:)
  @limit = limit
  @window = window
  @store = {}
  @mutex = Mutex.new
  init_stats
end

Instance Attribute Details

#limitObject (readonly)

Returns the value of attribute limit.



10
11
12
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 10

def limit
  @limit
end

#windowObject (readonly)

Returns the value of attribute window.



10
11
12
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 10

def window
  @window
end

Instance Method Details

#allow?(key, weight: 1) ⇒ Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 20

def allow?(key, weight: 1)
  @mutex.synchronize { try_acquire(key, weight) }
end

#clearvoid

This method returns an undefined value.

Clear state for all keys (resets quotas and stats for every tracked key)



50
51
52
53
54
55
56
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 50

def clear
  @mutex.synchronize do
    @store.clear
    @stats_store.clear
  end
  nil
end

#drain(key = :default) ⇒ Integer

Forcefully consume all remaining capacity for a key.

Parameters:

  • key (Symbol, String) (defaults to: :default)

    the rate limit key

Returns:

  • (Integer)

    the number of slots drained



70
71
72
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 70

def drain(key = :default)
  @mutex.synchronize { drain_entries(key) }
end

#info(key) ⇒ Object



58
59
60
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 58

def info(key)
  @mutex.synchronize { build_info(key) }
end

#peek(key) ⇒ Object



24
25
26
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 24

def peek(key)
  @mutex.synchronize { count_remaining(key).positive? }
end

#refund(key, amount: 1) ⇒ Object



62
63
64
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 62

def refund(key, amount: 1)
  @mutex.synchronize { refund_entries(key, amount) }
end

#remaining(key) ⇒ Object



28
29
30
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 28

def remaining(key)
  @mutex.synchronize { count_remaining(key) }
end

#reset(key) ⇒ Object



43
44
45
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 43

def reset(key)
  @mutex.synchronize { @store.delete(key.to_s) }
end

#retry_after(key = :default) ⇒ Float

Seconds until the next request would be allowed, suitable for the HTTP Retry-After header. Returns 0.0 when a request is allowed right now.

Parameters:

  • key (Symbol, String) (defaults to: :default)

    the rate limit key

Returns:

  • (Float)

    seconds until next allowed request (0.0 if allowed now)



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 97

def retry_after(key = :default)
  @mutex.synchronize do
    cleanup(key)
    entries = fetch_entries(key)
    return 0.0 if entries.length < @limit

    oldest = entries.min
    return 0.0 if oldest.nil?

    wait = (oldest + @window) - now
    [wait, 0.0].max
  end
end

#used(key) ⇒ Integer

Number of currently consumed slots for a key (after expiring old entries).

Parameters:

  • key (Symbol, String)

    the rate limit key

Returns:

  • (Integer)

    count of active entries in the window



36
37
38
39
40
41
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 36

def used(key)
  @mutex.synchronize do
    cleanup(key)
    fetch_entries(key).length
  end
end

#wait_time(key = :default) ⇒ Float

Seconds until the next request would be allowed

Parameters:

  • key (Symbol, String) (defaults to: :default)

    the rate limit key

Returns:

  • (Float)

    seconds to wait (0 if allowed now)



78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 78

def wait_time(key = :default)
  @mutex.synchronize do
    cleanup(key)
    entries = fetch_entries(key)
    return 0.0 if entries.length < @limit

    oldest = entries.min
    return 0.0 if oldest.nil?

    wait = oldest + @window - now
    [wait, 0.0].max
  end
end

#window_reset_at(key = :default) ⇒ Time?

Time when the current window expires

Parameters:

  • key (Symbol, String) (defaults to: :default)

    the rate limit key

Returns:

  • (Time, nil)

    absolute time when window resets, nil if no requests



115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/philiprehberger/rate_limiter/sliding_window.rb', line 115

def window_reset_at(key = :default)
  @mutex.synchronize do
    entries = fetch_entries(key)
    return nil if entries.empty?

    cleanup(key)
    entries = fetch_entries(key)
    return nil if entries.empty?

    oldest = entries.min
    elapsed = now - oldest
    Time.now + (@window - elapsed)
  end
end