gruf-queue 0.1.2 → 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: 93e75c950c181dceecb26c7bb037014232fb190602c85800702c6b8bf29329c7
4
- data.tar.gz: a81edb2a6d96d1c514c850b20ed0422eeed701e296497149df0e57597cc1875e
3
+ metadata.gz: cc1f1ae6ceeae244f3240289293550ac50d4817c146bdd0ae08c0ba4aac3d6b9
4
+ data.tar.gz: 510388ca36e935b14f7d727acc5f74a46969018b87cce11bfedf6a0a5723ba1b
5
5
  SHA512:
6
- metadata.gz: 6d6f81af6800c4f8aca41a5400609e147b011b24ae9c6864b3caede6492889e7b9b8e9912274828f9bb35f57793b93355a50b918527d9383b3a719ad7346e509
7
- data.tar.gz: e4efc9c13fe4c3867f65312175f65896e6dcd1f65c52ecbec99727ccb2a62e3c4f527ba7afc941ff3ec53faa9c212175b28bf286f753eee6190e47119eb41cc4
6
+ metadata.gz: 456159e1ad576446eb160c81a5fd285a8892c005cafc89c3607fcf8371da9e65e98481f14d44760f798cba36abda25e55c80199779e5e68339b6836faf5a32b3
7
+ data.tar.gz: 2c4e4d90a9667a860de5fb243d0d1c8d4fa8cae4499307c5f65f9e2b0dc7827973662ea24b03c1d1b000807a880645c272b93a52c8b4cd373c01e96c765028a7
data/CHANGELOG.md CHANGED
@@ -5,6 +5,56 @@ 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
+
31
+ ## [0.1.3]
32
+
33
+ ### Changed
34
+ - **BREAKING**: Removed ServerFactory - use `QueuedRpcServer.new()` directly instead
35
+ - **BREAKING**: Removed Configuration wrapper - use Gruf's native configuration
36
+ - **BREAKING**: Removed custom ConnectionReset interceptor - use gruf's built-in `Gruf::Interceptors::ActiveRecord::ConnectionReset`
37
+ - Simplified plugin.rb from 104 to 60 lines by removing complex validation logic
38
+ - Merged pool.rb into pool_enhancements.rb for better organization
39
+
40
+ ### Removed
41
+ - `Gruf::Queue::ServerFactory` - unnecessary abstraction layer
42
+ - `Gruf::Queue::Configuration` - wrapper around Gruf's native config
43
+ - `Gruf::Queue::Interceptors::ConnectionReset` - replaced with gruf's built-in
44
+ - `Gruf::Queue::Pool` class - functionality moved to PoolEnhancements module
45
+ - Obsolete test files and redundant test cases
46
+
47
+ ### Improved
48
+ - **730 lines of code removed** - dramatically simplified codebase
49
+ - More direct and intuitive API without unnecessary abstractions
50
+ - Better integration with gruf's native features
51
+ - Cleaner test suite with eliminated logging noise
52
+ - Improved maintainability and reduced complexity
53
+
54
+ ### Fixed
55
+ - Suppressed Gruf logger output during tests for cleaner test runs
56
+ - Updated all documentation and examples to reflect simplified API
57
+
8
58
  ## [0.1.2]
9
59
 
10
60
  ### Changed
data/README.md CHANGED
@@ -3,291 +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::Queue::Interceptors::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
- ### Using Server Factory
112
-
113
- For advanced server creation:
47
+ ## How it works
114
48
 
