falcon-limiter 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f3dddcc61c0937bb6126b7a27298982a6159438479753b717fff59491ab9048
4
- data.tar.gz: 6430a166f1ffb09e7dbdc6b0e4897abdcfc82c4b299af550aebe3a0ace1deba5
3
+ metadata.gz: 19aed9c67b7fe7b68716eb8b4db0ffaa0424733a2e3e18abda65e367293fda17
4
+ data.tar.gz: 1eabdea4a1110e92a86a5d30bc1dba115cc10f7d33fb8ef545d3f5f6806eaac0
5
5
  SHA512:
6
- metadata.gz: 4313993a4aa621ed7be5449759e8e874224c1d7ad88f1708e54f8fc2b070064b8ec43cce7b43775d98295c90eb46bd273592bd592331d20cb0d3f7990cef0a4a
7
- data.tar.gz: a4b12e91879182a082573553293615843d07f08c8aa67d4f9a2aa13af1121dda6b13cff654887e7da585d21eb10f1d7a6c06d009818e03fbcb2f6a363b9b6a87
6
+ metadata.gz: be434c0ba702339891fbf77bf78ce76610e5b833abf304ac7c65913aceac28a04a0ac4560d75e29c5866d8fa13598482b62aa949d9bef6d326eb62b2ccfcd1f7
7
+ data.tar.gz: 51344952a67ae841d4f3284fe084178ba6af764e482c7f16d730c5d22a3ffc64560af6010602c416427c8f42706e822b3228073ee84714f7508c07b787f2da18
checksums.yaml.gz.sig CHANGED
Binary file
@@ -0,0 +1,116 @@
1
+ # Getting Started
2
+
3
+ This guide explains how to get started with `falcon-limiter` for advanced concurrency control and resource limiting in Falcon web applications.
4
+
5
+ ## Installation
6
+
7
+ Add the gem to your project:
8
+
9
+ ```bash
10
+ $ bundle add falcon-limiter
11
+ ```
12
+
13
+ ## Core Concepts
14
+
15
+ `falcon-limiter` has one main concept:
16
+
17
+ - {ruby Falcon::Limiter::LongTask} represents operations that take significant time but aren't CPU-intensive (like database queries or API calls).
18
+
19
+ When you start a Long Task, the server:
20
+
21
+ - Releases the connection token so new requests can be accepted.
22
+ - Continues processing your I/O operation in the background.
23
+ - Prevents too many I/O operations from running simultaneously.
24
+ - Maintains responsiveness for CPU-bound requests.
25
+
26
+ This means your server can handle many concurrent I/O operations without blocking quick CPU-bound requests.
27
+
28
+ ## Usage
29
+
30
+ The easiest way to get started is using the {ruby Falcon::Limiter::Environment} module:
31
+
32
+ ```ruby
33
+ #!/usr/bin/env falcon-host
34
+ # frozen_string_literal: true
35
+
36
+ require "falcon/limiter/environment"
37
+ require "falcon/environment/rack"
38
+
39
+ service "myapp.localhost" do
40
+ include Falcon::Environment::Rack
41
+ include Falcon::Limiter::Environment
42
+
43
+ # If you use a custom endpoint, you need to use it with the limiter wrapper:
44
+ endpoint do
45
+ Async::HTTP::Endpoint.parse("http://localhost:9292").with(wrapper: limiter_wrapper)
46
+ end
47
+ end
48
+ ```
49
+
50
+ Then in your Rack application:
51
+
52
+ ```ruby
53
+ # config.ru
54
+ require "falcon/limiter/long_task"
55
+
56
+ run do |env|
57
+ path = env["PATH_INFO"]
58
+
59
+ case path
60
+ when "/io"
61
+ # For I/O bound work, start a long task to release the connection token:
62
+ Falcon::Limiter::LongTask.current.start
63
+
64
+ # Long I/O operation (database query, external API call, etc.).
65
+ sleep(5) # Simulating I/O.
66
+
67
+ when "/cpu"
68
+ # For CPU bound work, keep the connection token.
69
+ # This ensures only limited CPU work happens concurrently.
70
+ sleep(5) # Simulating CPU work.
71
+ end
72
+
73
+ [200, {"content-type" => "text/plain"}, ["Request completed"]]
74
+ end
75
+ ```
76
+
77
+ ### Understanding the Behavior
78
+
79
+ With the default configuration:
80
+
81
+ - **Connection limit**: 1 (only 1 connection accepted at a time)
82
+ - **Long task limit**: 10 (up to 10 concurrent I/O operations)
83
+
84
+ This means:
85
+
86
+ 1. **CPU-bound requests** (`/cpu`) will be processed sequentially - only one at a time
87
+ 2. **I/O-bound requests** (`/io`) can run concurrently (up to 10) because they release their connection token
88
+ 3. **Mixed workloads** work optimally - I/O requests don't block CPU requests from being accepted
89
+
90
+ ### Configuration
91
+
92
+ You can customize the limits by overriding the environment methods:
93
+
94
+ ```ruby
95
+ service "myapp.localhost" do
96
+ include Falcon::Environment::Rack
97
+ include Falcon::Limiter::Environment
98
+
99
+ # Override default limits
100
+ def limiter_maximum_connections
101
+ 2 # Allow 2 concurrent connections
102
+ end
103
+
104
+ def limiter_maximum_long_tasks
105
+ 8 # Allow 8 concurrent I/O operations
106
+ end
107
+
108
+ def limiter_start_delay
109
+ 0.05 # Reduce delay before starting long tasks
110
+ end
111
+
112
+ endpoint do
113
+ Async::HTTP::Endpoint.parse("http://localhost:9292").with(wrapper: limiter_wrapper)
114
+ end
115
+ end
116
+ ```
@@ -0,0 +1,16 @@
1
+ # Automatically generated context index for Utopia::Project guides.
2
+ # Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
3
+ ---
4
+ description: Advanced concurrency control and resource limiting for Falcon web server.
5
+ metadata:
6
+ documentation_uri: https://socketry.github.io/falcon-limiter/
7
+ source_code_uri: https://github.com/socketry/falcon-limiter.git
8
+ files:
9
+ - path: getting-started.md
10
+ title: Getting Started
11
+ description: This guide explains how to get started with `falcon-limiter` for advanced
12
+ concurrency control and resource limiting in Falcon web applications.
13
+ - path: long-tasks.md
14
+ title: Long Tasks
15
+ description: This guide explains how to use <code class="language-ruby">Falcon::Limiter::LongTask</code>
16
+ to effectively manage I/O vs CPU bound workloads in your Falcon applications.
@@ -0,0 +1,160 @@
1
+ # Long Tasks
2
+
3
+ This guide explains how to use {ruby Falcon::Limiter::LongTask} to effectively manage I/O vs CPU bound workloads in your Falcon applications.
4
+
5
+ ## Understanding Long Tasks
6
+
7
+ A **long task** in `falcon-limiter` is any operation that takes significant time (1+ seconds) and isn't CPU-bound, typically I/O operations like:
8
+
9
+ - Database queries.
10
+ - External API calls.
11
+ - File system operations.
12
+ - Network requests.
13
+ - Message queue operations.
14
+
15
+ The key insight is that during I/O operations, your application is waiting rather than consuming CPU resources. Long tasks allow the server to:
16
+
17
+ 1. **Release the connection token** during I/O, allowing other requests to be accepted.
18
+ 2. **Maintain responsiveness** by not blocking CPU-bound requests.
19
+ 3. **Optimize resource utilization** by running multiple I/O operations concurrently.
20
+
21
+ ## Usage
22
+
23
+ When using {ruby Falcon::Limiter::Environment}, a long task is automatically created for each request:
24
+
25
+ ```ruby
26
+ # In your Rack application
27
+ run do |env|
28
+ # Long task is available via Falcon::Limiter::LongTask.current
29
+ long_task = Falcon::Limiter::LongTask.current
30
+
31
+ if long_task
32
+ # Start the long task for I/O operations
33
+ long_task.start
34
+
35
+ # Perform I/O operation
36
+ database_query
37
+
38
+ # Long task automatically stops when request completes
39
+ end
40
+
41
+ [200, {}, ["Response"]]
42
+ end
43
+ ```
44
+
45
+ ### Custom Delays
46
+
47
+ A delay is used to avoid starting a long task until we know that it's likely to be slow.
48
+
49
+ ```ruby
50
+ run do |env|
51
+ path = env["PATH_INFO"]
52
+
53
+ case path
54
+ when "/io"
55
+ # Start long task with default delay (0.1 seconds):
56
+ Falcon::Limiter::LongTask.current.start
57
+
58
+ # Perform I/O operation:
59
+ external_api_call
60
+
61
+ when "/io-immediate"
62
+ # Start immediately without delay:
63
+ Falcon::Limiter::LongTask.current.start(delay: false)
64
+
65
+ # Perform I/O operation:
66
+ database_query
67
+
68
+ when "/io-custom-delay"
69
+ # Start with custom delay:
70
+ Falcon::Limiter::LongTask.current.start(delay: 0.5)
71
+
72
+ # Perform I/O operation:
73
+ slow_file_operation
74
+
75
+ when "/cpu"
76
+ # Don't start long task for CPU-bound work:
77
+ cpu_intensive_calculation
78
+ end
79
+
80
+ [200, {}, ["Completed"]]
81
+ end
82
+ ```
83
+
84
+ ### Block-based Long Tasks
85
+
86
+ You can use long tasks with blocks for automatic cleanup:
87
+
88
+ ```ruby
89
+ require "net/http"
90
+
91
+ run do |env|
92
+ path = env["PATH_INFO"]
93
+
94
+ case path
95
+ when "/api/weather"
96
+ # Use block-based long task for automatic cleanup:
97
+ response = Falcon::Limiter::LongTask.current.start do |long_task|
98
+ # Make external API call:
99
+ uri = URI("https://api.openweathermap.org/data/2.5/weather?q=London")
100
+ Net::HTTP.get_response(uri)
101
+ # Automatic LongTask#stop when block exits.
102
+ end
103
+
104
+ # CPU work happens outside the long task:
105
+ result = JSON.parse(response.body)
106
+ [200, {"content-type" => "application/json"}, [result.to_json]]
107
+
108
+ when "/api/database"
109
+ # Multiple I/O operations in one long task:
110
+ users, enriched_data = Falcon::Limiter::LongTask.current.start do
111
+ # Database query
112
+ users = database.query("SELECT * FROM users WHERE active = true")
113
+
114
+ # External service call:
115
+ uri = URI("https://api.example.com/enrich")
116
+ enriched_data = Net::HTTP.post_form(uri, {user_ids: users.map(&:id)})
117
+
118
+ [users, enriched_data]
119
+ end
120
+
121
+ # CPU work happens outside the long task:
122
+ parsed_data = JSON.parse(enriched_data.body)
123
+ result = users.zip(parsed_data)
124
+ [200, {"content-type" => "application/json"}, [result.to_json]]
125
+ end
126
+ end
127
+ ```
128
+
129
+ The block form ensures the long task is properly stopped even if an exception occurs. They can also be nested.
130
+
131
+ ## Long Task Lifecycle
132
+
133
+ ### 1. Creation
134
+
135
+ Long tasks are created automatically by {ruby Falcon::Limiter::Middleware} for each request when long task support is enabled.
136
+
137
+ ### 2. Starting
138
+
139
+ When you call `start()`, the long task:
140
+
141
+ - Waits for the configured delay (default: 0.1 seconds).
142
+ - Acquires a long task token from the limiter.
143
+ - Releases the connection token, allowing new connections.
144
+ - Marks the connection as non-persistent to prevent token leakage
145
+
146
+ ### 3. Execution
147
+
148
+ During long task execution:
149
+
150
+ - The connection token is released, so new requests can be accepted.
151
+ - The long task token prevents too many I/O operations from running concurrently.
152
+ - Your I/O operation runs normally.
153
+
154
+ ### 4. Completion
155
+
156
+ When the request completes, the long task automatically:
157
+
158
+ - Releases the long task token.
159
+ - Re-acquires the connection token with high priority.
160
+ - Cleans up resources.
@@ -15,11 +15,11 @@ module Falcon
15
15
  # Provides simple, declarative configuration for concurrency limiting.
