gruf-queue 0.1.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93e75c950c181dceecb26c7bb037014232fb190602c85800702c6b8bf29329c7
4
- data.tar.gz: a81edb2a6d96d1c514c850b20ed0422eeed701e296497149df0e57597cc1875e
3
+ metadata.gz: 16b3387c40ed0f1175b3b6ee1127a15825788f17ae1ccd829385462d58c49913
4
+ data.tar.gz: d6694faf0538b2f6085a318433389ccf073c2f93876e78f1fb770bc061f6273d
5
5
  SHA512:
6
- metadata.gz: 6d6f81af6800c4f8aca41a5400609e147b011b24ae9c6864b3caede6492889e7b9b8e9912274828f9bb35f57793b93355a50b918527d9383b3a719ad7346e509
7
- data.tar.gz: e4efc9c13fe4c3867f65312175f65896e6dcd1f65c52ecbec99727ccb2a62e3c4f527ba7afc941ff3ec53faa9c212175b28bf286f753eee6190e47119eb41cc4
6
+ metadata.gz: b69971940855b247545a1488522d7b0abb9833c9d2eea4b4f8399e664fe9910f3a044a07e967e4cb3f8da91188b758d6ebdf5fd3097b477a51cf707d3275e13e
7
+ data.tar.gz: bc4d7414d8fdb9223721928facbb5b17db77bd753904a1419af72729ebaae22dcbcaf60244a53fca058ece126d9b1c16a284a3eb1ad0b7fbb92a1352a85d7591
data/CHANGELOG.md CHANGED
@@ -5,6 +5,33 @@ 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.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
+
8
35
  ## [0.1.2]
9
36
 
10
37
  ### Changed
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::Queue::Interceptors::ConnectionReset,
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
- ### Using Server Factory
111
+ ### Custom Server Configuration
112
112
 
113
- For advanced server creation:
113
+ For custom server instances with specific options:
114
114
 
115
115
  ```ruby
116
- # Create custom server instances
117
- server = Gruf::Queue::ServerFactory.create_server(
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::Queue::Interceptors::ConnectionReset,
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::Queue::Interceptors::ConnectionReset`
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
@@ -6,32 +6,18 @@ 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
29
-
30
- @installed = true
31
- true
32
- rescue StandardError
33
- false
34
- end
15
+ enhance_grpc_pool
16
+ configure_gruf
17
+ @installed = true
18
+ true
19
+ rescue StandardError
20
+ false
35
21
  end
36
22
 
37
23
  def installed?
@@ -40,63 +26,38 @@ module Gruf
40
26
 
41
27
  def reset!
42
28
  @installed = false
43
- Configuration.reset! if Configuration.respond_to?(:reset!)
44
29
  end
45
30
 
46
31
  private
47
32
 
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
33
  def enhance_grpc_pool
96
34
  return if ::GRPC::Pool.included_modules.include?(Gruf::Queue::PoolEnhancements)
97
35
 
98
36
  ::GRPC::Pool.prepend(Gruf::Queue::PoolEnhancements)
99
37
  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
100
61
  end
101
62
  end
102
63
  end
@@ -10,6 +10,9 @@ module Gruf
10
10
  # @example Usage
11
11
  # GRPC::Pool.prepend(Gruf::Queue::PoolEnhancements)
12
12
  module PoolEnhancements
13
+ # Default keep-alive time for worker threads (in seconds)
14
+ DEFAULT_KEEP_ALIVE = 600
15
+
13
16
  def jobs_waiting
14
17
  @jobs&.size || 0
15
18
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'pool'
3
+ require_relative 'pool_enhancements'
4
4
 
5
5
  module Gruf
6
6
  module Queue
@@ -32,7 +32,7 @@ module Gruf
32
32
 
33
33
  def initialize(pool_size: DEFAULT_POOL_SIZE,
34
34
  max_waiting_requests: DEFAULT_MAX_WAITING_REQUESTS,
35
- pool_keep_alive: Pool::DEFAULT_KEEP_ALIVE,
35
+ pool_keep_alive: PoolEnhancements::DEFAULT_KEEP_ALIVE,
36
36
  poll_period: DEFAULT_POLL_PERIOD,
37
37
  **args)
38
38
  super
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gruf
4
4
  module Queue
5
- VERSION = '0.1.2'
5
+ VERSION = '0.1.3'
6
6
  end
7
7
  end
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/pool'
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.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ether Moon
@@ -135,13 +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/pool.rb
142
139
  - lib/gruf/queue/pool_enhancements.rb
143
140
  - lib/gruf/queue/queued_rpc_server.rb
144
- - lib/gruf/queue/server_factory.rb
145
141
  - lib/gruf/queue/version.rb
146
142
  homepage: https://github.com/ether-moon/gruf-queue
147
143
  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,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