Class: Async::Limiter::Timing::SlidingWindow
- Inherits:
-
Object
- Object
- Async::Limiter::Timing::SlidingWindow
- Defined in:
- lib/async/limiter/timing/sliding_window.rb
Overview
Sliding window timing strategy with rolling time periods.
This strategy enforces rate limiting with a rolling window that slides continuously, providing smoother rate limiting than fixed windows.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#duration ⇒ Object
readonly
Returns the value of attribute duration.
- #Duration of the timing window in seconds.(ofthetimingwindow) ⇒ Object readonly
-
#limit ⇒ Object
readonly
Returns the value of attribute limit.
- #Maximum tasks allowed per window.(tasksallowedperwindow.) ⇒ Object readonly
Instance Method Summary collapse
-
#acquire(cost = 1) ⇒ Object
Record that a task was acquired.
-
#can_acquire?(cost = 1, current_time = Clock.now) ⇒ Boolean
Check if a task can be acquired based on window constraints.
-
#initialize(duration, burst, limit) ⇒ SlidingWindow
constructor
Initialize a window timing strategy.
-
#maximum_cost ⇒ Object
Maximum cost this timing strategy can support.
-
#statistics ⇒ Object
Get current timing strategy statistics.
-
#wait(mutex, deadline = nil, cost = 1) ⇒ Object
Wait for timing constraints to be satisfied.
-
#window_changed?(current_time, last_window_start) ⇒ Boolean
Check if the window has changed since the last window start.
-
#window_start_time(current_time) ⇒ Object
Calculate the start time of the current window for the given time.
Constructor Details
#initialize(duration, burst, limit) ⇒ SlidingWindow
Initialize a window timing strategy.
28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 28 def initialize(duration, burst, limit) raise ArgumentError, "duration must be positive" unless duration.positive? @duration = duration @burst = burst @limit = limit @start_time = nil @count = 0 @frame_start_time = nil end |
Instance Attribute Details
#duration ⇒ Object (readonly)
Returns the value of attribute duration.
22 23 24 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 22 def duration @duration end |
#Duration of the timing window in seconds.(ofthetimingwindow) ⇒ Object (readonly)
22 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 22 attr_reader :duration |
#limit ⇒ Object (readonly)
Returns the value of attribute limit.
19 20 21 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 19 def limit @limit end |
#Maximum tasks allowed per window.(tasksallowedperwindow.) ⇒ Object (readonly)
19 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 19 attr_reader :limit |
Instance Method Details
#acquire(cost = 1) ⇒ Object
Record that a task was acquired.
65 66 67 68 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 65 def acquire(cost = 1) @count += cost @frame_start_time = Clock.now end |
#can_acquire?(cost = 1, current_time = Clock.now) ⇒ Boolean
Check if a task can be acquired based on window constraints.
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 50 def can_acquire?(cost = 1, current_time = Clock.now) # Update window if needed if window_changed?(current_time, @start_time) @start_time = window_start_time(current_time) @count = 0 end frame_changed = frame_changed?(current_time) # Check both window and frame constraints with cost @burst.can_acquire?(@count + cost - 1, @limit, frame_changed) end |
#maximum_cost ⇒ Object
Maximum cost this timing strategy can support.
42 43 44 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 42 def maximum_cost @limit end |
#statistics ⇒ Object
Get current timing strategy statistics.
130 131 132 133 134 135 136 137 138 139 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 130 def statistics { name: "SlidingWindow", window_duration: @duration, window_limit: @limit, current_window_count: @count, window_utilization_percentage: (@count.to_f / @limit) * 100, burst: @burst.statistics, } end |
#wait(mutex, deadline = nil, cost = 1) ⇒ Object
Wait for timing constraints to be satisfied. Sleeps until the next window or frame becomes available, or returns immediately if ready.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 76 def wait(mutex, deadline = nil, cost = 1) # Only wait if we can't acquire right now: until can_acquire?(cost, Clock.now) # Handle non-blocking case if deadline&.expired? || (deadline && deadline.remaining == 0) return false end next_time = @burst.next_acquire_time( @start_time, @duration, @frame_start_time, @duration / @limit.to_f ) current_time = Clock.now wait_time = next_time - current_time return true if wait_time <= 0 # Check if wait would exceed deadline remaining = deadline&.remaining if remaining && wait_time > remaining return false # Would exceed deadline end # Wait for the required time (or remaining time if deadline specified) actual_wait = remaining ? [wait_time, remaining].min : wait_time mutex.sleep(actual_wait) # Release mutex during sleep end return true end |
#window_changed?(current_time, last_window_start) ⇒ Boolean
Check if the window has changed since the last window start.
123 124 125 126 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 123 def window_changed?(current_time, last_window_start) return true if last_window_start.nil? last_window_start + @duration <= current_time end |
#window_start_time(current_time) ⇒ Object
Calculate the start time of the current window for the given time. Default implementation provides sliding window behavior.
115 116 117 |
# File 'lib/async/limiter/timing/sliding_window.rb', line 115 def window_start_time(current_time) current_time # Sliding window: always starts now end |