16
16
  # Override these methods in your service to customize behavior.
17
17
  module Environment
18
- # Maximum number of concurrent long tasks (default: 4).
18
+ # Maximum number of concurrent long tasks (default: 10).
19
19
  # If this is nil or non-positive, long task support will be disabled.
20
20
  # @returns [Integer] The maximum number of concurrent long tasks.
21
21
  def limiter_maximum_long_tasks
22
- 4
22
+ 10
23
23
  end
24
24
 
25
25
  # @returns [Integer] The maximum number of concurrent connection accepts.
@@ -66,6 +66,7 @@ module Falcon
66
66
  limiter_middleware(super)
67
67
  end
68
68
 
69
+ # @returns [Falcon::Limiter::Wrapper] The wrapper for the connection limiter.
69
70
  def limiter_wrapper
70
71
  Wrapper.new(connection_limiter)
71
72
  end
@@ -65,12 +65,18 @@ module Falcon
65
65
  @delayed_start_task = nil
66
66
  end
67
67
 
68
- # Check if the long task has been started.
69
- # @returns [Boolean] True if the long task token has been acquired, false otherwise.
68
+ # Check if the long task has been started, but not necessarily acquired (e.g. if there was a delay).
69
+ # @returns [Boolean] If the long task has been started.
70
70
  def started?
