Class: Falcon::Limiter::LongTask

Inherits:
Object
  • Object
show all
Defined in:
lib/falcon/limiter/long_task.rb

Overview

Manages long-running tasks by releasing connection tokens during I/O operations to prevent contention and maintain server responsiveness.

A long task is any long (1+ sec) operation that isn’t CPU-bound (usually long I/O). Starting a long task lets the server accept one more (potentially CPU-bound) request. This allows us to handle many concurrent I/O bound requests, without adding contention (which impacts latency).

Constant Summary collapse

STOP_PRIORITY =

The priority to use when stopping a long task to re-acquire the connection token.

1000

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request, limiter, connection_token = nil, start_delay: 0.1) ⇒ LongTask

Initialize a new long task with the specified configuration.



58
59
60
61
62
63
64
65
66
# File 'lib/falcon/limiter/long_task.rb', line 58

def initialize(request, limiter, connection_token = nil, start_delay: 0.1)
	@request = request
	@limiter = limiter
	@connection_token = connection_token
	@start_delay = start_delay
	
	@token = Async::Limiter::Token.new(@limiter)
	@delayed_start_task = nil
end

Class Method Details

.currentObject



23
24
25
# File 'lib/falcon/limiter/long_task.rb', line 23

def self.current
	Fiber.current.falcon_limiter_long_task
end

.current=(long_task) ⇒ Object

Assign the current long task.



28
29
30
# File 'lib/falcon/limiter/long_task.rb', line 28

def self.current=(long_task)
	Fiber.current.falcon_limiter_long_task = long_task
end

.for(request, limiter, **options) ⇒ Object

Create a long task for the given request. Extracts connection token from the request if available for proper token management.



47
48
49
50
51
52
# File 'lib/falcon/limiter/long_task.rb', line 47

def self.for(request, limiter, **options)
	# Get connection token from request if possible:
	connection_token = request&.connection&.stream&.io&.token rescue nil
	
	return new(request, limiter, connection_token, **options)
end

Instance Method Details

#acquired?Boolean

Check if the long task has been acquired.

Returns:

  • (Boolean)


76
77
78
# File 'lib/falcon/limiter/long_task.rb', line 76

def acquired?
	@token.acquired?
end

#start(delay: @start_delay) ⇒ Object

Start the long task, optionally with a delay to avoid overhead for short operations



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
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/falcon/limiter/long_task.rb', line 81

def start(delay: @start_delay)
	# If already started, nothing to do:
	if started?
		if block_given?
			return yield self
		else
			return self
		end
	end
	
	if delay == true
		delay = @start_delay
	elsif delay == false
		delay = nil
	end
	
	# Otherwise, start the long task:
	if delay&.positive?
		# Wait specified delay before starting the long task:
		@delayed_start_task = Async do
			sleep(delay)
			self.acquire
		rescue Async::Stop
			# Gracefully exit on stop.
		ensure
			@delayed_start_task = nil
		end
	else
		# Start the long task immediately:
		self.acquire
	end
	
	return self unless block_given?
	
	begin
		yield self
	ensure
		self.stop
	end
end

#started?Boolean

Check if the long task has been started, but not necessarily acquired (e.g. if there was a delay).

Returns:

  • (Boolean)


70
71
72
# File 'lib/falcon/limiter/long_task.rb', line 70

def started?
	@token.acquired? || @delayed_start_task
end

#stop(force: false, **options) ⇒ Object

Stop the long task and restore connection token



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/falcon/limiter/long_task.rb', line 123

def stop(force: false, **options)
	if delayed_start_task = @delayed_start_task
		@delayed_start_task = nil
		delayed_start_task.stop
	end
	
	# Re-acquire the connection token with high priority than inbound requests:
	options[:priority] ||= STOP_PRIORITY
	
	# Release the long task token:
	release(force, **options)
end

#withObject

Execute the block with the current long task.



33
34
35
36
37
38
39
# File 'lib/falcon/limiter/long_task.rb', line 33

def with
	previous = self.class.current
	self.class.current = self
	yield
ensure
	self.class.current = previous
end