gruf-queue 0.1.3 → 0.1.5
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 +4 -4
- data/CHANGELOG.md +52 -0
- data/README.md +19 -272
- data/lib/gruf/queue/plugin.rb +2 -31
- data/lib/gruf/queue/pool_enhancements.rb +50 -15
- data/lib/gruf/queue/version.rb +1 -1
- data/lib/gruf-queue.rb +0 -1
- metadata +1 -2
- data/lib/gruf/queue/queued_rpc_server.rb +0 -105
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c95bbcdd543d4648972f17a59e97f8c3113964fd0f3a38da423a17f6a98913e
|
4
|
+
data.tar.gz: 7079457c7e33fc6a272f8974998a5db14d743e234197625bd5b71c28332273d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55862eaad56dfe99040d110c419581b58bd12a418d2729ef73788332ab9ddaac6b39638f19de2a104550c696bd0a92e61dbc531bfbae664d4725b681322f3b7f
|
7
|
+
data.tar.gz: 64de88a3ec9c97d34bf32c76fae59cd4b3b9340b5a13a0435ed6e1b6ab9e3dde7ba34f0f0d18e297c065c32e6108ec6581abe826d3f6c2c0a80f917aa1ed2f60
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,58 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [0.1.5]
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
- **BREAKING**: Complete rewrite of pool enhancement architecture
|
12
|
+
- Eliminated AlwaysReadyQueue and all worker queue management complexity
|
13
|
+
- Simplified to direct job scheduling with `@jobs << [blk, args]`
|
14
|
+
- Custom `schedule`, `start`, and `loop_execute_jobs` methods
|
15
|
+
- Workers now simply loop and pop jobs from single @jobs queue
|
16
|
+
|
17
|
+
### Added
|
18
|
+
- Custom job execution loop with proper error handling
|
19
|
+
- Thread-safe job scheduling with stop_mutex synchronization
|
20
|
+
- Graceful shutdown support integrated into job processing loop
|
21
|
+
|
22
|
+
### Removed
|
23
|
+
- AlwaysReadyQueue class and all null object pattern code
|
24
|
+
- Complex worker state tracking and ready_workers queue management
|
25
|
+
- Unnecessary abstractions and indirection layers
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
- Eliminated all worker queue complexity and potential race conditions
|
29
|
+
- Much more predictable and maintainable job processing
|
30
|
+
- Simplified shutdown process with clear control flow
|
31
|
+
|
32
|
+
### Improved
|
33
|
+
- Dramatically simplified codebase with single job queue approach
|
34
|
+
- Better performance through reduced complexity
|
35
|
+
- Easier to understand and debug job processing flow
|
36
|
+
|
37
|
+
## [0.1.4]
|
38
|
+
|
39
|
+
### Changed
|
40
|
+
- Renamed `AvaiallableQueue` to `AlwaysReadyQueue` (fixed typo)
|
41
|
+
- Improved null object pattern implementation for worker queue management
|
42
|
+
- Simplified README.md to focus on core functionality
|
43
|
+
|
44
|
+
### Added
|
45
|
+
- Graceful shutdown support with `@stopped` flag checking
|
46
|
+
- Enhanced test coverage for environment variable configuration
|
47
|
+
- Comprehensive test suite for graceful shutdown behavior
|
48
|
+
- Thread-safe shutdown handling for GRPC::Pool enhancements
|
49
|
+
|
50
|
+
### Fixed
|
51
|
+
- Fixed graceful shutdown issues that prevented proper signal handling
|
52
|
+
- Improved AlwaysReadyQueue method implementations for better compatibility
|
53
|
+
- Enhanced error handling in ready_for_work? method
|
54
|
+
|
55
|
+
### Improved
|
56
|
+
- More accurate documentation reflecting actual gem purpose
|
57
|
+
- Better test organization with focused integration tests
|
58
|
+
- Cleaner code structure with proper separation of concerns
|
59
|
+
|
8
60
|
## [0.1.3]
|
9
61
|
|
10
62
|
### Changed
|
data/README.md
CHANGED
@@ -3,287 +3,56 @@
|
|
3
3
|
[](https://badge.fury.io/rb/gruf-queue)
|
4
4
|
[](https://ruby-lang.org)
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
6
|
-
[](https://github.com/your-org/gruf-queue/actions)
|
7
6
|
|
8
|
-
|
7
|
+
Simple GRPC::Pool enhancement that replaces worker-based capacity checking with job count based threshold.
|
9
8
|
|
10
|
-
##
|
9
|
+
## What it does
|
11
10
|
|
12
|
-
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
16
|
-
|
17
|
-
🔌 **Plugin Architecture**
|
18
|
-
- Zero-configuration auto-installation
|
19
|
-
- Non-invasive integration with existing Gruf applications
|
20
|
-
- Modular design with configurable components
|
21
|
-
|
22
|
-
🛡️ **Reliability & Safety**
|
23
|
-
- Thread-safe operations with proper synchronization
|
24
|
-
- Graceful error handling and recovery
|
25
|
-
- Smart ActiveRecord connection management
|
26
|
-
|
27
|
-
📊 **Observability**
|
28
|
-
- Structured logging with metadata for monitoring
|
29
|
-
- Thread naming for better debugging
|
30
|
-
- Comprehensive error reporting with context
|
11
|
+
- Simplifies `GRPC::Pool` by removing complex worker queue management
|
12
|
+
- Direct job scheduling to `@jobs` queue with idle thread processing
|
13
|
+
- Uses `jobs_waiting < threshold` for capacity management
|
14
|
+
- Configurable threshold via `GRUF_MAX_WAITING_REQUESTS` (default: 60)
|
15
|
+
- Supports graceful shutdown
|
31
16
|
|
32
17
|
## Installation
|
33
18
|
|
34
|
-
Add this line to your application's Gemfile:
|
35
|
-
|
36
19
|
```ruby
|
37
20
|
gem 'gruf-queue'
|
38
21
|
```
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
```bash
|
43
|
-
$ bundle install
|
44
|
-
```
|
45
|
-
|
46
|
-
## Quick Start
|
47
|
-
|
48
|
-
### Zero Configuration Setup
|
49
|
-
|
50
|
-
The simplest way to use gruf-queue is to just require it:
|
23
|
+
## Usage
|
51
24
|
|
52
25
|
```ruby
|
53
26
|
require 'gruf-queue'
|
54
27
|
|
55
|
-
#
|
56
|
-
|
28
|
+
# Optional: Set threshold (default is 60)
|
29
|
+
ENV['GRUF_MAX_WAITING_REQUESTS'] = '100'
|
30
|
+
|
31
|
+
# Start server normally
|
57
32
|
Gruf::Server.new(
|
58
33
|
hostname: '0.0.0.0:9001',
|
59
34
|
services: [YourService]
|
60
35
|
).start
|
61
36
|
```
|
62
37
|
|
63
|
-
|
64
|
-
- ✅ Configures Gruf to use `QueuedRpcServer`
|
65
|
-
- ✅ Registers the `ConnectionReset` interceptor (if ActiveRecord is available)
|
66
|
-
- ✅ Sets up enhanced thread pool management
|
67
|
-
- ✅ Enables structured logging
|
68
|
-
|
69
|
-
### Manual Installation Control
|
70
|
-
|
71
|
-
To disable auto-installation and configure manually:
|
38
|
+
## Configuration
|
72
39
|
|
73
40
|
```ruby
|
74
41
|
# Disable auto-installation
|
75
42
|
ENV['GRUF_QUEUE_NO_AUTO_INSTALL'] = 'true'
|
76
43
|
|
77
|
-
require 'gruf-queue'
|
78
|
-
|
79
44
|
# Manual installation
|
80
45
|
Gruf::Queue::Plugin.install!
|
81
|
-
|
82
|
-
# Or configure components individually
|
83
|
-
Gruf.configure do |config|
|
84
|
-
config.rpc_server = Gruf::Queue::QueuedRpcServer
|
85
|
-
|
86
|
-
config.interceptors.use(
|
87
|
-
Gruf::Interceptors::ActiveRecord::ConnectionReset,
|
88
|
-
enabled: true,
|
89
|
-
target_classes: [ActiveRecord::Base]
|
90
|
-
)
|
91
|
-
end
|
92
|
-
```
|
93
|
-
|
94
|
-
## Configuration
|
95
|
-
|
96
|
-
### Server Configuration
|
97
|
-
|
98
|
-
```ruby
|
99
|
-
# Configure the QueuedRpcServer with custom settings
|
100
|
-
Gruf.configure do |config|
|
101
|
-
config.rpc_server = Gruf::Queue::QueuedRpcServer
|
102
|
-
config.rpc_server_options = {
|
103
|
-
pool_size: 10, # Thread pool size
|
104
|
-
max_waiting_requests: 20, # Queue capacity
|
105
|
-
pool_keep_alive: 300, # Thread keep-alive time
|
106
|
-
poll_period: 1 # Polling interval
|
107
|
-
}
|
108
|
-
end
|
109
46
|
```
|
110
47
|
|
111
|
-
|
112
|
-
|
113
|
-
For custom server instances with specific options:
|
48
|
+
## How it works
|
114
49
|
|
115
50
|
```ruby
|
116
|
-
#
|
117
|
-
|
118
|
-
pool_size: 15,
|
119
|
-
max_waiting_requests: 30
|
120
|
-
)
|
51
|
+
# Before: GRPC::Pool tracks actual worker availability
|
52
|
+
pool.ready_for_work? # Complex worker state checking
|
121
53
|
|
122
|
-
|
123
|
-
|
124
|
-
services: [YourService],
|
125
|
-
rpc_server: server
|
126
|
-
).start
|
127
|
-
```
|
128
|
-
|
129
|
-
### ActiveRecord Connection Management
|
130
|
-
|
131
|
-
The `ConnectionReset` interceptor automatically manages database connections:
|
132
|
-
|
133
|
-
```ruby
|
134
|
-
# Automatically registered when ActiveRecord is detected
|
135
|
-
# Customizable with additional target classes:
|
136
|
-
Gruf.configure do |config|
|
137
|
-
config.interceptors.use(
|
138
|
-
Gruf::Interceptors::ActiveRecord::ConnectionReset,
|
139
|
-
enabled: true,
|
140
|
-
target_classes: [ActiveRecord::Base, CustomConnectionClass]
|
141
|
-
)
|
142
|
-
end
|
143
|
-
```
|
144
|
-
|
145
|
-
## Architecture
|
146
|
-
|
147
|
-
### Core Components
|
148
|
-
|
149
|
-
#### `Gruf::Queue::QueuedRpcServer`
|
150
|
-
Enhanced gRPC server with intelligent resource management:
|
151
|
-
- Monitors thread pool capacity
|
152
|
-
- Automatically sends `RESOURCE_EXHAUSTED` when overloaded
|
153
|
-
- Provides structured error handling
|
154
|
-
|
155
|
-
#### `Gruf::Queue::Pool`
|
156
|
-
Advanced thread pool implementation:
|
157
|
-
- Extends `GRPC::Pool` with enhanced job scheduling
|
158
|
-
- Structured logging with thread identification
|
159
|
-
- Comprehensive error isolation and recovery
|
160
|
-
|
161
|
-
#### `Gruf::Interceptors::ActiveRecord::ConnectionReset`
|
162
|
-
Smart database connection management:
|
163
|
-
- Automatically resets ActiveRecord connections after each request
|
164
|
-
- Validates connection handlers before attempting reset
|
165
|
-
- Graceful error handling to prevent request failures
|
166
|
-
|
167
|
-
#### `Gruf::Queue::Plugin`
|
168
|
-
Plugin management system:
|
169
|
-
- Idempotent installation process
|
170
|
-
- Comprehensive error handling during setup
|
171
|
-
- Validation of dependencies and requirements
|
172
|
-
|
173
|
-
### Plugin Lifecycle
|
174
|
-
|
175
|
-
```ruby
|
176
|
-
# Plugin installation process
|
177
|
-
Gruf::Queue::Plugin.install! # => true (success) or false (failure)
|
178
|
-
|
179
|
-
# Check installation status
|
180
|
-
Gruf::Queue::Plugin.installed? # => true/false
|
181
|
-
|
182
|
-
# Reset for testing (test environments only)
|
183
|
-
Gruf::Queue::Plugin.reset!
|
184
|
-
```
|
185
|
-
|
186
|
-
## Advanced Usage
|
187
|
-
|
188
|
-
### Custom Server Implementation
|
189
|
-
|
190
|
-
```ruby
|
191
|
-
# Create a custom server class
|
192
|
-
class MyCustomServer < Gruf::Queue::QueuedRpcServer
|
193
|
-
def initialize(*)
|
194
|
-
super
|
195
|
-
# Custom initialization logic
|
196
|
-
end
|
197
|
-
|
198
|
-
private
|
199
|
-
|
200
|
-
def handle_custom_logic
|
201
|
-
# Your custom server logic
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
# Use with gruf-queue
|
206
|
-
Gruf.configure do |config|
|
207
|
-
config.rpc_server = MyCustomServer
|
208
|
-
end
|
209
|
-
```
|
210
|
-
|
211
|
-
### Monitoring and Observability
|
212
|
-
|
213
|
-
```ruby
|
214
|
-
# Structured logging is automatically enabled
|
215
|
-
# Logs include contextual information:
|
216
|
-
# - Thread IDs for debugging
|
217
|
-
# - Error details with full context
|
218
|
-
# - Performance metrics
|
219
|
-
# - Resource utilization info
|
220
|
-
|
221
|
-
# Example log output:
|
222
|
-
# [INFO] Starting thread pool target_size=10
|
223
|
-
# [DEBUG] Worker thread started thread_id=47185656920560
|
224
|
-
# [WARN] Job execution failed error=SomeError error_class=StandardError
|
225
|
-
```
|
226
|
-
|
227
|
-
### Environment-specific Configuration
|
228
|
-
|
229
|
-
```ruby
|
230
|
-
# Production settings
|
231
|
-
if Rails.env.production?
|
232
|
-
Gruf.configure do |config|
|
233
|
-
config.rpc_server_options = {
|
234
|
-
pool_size: 20,
|
235
|
-
max_waiting_requests: 50,
|
236
|
-
pool_keep_alive: 600
|
237
|
-
}
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
# Development settings
|
242
|
-
if Rails.env.development?
|
243
|
-
Gruf.configure do |config|
|
244
|
-
config.rpc_server_options = {
|
245
|
-
pool_size: 5,
|
246
|
-
max_waiting_requests: 10,
|
247
|
-
pool_keep_alive: 60
|
248
|
-
}
|
249
|
-
end
|
250
|
-
end
|
251
|
-
```
|
252
|
-
|
253
|
-
## Performance Considerations
|
254
|
-
|
255
|
-
- **Thread Pool Sizing**: Set `pool_size` based on your server's CPU cores and expected load
|
256
|
-
- **Queue Capacity**: Configure `max_waiting_requests` to handle traffic spikes
|
257
|
-
- **Connection Management**: The `ConnectionReset` interceptor prevents connection leaks in threaded environments
|
258
|
-
- **Resource Monitoring**: Use structured logs to monitor resource utilization
|
259
|
-
|
260
|
-
## Troubleshooting
|
261
|
-
|
262
|
-
### Common Issues
|
263
|
-
|
264
|
-
**Server not starting with custom configuration:**
|
265
|
-
```ruby
|
266
|
-
# Ensure plugin is installed before configuration
|
267
|
-
Gruf::Queue::Plugin.install!
|
268
|
-
# Then configure...
|
269
|
-
```
|
270
|
-
|
271
|
-
**ActiveRecord connection issues:**
|
272
|
-
```ruby
|
273
|
-
# Verify ActiveRecord is loaded before gruf-queue
|
274
|
-
require 'active_record'
|
275
|
-
require 'gruf-queue'
|
276
|
-
```
|
277
|
-
|
278
|
-
**High memory usage:**
|
279
|
-
```ruby
|
280
|
-
# Adjust pool size and keep-alive settings
|
281
|
-
Gruf.configure do |config|
|
282
|
-
config.rpc_server_options = {
|
283
|
-
pool_size: 8, # Reduce if memory constrained
|
284
|
-
pool_keep_alive: 60 # Shorter keep-alive
|
285
|
-
}
|
286
|
-
end
|
54
|
+
# After: Simple job count threshold
|
55
|
+
pool.ready_for_work? # jobs_waiting < max_waiting_requests
|
287
56
|
```
|
288
57
|
|
289
58
|
## Requirements
|
@@ -292,28 +61,6 @@ end
|
|
292
61
|
- Gruf >= 2.21.0
|
293
62
|
- gRPC >= 1.0
|
294
63
|
|
295
|
-
## Development
|
296
|
-
|
297
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
298
|
-
|
299
|
-
```bash
|
300
|
-
# Install dependencies
|
301
|
-
$ bundle install
|
302
|
-
|
303
|
-
# Run tests
|
304
|
-
$ bundle exec rspec
|
305
|
-
|
306
|
-
# Run linting
|
307
|
-
$ bundle exec rubocop
|
308
|
-
|
309
|
-
# Install locally
|
310
|
-
$ bundle exec rake install
|
311
|
-
```
|
312
|
-
|
313
|
-
## Contributing
|
314
|
-
|
315
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/ether-moon/gruf-queue. This project is intended to be a safe, welcoming space for collaboration.
|
316
|
-
|
317
64
|
## License
|
318
65
|
|
319
|
-
|
66
|
+
MIT License
|
data/lib/gruf/queue/plugin.rb
CHANGED
@@ -13,17 +13,11 @@ module Gruf
|
|
13
13
|
return false if @installed
|
14
14
|
|
15
15
|
enhance_grpc_pool
|
16
|
-
configure_gruf
|
17
|
-
@installed = true
|
18
|
-
true
|
19
|
-
rescue StandardError
|
20
|
-
false
|
21
|
-
end
|
22
16
|
|
23
|
-
|
24
|
-
!!@installed
|
17
|
+
@installed = true
|
25
18
|
end
|
26
19
|
|
20
|
+
# Reset installation state for testing
|
27
21
|
def reset!
|
28
22
|
@installed = false
|
29
23
|
end
|
@@ -35,29 +29,6 @@ module Gruf
|
|
35
29
|
|
36
30
|
::GRPC::Pool.prepend(Gruf::Queue::PoolEnhancements)
|
37
31
|
end
|
38
|
-
|
39
|
-
def configure_gruf
|
40
|
-
Gruf.configure do |config|
|
41
|
-
# Set default server options compatible with QueuedRpcServer
|
42
|
-
config.rpc_server_options = {
|
43
|
-
pool_size: QueuedRpcServer::DEFAULT_POOL_SIZE,
|
44
|
-
max_waiting_requests: QueuedRpcServer::DEFAULT_MAX_WAITING_REQUESTS,
|
45
|
-
poll_period: QueuedRpcServer::DEFAULT_POLL_PERIOD,
|
46
|
-
pool_keep_alive: PoolEnhancements::DEFAULT_KEEP_ALIVE,
|
47
|
-
}
|
48
|
-
|
49
|
-
if defined?(ActiveRecord)
|
50
|
-
require 'gruf/interceptors/active_record/connection_reset'
|
51
|
-
# Configure interceptors at the global level
|
52
|
-
if Gruf.respond_to?(:interceptors)
|
53
|
-
Gruf.interceptors.use(
|
54
|
-
Gruf::Interceptors::ActiveRecord::ConnectionReset,
|
55
|
-
target_classes: [ActiveRecord::Base],
|
56
|
-
)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
32
|
end
|
62
33
|
end
|
63
34
|
end
|
@@ -2,27 +2,62 @@
|
|
2
2
|
|
3
3
|
module Gruf
|
4
4
|
module Queue
|
5
|
-
# Enhanced
|
6
|
-
#
|
7
|
-
# Provides essential queue management features while preserving the original
|
8
|
-
# GRPC::Pool class structure and inheritance hierarchy.
|
9
|
-
#
|
10
|
-
# @example Usage
|
11
|
-
# GRPC::Pool.prepend(Gruf::Queue::PoolEnhancements)
|
5
|
+
# Enhanced GRPC::Pool with job count based capacity management
|
12
6
|
module PoolEnhancements
|
13
|
-
|
14
|
-
DEFAULT_KEEP_ALIVE = 600
|
7
|
+
DEFAULT_MAX_WAITING_REQUESTS = 60
|
15
8
|
|
16
|
-
def
|
17
|
-
|
9
|
+
def initialize(...)
|
10
|
+
super
|
11
|
+
@max_waiting_requests = ENV.fetch('GRUF_MAX_WAITING_REQUESTS', DEFAULT_MAX_WAITING_REQUESTS).to_i
|
18
12
|
end
|
19
13
|
|
20
14
|
def schedule(*args, &blk)
|
21
|
-
return
|
15
|
+
return if blk.nil?
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
@stop_mutex.synchronize do
|
18
|
+
if @stopped
|
19
|
+
GRPC.logger.warn('did not schedule job, already stopped')
|
20
|
+
return
|
21
|
+
end
|
22
|
+
|
23
|
+
@jobs << [blk, args]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def start
|
28
|
+
@stop_mutex.synchronize do
|
29
|
+
raise 'already stopped' if @stopped
|
30
|
+
end
|
31
|
+
until @workers.size == @size.to_i
|
32
|
+
next_thread = Thread.new do
|
33
|
+
catch(:exit) do # allows { throw :exit } to kill a thread
|
34
|
+
loop_execute_jobs
|
35
|
+
end
|
36
|
+
remove_current_thread
|
37
|
+
end
|
38
|
+
@workers << next_thread
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def loop_execute_jobs
|
43
|
+
loop do
|
44
|
+
begin
|
45
|
+
blk, args = @jobs.pop
|
46
|
+
blk.call(*args)
|
47
|
+
rescue StandardError, GRPC::Core::CallError => e
|
48
|
+
GRPC.logger.warn('Error in worker thread')
|
49
|
+
GRPC.logger.warn(e)
|
50
|
+
end
|
51
|
+
@stop_mutex.synchronize do
|
52
|
+
return if @stopped
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def ready_for_work?
|
58
|
+
return false if @stopped
|
59
|
+
|
60
|
+
jobs_waiting < @max_waiting_requests
|
26
61
|
end
|
27
62
|
end
|
28
63
|
end
|
data/lib/gruf/queue/version.rb
CHANGED
data/lib/gruf-queue.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gruf-queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ether Moon
|
@@ -137,7 +137,6 @@ files:
|
|
137
137
|
- lib/gruf-queue.rb
|
138
138
|
- lib/gruf/queue/plugin.rb
|
139
139
|
- lib/gruf/queue/pool_enhancements.rb
|
140
|
-
- lib/gruf/queue/queued_rpc_server.rb
|
141
140
|
- lib/gruf/queue/version.rb
|
142
141
|
homepage: https://github.com/ether-moon/gruf-queue
|
143
142
|
licenses:
|
@@ -1,105 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'pool_enhancements'
|
4
|
-
|
5
|
-
module Gruf
|
6
|
-
module Queue
|
7
|
-
# Enhanced gRPC server with queue-based request handling and resource exhaustion protection.
|
8
|
-
#
|
9
|
-
# Extends GRPC::RpcServer to provide intelligent thread pool management with
|
10
|
-
# automatic RESOURCE_EXHAUSTED responses when the server is overloaded.
|
11
|
-
#
|
12
|
-
# @example Basic usage
|
13
|
-
# server = Gruf::Queue::QueuedRpcServer.new(pool_size: 20)
|
14
|
-
# server.handle(MyService)
|
15
|
-
# server.run_till_terminated
|
16
|
-
class QueuedRpcServer < ::GRPC::RpcServer
|
17
|
-
# Default pool size for the thread pool
|
18
|
-
DEFAULT_POOL_SIZE = 30
|
19
|
-
|
20
|
-
# Default maximum number of waiting requests
|
21
|
-
DEFAULT_MAX_WAITING_REQUESTS = 60
|
22
|
-
|
23
|
-
# Default poll period for the server
|
24
|
-
DEFAULT_POLL_PERIOD = 1
|
25
|
-
|
26
|
-
# No-op procedure for gRPC active call creation
|
27
|
-
NOOP_PROC = proc { |x| x }.freeze
|
28
|
-
private_constant :NOOP_PROC
|
29
|
-
|
30
|
-
# Error message for resource exhaustion
|
31
|
-
RESOURCE_EXHAUSTED_MESSAGE = 'No free threads in thread pool'
|
32
|
-
|
33
|
-
def initialize(pool_size: DEFAULT_POOL_SIZE,
|
34
|
-
max_waiting_requests: DEFAULT_MAX_WAITING_REQUESTS,
|
35
|
-
pool_keep_alive: PoolEnhancements::DEFAULT_KEEP_ALIVE,
|
36
|
-
poll_period: DEFAULT_POLL_PERIOD,
|
37
|
-
**args)
|
38
|
-
super
|
39
|
-
|
40
|
-
@pool_size = pool_size
|
41
|
-
@max_waiting_requests = max_waiting_requests
|
42
|
-
@pool_keep_alive = pool_keep_alive
|
43
|
-
@poll_period = poll_period
|
44
|
-
end
|
45
|
-
|
46
|
-
def available?(an_rpc)
|
47
|
-
job_count = safe_job_count
|
48
|
-
return an_rpc if job_count < @max_waiting_requests
|
49
|
-
|
50
|
-
send_resource_exhausted_response(an_rpc)
|
51
|
-
false
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
def safe_job_count
|
57
|
-
return 0 unless @pool
|
58
|
-
|
59
|
-
@pool.jobs_waiting
|
60
|
-
rescue StandardError => e
|
61
|
-
Gruf.logger.warn("Failed to get job count: #{e.message}") if defined?(Gruf) && Gruf.respond_to?(:logger)
|
62
|
-
@max_waiting_requests
|
63
|
-
end
|
64
|
-
|
65
|
-
def send_resource_exhausted_response(an_rpc)
|
66
|
-
Gruf.logger.warn('no free worker threads currently') if defined?(Gruf) && Gruf.respond_to?(:logger)
|
67
|
-
|
68
|
-
begin
|
69
|
-
if an_rpc.respond_to?(:send_status)
|
70
|
-
an_rpc.send_status(
|
71
|
-
::GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED,
|
72
|
-
RESOURCE_EXHAUSTED_MESSAGE,
|
73
|
-
{},
|
74
|
-
)
|
75
|
-
return
|
76
|
-
end
|
77
|
-
|
78
|
-
active_call = ::GRPC::ActiveCall.new(
|
79
|
-
an_rpc.call,
|
80
|
-
NOOP_PROC,
|
81
|
-
NOOP_PROC,
|
82
|
-
an_rpc.deadline,
|
83
|
-
metadata_received: true,
|
84
|
-
started: false,
|
85
|
-
)
|
86
|
-
|
87
|
-
active_call.send_status(
|
88
|
-
::GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED,
|
89
|
-
RESOURCE_EXHAUSTED_MESSAGE,
|
90
|
-
)
|
91
|
-
rescue TypeError => e
|
92
|
-
raise unless e.message.include?('Core::Call')
|
93
|
-
|
94
|
-
warn "RESOURCE_EXHAUSTED: #{RESOURCE_EXHAUSTED_MESSAGE}" if defined?(RSpec)
|
95
|
-
end
|
96
|
-
rescue StandardError => e
|
97
|
-
if defined?(Gruf) && Gruf.respond_to?(:logger)
|
98
|
-
Gruf.logger.error("Failed to send resource exhausted response: #{e.message}")
|
99
|
-
else
|
100
|
-
warn "Failed to send resource exhausted response: #{e.message}"
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|