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

Instance Attribute Summary collapse

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.



60
61
62
63
64
65
66
67
68
69
# File 'lib/falcon/limiter/long_task.rb', line 60

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
	@tags = nil
end

Instance Attribute Details

#tagsObject (readonly)

Returns the value of attribute tags.



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

def tags
  @tags
end

Class Method Details

.currentObject



25
26
27
# File 'lib/falcon/limiter/long_task.rb', line 25

def self.current
	Fiber.current.falcon_limiter_long_task
end

.current=(long_task) ⇒ Object

Assign the current long task.



30
31
32
# File 'lib/falcon/limiter/long_task.rb', line 30

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.



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

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)


79
80
81
# File 'lib/falcon/limiter/long_task.rb', line 79

def acquired?
	@token.acquired?
end

#pending?Boolean

Check if the long task is waiting to acquire the long task token.

Returns:

  • (Boolean)


85
86
87
# File 'lib/falcon/limiter/long_task.rb', line 85

def pending?
	@delayed_start_task != nil
end

#start(delay: @start_delay, tags: nil) ⇒ Object

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



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
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/falcon/limiter/long_task.rb', line 90

def start(delay: @start_delay, tags: nil)
	# 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
	
	@tags = tags
	
	# 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)


73
74
75
# File 'lib/falcon/limiter/long_task.rb', line 73

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

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

Stop the long task and restore connection token



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/falcon/limiter/long_task.rb', line 134

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)
ensure
	@tags = nil
end

#withObject

Execute the block with the current long task.



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

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