gruf-queue 0.1.1 → 0.1.3
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 +58 -2
- data/README.md +8 -12
- data/lib/gruf/queue/plugin.rb +32 -126
- data/lib/gruf/queue/pool_enhancements.rb +29 -0
- data/lib/gruf/queue/queued_rpc_server.rb +9 -32
- data/lib/gruf/queue/version.rb +1 -1
- data/lib/gruf-queue.rb +1 -4
- metadata +2 -5
- data/lib/gruf/queue/configuration.rb +0 -107
- data/lib/gruf/queue/interceptors/connection_reset.rb +0 -159
- data/lib/gruf/queue/pool.rb +0 -156
- data/lib/gruf/queue/server_factory.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16b3387c40ed0f1175b3b6ee1127a15825788f17ae1ccd829385462d58c49913
|
4
|
+
data.tar.gz: d6694faf0538b2f6085a318433389ccf073c2f93876e78f1fb770bc061f6273d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b69971940855b247545a1488522d7b0abb9833c9d2eea4b4f8399e664fe9910f3a044a07e967e4cb3f8da91188b758d6ebdf5fd3097b477a51cf707d3275e13e
|
7
|
+
data.tar.gz: bc4d7414d8fdb9223721928facbb5b17db77bd753904a1419af72729ebaae22dcbcaf60244a53fca058ece126d9b1c16a284a3eb1ad0b7fbb92a1352a85d7591
|
data/CHANGELOG.md
CHANGED
@@ -5,7 +5,63 @@ 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.
|
8
|
+
## [0.1.3]
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
- **BREAKING**: Removed ServerFactory - use `QueuedRpcServer.new()` directly instead
|
12
|
+
- **BREAKING**: Removed Configuration wrapper - use Gruf's native configuration
|
13
|
+
- **BREAKING**: Removed custom ConnectionReset interceptor - use gruf's built-in `Gruf::Interceptors::ActiveRecord::ConnectionReset`
|
14
|
+
- Simplified plugin.rb from 104 to 60 lines by removing complex validation logic
|
15
|
+
- Merged pool.rb into pool_enhancements.rb for better organization
|
16
|
+
|
17
|
+
### Removed
|
18
|
+
- `Gruf::Queue::ServerFactory` - unnecessary abstraction layer
|
19
|
+
- `Gruf::Queue::Configuration` - wrapper around Gruf's native config
|
20
|
+
- `Gruf::Queue::Interceptors::ConnectionReset` - replaced with gruf's built-in
|
21
|
+
- `Gruf::Queue::Pool` class - functionality moved to PoolEnhancements module
|
22
|
+
- Obsolete test files and redundant test cases
|
23
|
+
|
24
|
+
### Improved
|
25
|
+
- **730 lines of code removed** - dramatically simplified codebase
|
26
|
+
- More direct and intuitive API without unnecessary abstractions
|
27
|
+
- Better integration with gruf's native features
|
28
|
+
- Cleaner test suite with eliminated logging noise
|
29
|
+
- Improved maintainability and reduced complexity
|
30
|
+
|
31
|
+
### Fixed
|
32
|
+
- Suppressed Gruf logger output during tests for cleaner test runs
|
33
|
+
- Updated all documentation and examples to reflect simplified API
|
34
|
+
|
35
|
+
## [0.1.2]
|
36
|
+
|
37
|
+
### Changed
|
38
|
+
- **BREAKING**: Simplified logging system by removing custom log helpers
|
39
|
+
- Removed all custom logging methods (log_structured, log_debug, log_error)
|
40
|
+
- Simplified error handling to use Gruf.logger directly only where critical
|
41
|
+
- Eliminated redundant logging as it's not the gem's responsibility
|
42
|
+
- Removed excessive YARD documentation and inline comments for better readability
|
43
|
+
- Replaced const replacement with safer module prepend pattern
|
44
|
+
- Enhanced GRPC::Pool using PoolEnhancements module without class replacement
|
45
|
+
- Improved error handling and recovery mechanisms
|
46
|
+
- Updated test suite to handle new module prepend architecture
|
47
|
+
|
48
|
+
### Improved
|
49
|
+
- Code is now more readable with essential documentation only
|
50
|
+
- Reduced complexity following single responsibility principle
|
51
|
+
- Applications handle their own logging strategy
|
52
|
+
- Fixed all rubocop style issues
|
53
|
+
|
54
|
+
### Fixed
|
55
|
+
- **CRITICAL**: Fixed parameter passing to parent GRPC::RpcServer class
|
56
|
+
- Fixed Rails initialization timing issues with auto-install
|
57
|
+
- Fixed thread pool management with proper parameter inheritance
|
58
|
+
|
59
|
+
### Added
|
60
|
+
- PoolEnhancements module for clean GRPC::Pool extension
|
61
|
+
- Improved thread safety and pool management
|
62
|
+
- Better error isolation in worker threads
|
63
|
+
|
64
|
+
## [0.1.0]
|
9
65
|
|
10
66
|
### Added
|
11
67
|
- Initial release of gruf-queue
|
@@ -28,4 +84,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
28
84
|
- **Observability**: Structured logging with metadata for monitoring and debugging
|
29
85
|
- **Customization**: Flexible configuration options for different deployment scenarios
|
30
86
|
|
31
|
-
[0.1.0]: https://github.com/ether-moon/gruf-queue/releases/tag/v0.1.0
|
87
|
+
[0.1.0]: https://github.com/ether-moon/gruf-queue/releases/tag/v0.1.0
|
data/README.md
CHANGED
@@ -84,7 +84,7 @@ Gruf.configure do |config|
|
|
84
84
|
config.rpc_server = Gruf::Queue::QueuedRpcServer
|
85
85
|
|
86
86
|
config.interceptors.use(
|
87
|
-
Gruf::
|
87
|
+
Gruf::Interceptors::ActiveRecord::ConnectionReset,
|
88
88
|
enabled: true,
|
89
89
|
target_classes: [ActiveRecord::Base]
|
90
90
|
)
|
@@ -108,19 +108,15 @@ Gruf.configure do |config|
|
|
108
108
|
end
|
109
109
|
```
|
110
110
|
|
111
|
-
###
|
111
|
+
### Custom Server Configuration
|
112
112
|
|
113
|
-
For
|
113
|
+
For custom server instances with specific options:
|
114
114
|
|
115
115
|
```ruby
|
116
|
-
# Create custom server instances
|
117
|
-
server = Gruf::Queue::
|
116
|
+
# Create custom server instances directly
|
117
|
+
server = Gruf::Queue::QueuedRpcServer.new(
|
118
118
|
pool_size: 15,
|
119
|
-
max_waiting_requests: 30
|
120
|
-
server: Gruf::Queue::QueuedRpcServer,
|
121
|
-
interceptors: [
|
122
|
-
Gruf::Queue::Interceptors::ConnectionReset
|
123
|
-
]
|
119
|
+
max_waiting_requests: 30
|
124
120
|
)
|
125
121
|
|
126
122
|
Gruf::Server.new(
|
@@ -139,7 +135,7 @@ The `ConnectionReset` interceptor automatically manages database connections:
|
|
139
135
|
# Customizable with additional target classes:
|
140
136
|
Gruf.configure do |config|
|
141
137
|
config.interceptors.use(
|
142
|
-
Gruf::
|
138
|
+
Gruf::Interceptors::ActiveRecord::ConnectionReset,
|
143
139
|
enabled: true,
|
144
140
|
target_classes: [ActiveRecord::Base, CustomConnectionClass]
|
145
141
|
)
|
@@ -162,7 +158,7 @@ Advanced thread pool implementation:
|
|
162
158
|
- Structured logging with thread identification
|
163
159
|
- Comprehensive error isolation and recovery
|
164
160
|
|
165
|
-
#### `Gruf::
|
161
|
+
#### `Gruf::Interceptors::ActiveRecord::ConnectionReset`
|
166
162
|
Smart database connection management:
|
167
163
|
- Automatically resets ActiveRecord connections after each request
|
168
164
|
- Validates connection handlers before attempting reset
|
data/lib/gruf/queue/plugin.rb
CHANGED
@@ -1,155 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'pool_enhancements'
|
4
|
+
|
3
5
|
module Gruf
|
4
6
|
module Queue
|
5
7
|
# Plugin management for gruf-queue integration.
|
6
8
|
#
|
7
|
-
# Provides
|
8
|
-
# including RPC server setup and interceptor registration.
|
9
|
-
#
|
10
|
-
# @example Install plugin
|
11
|
-
# Gruf::Queue::Plugin.install! # => true
|
12
|
-
#
|
13
|
-
# @example Check installation status
|
14
|
-
# Gruf::Queue::Plugin.installed? # => true
|
9
|
+
# Provides simple installation and configuration of queue-specific settings.
|
15
10
|
class Plugin
|
16
11
|
class << self
|
17
|
-
# Install and configure the gruf-queue plugin.
|
18
|
-
# This method is idempotent and can be called multiple times safely.
|
19
|
-
#
|
20
|
-
# @return [Boolean] true if installation completed, false if already installed
|
21
12
|
def install!
|
22
13
|
return false if @installed
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
configure_rpc_server(config)
|
31
|
-
configure_interceptors(config)
|
32
|
-
end
|
33
|
-
|
34
|
-
@installed = true
|
35
|
-
true
|
36
|
-
rescue StandardError => e
|
37
|
-
# Log the error but don't raise to avoid breaking the application startup
|
38
|
-
log_installation_error(e)
|
39
|
-
false
|
40
|
-
end
|
15
|
+
enhance_grpc_pool
|
16
|
+
configure_gruf
|
17
|
+
@installed = true
|
18
|
+
true
|
19
|
+
rescue StandardError
|
20
|
+
false
|
41
21
|
end
|
42
22
|
|
43
|
-
# Check installation status.
|
44
|
-
#
|
45
|
-
# @return [Boolean] true if installed, false otherwise
|
46
23
|
def installed?
|
47
24
|
!!@installed
|
48
25
|
end
|
49
26
|
|
50
|
-
# Reset installation state for testing.
|
51
|
-
#
|
52
|
-
# @return [void]
|
53
27
|
def reset!
|
54
28
|
@installed = false
|
55
|
-
# Also reset the configuration state
|
56
|
-
Configuration.reset! if Configuration.respond_to?(:reset!)
|
57
29
|
end
|
58
30
|
|
59
31
|
private
|
60
32
|
|
61
|
-
|
62
|
-
|
63
|
-
# @param config [Object] Gruf configuration object
|
64
|
-
# @return [void]
|
65
|
-
# @api private
|
66
|
-
def configure_rpc_server(config)
|
67
|
-
config.rpc_server = QueuedRpcServer
|
68
|
-
end
|
69
|
-
|
70
|
-
# Configure interceptors if ActiveRecord is available.
|
71
|
-
#
|
72
|
-
# @param config [Object] Gruf configuration object
|
73
|
-
# @return [void]
|
74
|
-
# @api private
|
75
|
-
def configure_interceptors(config)
|
76
|
-
return unless defined?(ActiveRecord)
|
77
|
-
|
78
|
-
validate_active_record_availability
|
79
|
-
ensure_interceptors_registry_available(config)
|
80
|
-
|
81
|
-
config.interceptors.use(
|
82
|
-
Interceptors::ConnectionReset,
|
83
|
-
enabled: true,
|
84
|
-
target_classes: [ActiveRecord::Base],
|
85
|
-
)
|
86
|
-
end
|
87
|
-
|
88
|
-
# Validate ActiveRecord connection handler availability.
|
89
|
-
#
|
90
|
-
# @raise [StandardError] if ActiveRecord doesn't support connection_handler
|
91
|
-
# @return [void]
|
92
|
-
# @api private
|
93
|
-
def validate_active_record_availability
|
94
|
-
return if ActiveRecord::Base.respond_to?(:connection_handler)
|
95
|
-
|
96
|
-
raise StandardError, 'ActiveRecord::Base does not support connection_handler'
|
97
|
-
end
|
98
|
-
|
99
|
-
# Ensure interceptors registry is available and initialized.
|
100
|
-
#
|
101
|
-
# Handles Rails initialization timing issues where gruf-queue loads
|
102
|
-
# before Gruf's configuration is fully initialized.
|
103
|
-
#
|
104
|
-
# @param config [Object] Gruf configuration object
|
105
|
-
# @return [void]
|
106
|
-
# @raise [StandardError] if interceptors registry cannot be initialized
|
107
|
-
# @api private
|
108
|
-
def ensure_interceptors_registry_available(config)
|
109
|
-
# Check if interceptors registry exists and is initialized
|
110
|
-
return if config.respond_to?(:interceptors) && !config.interceptors.nil?
|
111
|
-
|
112
|
-
# Initialize interceptors registry if missing
|
113
|
-
begin
|
114
|
-
require 'gruf/interceptors/registry'
|
115
|
-
rescue LoadError => e
|
116
|
-
raise StandardError, "Failed to load Gruf interceptors registry: #{e.message}"
|
117
|
-
end
|
118
|
-
|
119
|
-
if config.respond_to?(:interceptors=)
|
120
|
-
config.interceptors = ::Gruf::Interceptors::Registry.new
|
121
|
-
elsif config.respond_to?(:define_singleton_method)
|
122
|
-
# Fallback: define interceptors accessor methods dynamically
|
123
|
-
registry = ::Gruf::Interceptors::Registry.new
|
124
|
-
config.define_singleton_method(:interceptors) { registry }
|
125
|
-
config.define_singleton_method(:interceptors=) { |val| registry = val }
|
126
|
-
else
|
127
|
-
raise StandardError, 'Cannot initialize interceptors registry: configuration object lacks required methods'
|
128
|
-
end
|
129
|
-
|
130
|
-
# Verify initialization was successful
|
131
|
-
return if config.respond_to?(:interceptors) && config.interceptors.respond_to?(:use)
|
33
|
+
def enhance_grpc_pool
|
34
|
+
return if ::GRPC::Pool.included_modules.include?(Gruf::Queue::PoolEnhancements)
|
132
35
|
|
133
|
-
|
36
|
+
::GRPC::Pool.prepend(Gruf::Queue::PoolEnhancements)
|
134
37
|
end
|
135
38
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
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
|
153
59
|
end
|
154
60
|
end
|
155
61
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gruf
|
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)
|
12
|
+
module PoolEnhancements
|
13
|
+
# Default keep-alive time for worker threads (in seconds)
|
14
|
+
DEFAULT_KEEP_ALIVE = 600
|
15
|
+
|
16
|
+
def jobs_waiting
|
17
|
+
@jobs&.size || 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def schedule(*args, &blk)
|
21
|
+
return false if blk.nil?
|
22
|
+
|
23
|
+
super
|
24
|
+
rescue StandardError
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'pool_enhancements'
|
4
4
|
|
5
5
|
module Gruf
|
6
6
|
module Queue
|
@@ -30,32 +30,19 @@ module Gruf
|
|
30
30
|
# Error message for resource exhaustion
|
31
31
|
RESOURCE_EXHAUSTED_MESSAGE = 'No free threads in thread pool'
|
32
32
|
|
33
|
-
# Initialize the QueuedRpcServer with custom pool settings.
|
34
|
-
#
|
35
|
-
# @param pool_size [Integer] Number of worker threads in the pool
|
36
|
-
# @param max_waiting_requests [Integer] Maximum requests to queue before rejecting
|
37
|
-
# @param pool_keep_alive [Integer] Thread keep-alive time in seconds
|
38
|
-
# @param poll_period [Integer] Server polling interval in seconds
|
39
|
-
# @param args [Hash] Additional arguments passed to GRPC::RpcServer
|
40
33
|
def initialize(pool_size: DEFAULT_POOL_SIZE,
|
41
34
|
max_waiting_requests: DEFAULT_MAX_WAITING_REQUESTS,
|
42
|
-
pool_keep_alive:
|
35
|
+
pool_keep_alive: PoolEnhancements::DEFAULT_KEEP_ALIVE,
|
43
36
|
poll_period: DEFAULT_POLL_PERIOD,
|
44
37
|
**args)
|
45
|
-
super
|
38
|
+
super
|
46
39
|
|
47
40
|
@pool_size = pool_size
|
48
41
|
@max_waiting_requests = max_waiting_requests
|
49
42
|
@pool_keep_alive = pool_keep_alive
|
50
43
|
@poll_period = poll_period
|
51
|
-
|
52
|
-
@pool = Gruf::Queue::Pool.new(@pool_size, keep_alive: @pool_keep_alive)
|
53
44
|
end
|
54
45
|
|
55
|
-
# Check if server can handle the request based on current queue load.
|
56
|
-
#
|
57
|
-
# @param an_rpc [Object] The incoming RPC request
|
58
|
-
# @return [Object, false] The RPC if available, false if resource exhausted
|
59
46
|
def available?(an_rpc)
|
60
47
|
job_count = safe_job_count
|
61
48
|
return an_rpc if job_count < @max_waiting_requests
|
@@ -66,26 +53,19 @@ module Gruf
|
|
66
53
|
|
67
54
|
private
|
68
55
|
|
69
|
-
# Get current job count from pool with error handling.
|
70
|
-
#
|
71
|
-
# @return [Integer] Number of jobs waiting, or max_waiting_requests on error
|
72
56
|
def safe_job_count
|
57
|
+
return 0 unless @pool
|
58
|
+
|
73
59
|
@pool.jobs_waiting
|
74
60
|
rescue StandardError => e
|
75
|
-
|
76
|
-
# Assume maximum load if we can't determine the actual count
|
61
|
+
Gruf.logger.warn("Failed to get job count: #{e.message}") if defined?(Gruf) && Gruf.respond_to?(:logger)
|
77
62
|
@max_waiting_requests
|
78
63
|
end
|
79
64
|
|
80
|
-
# Send RESOURCE_EXHAUSTED status to client with error handling.
|
81
|
-
#
|
82
|
-
# @param an_rpc [Object] The RPC to send error response to
|
83
|
-
# @return [void]
|
84
65
|
def send_resource_exhausted_response(an_rpc)
|
85
|
-
|
66
|
+
Gruf.logger.warn('no free worker threads currently') if defined?(Gruf) && Gruf.respond_to?(:logger)
|
86
67
|
|
87
68
|
begin
|
88
|
-
# Try calling send_status directly on the RPC object first (for test mocks)
|
89
69
|
if an_rpc.respond_to?(:send_status)
|
90
70
|
an_rpc.send_status(
|
91
71
|
::GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED,
|
@@ -95,7 +75,6 @@ module Gruf
|
|
95
75
|
return
|
96
76
|
end
|
97
77
|
|
98
|
-
# Create a new active call that knows that metadata hasn't been sent yet
|
99
78
|
active_call = ::GRPC::ActiveCall.new(
|
100
79
|
an_rpc.call,
|
101
80
|
NOOP_PROC,
|
@@ -110,15 +89,13 @@ module Gruf
|
|
110
89
|
RESOURCE_EXHAUSTED_MESSAGE,
|
111
90
|
)
|
112
91
|
rescue TypeError => e
|
113
|
-
# Handle test scenario where an_rpc.call is a mock object
|
114
92
|
raise unless e.message.include?('Core::Call')
|
115
93
|
|
116
|
-
# In test environment, just log the response
|
117
94
|
warn "RESOURCE_EXHAUSTED: #{RESOURCE_EXHAUSTED_MESSAGE}" if defined?(RSpec)
|
118
95
|
end
|
119
96
|
rescue StandardError => e
|
120
|
-
if
|
121
|
-
|
97
|
+
if defined?(Gruf) && Gruf.respond_to?(:logger)
|
98
|
+
Gruf.logger.error("Failed to send resource exhausted response: #{e.message}")
|
122
99
|
else
|
123
100
|
warn "Failed to send resource exhausted response: #{e.message}"
|
124
101
|
end
|
data/lib/gruf/queue/version.rb
CHANGED
data/lib/gruf-queue.rb
CHANGED
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'gruf'
|
4
4
|
require 'gruf/queue/version'
|
5
|
-
require 'gruf/queue/
|
5
|
+
require 'gruf/queue/pool_enhancements'
|
6
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'
|
10
7
|
require 'gruf/queue/plugin'
|
11
8
|
|
12
9
|
# 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.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ether Moon
|
@@ -135,12 +135,9 @@ 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/
|
139
|
+
- lib/gruf/queue/pool_enhancements.rb
|
142
140
|
- lib/gruf/queue/queued_rpc_server.rb
|
143
|
-
- lib/gruf/queue/server_factory.rb
|
144
141
|
- lib/gruf/queue/version.rb
|
145
142
|
homepage: https://github.com/ether-moon/gruf-queue
|
146
143
|
licenses:
|
@@ -1,107 +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
|
-
# Apply queue-specific configuration to Gruf.
|
16
|
-
#
|
17
|
-
# @return [void]
|
18
|
-
# @raise [StandardError] if configuration fails
|
19
|
-
def configure
|
20
|
-
return if configured?
|
21
|
-
|
22
|
-
begin
|
23
|
-
Gruf.configure do |config|
|
24
|
-
enhance_gruf_configuration(config)
|
25
|
-
end
|
26
|
-
|
27
|
-
@configured = true
|
28
|
-
log_debug('Gruf configuration enhanced with rpc_server support', version: Gruf::Queue::VERSION)
|
29
|
-
rescue StandardError => e
|
30
|
-
log_error('Failed to enhance Gruf configuration', error: e.message, error_class: e.class.name)
|
31
|
-
raise
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Check if configuration has been applied.
|
36
|
-
#
|
37
|
-
# @return [Boolean] true if configured, false otherwise
|
38
|
-
def configured?
|
39
|
-
!!@configured
|
40
|
-
end
|
41
|
-
|
42
|
-
# Reset configuration state for testing.
|
43
|
-
#
|
44
|
-
# @return [void]
|
45
|
-
# @raise [RuntimeError] if not in test environment
|
46
|
-
def reset!
|
47
|
-
raise 'reset! can only be called in test environments' unless test_environment?
|
48
|
-
|
49
|
-
@configured = false
|
50
|
-
|
51
|
-
# Reset Gruf configuration to clean state
|
52
|
-
if defined?(Gruf) && Gruf.respond_to?(:configuration)
|
53
|
-
config = Gruf.configuration
|
54
|
-
config.rpc_server = nil if config.respond_to?(:rpc_server=)
|
55
|
-
end
|
56
|
-
|
57
|
-
log_debug('Gruf configuration state reset', reason: 'test_environment')
|
58
|
-
end
|
59
|
-
|
60
|
-
# Enhance Gruf configuration with rpc_server support
|
61
|
-
def enhance_gruf_configuration(config)
|
62
|
-
config.define_singleton_method(:rpc_server) { @rpc_server }
|
63
|
-
config.define_singleton_method(:rpc_server=) { |val| @rpc_server = val }
|
64
|
-
config.rpc_server = nil unless config.respond_to?(:rpc_server)
|
65
|
-
|
66
|
-
# Make the configuration accessible via Gruf.configuration if not already available
|
67
|
-
return if Gruf.respond_to?(:configuration)
|
68
|
-
|
69
|
-
Gruf.define_singleton_method(:configuration) { config }
|
70
|
-
end
|
71
|
-
|
72
|
-
# Check if running in test environment
|
73
|
-
def test_environment?
|
74
|
-
ENV['RACK_ENV'] == 'test' ||
|
75
|
-
ENV['RAILS_ENV'] == 'test' ||
|
76
|
-
defined?(RSpec)
|
77
|
-
end
|
78
|
-
|
79
|
-
# Log debug message
|
80
|
-
def log_debug(message, **metadata)
|
81
|
-
return unless defined?(Gruf) && Gruf.respond_to?(:logger)
|
82
|
-
|
83
|
-
if metadata.any?
|
84
|
-
Gruf.logger.debug(metadata.merge(message: message))
|
85
|
-
else
|
86
|
-
Gruf.logger.debug(message)
|
87
|
-
end
|
88
|
-
rescue StandardError
|
89
|
-
# Ignore logging errors
|
90
|
-
end
|
91
|
-
|
92
|
-
# Log error message
|
93
|
-
def log_error(message, **metadata)
|
94
|
-
return unless defined?(Gruf) && Gruf.respond_to?(:logger)
|
95
|
-
return if test_environment? # Skip error logging in test environment
|
96
|
-
|
97
|
-
if metadata.any?
|
98
|
-
Gruf.logger.error(metadata.merge(message: message))
|
99
|
-
else
|
100
|
-
Gruf.logger.error(message)
|
101
|
-
end
|
102
|
-
rescue StandardError
|
103
|
-
# Ignore logging errors
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
@@ -1,159 +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
|
-
# Initialize interceptor with request context and options.
|
18
|
-
#
|
19
|
-
# @param request [Object] gRPC request object
|
20
|
-
# @param call [Object] gRPC call object
|
21
|
-
# @param method [Object] gRPC method object
|
22
|
-
# @param options [Hash] Configuration options
|
23
|
-
# @option options [Boolean] :enabled (true) Enable/disable interceptor
|
24
|
-
# @option options [Array<Class>] :target_classes Classes to reset connections for
|
25
|
-
def initialize(request, call, method, options = {})
|
26
|
-
@request = request
|
27
|
-
@call = call
|
28
|
-
@method = method
|
29
|
-
@options = options || {}
|
30
|
-
# Set default enabled value if not provided
|
31
|
-
@options[:enabled] = true unless @options.key?(:enabled)
|
32
|
-
end
|
33
|
-
|
34
|
-
# Execute request and reset connections on completion.
|
35
|
-
#
|
36
|
-
# @yield Block containing the actual request handling
|
37
|
-
# @return [Object] Result of yielded block
|
38
|
-
def call
|
39
|
-
yield
|
40
|
-
ensure
|
41
|
-
begin
|
42
|
-
reset_connections if enabled?
|
43
|
-
rescue StandardError => e
|
44
|
-
# Log error but don't prevent handler execution
|
45
|
-
log_message(:error, "Connection reset failed: #{e.message}")
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
# Get interceptor configuration options.
|
52
|
-
#
|
53
|
-
# @return [Hash] Configuration options
|
54
|
-
attr_reader :options
|
55
|
-
|
56
|
-
# Check if interceptor is enabled and has valid target classes.
|
57
|
-
#
|
58
|
-
# @return [Boolean] true if should execute, false otherwise
|
59
|
-
def enabled?
|
60
|
-
# Check if explicitly disabled
|
61
|
-
return false if options[:enabled] == false
|
62
|
-
|
63
|
-
# If no target classes are provided and ActiveRecord is not available, disable
|
64
|
-
return false if target_classes.empty?
|
65
|
-
|
66
|
-
true
|
67
|
-
end
|
68
|
-
|
69
|
-
# Reset connections for all valid target classes.
|
70
|
-
#
|
71
|
-
# @return [void]
|
72
|
-
def reset_connections
|
73
|
-
validated_classes = validate_target_classes
|
74
|
-
return if validated_classes.empty?
|
75
|
-
|
76
|
-
reset_count = 0
|
77
|
-
validated_classes.each do |klass|
|
78
|
-
reset_count += 1 if reset_connection_for_class(klass)
|
79
|
-
end
|
80
|
-
|
81
|
-
log_reset_summary(reset_count, validated_classes.size) if reset_count.positive?
|
82
|
-
end
|
83
|
-
|
84
|
-
def target_classes
|
85
|
-
# Use instance variable if set (for testing)
|
86
|
-
return Array(@target_classes).compact if instance_variable_defined?(:@target_classes)
|
87
|
-
|
88
|
-
# Otherwise use options or default
|
89
|
-
default_classes = defined?(::ActiveRecord::Base) ? [::ActiveRecord::Base] : []
|
90
|
-
classes = options.fetch(:target_classes, default_classes)
|
91
|
-
Array(classes).compact
|
92
|
-
end
|
93
|
-
|
94
|
-
# Validate and filter target classes to ensure they support connection handling
|
95
|
-
def validate_target_classes
|
96
|
-
target_classes.select do |klass|
|
97
|
-
valid_class?(klass)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# Check if a class is valid for connection reset capability
|
102
|
-
def valid_class?(klass)
|
103
|
-
unless klass.is_a?(Class)
|
104
|
-
log_validation_warning(klass, 'not a class')
|
105
|
-
return false
|
106
|
-
end
|
107
|
-
|
108
|
-
unless klass.respond_to?(:connection_handler)
|
109
|
-
log_validation_warning(klass, 'does not respond to connection_handler')
|
110
|
-
return false
|
111
|
-
end
|
112
|
-
|
113
|
-
true
|
114
|
-
end
|
115
|
-
|
116
|
-
# Reset connections for a single class
|
117
|
-
def reset_connection_for_class(klass)
|
118
|
-
handler = klass.connection_handler
|
119
|
-
handler.clear_active_connections!(:all) if handler.respond_to?(:clear_active_connections!)
|
120
|
-
handler.clear_reloadable_connections! if handler.respond_to?(:clear_reloadable_connections!)
|
121
|
-
true
|
122
|
-
rescue StandardError => e
|
123
|
-
log_reset_error(klass, e)
|
124
|
-
false
|
125
|
-
end
|
126
|
-
|
127
|
-
# Log validation warnings
|
128
|
-
def log_validation_warning(klass, reason)
|
129
|
-
message = "Skipping connection reset for #{klass.inspect}: #{reason}"
|
130
|
-
log_message(:debug, message)
|
131
|
-
end
|
132
|
-
|
133
|
-
# Log connection reset errors
|
134
|
-
def log_reset_error(klass, error)
|
135
|
-
message = "Failed to reset connections for #{klass}: #{error.message}"
|
136
|
-
log_message(:warn, message)
|
137
|
-
end
|
138
|
-
|
139
|
-
# Log reset operation summary
|
140
|
-
def log_reset_summary(reset_count, total_count)
|
141
|
-
message = "Reset connections for #{reset_count}/#{total_count} classes"
|
142
|
-
log_message(:debug, message)
|
143
|
-
end
|
144
|
-
|
145
|
-
# Centralized logging with fallback chain
|
146
|
-
def log_message(level, message)
|
147
|
-
if defined?(Rails) && Rails.logger.respond_to?(level)
|
148
|
-
Rails.logger.public_send(level, message)
|
149
|
-
elsif defined?(GRPC) && GRPC.logger.respond_to?(level)
|
150
|
-
GRPC.logger.public_send(level, message)
|
151
|
-
elsif level == :warn
|
152
|
-
# Fallback to stderr for critical messages
|
153
|
-
warn(message)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
data/lib/gruf/queue/pool.rb
DELETED
@@ -1,156 +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
|
-
# Initialize pool with specified size and keep-alive time.
|
19
|
-
#
|
20
|
-
# @param size [Integer] Number of worker threads to maintain
|
21
|
-
# @param keep_alive [Integer] Thread keep-alive time in seconds
|
22
|
-
def initialize(size, keep_alive: DEFAULT_KEEP_ALIVE)
|
23
|
-
super
|
24
|
-
end
|
25
|
-
|
26
|
-
# Get number of jobs currently waiting in queue.
|
27
|
-
#
|
28
|
-
# @return [Integer] Count of queued jobs
|
29
|
-
def jobs_waiting
|
30
|
-
@jobs&.size || 0
|
31
|
-
end
|
32
|
-
|
33
|
-
# Schedule block for execution on worker thread.
|
34
|
-
#
|
35
|
-
# @param args [Array] Arguments to pass to block
|
36
|
-
# @param blk [Proc] Block to execute
|
37
|
-
# @return [Boolean] true if scheduled, false if stopped or nil block
|
38
|
-
def schedule(*args, &blk)
|
39
|
-
return false if blk.nil?
|
40
|
-
|
41
|
-
@stop_mutex.synchronize do
|
42
|
-
if @stopped
|
43
|
-
log_structured(:warn, 'Job scheduling rejected: pool already stopped')
|
44
|
-
return false
|
45
|
-
end
|
46
|
-
log_structured(:debug, 'Job scheduled for execution')
|
47
|
-
|
48
|
-
@jobs << [blk, args]
|
49
|
-
true
|
50
|
-
end
|
51
|
-
rescue StandardError => e
|
52
|
-
log_structured(:error, 'Failed to schedule job', error: e.message)
|
53
|
-
false
|
54
|
-
end
|
55
|
-
|
56
|
-
# Start thread pool and create worker threads.
|
57
|
-
#
|
58
|
-
# @raise [RuntimeError] if pool is already stopped
|
59
|
-
# @return [void]
|
60
|
-
def start
|
61
|
-
@stop_mutex.synchronize do
|
62
|
-
raise 'Pool already stopped' if @stopped
|
63
|
-
end
|
64
|
-
|
65
|
-
target_size = @size.to_i
|
66
|
-
log_structured(:info, 'Starting thread pool', target_size: target_size)
|
67
|
-
|
68
|
-
until @workers.size == target_size
|
69
|
-
next_thread = create_worker_thread
|
70
|
-
@workers << next_thread if next_thread
|
71
|
-
end
|
72
|
-
|
73
|
-
log_structured(:info, 'Thread pool started', worker_count: @workers.size)
|
74
|
-
rescue StandardError => e
|
75
|
-
log_structured(:error, 'Failed to start thread pool', error: e.message)
|
76
|
-
raise
|
77
|
-
end
|
78
|
-
|
79
|
-
protected
|
80
|
-
|
81
|
-
# Create new worker thread with error handling.
|
82
|
-
#
|
83
|
-
# @return [Thread, nil] Worker thread or nil on error
|
84
|
-
def create_worker_thread
|
85
|
-
Thread.new do
|
86
|
-
catch(:exit) do
|
87
|
-
# allows { throw :exit } to kill a thread
|
88
|
-
_loop_execute_jobs
|
89
|
-
end
|
90
|
-
remove_current_thread
|
91
|
-
end
|
92
|
-
rescue StandardError => e
|
93
|
-
log_structured(:error, 'Failed to create worker thread', error: e.message)
|
94
|
-
nil
|
95
|
-
end
|
96
|
-
|
97
|
-
# Main worker thread loop for job execution.
|
98
|
-
#
|
99
|
-
# @return [void]
|
100
|
-
# @api private
|
101
|
-
def _loop_execute_jobs
|
102
|
-
Thread.current.name = "gruf-queue-worker-#{Thread.current.object_id}"
|
103
|
-
log_structured(:debug, 'Worker thread started', thread_id: Thread.current.object_id)
|
104
|
-
|
105
|
-
loop do
|
106
|
-
begin
|
107
|
-
blk, args = @jobs.pop
|
108
|
-
execute_job_safely(blk, args)
|
109
|
-
rescue StandardError => e
|
110
|
-
log_structured(:error, 'Unexpected error in worker thread',
|
111
|
-
error: e.message,
|
112
|
-
backtrace: e.backtrace&.first(5))
|
113
|
-
end
|
114
|
-
|
115
|
-
@stop_mutex.synchronize do
|
116
|
-
return if @stopped
|
117
|
-
end
|
118
|
-
end
|
119
|
-
ensure
|
120
|
-
log_structured(:debug, 'Worker thread stopping', thread_id: Thread.current.object_id)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Execute job with error isolation and logging.
|
124
|
-
#
|
125
|
-
# @param blk [Proc] Job block to execute
|
126
|
-
# @param args [Array] Arguments for the block
|
127
|
-
# @return [void]
|
128
|
-
def execute_job_safely(blk, args)
|
129
|
-
blk.call(*args)
|
130
|
-
rescue StandardError, GRPC::Core::CallError => e
|
131
|
-
log_structured(:warn, 'Job execution failed',
|
132
|
-
error: e.message,
|
133
|
-
error_class: e.class.name)
|
134
|
-
# Log full backtrace only in debug mode to avoid log spam
|
135
|
-
log_structured(:debug, 'Job execution backtrace', backtrace: e.backtrace) if e.backtrace
|
136
|
-
end
|
137
|
-
|
138
|
-
# Log message with structured metadata.
|
139
|
-
#
|
140
|
-
# @param level [Symbol] Log level (:info, :warn, :error, :debug)
|
141
|
-
# @param message [String] Base log message
|
142
|
-
# @param metadata [Hash] Additional metadata to include
|
143
|
-
# @return [void]
|
144
|
-
def log_structured(level, message, **metadata)
|
145
|
-
return unless GRPC.logger.respond_to?(level)
|
146
|
-
|
147
|
-
if metadata.any?
|
148
|
-
formatted_message = "#{message} #{metadata.map { |k, v| "#{k}=#{v}" }.join(' ')}"
|
149
|
-
GRPC.logger.public_send(level, formatted_message)
|
150
|
-
else
|
151
|
-
GRPC.logger.public_send(level, message)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
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
|