Class: Async::Limiter::Timing::SlidingWindow

Inherits:
Object
  • Object
show all
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

FixedWindow

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(duration, burst, limit) ⇒ SlidingWindow

Initialize a window timing strategy.

Raises:

  • (ArgumentError)


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

#durationObject (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

#limitObject (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.

Returns:

  • (Boolean)


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_costObject

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

#statisticsObject

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.

Returns:

  • (Boolean)


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