115
49
  ```ruby
116
- # Create custom server instances
117
- server = Gruf::Queue::ServerFactory.create_server(
118
- pool_size: 15,
119
- max_waiting_requests: 30,
120
- server: Gruf::Queue::QueuedRpcServer,
121
- interceptors: [
122
- Gruf::Queue::Interceptors::ConnectionReset
123
- ]
124
- )
50
+ # Before: GRPC::Pool tracks actual worker availability
51
+ pool.ready_for_work? # Complex worker state checking
125
52
 
126
- Gruf::Server.new(
127
- hostname: '0.0.0.0:9001',
128
- services: [YourService],
129
- rpc_server: server
130
- ).start
131
- ```
132
-
133
- ### ActiveRecord Connection Management
134
-
135
- The `ConnectionReset` interceptor automatically manages database connections:
136
-
137
- ```ruby
138
- # Automatically registered when ActiveRecord is detected
139
- # Customizable with additional target classes:
140
- Gruf.configure do |config|
141
- config.interceptors.use(
142
- Gruf::Queue::Interceptors::ConnectionReset,
143
- enabled: true,
144
- target_classes: [ActiveRecord::Base, CustomConnectionClass]
145
- )
146
- end
147
- ```
148
-
149
- ## Architecture
150
-
151
- ### Core Components
152
-
153
- #### `Gruf::Queue::QueuedRpcServer`
154
- Enhanced gRPC server with intelligent resource management:
155
- - Monitors thread pool capacity
156
- - Automatically sends `RESOURCE_EXHAUSTED` when overloaded
157
- - Provides structured error handling
158
-
159
- #### `Gruf::Queue::Pool`
160
- Advanced thread pool implementation:
161
- - Extends `GRPC::Pool` with enhanced job scheduling
162
- - Structured logging with thread identification
163
- - Comprehensive error isolation and recovery
164
-
165
- #### `Gruf::Queue::Interceptors::ConnectionReset`
166
- Smart database connection management:
167
- - Automatically resets ActiveRecord connections after each request
168
- - Validates connection handlers before attempting reset
169
- - Graceful error handling to prevent request failures
170
-
171
- #### `Gruf::Queue::Plugin`
172
- Plugin management system:
173
- - Idempotent installation process
174
- - Comprehensive error handling during setup
175
- - Validation of dependencies and requirements
176
-
177
- ### Plugin Lifecycle
178
-
179
- ```ruby
180
- # Plugin installation process
181
- Gruf::Queue::Plugin.install! # => true (success) or false (failure)
182
-
183
- # Check installation status
184
- Gruf::Queue::Plugin.installed? # => true/false
185
-
186
- # Reset for testing (test environments only)
187
- Gruf::Queue::Plugin.reset!
188
- ```
189
-
190
- ## Advanced Usage
191
-
192
- ### Custom Server Implementation
193
-
194
- ```ruby
195
- # Create a custom server class
196
- class MyCustomServer < Gruf::Queue::QueuedRpcServer
197
- def initialize(*)
198
- super
199
- # Custom initialization logic
200
- end
201
-
202
- private
203
-
204
- def handle_custom_logic
205
- # Your custom server logic
206
- end
207
- end
208
-
209
- # Use with gruf-queue
210
- Gruf.configure do |config|
211
- config.rpc_server = MyCustomServer
212
- end
213
- ```
214
-
215
- ### Monitoring and Observability
216
-
217
- ```ruby
218
- # Structured logging is automatically enabled
219
- # Logs include contextual information:
220
- # - Thread IDs for debugging
221
- # - Error details with full context
222
- # - Performance metrics
223
- # - Resource utilization info
224
-
225
- # Example log output:
226
- # [INFO] Starting thread pool target_size=10
227
- # [DEBUG] Worker thread started thread_id=47185656920560
228
- # [WARN] Job execution failed error=SomeError error_class=StandardError
229
- ```
230
-
231
- ### Environment-specific Configuration
232
-
233
- ```ruby
234
- # Production settings
235
- if Rails.env.production?
236
- Gruf.configure do |config|
237
- config.rpc_server_options = {
238
- pool_size: 20,
239
- max_waiting_requests: 50,
240
- pool_keep_alive: 600
241
- }
242
- end
243
- end
244
-
245
- # Development settings
246
- if Rails.env.development?
247
- Gruf.configure do |config|
248
- config.rpc_server_options = {
249
- pool_size: 5,
250
- max_waiting_requests: 10,
251
- pool_keep_alive: 60
252
- }
253
- end
254
- end
255
- ```
256
-
257
- ## Performance Considerations
258
-
259
- - **Thread Pool Sizing**: Set `pool_size` based on your server's CPU cores and expected load
260
- - **Queue Capacity**: Configure `max_waiting_requests` to handle traffic spikes
261
- - **Connection Management**: The `ConnectionReset` interceptor prevents connection leaks in threaded environments
262
- - **Resource Monitoring**: Use structured logs to monitor resource utilization
263
-
264
- ## Troubleshooting
265
-
266
- ### Common Issues
267
-
268
- **Server not starting with custom configuration:**
269
- ```ruby
270
- # Ensure plugin is installed before configuration
271
- Gruf::Queue::Plugin.install!
272
- # Then configure...
273
- ```
274
-
275
- **ActiveRecord connection issues:**
276
- ```ruby
277
- # Verify ActiveRecord is loaded before gruf-queue
278
- require 'active_record'
279
- require 'gruf-queue'
280
- ```
281
-
282
- **High memory usage:**
283
- ```ruby
284
- # Adjust pool size and keep-alive settings
285
- Gruf.configure do |config|
286
- config.rpc_server_options = {
287
- pool_size: 8, # Reduce if memory constrained
288
- pool_keep_alive: 60 # Shorter keep-alive
289
- }
290
- end
53
+ # After: Simple job count threshold
54
+ pool.ready_for_work? # jobs_waiting < max_waiting_requests
291
55
  ```
