gruf-queue 0.1.3 → 0.1.4

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: 16b3387c40ed0f1175b3b6ee1127a15825788f17ae1ccd829385462d58c49913
4
- data.tar.gz: d6694faf0538b2f6085a318433389ccf073c2f93876e78f1fb770bc061f6273d
3
+ metadata.gz: cc1f1ae6ceeae244f3240289293550ac50d4817c146bdd0ae08c0ba4aac3d6b9
4
+ data.tar.gz: 510388ca36e935b14f7d727acc5f74a46969018b87cce11bfedf6a0a5723ba1b
5
5
  SHA512:
6
- metadata.gz: b69971940855b247545a1488522d7b0abb9833c9d2eea4b4f8399e664fe9910f3a044a07e967e4cb3f8da91188b758d6ebdf5fd3097b477a51cf707d3275e13e
7
- data.tar.gz: bc4d7414d8fdb9223721928facbb5b17db77bd753904a1419af72729ebaae22dcbcaf60244a53fca058ece126d9b1c16a284a3eb1ad0b7fbb92a1352a85d7591
6
+ metadata.gz: 456159e1ad576446eb160c81a5fd285a8892c005cafc89c3607fcf8371da9e65e98481f14d44760f798cba36abda25e55c80199779e5e68339b6836faf5a32b3
7
+ data.tar.gz: 2c4e4d90a9667a860de5fb243d0d1c8d4fa8cae4499307c5f65f9e2b0dc7827973662ea24b03c1d1b000807a880645c272b93a52c8b4cd373c01e96c765028a7
data/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ 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.4]
9
+
10
+ ### Changed
11
+ - Renamed `AvaiallableQueue` to `AlwaysReadyQueue` (fixed typo)
12
+ - Improved null object pattern implementation for worker queue management
13
+ - Simplified README.md to focus on core functionality
14
+
15
+ ### Added
16
+ - Graceful shutdown support with `@stopped` flag checking
17
+ - Enhanced test coverage for environment variable configuration
18
+ - Comprehensive test suite for graceful shutdown behavior
19
+ - Thread-safe shutdown handling for GRPC::Pool enhancements
20
+
21
+ ### Fixed
22
+ - Fixed graceful shutdown issues that prevented proper signal handling
23
+ - Improved AlwaysReadyQueue method implementations for better compatibility
24
+ - Enhanced error handling in ready_for_work? method
25
+
26
+ ### Improved
27
+ - More accurate documentation reflecting actual gem purpose
28
+ - Better test organization with focused integration tests
29
+ - Cleaner code structure with proper separation of concerns
30
+
8
31
  ## [0.1.3]
9
32
 
10
33
  ### Changed