71
71
  @token.acquired? || @delayed_start_task
72
72
  end
73
73
 
74
+ # Check if the long task has been acquired.
75
+ # @returns [Boolean] If the long task token has been acquired.
76
+ def acquired?
77
+ @token.acquired?
78
+ end
79
+
74
80
  # Start the long task, optionally with a delay to avoid overhead for short operations
75
81
  def start(delay: @start_delay)
76
82
  # If already started, nothing to do:
@@ -88,10 +94,6 @@ module Falcon
88
94
  delay = nil
89
95
  end
90
96
 
91
- def acquired?
92
- @token.acquired?
93
- end
94
-
95
97
  # Otherwise, start the long task:
96
98
  if delay&.positive?
97
99
  # Wait specified delay before starting the long task:
@@ -16,9 +16,9 @@ module Falcon
16
16
  # Initialize the middleware with limiting configuration.
17
17
  # @parameter delegate [Object] The next middleware in the chain to call.
18
18
  # @parameter connection_limiter [Async::Limiter] Connection limiter instance for managing accepts.
19
- # @parameter maximum_long_tasks [Integer] Maximum number of concurrent long tasks (default: 4).
19
+ # @parameter maximum_long_tasks [Integer] Maximum number of concurrent long tasks (default: 10).
20
20
  # @parameter start_delay [Float] Delay in seconds before starting long tasks (default: 0.1).