292
56
 
293
57
  ## Requirements
@@ -296,28 +60,6 @@ end
296
60
  - Gruf >= 2.21.0
297
61
  - gRPC >= 1.0
298
62
 
299
- ## Development
300
-
301
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
302
-
303
- ```bash
304
- # Install dependencies
305
- $ bundle install
306
-
307
- # Run tests
308
- $ bundle exec rspec
309
-
310
- # Run linting
311
- $ bundle exec rubocop
312
-
313
- # Install locally
314
- $ bundle exec rake install
315
- ```
316
-
317
- ## Contributing
318
-
319
- 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.
320
-
321
63
  ## License
322
64
 
323
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
65
+ MIT License
@@ -6,92 +6,24 @@ module Gruf
6
6
  module Queue
7
7
  # Plugin management for gruf-queue integration.
8
8
  #
9
- # Provides idempotent installation and configuration of queue-specific settings
10
- # including RPC server setup and interceptor registration.
11
- #
12
- # @example Install plugin
13
- # Gruf::Queue::Plugin.install! # => true
14
- #
15
- # @example Check installation status
16
- # Gruf::Queue::Plugin.installed? # => true
9
+ # Provides simple installation and configuration of queue-specific settings.
17
10
  class Plugin
18
11
  class << self
19
12
  def install!
20
13
  return false if @installed
21
14
 
22
- begin
23
- Configuration.configure unless Configuration.configured?
24
- enhance_grpc_pool
25
- Gruf.configure do |config|
26
- configure_rpc_server(config)
27
- configure_interceptors(config)
28
- end
15
+ enhance_grpc_pool
29
16
 
30
- @installed = true
31
- true
32
- rescue StandardError
33
- false
34
- end
35
- end
36
-
37
- def installed?
38
- !!@installed
17
+ @installed = true
39
18
  end
40
19
 
20
+ # Reset installation state for testing
41
21
  def reset!
42
22
  @installed = false
43
- Configuration.reset! if Configuration.respond_to?(:reset!)
44
23
  end
45
24
 
46
25
  private
47
26
 
48
- def configure_rpc_server(config)
49
- config.rpc_server = QueuedRpcServer
50
- end
51
-
52
- def configure_interceptors(config)
53
- return unless defined?(ActiveRecord)
54
-
55
- validate_active_record_availability
56
- ensure_interceptors_registry_available(config)
57
-
58
- config.interceptors.use(
59
- Interceptors::ConnectionReset,
60
- enabled: true,
61
- target_classes: [ActiveRecord::Base],
62
- )
63
- end
64
-
65
- def validate_active_record_availability
66
- return if ActiveRecord::Base.respond_to?(:connection_handler)
67
-
68
- raise StandardError, 'ActiveRecord::Base does not support connection_handler'
69
- end
70
-
71
- def ensure_interceptors_registry_available(config)
72
- return if config.respond_to?(:interceptors) && !config.interceptors.nil?
73
-
74
- begin
75
- require 'gruf/interceptors/registry'
76
- rescue LoadError => e
77
- raise StandardError, "Failed to load Gruf interceptors registry: #{e.message}"
78
- end
79
-
80
- if config.respond_to?(:interceptors=)
81
- config.interceptors = ::Gruf::Interceptors::Registry.new
82
- elsif config.respond_to?(:define_singleton_method)
83
- registry = ::Gruf::Interceptors::Registry.new
84
- config.define_singleton_method(:interceptors) { registry }
85
- config.define_singleton_method(:interceptors=) { |val| registry = val }
86
- else
87
- raise StandardError, 'Cannot initialize interceptors registry: configuration object lacks required methods'
88
- end
89
-
90
- return if config.respond_to?(:interceptors) && config.interceptors.respond_to?(:use)
91
-
92
- raise StandardError, 'Interceptors registry initialization failed: missing use method'
93
- end
94
-
95
27
  def enhance_grpc_pool