data/README.md CHANGED
@@ -3,287 +3,55 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/gruf-queue.svg)](https://badge.fury.io/rb/gruf-queue)
4
4
  [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2.0-red.svg)](https://ruby-lang.org)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
- [![Build Status](https://github.com/your-org/gruf-queue/workflows/CI/badge.svg)](https://github.com/your-org/gruf-queue/actions)
7
6
 
8
- A high-performance, queue-based gRPC server extension for Gruf that provides enhanced thread pool management, intelligent resource handling, and seamless database connection management.
7
+ Simple GRPC::Pool enhancement that replaces worker-based capacity checking with job count based threshold.
9
8
 
10
- ## Features
9
+ ## What it does
11
10
 
12
- 🚀 **Enhanced Performance**
13
- - Queue-based request processing with intelligent job scheduling
14
- - Resource exhaustion protection with automatic `RESOURCE_EXHAUSTED` responses
15
- - Structured logging with comprehensive error handling and debugging support
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
+ - Replaces `GRPC::Pool` worker queue management with a simple job count check
12
+ - Uses `jobs_waiting < threshold` instead of tracking actual worker availability
13
+ - Configurable threshold via `GRUF_MAX_WAITING_REQUESTS` (default: 60)
14
+ - Supports graceful shutdown
31
15
 
32
16
  ## Installation
33
17
 
34
- Add this line to your application's Gemfile:
35
-
36
18
  ```ruby
37
19
  gem 'gruf-queue'
38
20
  ```
39
21
 
40
- And then execute:
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:
22
+ ## Usage
51
23
 
52
24
  ```ruby
53
25
  require 'gruf-queue'
54
26
 
55
- # That's it! Everything is automatically configured.
56
- # Start your Gruf server as usual:
27
+ # Optional: Set threshold (default is 60)
28
+ ENV['GRUF_MAX_WAITING_REQUESTS'] = '100'
29
+
30
+ # Start server normally
57
31
  Gruf::Server.new(
58
32
  hostname: '0.0.0.0:9001',
59
33
  services: [YourService]
60
34
  ).start
61
35
  ```
62
36
 
63
- When you require `gruf-queue`, it automatically:
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:
37
+ ## Configuration
72
38
 
73
39
  ```ruby
74
40
  # Disable auto-installation
75
41
  ENV['GRUF_QUEUE_NO_AUTO_INSTALL'] = 'true'
76
42
 
77
- require 'gruf-queue'
78
-
79
43
  # Manual installation
80
44
  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
45
  ```
110
46
 
111
- ### Custom Server Configuration
112
-
113
- For custom server instances with specific options:
47
+ ## How it works
114
48
 
115
49
  ```ruby
116
- # Create custom server instances directly
117
- server = Gruf::Queue::QueuedRpcServer.new(
118
- pool_size: 15,
119
- max_waiting_requests: 30
120
- )
50
+ # Before: GRPC::Pool tracks actual worker availability
51
+ pool.ready_for_work? # Complex worker state checking
121
52
 
122
- Gruf::Server.new(
123
- hostname: '0.0.0.0:9001',
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
53
+ # After: Simple job count threshold
54
+ pool.ready_for_work? # jobs_waiting < max_waiting_requests
287
55
  ```
288
56
 
289
57
  ## Requirements
@@ -292,28 +60,6 @@ end
292
60
  - Gruf >= 2.21.0
293
61
  - gRPC >= 1.0
294
62
 
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
63
  ## License
318
64
 
319
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
65
+ MIT License
@@ -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
- def installed?
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,34 @@
2
2
 
3
3
  module Gruf
4
4
  module Queue
5
- # Enhanced functionality for GRPC::Pool without intrusive modifications.
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
+ # Null object implementation for worker queue management.
6
+ # Provides clean interface without complex worker state tracking.
7
+ class AlwaysReadyQueue
8
+ def empty? = false
9
+
10
+ def <<(*) = self
11
+
12
+ def push(*) = self
13
+
14
+ def pop = ::Queue.new
15
+
16
+ def size = 1
17
+ end
18
+
19
+ # Enhanced GRPC::Pool with job count based capacity management
12
20
  module PoolEnhancements
13
- # Default keep-alive time for worker threads (in seconds)
14
- DEFAULT_KEEP_ALIVE = 600
21
+ DEFAULT_MAX_WAITING_REQUESTS = 60
15
22
 
16
- def jobs_waiting
17
- @jobs&.size || 0
23
+ def initialize(...)
24
+ super
25
+ @ready_workers = AlwaysReadyQueue.new
26
+ @max_waiting_requests = ENV.fetch('GRUF_MAX_WAITING_REQUESTS', DEFAULT_MAX_WAITING_REQUESTS).to_i
18
27
  end
19
28
 
20
- def schedule(*args, &blk)
21
- return false if blk.nil?
29
+ def ready_for_work?
30
+ return false if @stopped
22
31
 
23
- super
24
- rescue StandardError
25
- false
32
+ jobs_waiting < @max_waiting_requests
26
33
  end
27
34
  end
28
35
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gruf
4
4
  module Queue
5
- VERSION = '0.1.3'
5
+ VERSION = '0.1.4'
6
6
  end
7
7
  end
data/lib/gruf-queue.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require 'gruf'
4
4
  require 'gruf/queue/version'
5
5
  require 'gruf/queue/pool_enhancements'
6
- require 'gruf/queue/queued_rpc_server'
7
6
  require 'gruf/queue/plugin'
8
7
 
9
8
  # Auto-install plugin when required unless explicitly disabled
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.3
4
+ version: 0.1.4
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