21
- def initialize(delegate, connection_limiter:, maximum_long_tasks: 4, start_delay: 0.1)
21
+ def initialize(delegate, connection_limiter:, maximum_long_tasks: 10, start_delay: 0.1)
22
22
  super(delegate)
23
23
 
24
24
  @maximum_long_tasks = maximum_long_tasks
@@ -49,17 +49,9 @@ module Falcon
49
49
  end
50
50
  end
51
51
 
52
- # Support for method_missing delegation by checking delegate's methods.
53
- # @parameter method [Symbol] The method name to check.
54
- # @parameter include_private [Boolean] Whether to include private methods (default: false).
55
- # @returns [Boolean] True if the delegate responds to the method.
56
- def respond_to_missing?(method, include_private = false)
57
- @delegate.respond_to?(method, include_private) || super
58
- end
59
-
60
52
  # Forward common inspection methods
61
53
  def inspect
62
- "#<#{self.class}(#{@delegate.class}) #{@delegate.inspect}>"
54
+ "#<#{Socket} #{@delegate.inspect}>"
63
55
  end
64
56
 
65
57
  # String representation of the wrapped socket.
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Falcon
7
7
  module Limiter
8
- VERSION = "0.1.0"
8
+ VERSION = "0.1.2"
9
9
  end
10
10
  end
@@ -43,6 +43,10 @@ module Falcon
43
43
  token.release unless socket
44
44
  end
45
45
 
46
+ # Wrap the socket with a transparent token management.
47
+ # @parameter socket [Object] The socket to wrap.
48
+ # @parameter token [Async::Limiter::Token] The limiter token to release when socket closes.
49
+ # @returns [Falcon::Limiter::Socket] The wrapped socket.
46
50
  def wrap_socket(socket, token)
47
51
  Socket.new(socket, token)
48
52
  end
data/readme.md CHANGED
@@ -8,126 +8,33 @@ Advanced concurrency control and resource limiting for Falcon web server, built
8
8
 
9
9
  This gem provides sophisticated concurrency management for Falcon applications by:
10
10
 
11
- - **Connection Limiting**: Control the number of concurrent connections to prevent server overload
12
- - **Long Task Management**: Handle I/O vs CPU bound workloads effectively by releasing resources during long operations
13
- - **Priority-based Resource Allocation**: Higher priority tasks get preferential access to limited resources
14
- - **Automatic Resource Cleanup**: Ensures proper resource release even when exceptions occur
15
- - **Built-in Statistics**: Monitor resource utilization and contention
11
+ - **Connection Limiting**: Control the number of concurrent connections to prevent server overload.
12
+ - **Long Task Management**: Handle I/O vs CPU bound workloads effectively by releasing resources during long operations.
13
+ - **Priority-based Resource Allocation**: Higher priority tasks get preferential access to limited resources.
14
+ - **Automatic Resource Cleanup**: Ensures proper resource release even when exceptions occur.
15
+ - **Built-in Statistics**: Monitor resource utilization and contention.
16
16
 
17
- ## Installation
17
+ ## Usage
18
18
 