96
28
  return if ::GRPC::Pool.included_modules.include?(Gruf::Queue::PoolEnhancements)
97
29
 
@@ -2,24 +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
- def jobs_waiting
14
- @jobs&.size || 0
21
+ DEFAULT_MAX_WAITING_REQUESTS = 60
22
+
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
15
27
  end
16
28
 
17
- def schedule(*args, &blk)
18
- return false if blk.nil?
29
+ def ready_for_work?
30
+ return false if @stopped
19
31
 
20
- super
21
- rescue StandardError
22
- false
32
+ jobs_waiting < @max_waiting_requests
23
33
  end
24
34
  end
25
35
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gruf
4
4
  module Queue
5
- VERSION = '0.1.2'
5
+ VERSION = '0.1.4'
6
6
  end
7
7
  end
data/lib/gruf-queue.rb CHANGED
@@ -2,11 +2,7 @@
2
2
 
3
3
  require 'gruf'
4
4
  require 'gruf/queue/version'
5
- require 'gruf/queue/pool'
6
- require 'gruf/queue/queued_rpc_server'
7
- require 'gruf/queue/configuration'
8
- require 'gruf/queue/server_factory'
9
- require 'gruf/queue/interceptors/connection_reset'
5
+ require 'gruf/queue/pool_enhancements'
10
6
  require 'gruf/queue/plugin'
11
7
 
12
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.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ether Moon
@@ -135,13 +135,8 @@ files:
135
135
  - README.md
136
136
  - gruf-queue.gemspec
137
137
  - lib/gruf-queue.rb
138
- - lib/gruf/queue/configuration.rb
139
- - lib/gruf/queue/interceptors/connection_reset.rb
140
138
  - lib/gruf/queue/plugin.rb
141
- - lib/gruf/queue/pool.rb
142
139
  - lib/gruf/queue/pool_enhancements.rb
143
- - lib/gruf/queue/queued_rpc_server.rb
144
- - lib/gruf/queue/server_factory.rb
145
140
  - lib/gruf/queue/version.rb
146
141
  homepage: https://github.com/ether-moon/gruf-queue
147
142
  licenses:
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gruf
4
- module Queue
5
- # Configuration management for gruf-queue integration.
6
- #
7
- # Enhances Gruf configuration with queue-specific settings and provides
8
- # idempotent configuration setup with proper error handling.
9
- #
10
- # @example Configure Gruf
11
- # Gruf::Queue::Configuration.configure
12
- module Configuration
13
- module_function
14
-
15
- def configure
16
- return if configured?
17
-
18
- begin
19
- Gruf.configure do |config|
20
- enhance_gruf_configuration(config)
21
- end
22
-
23
- @configured = true
24
- rescue StandardError => e
25
- if defined?(Gruf) && Gruf.respond_to?(:logger)
26
- Gruf.logger.error("Failed to enhance Gruf configuration: #{e.message}")
27
- end
28
- raise
29
- end
30
- end
31
-
32
- def configured?
33
- !!@configured
34
- end
35
-
36
- def reset!
37
- raise 'reset! can only be called in test environments' unless test_environment?
38
-
39
- @configured = false
40
-
41
- return unless defined?(Gruf) && Gruf.respond_to?(:configuration)
42
-
43
- config = Gruf.configuration
44
- config.rpc_server = nil if config.respond_to?(:rpc_server=)
45
- end
46
-
47
- def enhance_gruf_configuration(config)
48
- config.define_singleton_method(:rpc_server) { @rpc_server }
49
- config.define_singleton_method(:rpc_server=) { |val| @rpc_server = val }
50
- config.rpc_server = nil unless config.respond_to?(:rpc_server)
51
-
52
- return if Gruf.respond_to?(:configuration)
53
-
54
- Gruf.define_singleton_method(:configuration) { config }
55
- end
56
-
57
- def test_environment?
58
- ENV['RACK_ENV'] == 'test' ||
59
- ENV['RAILS_ENV'] == 'test' ||
60
- defined?(RSpec)
61
- end
62
- end
63
- end
64
- end
@@ -1,87 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gruf
4
- module Queue
5
- module Interceptors
6
- # ActiveRecord connection reset interceptor for threaded gRPC environments.
7
- #
8
- # Automatically resets database connections after each request to prevent
9
- # connection leaks and stale connections in multi-threaded environments.
10
- #
11
- # @example Basic usage with Gruf
12
- # config.interceptors.use(
13
- # Gruf::Queue::Interceptors::ConnectionReset,
14
- # target_classes: [ActiveRecord::Base]
15
- # )
16
- class ConnectionReset < ::Gruf::Interceptors::ServerInterceptor
17
- def initialize(request, call, method, options = {})
18
- @request = request
19
- @call = call
20
- @method = method
21
- @options = options || {}
22
- @options[:enabled] = true unless @options.key?(:enabled)
23
- end
24
-
25
- def call
26
- yield
27
- ensure
28
- begin
29
- reset_connections if enabled?
30
- rescue StandardError
31
- # Ignore connection reset errors silently
32
- end
33
- end
34
-
35
- private
36
-
37
- attr_reader :options
38
-
39
- def enabled?
40
- return false if options[:enabled] == false
41
- return false if target_classes.empty?
42
-
43
- true
44
- end
45
-
46
- def reset_connections
47
- validated_classes = validate_target_classes
48
- return if validated_classes.empty?
49
-
50
- validated_classes.each do |klass|
51
- reset_connection_for_class(klass)
52
- end
53
- end
54
-
55
- def target_classes
56
- return Array(@target_classes).compact if instance_variable_defined?(:@target_classes)
57
-
58
- default_classes = defined?(::ActiveRecord::Base) ? [::ActiveRecord::Base] : []
59
- classes = options.fetch(:target_classes, default_classes)
60
- Array(classes).compact
61
- end
62
-
63
- def validate_target_classes
64
- target_classes.select do |klass|
65
- valid_class?(klass)
66
- end
67
- end
68
-
69
- def valid_class?(klass)
70
- return false unless klass.is_a?(Class)
71
- return false unless klass.respond_to?(:connection_handler)
72
-
73
- true
74
- end
75
-
76
- def reset_connection_for_class(klass)
77
- handler = klass.connection_handler
78
- handler.clear_active_connections!(:all) if handler.respond_to?(:clear_active_connections!)
79
- handler.clear_reloadable_connections! if handler.respond_to?(:clear_reloadable_connections!)
80
- true
81
- rescue StandardError
82
- false
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,89 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gruf
4
- module Queue
5
- # Enhanced thread pool with structured logging and improved error handling.
6
- #
7
- # Extends GRPC::Pool to provide better job scheduling, thread safety,
8
- # and comprehensive error isolation for gRPC server request handling.
9
- #
10
- # @example Basic usage
11
- # pool = Gruf::Queue::Pool.new(10, keep_alive: 300)
12
- # pool.schedule { puts "Hello from worker thread" }
13
- # pool.start
14
- class Pool < ::GRPC::Pool
15
- # Default keep-alive time for worker threads (in seconds)
16
- DEFAULT_KEEP_ALIVE = 600
17
-
18
- def initialize(size, keep_alive: DEFAULT_KEEP_ALIVE)
19
- super
20
- end
21
-
22
- def jobs_waiting
23
- @jobs&.size || 0
24
- end
25
-
26
- def schedule(*args, &blk)
27
- return false if blk.nil?
28
-
29
- @stop_mutex.synchronize do
30
- return false if @stopped
31
-
32
- @jobs << [blk, args]
33
- true
34
- end
35
- rescue StandardError
36
- false
37
- end
38
-
39
- def start
40
- @stop_mutex.synchronize do
41
- raise 'Pool already stopped' if @stopped
42
- end
43
-
44
- target_size = @size.to_i
45
-
46
- until @workers.size == target_size
47
- next_thread = create_worker_thread
48
- @workers << next_thread if next_thread
49
- end
50
- end
51
-
52
- protected
53
-
54
- def create_worker_thread
55
- Thread.new do
56
- catch(:exit) do
57
- _loop_execute_jobs
58
- end
59
- remove_current_thread
60
- end
61
- rescue StandardError
62
- nil
63
- end
64
-
65
- def _loop_execute_jobs
66
- Thread.current.name = "gruf-queue-worker-#{Thread.current.object_id}"
67
-
68
- loop do
69
- begin
70
- blk, args = @jobs.pop
71
- execute_job_safely(blk, args)
72
- rescue ThreadError, SystemStackError, IOError
73
- # Ignore system-level errors to prevent worker thread death
74
- end
75
-
76
- @stop_mutex.synchronize do
77
- return if @stopped
78
- end
79
- end
80
- end
81
-
82
- def execute_job_safely(blk, args)
83
- blk.call(*args)
84
- rescue SystemStackError, IOError, GRPC::Core::CallError, GRPC::BadStatus, Timeout::Error
85
- # Ignore system/network/gRPC errors to prevent worker thread death
86
- end
87
- end
88
- end
89
- end
@@ -1,105 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'pool'
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: Pool::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
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gruf
4
- module Queue
5
- # Factory for creating configured gRPC servers with queue-specific options.
6
- #
7
- # Provides centralized server creation with intelligent defaults, option validation,
8
- # and fallback handling for different server types.
9
- #
10
- # @example Create basic server
11
- # server = Gruf::Queue::ServerFactory.create_server(pool_size: 20)
12
- #
13
- # @example Create with custom server class
14
- # server = Gruf::Queue::ServerFactory.create_server(
15
- # server: MyCustomServer,
16
- # pool_size: 15
17
- # )
18
- module ServerFactory
19
- module_function
20
-
21
- # Create a new gRPC server instance with the provided options.
22
- #
23
- # @param opts [Hash] Configuration options for the server
24
- # @option opts [Class] :server Custom server class to use
25
- # @option opts [Integer] :pool_size Thread pool size
26
- # @option opts [Proc] :event_listener_proc Event listener procedure
27
- # @return [GRPC::RpcServer] Configured server instance
28
- def create_server(opts = {})
29
- # Default to QueuedRpcServer if no server specified
30
- rpc_server = opts.fetch(:server, nil)
31
- rpc_server ||= (defined?(Gruf) && Gruf.respond_to?(:rpc_server) && Gruf.rpc_server) || QueuedRpcServer
32
-
33
- # Validate server class
34
- validate_server_class!(rpc_server) if opts.key?(:server)
35
-
36
- default_options = get_default_options
37
-
38
- server_options = {
39
- pool_size: opts.fetch(:pool_size, default_options[:pool_size]),
40
- max_waiting_requests: opts.fetch(:max_waiting_requests, default_options[:max_waiting_requests]),
41
- poll_period: opts.fetch(:poll_period, default_options[:poll_period]),
42
- pool_keep_alive: opts.fetch(:pool_keep_alive, default_options[:pool_keep_alive]),
43
- connect_md_proc: opts.fetch(:connect_md_proc, nil),
44
- server_args: opts.fetch(:server_args, default_options[:server_args]),
45
- }
46
-
47
- # Handle interceptors via Gruf configuration if available
48
- interceptors = opts.fetch(:interceptors, [])
49
- if interceptors.any? && defined?(Gruf) && Gruf.respond_to?(:configuration)
50
- interceptors.each do |interceptor|
51
- Gruf.configuration.interceptors.use(interceptor, enabled: true)
52
- end
53
- elsif interceptors.any?
54
- server_options[:interceptors] = interceptors
55
- end
56
-
57
- begin
58
- rpc_server.new(**server_options)
59
- rescue ArgumentError => e
60
- raise unless e.message.include?('unknown keywords')
61
-
62
- # Filter out unsupported options for basic GRPC::RpcServer
63
- filtered_options = server_options.except(:pool_size, :max_waiting_requests, :poll_period, :pool_keep_alive)
64
- rpc_server.new(**filtered_options)
65
- end
66
- end
67
-
68
- # Validate server class inherits from GRPC::RpcServer.
69
- #
70
- # @param server_class [Class] Server class to validate
71
- # @raise [ArgumentError] if not a valid GRPC server class
72
- # @return [void]
73
- def validate_server_class!(server_class)
74
- return if server_class.is_a?(Class) && server_class <= ::GRPC::RpcServer
75
-
76
- raise ArgumentError, "Server must be a subclass of GRPC::RpcServer, got #{server_class}"
77
- end
78
- module_function :validate_server_class!
79
-
80
- # Get default server options with Gruf integration fallback.
81
- #
82
- # @return [Hash] Default configuration options
83
- def get_default_options
84
- if defined?(Gruf) && Gruf.respond_to?(:rpc_server_options)
85
- Gruf.rpc_server_options
86
- else
87
- {
88
- pool_size: QueuedRpcServer::DEFAULT_POOL_SIZE,
89
- max_waiting_requests: QueuedRpcServer::DEFAULT_MAX_WAITING_REQUESTS,
90
- poll_period: QueuedRpcServer::DEFAULT_POLL_PERIOD,
91
- pool_keep_alive: Pool::DEFAULT_KEEP_ALIVE,
92
- server_args: {},
93
- }
94
- end
95
- end
96
- module_function :get_default_options
97
- end
98
- end
99
- end