19
- Add this line to your application's Gemfile:
19
+ Please see the [project documentation](https://socketry.github.io/falcon-limiter/) for more details.
20
20
 
21
- ``` ruby
22
- gem 'falcon-limiter'
23
- ```
21
+ - [Getting Started](https://socketry.github.io/falcon-limiter/guides/getting-started/index) - This guide explains how to get started with `falcon-limiter` for advanced concurrency control and resource limiting in Falcon web applications.
24
22
 
25
- ## Usage
23
+ - [Long Tasks](https://socketry.github.io/falcon-limiter/guides/long-tasks/index) - This guide explains how to use <code class="language-ruby">Falcon::Limiter::LongTask</code> to effectively manage I/O vs CPU bound workloads in your Falcon applications.
26
24
 
27
- Please see the [project documentation](https://socketry.github.io/falcon-limiter/) for more details.
25
+ ## Releases
26
+
27
+ Please see the [project releases](https://socketry.github.io/falcon-limiter/releases/index) for all releases.
28
28
 
29
- ### Basic Falcon Environment Integration
30
-
31
- ``` ruby
32
- #!/usr/bin/env falcon-host
33
-
34
- require "falcon-limiter"
35
-
36
- service "myapp.localhost" do
37
- include Falcon::Environment::Limiter
38
-
39
- # Configure concurrency limits
40
- limiter_configuration.max_long_tasks = 8
41
- limiter_configuration.max_accepts = 2
42
-
43
- scheme "http"
44
- url "http://localhost:9292"
45
-
46
- rack_app do
47
- run lambda { |env|
48
- # Start long task for I/O bound work
49
- Falcon::Limiter::LongTask.current&.start
50
-
51
- # Long I/O operation (database query, external API call, etc.)
52
- external_api_call
53
-
54
- # Optional manual stop (auto-cleanup on response end)
55
- Falcon::Limiter::LongTask.current&.stop
56
-
57
- [200, {}, ["OK"]]
58
- }
59
- end
60
- end
61
- ```
62
-
63
- ### Manual Middleware Setup
64
-
65
- ``` ruby
66
- require "falcon-limiter"
67
- require "protocol/http/middleware"
68
-
69
- # Configure middleware stack
70
- middleware = Protocol::HTTP::Middleware.build do
71
- use Falcon::Limiter::Middleware,
72
- max_long_tasks: 4,
73
- max_accepts: 2
74
- use Protocol::Rack::Adapter
75
- run rack_app
76
- end
77
- ```
78
-
79
- ### Direct Semaphore Usage
80
-
81
- ``` ruby
82
- require "falcon-limiter"
83
-
84
- # Create a semaphore for database connections
85
- db_semaphore = Falcon::Limiter::Semaphore.new(5)
86
-
87
- # Acquire a connection
88
- token = db_semaphore.acquire
89
-
90
- begin
91
- # Use database connection
92
- database_operation
93
- ensure
94
- # Release the connection
95
- token.release
96
- end
97
- ```
98
-
99
- ## Configuration
100
-
101
- Configure limits using environment variables or programmatically:
102
-
103
- ``` bash
104
- export FALCON_LIMITER_MAX_LONG_TASKS=8
105
- export FALCON_LIMITER_MAX_ACCEPTS=2
106
- export FALCON_LIMITER_START_DELAY=0.6
107
- ```
108
-
109
- Or in code:
110
-
111
- ``` ruby
112
- Falcon::Limiter.configure do |config|
113
- config.max_long_tasks = 8
114
- config.max_accepts = 2
115
- config.start_delay = 0.6
116
- end
117
- ```
118
-
119
- ## Architecture
120
-
121
- Falcon Limiter is built on top of [async-limiter](https://github.com/socketry/async-limiter), providing:
122
-
123
- - **Thread-safe resource management** using priority queues
124
- - **Integration with Falcon's HTTP pipeline** through Protocol::HTTP::Middleware
125
- - **Automatic connection token management** for optimal resource utilization
126
- - **Priority-based task scheduling** to prevent resource starvation
127
-
128
- ## Development
129
-
130
- After checking out the repo, run `bundle install` to install dependencies. Then, run `bundle exec sus` to run the tests.
29
+ ### v0.1.0
30
+
31
+ - Initial implementation.
32
+
33
+ ## See Also
34
+
35
+ - [falcon](https://github.com/socketry/falcon) - A fast, asynchronous, rack-compatible web server.
36
+ - [async-limiter](https://github.com/socketry/async-limiter) - Execution rate limiting for Async.
37
+ - [async](https://github.com/socketry/async) - A concurrency framework for Ruby.
131
38
 
132
39
  ## Contributing
133
40
 
@@ -146,15 +53,3 @@ In order to protect users of this project, we require all contributors to comply
146
53
  ### Community Guidelines
147
54
 
148
55
  This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
149
-
150
- ## Releases
151
-
152
- Please see the [project releases](https://socketry.github.io/falcon-limiter/releases/index) for all releases.
153
-
154
- ### v0.1.0
155
-
156
- ## See Also
157
-
158
- - [falcon](https://github.com/socketry/falcon) - A fast, asynchronous, rack-compatible web server.
159
- - [async-limiter](https://github.com/socketry/async-limiter) - Execution rate limiting for Async.
160
- - [async](https://github.com/socketry/async) - A concurrency framework for Ruby.
data/releases.md CHANGED
@@ -1,3 +1,5 @@
1
1
  # Releases
2
2
 
3
3
  ## v0.1.0
4
+
5
+ - Initial implementation.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: falcon-limiter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Teeter
@@ -59,14 +59,9 @@ executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
61
61
  files:
62
- - examples/basic_limiting.rb
63
- - examples/environment_usage.rb
64
- - examples/falcon_environment.rb
65
- - examples/limiter/README.md
66
- - examples/limiter/config.ru
67
- - examples/limiter/falcon.rb
68
- - examples/load/config.ru
69
- - examples/load/falcon.rb
62
+ - context/getting-started.md
63
+ - context/index.yaml
64
+ - context/long-tasks.md
70
65
  - lib/falcon/limiter.rb
71
66
  - lib/falcon/limiter/environment.rb
72
67
  - lib/falcon/limiter/long_task.rb
metadata.gz.sig CHANGED
Binary file
@@ -1,40 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Released under the MIT License.
5
- # Copyright, 2025, by Samuel Williams.
6
-
7
- require_relative "../lib/falcon/limiter"
8
-
9
- # Create a semaphore with a limit of 3 concurrent resources
10
- semaphore = Falcon::Limiter::Semaphore.new(3)
11
-
12
- puts "Available resources: #{semaphore.available_count}"
13
- puts "Acquired resources: #{semaphore.acquired_count}"
14
-
15
- # Simulate acquiring and releasing resources
16
- tasks = []
17
-
18
- 10.times do |i|
19
- tasks << Thread.new do
20
- puts "Task #{i}: Waiting for resource..."
21
-
22
- # Acquire a resource token
23
- token = semaphore.acquire
24
-
25
- puts "Task #{i}: Acquired resource (#{semaphore.available_count} remaining)"
26
-
27
- # Simulate work
28
- sleep(rand * 2)
29
-
30
- # Release the resource
31
- token.release
32
-
33
- puts "Task #{i}: Released resource (#{semaphore.available_count} available)"
34
- end
35
- end
36
-
37
- # Wait for all tasks to complete
38
- tasks.each(&:join)
39
-
40
- puts "Final state - Available: #{semaphore.available_count}, Acquired: #{semaphore.acquired_count}"
@@ -1,77 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Released under the MIT License.
5
- # Copyright, 2025, by Samuel Williams.
6
-
7
- require_relative "../lib/falcon/limiter"
8
-
9
- # Example service module that includes the limiter environment
10
- module MyService
11
- include Falcon::Limiter::Environment
12
-
13
- # Customize limiter configuration by overriding methods
14
- def limiter_maximum_long_tasks
15
- 8 # Override default of 4
16
- end
17
-
18
- def limiter_maximum_accepts
19
- 3 # Override default of 1
20
- end
21
-
22
- def limiter_start_delay
23
- 0.8 # Override default of 0.1
24
- end
25
-
26
- # Mock middleware method
27
- def middleware
28
- proc {|_env| [200, {}, ["Base middleware"]]}
29
- end
30
-
31
- # Mock endpoint method
32
- def endpoint
33
- Object.new.tap do |endpoint|
34
- endpoint.define_singleton_method(:to_s) {"MockEndpoint"}
35
- end
36
- end
37
- end
38
-
39
- # Demonstrate the environment with proper async-service pattern
40
- require "async/service"
41
- environment = Async::Service::Environment.build(MyService)
42
- service = environment.evaluator
43
-
44
- puts "=== Falcon Limiter Environment Demo ==="
45
- puts
46
- puts "Configuration:"
47
- options = service.limiter_semaphore_options
48
- puts " Max Long Tasks: #{options[:maximum_long_tasks]}"
49
- puts " Max Accepts: #{options[:maximum_accepts]}"
50
- puts " Start Delay: #{options[:start_delay]}s"
51
- puts
52
-
53
- puts "Semaphore:"
54
- semaphore = service.limiter_semaphore
55
- puts " Limiter Semaphore: #{semaphore.available_count}/#{semaphore.limit} available"
56
- puts " Limited: #{semaphore.limited?}"
57
- puts " Waiting: #{semaphore.waiting_count}"
58
- puts
59
-
60
- puts "Middleware Integration:"
61
- base_middleware = service.middleware
62
- wrapped_middleware = service.limiter_middleware(base_middleware)
63
- puts " Base middleware: #{base_middleware.class}"
64
- puts " Wrapped middleware: #{wrapped_middleware.class}"
65
- puts
66
-
67
- puts "Endpoint Integration:"
68
- endpoint = service.endpoint
69
- puts " Final Endpoint: #{endpoint.class}"
70
-
71
- # Check if it's actually wrapped (only if it's a Wrapper)
72
- if endpoint.respond_to?(:semaphore)
73
- puts " Semaphore: #{endpoint.semaphore.class}"
74
- puts " Same semaphore instance: #{endpoint.semaphore == service.limiter_semaphore}"
75
- else
76
- puts " Note: Endpoint is not wrapped (likely mock object)"
77
- end
@@ -1,55 +0,0 @@
1
- #!/usr/bin/env falcon-host
2
- # frozen_string_literal: true
3
-
4
- # Released under the MIT License.
5
- # Copyright, 2025, by Samuel Williams.
6
-
7
- require_relative "../lib/falcon/limiter"
8
-
9
- service "limiter-example.localhost" do
10
- include Falcon::Limiter::Environment
11
-
12
- # Configure concurrency limits by overriding methods
13
- def limiter_max_long_tasks = 4
14
- def limiter_max_accepts = 2
15
- def limiter_start_delay = 0.5
16
-
17
- scheme "http"
18
- url "http://localhost:9292"
19
-
20
- rack_app do
21
- run lambda {|env|
22
- request = env["protocol.http.request"]
23
-
24
- case env["PATH_INFO"]
25
- when "/fast"
26
- # Fast response - no need for long task
27
- [200, { "Content-Type" => "text/plain" }, ["Fast response"]]
28
-
29
- when "/slow"
30
- # Slow response - use long task management
31
- Falcon::Limiter::LongTask.current&.start
32
-
33
- # Simulate I/O operation (database query, API call, etc.)
34
- sleep(2)
35
-
36
- Falcon::Limiter::LongTask.current&.stop
37
-
38
- [200, { "Content-Type" => "text/plain" }, ["Slow response completed"]]
39
-
40
- when "/stats"
41
- # Show limiter statistics
42
- stats = if respond_to?(:statistics)
43
- statistics
44
- else
45
- { message: "Statistics not available" }
46
- end
47
-
48
- [200, { "Content-Type" => "application/json" }, [stats.to_json]]
49
-
50
- else
51
- [404, { "Content-Type" => "text/plain" }, ["Not found"]]
52
- end
53
- }
54
- end
55
- end
@@ -1,95 +0,0 @@
1
- # Falcon Limiter Examples
2
-
3
- This directory contains examples demonstrating the Falcon::Limiter functionality for managing concurrent workloads.
4
-
5
- ## Overview
6
-
7
- The Falcon::Limiter system helps distinguish between I/O bound and CPU bound workloads:
8
-
9
- - **I/O bound work**: Long tasks that benefit from releasing connection tokens to improve concurrency
10
- - **CPU bound work**: Tasks that should keep connection tokens to prevent GVL contention
11
-
12
- ## Examples
13
-
14
- ### 1. Falcon Environment (`falcon.rb`)
15
-
16
- Uses `Falcon::Environment::Limiter` for turn-key setup:
17
-
18
- ```bash
19
- falcon-host ./falcon.rb
20
- ```
21
-
22
- **Endpoints:**
23
- - `/fast` - Quick response without long task
24
- - `/slow` - I/O bound task using long task management
25
- - `/cpu` - CPU bound task without long task
26
- - `/stats` - Show limiter statistics
27
-
28
- ### 2. Rack Application (`config.ru`)
29
-
30
- Basic Rack app with manual limiter middleware:
31
-
32
- ```bash
33
- falcon serve -c config.ru
34
- ```
35
-
36
- **Endpoints:**
37
- - `/long-io` - Long I/O operation with long task
38
- - `/short` - Short operation (long task delay avoids overhead)
39
- - `/token-info` - Shows connection token information
40
- - `/` - Simple hello response
41
-
42
- ## Key Concepts
43
-
44
- ### Long Task Management
45
-
46
- ```ruby
47
- # For I/O bound operations
48
- request.long_task&.start
49
- external_api_call() # Long I/O operation
50
- request.long_task&.stop # Optional - auto cleanup on response end
51
- ```
52
-
53
- ### Connection Token Release
54
-
55
- Long tasks automatically:
56
- 1. Extract and release connection tokens during I/O operations
57
- 2. Acquire long task tokens from a separate semaphore
58
- 3. Allow more connections to be accepted while I/O is pending
59
- 4. Clean up automatically when response finishes
60
-
61
- ### Configuration
62
-
63
- ```ruby
64
- # Environment-based
65
- ENV["FALCON_LIMITER_MAX_LONG_TASKS"] = "8"
66
- ENV["FALCON_LIMITER_MAX_ACCEPTS"] = "2"
67
-
68
- # Or programmatic
69
- Falcon::Limiter.configure do |config|
70
- config.max_long_tasks = 8
71
- config.max_accepts = 2
72
- config.start_delay = 0.6
73
- end
74
- ```
75
-
76
- ## Testing Load Scenarios
77
-
78
- Test with multiple concurrent requests:
79
-
80
- ```bash
81
- # Test slow endpoint concurrency
82
- curl -s "http://localhost:9292/slow" &
83
- curl -s "http://localhost:9292/slow" &
84
- curl -s "http://localhost:9292/slow" &
85
-
86
- # Should still be responsive for fast requests
87
- curl -s "http://localhost:9292/fast"
88
- ```
89
-
90
- ## Benefits
91
-
92
- 1. **Better Concurrency**: I/O operations don't block connection acceptance
93
- 2. **Graceful Degradation**: System remains responsive under high load
94
- 3. **Resource Management**: Prevents GVL contention for CPU work
95
- 4. **Automatic Cleanup**: Long tasks clean up automatically on response completion
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env falcon --verbose serve -c
2
- # frozen_string_literal: true
3
-
4
- require "falcon/limiter"
5
-
6
- # Configure limiter globally
7
- Falcon::Limiter.configure do |config|
8
- config.max_long_tasks = 4
9
- config.max_accepts = 2
10
- config.start_delay = 0.1
11
- end
12
-
13
- # Basic Rack app demonstrating limiter usage
14
- run lambda {|env|
15
- request = env["protocol.http.request"]
16
- path = env["PATH_INFO"]
17
-
18
- case path
19
- when "/long-io"
20
- # Start long task for I/O bound work
21
- request.long_task&.start
22
-
23
- # Simulate database query or external API call
24
- sleep(1.5)
25
-
26
- [200, { "content-type" => "text/plain" }, ["Long I/O operation completed at #{Time.now}"]]
27
-
28
- when "/short"
29
- # Short operation - long task overhead avoided by delay
30
- sleep(0.05)
31
- [200, { "content-type" => "text/plain" }, ["Short operation at #{Time.now}"]]
32
-
33
- when "/token-info"
34
- # Show connection token information
35
- token_info = "No connection token"
36
-
37
- if request.respond_to?(:connection)
38
- io = request.connection.stream.io
39
- if io.respond_to?(:token)
40
- token = io.token
41
- token_info = "Token: #{token.inspect}, Released: #{token.released?}"
42
- end
43
- end
44
-
45
- [200, { "content-type" => "text/plain" }, [token_info]]
46
-
47
- else
48
- [200, { "content-type" => "text/plain" }, ["Hello from Falcon Limiter!"]]
49
- end
50
- }
@@ -1,60 +0,0 @@
1
- #!/usr/bin/env falcon-host
2
- # frozen_string_literal: true
3
-
4
- # Released under the MIT License.
5
- # Copyright, 2025, by Samuel Williams.
6
-
7
- require "falcon/limiter"
8
-
9
- service "limiter-example.localhost" do
10
- include Falcon::Environment::Limiter
11
-
12
- # Configure limiter settings
13
- limiter_configuration.max_long_tasks = 4
14
- limiter_configuration.max_accepts = 2
15
- limiter_configuration.start_delay = 0.1 # Shorter delay for demo
16
-
17
- scheme "http"
18
- url "http://localhost:9292"
19
-
20
- rack_app do
21
- run lambda {|env|
22
- # Access HTTP request directly
23
- request = env["protocol.http.request"]
24
- path = env["PATH_INFO"]
25
-
26
- case path
27
- when "/fast"
28
- # Fast request - no long task needed
29
- [200, { "content-type" => "text/plain" }, ["Fast response: #{Time.now}"]]
30
-
31
- when "/slow"
32
- # Slow I/O bound request - use long task
33
- request.long_task&.start
34
-
35
- # Simulate I/O operation
36
- sleep(2.0)
37
-
38
- # Optional manual stop (auto-cleanup on response end)
39
- request.long_task&.stop
40
-
41
- [200, { "content-type" => "text/plain" }, ["Slow response: #{Time.now}"]]
42
-
43
- when "/cpu"
44
- # CPU bound request - don't use long task to prevent GVL contention
45
- # Simulate CPU work
46
- result = (1..1_000_000).sum
47
-
48
- [200, { "content-type" => "text/plain" }, ["CPU result: #{result}"]]
49
-
50
- when "/stats"
51
- # Show limiter statistics
52
- stats = statistics
53
- [200, { "content-type" => "application/json" }, [stats.to_json]]
54
-
55
- else
56
- [404, { "content-type" => "text/plain" }, ["Not found"]]
57
- end
58
- }
59
- end
60
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "falcon/limiter/long_task"
4
-
5
- run do |env|
6
- path = env["PATH_INFO"]
7
-
8
- case path
9
- when "/io"
10
- Console.info(self, "Starting \"I/O intensive\" task...")
11
- Falcon::Limiter::LongTask.current.start
12
- sleep(10)
13
- when "/cpu"
14
- Console.info(self, "Starting \"CPU intensive\" task...")
15
- sleep(10)
16
- end
17
-
18
- [200, {"content-type" => "text/plain"}, ["Hello from Falcon Limiter!"]]
19
- end
@@ -1,19 +0,0 @@
1
- #!/usr/bin/env falcon-host
2
- # frozen_string_literal: true
3
-
4
- # Released under the MIT License.
5
- # Copyright, 2025, by Samuel Williams.
6
-
7
- require "falcon/limiter/environment"
8
- require "falcon/environment/rack"
9
-
10
- service "hello.localhost" do
11
- include Falcon::Environment::Rack
12
- include Falcon::Limiter::Environment
13
-
14
- endpoint do
15
- Async::HTTP::Endpoint.parse("http://localhost:9292").with(wrapper: limiter_wrapper)
16
- end
17
-
18
- count {1}
19
- end