gruf 2.13.1 → 2.17.0
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 +55 -7
- data/README.md +1 -1
- data/gruf.gemspec +12 -10
- data/lib/gruf/autoloaders.rb +76 -0
- data/lib/gruf/cli/executor.rb +95 -6
- data/lib/gruf/client/error.rb +0 -28
- data/lib/gruf/client/errors.rb +48 -0
- data/lib/gruf/client.rb +0 -3
- data/lib/gruf/configuration.rb +23 -5
- data/lib/gruf/controllers/autoloader.rb +96 -0
- data/lib/gruf/controllers/base.rb +3 -5
- data/lib/gruf/controllers/health_controller.rb +39 -0
- data/lib/gruf/controllers/request.rb +6 -0
- data/lib/gruf/controllers/service_binder.rb +73 -81
- data/lib/gruf/error.rb +0 -4
- data/lib/gruf/{logging.rb → grpc_logger.rb} +0 -14
- data/lib/gruf/integrations/rails/railtie.rb +30 -0
- data/lib/gruf/interceptors/active_record/connection_reset.rb +11 -2
- data/lib/gruf/interceptors/base.rb +0 -10
- data/lib/gruf/interceptors/instrumentation/request_logging/interceptor.rb +0 -3
- data/lib/gruf/logger.rb +32 -0
- data/lib/gruf/server.rb +48 -50
- data/lib/gruf/version.rb +1 -1
- data/lib/gruf.rb +18 -20
- metadata +30 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: feddcb49d9325078dfb0d7367f5e82dfea0d46a82e56784b10484c873ab375a5
|
4
|
+
data.tar.gz: b4614f08c2b6d8f62edfc1f34be0dffc7af4825b680e49aaf5c6160315314d32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e67b5fdaf175a0c8b839532d6024415f12c1fdbd91f44fdec5cfa1ac0d7c33e821b740a192f251664f6ff09ef5dc96d903c480a474d2f53d4b876189023550c
|
7
|
+
data.tar.gz: 4061112bf7ea91180fb5ea16d5d135cc3a1a7dab7d94ba479d462f3acca3aa183373f64eb5d8e7d9ad0442a457ed5b25e806e407f27c9d3c6ee0658d153b654c
|
data/CHANGELOG.md
CHANGED
@@ -2,11 +2,59 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
2
2
|
|
3
3
|
### Pending release
|
4
4
|
|
5
|
+
### 2.17.0
|
6
|
+
|
7
|
+
* [#179] Add Ruby 3.2 support
|
8
|
+
* [#178] Introduce read-write lock for code reloading; cover controller class resolution with code reloading.
|
9
|
+
* [#180] Support multiple databases connection reset in `Gruf::Interceptors::ActiveRecord::ConnectionReset`.
|
10
|
+
|
11
|
+
### 2.16.2
|
12
|
+
|
13
|
+
* [#175] Fix code reload thread-safety. Calls to `Zeitwerk::Loader#setup` are now made in a thread-safe manner.
|
14
|
+
|
15
|
+
### 2.16.1
|
16
|
+
|
17
|
+
* Fix issue where default gRPC health check was loaded even if unused or not desired; now only loaded when requested
|
18
|
+
|
19
|
+
### 2.16.0
|
20
|
+
|
21
|
+
- Add opt-in ability to serve the official [gRPC health check](https://github.com/grpc/grpc/blob/master/src/ruby/pb/grpc/health/v1/health_services_pb.rb)
|
22
|
+
automatically via `health_check_enabled` configuration option (or `GRUF_HEALTH_CHECK_ENABLED` environment
|
23
|
+
variable).
|
24
|
+
- Add `health_check_hook` configuration option to implement a custom response for the above gRPC built-in health check
|
25
|
+
- [#156] Allow passing a specific list of services to run via the gruf binstub
|
26
|
+
- [#163] Add `context` hash attribute to `Gruf::Controllers::Request` to allow interceptors to pass information down
|
27
|
+
to a gruf controller
|
28
|
+
- Drop Ruby 2.6 support (EOL'ed on March 31st, 2022)
|
29
|
+
|
30
|
+
### 2.15.1
|
31
|
+
|
32
|
+
- Fix issue where GRPC_SERVER_POOL_KEEP_ALIVE and GRPC_SERVER_POLL_PERIOD when set via ENV are not cast to int
|
33
|
+
|
34
|
+
### 2.15.0
|
35
|
+
|
36
|
+
- NOTE: This changes the way that gruf controllers are autoloaded. See [UPGRADING.md] for more details.
|
37
|
+
- Autoload Gruf Controllers with zeitwerk, allowing for code reloading in development environments
|
38
|
+
- Add `GRUF_CONTROLLERS_PATH` ENV to allow ENV-based runtime configuration of path to gruf controller files
|
39
|
+
- Move `ServiceBinder` from instance to static class for performance improvements
|
40
|
+
- Use zeitwerk for autoloading
|
41
|
+
- Update gem metadata
|
42
|
+
- Update `Gruf:Configuration#environment` to use `ENV.fetch`
|
43
|
+
|
44
|
+
### 2.14.1
|
45
|
+
|
46
|
+
- Fix issue where the server object hits thread contention in certain race conditions
|
47
|
+
|
48
|
+
### 2.14.0
|
49
|
+
|
50
|
+
- Set default client host to 0.0.0.0:9001 (same as default server host)
|
51
|
+
- Add support for Ruby 3.1
|
52
|
+
|
5
53
|
### 2.13.1
|
6
54
|
|
7
|
-
- Fix issue with race condition in server starts where servers may fail to bind connections and never reach
|
55
|
+
- Fix issue with race condition in server starts where servers may fail to bind connections and never reach
|
8
56
|
serving state (fixes #147)
|
9
|
-
|
57
|
+
|
10
58
|
### 2.13.0
|
11
59
|
|
12
60
|
- Remove server mutex handling in deference to core grpc signal handling
|
@@ -18,7 +66,7 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
18
66
|
|
19
67
|
### 2.11.0
|
20
68
|
|
21
|
-
- Restrict grpc gem to <= 1.41.0 due to regressions in grpc 1.42.x
|
69
|
+
- Restrict grpc gem to <= 1.41.0 due to regressions in grpc 1.42.x
|
22
70
|
- Fallback to stdout logger at INFO if no logger is setup
|
23
71
|
- Better handling of namespace collisions with Rails
|
24
72
|
- Add `GRPC_SERVER_HOST` and `GRPC_SERVER_PORT` for ENV configuration of the server host+port
|
@@ -58,7 +106,7 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
58
106
|
|
59
107
|
### 2.8.0
|
60
108
|
|
61
|
-
- Pass the controller request object into the request logging formatters [#92]
|
109
|
+
- Pass the controller request object into the request logging formatters [#92]
|
62
110
|
|
63
111
|
### 2.7.1
|
64
112
|
|
@@ -71,12 +119,12 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
71
119
|
### 2.6.1
|
72
120
|
|
73
121
|
- Add frozen_string_literal: true to files, update rubocop to 0.68
|
74
|
-
|
122
|
+
|
75
123
|
### 2.6.0
|
76
124
|
|
77
125
|
- Drop Ruby 2.2 support
|
78
|
-
- Abstract gruf controller's send to make it usable in filters
|
79
|
-
- Adjusts configuration reset into a Railtie for Rails systems to ensure proper OOE
|
126
|
+
- Abstract gruf controller's send to make it usable in filters
|
127
|
+
- Adjusts configuration reset into a Railtie for Rails systems to ensure proper OOE
|
80
128
|
- Bump rubocop to 0.64, address violations, update activesupport/concurrent-ruby dependencies to have a min version
|
81
129
|
|
82
130
|
### 2.5.2
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ up fast and efficiently at scale. Some of its features include:
|
|
17
17
|
still preserving gRPC BadStatus codes
|
18
18
|
* Server and client execution timings in responses
|
19
19
|
|
20
|
-
gruf currently has active support for gRPC 1.10.x+. gruf is compatible and tested with Ruby 2.
|
20
|
+
gruf currently has active support for gRPC 1.10.x+. gruf is compatible and tested with Ruby 2.6-3.2.
|
21
21
|
gruf is also not [Rails](https://github.com/rails/rails)-specific, and can be used in any Ruby framework
|
22
22
|
(such as [Grape](https://github.com/ruby-grape/grape) or [dry-rb](https://dry-rb.org/), for instance).
|
23
23
|
|
data/gruf.gemspec
CHANGED
@@ -15,8 +15,7 @@
|
|
15
15
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
16
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
17
|
#
|
18
|
-
|
19
|
-
require 'gruf/version'
|
18
|
+
require_relative 'lib/gruf/version'
|
20
19
|
|
21
20
|
Gem::Specification.new do |spec|
|
22
21
|
spec.name = 'gruf'
|
@@ -33,17 +32,19 @@ Gem::Specification.new do |spec|
|
|
33
32
|
spec.executables << 'gruf'
|
34
33
|
spec.require_paths = ['lib']
|
35
34
|
|
36
|
-
spec.required_ruby_version = '>= 2.
|
35
|
+
spec.required_ruby_version = '>= 2.7', '< 3.3'
|
37
36
|
|
38
|
-
spec.metadata
|
37
|
+
spec.metadata = {
|
38
|
+
'bug_tracker_uri' => 'https://github.com/bigcommerce/gruf/issues',
|
39
|
+
'changelog_uri' => 'https://github.com/bigcommerce/gruf/CHANGELOG.md',
|
40
|
+
'homepage_uri' => 'https://github.com/bigcommerce/gruf',
|
41
|
+
'rubygems_mfa_required' => 'true',
|
42
|
+
'source_code_uri' => 'https://github.com/bigcommerce/gruf',
|
43
|
+
'wiki_uri' => 'https://github.com/bigcommerce/gruf/wiki'
|
44
|
+
}
|
39
45
|
|
40
46
|
spec.add_development_dependency 'bundler-audit', '>= 0.6'
|
41
|
-
|
42
|
-
spec.add_development_dependency(
|
43
|
-
'factory_bot',
|
44
|
-
(Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5') ? '>= 6.1' : '~> 5.2')
|
45
|
-
)
|
46
|
-
# rubocop:enable Gemspec/RubyVersionGlobalsUsage
|
47
|
+
spec.add_development_dependency 'factory_bot', '>= 6.1'
|
47
48
|
spec.add_development_dependency 'ffaker', '>= 2.15'
|
48
49
|
spec.add_development_dependency 'pry', '~> 0.12'
|
49
50
|
spec.add_development_dependency 'pry-byebug', '>= 3.9'
|
@@ -64,4 +65,5 @@ Gem::Specification.new do |spec|
|
|
64
65
|
spec.add_runtime_dependency 'json', '>= 2.3'
|
65
66
|
spec.add_runtime_dependency 'slop', '>= 4.6'
|
66
67
|
spec.add_runtime_dependency 'thwait', '>= 0.1'
|
68
|
+
spec.add_runtime_dependency 'zeitwerk', '>= 2'
|
67
69
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2022-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
module Gruf
|
19
|
+
##
|
20
|
+
# Module for accessing Gruf zeitwerk-based autoloaders
|
21
|
+
#
|
22
|
+
module Autoloaders
|
23
|
+
class << self
|
24
|
+
include Enumerable
|
25
|
+
|
26
|
+
##
|
27
|
+
# Initialize the autoloaders with a given controllers path
|
28
|
+
#
|
29
|
+
# @param [String] controllers_path The path to Gruf Controllers
|
30
|
+
#
|
31
|
+
def load!(controllers_path:)
|
32
|
+
controllers(controllers_path: controllers_path)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Enumerate across the managed set of autoloaders
|
37
|
+
#
|
38
|
+
def each
|
39
|
+
yield controllers
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Reload all files managed by the autoloader
|
44
|
+
#
|
45
|
+
def reload
|
46
|
+
each(&:reload)
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Lazily instantiate and memoize the Gruf Controllers autoloader in a thread-safe manner
|
51
|
+
#
|
52
|
+
# @return [::Gruf::Controllers::Autoloader]
|
53
|
+
#
|
54
|
+
# rubocop:disable ThreadSafety/InstanceVariableInClassMethod
|
55
|
+
def controllers(controllers_path: nil)
|
56
|
+
controllers_mutex do
|
57
|
+
@controllers ||= ::Gruf::Controllers::Autoloader.new(path: controllers_path || ::Gruf.controllers_path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# rubocop:enable ThreadSafety/InstanceVariableInClassMethod
|
61
|
+
|
62
|
+
##
|
63
|
+
# Handle mutations to the controllers autoloader in a thread-safe manner
|
64
|
+
#
|
65
|
+
# rubocop:disable ThreadSafety/InstanceVariableInClassMethod
|
66
|
+
def controllers_mutex(&block)
|
67
|
+
@controllers_mutex ||= begin
|
68
|
+
require 'monitor'
|
69
|
+
Monitor.new
|
70
|
+
end
|
71
|
+
@controllers_mutex.synchronize(&block)
|
72
|
+
end
|
73
|
+
# rubocop:enable ThreadSafety/InstanceVariableInClassMethod
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/gruf/cli/executor.rb
CHANGED
@@ -23,8 +23,14 @@ module Gruf
|
|
23
23
|
# Handles execution of the gruf binstub, along with command-line arguments
|
24
24
|
#
|
25
25
|
class Executor
|
26
|
+
class NoServicesBoundError < StandardError; end
|
27
|
+
|
26
28
|
##
|
27
29
|
# @param [Hash|ARGV]
|
30
|
+
# @param [::Gruf::Server|NilClass] server
|
31
|
+
# @param [Array<Class>|NilClass] services
|
32
|
+
# @param [Gruf::Hooks::Executor|NilClass] hook_executor
|
33
|
+
# @param [Logger|NilClass] logger
|
28
34
|
#
|
29
35
|
def initialize(
|
30
36
|
args = ARGV,
|
@@ -35,7 +41,7 @@ module Gruf
|
|
35
41
|
)
|
36
42
|
@args = args
|
37
43
|
setup! # ensure we set some defaults from CLI here so we can allow configuration
|
38
|
-
@services = services
|
44
|
+
@services = services.is_a?(Array) ? services : []
|
39
45
|
@hook_executor = hook_executor || Gruf::Hooks::Executor.new(hooks: Gruf.hooks&.prepare)
|
40
46
|
@server = server || Gruf::Server.new(Gruf.server_options)
|
41
47
|
@logger = logger || Gruf.logger || ::Logger.new($stderr)
|
@@ -46,8 +52,11 @@ module Gruf
|
|
46
52
|
#
|
47
53
|
def run
|
48
54
|
exception = nil
|
55
|
+
|
56
|
+
# allow lazy registering globally as late as possible, this allows more flexible binstub injections
|
57
|
+
register_services!
|
58
|
+
|
49
59
|
begin
|
50
|
-
@services.each { |s| @server.add_service(s) }
|
51
60
|
@hook_executor.call(:before_server_start, server: @server)
|
52
61
|
@server.start!
|
53
62
|
rescue StandardError => e
|
@@ -67,14 +76,15 @@ module Gruf
|
|
67
76
|
# Setup options for CLI execution and configure Gruf based on inputs
|
68
77
|
#
|
69
78
|
def setup!
|
70
|
-
|
79
|
+
@options = parse_options
|
71
80
|
|
72
|
-
Gruf.server_binding_url =
|
73
|
-
if
|
81
|
+
Gruf.server_binding_url = @options[:host] if @options[:host]
|
82
|
+
if @options.suppress_default_interceptors?
|
74
83
|
Gruf.interceptors.remove(Gruf::Interceptors::ActiveRecord::ConnectionReset)
|
75
84
|
Gruf.interceptors.remove(Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
|
76
85
|
end
|
77
|
-
Gruf.backtrace_on_error = true if
|
86
|
+
Gruf.backtrace_on_error = true if @options.backtrace_on_error?
|
87
|
+
Gruf.health_check_enabled = true if @options.health_check?
|
78
88
|
end
|
79
89
|
|
80
90
|
##
|
@@ -89,6 +99,8 @@ module Gruf
|
|
89
99
|
exit(0)
|
90
100
|
end
|
91
101
|
o.string '--host', 'Specify the binding url for the gRPC service'
|
102
|
+
o.string '--services', 'Optional. Run gruf with only the passed gRPC service classes (comma-separated)'
|
103
|
+
o.bool '--health-check', 'Serve the default gRPC health check (defaults to false). '
|
92
104
|
o.bool '--suppress-default-interceptors', 'Do not use the default interceptors'
|
93
105
|
o.bool '--backtrace-on-error', 'Push backtraces on exceptions to the error serializer'
|
94
106
|
o.null '-v', '--version', 'print gruf version' do
|
@@ -97,6 +109,83 @@ module Gruf
|
|
97
109
|
end
|
98
110
|
end
|
99
111
|
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Register services; note that this happens after gruf is initialized, and right before the server is run.
|
115
|
+
# This will interpret the services to run in the following precedence:
|
116
|
+
# 1. initializer arguments to the executor
|
117
|
+
# 2. ARGV options (the --services option)
|
118
|
+
# 3. services set to the global gruf configuration (Gruf.services)
|
119
|
+
#
|
120
|
+
def register_services!
|
121
|
+
# wait to load controllers until last possible second to allow late configuration
|
122
|
+
::Gruf.autoloaders.load!(controllers_path: Gruf.controllers_path)
|
123
|
+
|
124
|
+
services = determine_services(@services)
|
125
|
+
services = bind_health_check!(services) if health_check_enabled?
|
126
|
+
|
127
|
+
services.map! { |s| s.is_a?(Class) ? s : s.constantize }
|
128
|
+
|
129
|
+
if services.any?
|
130
|
+
services.each { |s| @server.add_service(s) }
|
131
|
+
return
|
132
|
+
end
|
133
|
+
|
134
|
+
raise NoServicesBoundError
|
135
|
+
rescue NoServicesBoundError
|
136
|
+
@logger.fatal 'FATAL ERROR: No services bound to this gruf process; please bind a service to a Gruf ' \
|
137
|
+
'controller to start the server successfully'
|
138
|
+
exit(1)
|
139
|
+
rescue NameError => e
|
140
|
+
@logger.fatal 'FATAL ERROR: Could not start server; passed services to run are not loaded or valid ' \
|
141
|
+
"constants: #{e.message}"
|
142
|
+
exit(1)
|
143
|
+
rescue StandardError => e
|
144
|
+
@logger.fatal "FATAL ERROR: Could not start server: #{e.message}"
|
145
|
+
exit(1)
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# @return [Boolean]
|
150
|
+
#
|
151
|
+
def health_check_enabled?
|
152
|
+
::Gruf.health_check_enabled
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# Load the health check if enabled into the services array
|
157
|
+
#
|
158
|
+
# @param [Array<Class>] services
|
159
|
+
# @return [Array<Class>]
|
160
|
+
#
|
161
|
+
def bind_health_check!(services)
|
162
|
+
# do this here to trigger autoloading the controller in zeitwerk, since we don't explicitly load this
|
163
|
+
# controller. This binds the service and makes sure the method handlers are setup.
|
164
|
+
require 'gruf/controllers/health_controller'
|
165
|
+
# if we're already bound to the services array (say someone explicitly passes the health check in, skip)
|
166
|
+
return services if services.include?(::Grpc::Health::V1::Health::Service)
|
167
|
+
|
168
|
+
# otherwise, manually add the grpc service
|
169
|
+
services << ::Grpc::Health::V1::Health::Service
|
170
|
+
services
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Determine how we load services (initializer -> ARGV -> Gruf.services)
|
175
|
+
#
|
176
|
+
# @return [Array<Class>]
|
177
|
+
#
|
178
|
+
def determine_services(services = [])
|
179
|
+
# first check initializer arguments
|
180
|
+
return services if services.any?
|
181
|
+
|
182
|
+
# next check CLI arguments
|
183
|
+
services = @options[:services].to_s.split(',').map(&:strip).uniq
|
184
|
+
# finally, if none, use global gruf autoloaded services
|
185
|
+
services = (::Gruf.services || []) unless services.any?
|
186
|
+
|
187
|
+
services
|
188
|
+
end
|
100
189
|
end
|
101
190
|
end
|
102
191
|
end
|
data/lib/gruf/client/error.rb
CHANGED
@@ -37,33 +37,5 @@ module Gruf
|
|
37
37
|
super
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
41
|
-
##
|
42
|
-
# See https://github.com/grpc/grpc-go/blob/master/codes/codes.go for a detailed summary of each error type
|
43
|
-
#
|
44
|
-
module Errors
|
45
|
-
class Base < Gruf::Client::Error; end
|
46
|
-
class Error < Base; end
|
47
|
-
class Validation < Base; end
|
48
|
-
|
49
|
-
class Ok < Base; end
|
50
|
-
|
51
|
-
class InvalidArgument < Validation; end
|
52
|
-
class NotFound < Validation; end
|
53
|
-
class AlreadyExists < Validation; end
|
54
|
-
class OutOfRange < Validation; end
|
55
|
-
|
56
|
-
class Cancelled < Error; end
|
57
|
-
class DataLoss < Error; end
|
58
|
-
class DeadlineExceeded < Error; end
|
59
|
-
class FailedPrecondition < Error; end
|
60
|
-
class Internal < Error; end
|
61
|
-
class PermissionDenied < Error; end
|
62
|
-
class ResourceExhausted < Error; end
|
63
|
-
class Unauthenticated < Error; end
|
64
|
-
class Unavailable < Error; end
|
65
|
-
class Unimplemented < Error; end
|
66
|
-
class Unknown < Error; end
|
67
|
-
end
|
68
40
|
end
|
69
41
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
module Gruf
|
19
|
+
class Client < SimpleDelegator
|
20
|
+
##
|
21
|
+
# See https://github.com/grpc/grpc-go/blob/master/codes/codes.go for a detailed summary of each error type
|
22
|
+
#
|
23
|
+
module Errors
|
24
|
+
class Base < Gruf::Client::Error; end
|
25
|
+
class Error < Base; end
|
26
|
+
class Validation < Base; end
|
27
|
+
|
28
|
+
class Ok < Base; end
|
29
|
+
|
30
|
+
class InvalidArgument < Validation; end
|
31
|
+
class NotFound < Validation; end
|
32
|
+
class AlreadyExists < Validation; end
|
33
|
+
class OutOfRange < Validation; end
|
34
|
+
|
35
|
+
class Cancelled < Error; end
|
36
|
+
class DataLoss < Error; end
|
37
|
+
class DeadlineExceeded < Error; end
|
38
|
+
class FailedPrecondition < Error; end
|
39
|
+
class Internal < Error; end
|
40
|
+
class PermissionDenied < Error; end
|
41
|
+
class ResourceExhausted < Error; end
|
42
|
+
class Unauthenticated < Error; end
|
43
|
+
class Unavailable < Error; end
|
44
|
+
class Unimplemented < Error; end
|
45
|
+
class Unknown < Error; end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/gruf/client.rb
CHANGED
@@ -15,9 +15,6 @@
|
|
15
15
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
16
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
17
|
#
|
18
|
-
require_relative 'client/error'
|
19
|
-
require_relative 'client/error_factory'
|
20
|
-
|
21
18
|
module Gruf
|
22
19
|
##
|
23
20
|
# Abstracts out the calling interface for interacting with gRPC clients. Streamlines calling and provides
|
data/lib/gruf/configuration.rb
CHANGED
@@ -77,6 +77,13 @@ module Gruf
|
|
77
77
|
# @return [Integer] Internal cache expiry period (in seconds) for the SynchronizedClient
|
78
78
|
# @!attribute rpc_server_options
|
79
79
|
# @return [Hash] A hash of RPC options for GRPC server configuration
|
80
|
+
# @!attribute health_check_enabled
|
81
|
+
# @return [Boolean] If true, will load and register `Gruf::Controllers::HealthController` with the default gRPC
|
82
|
+
# health check to the loaded gRPC server
|
83
|
+
# @!attribute health_check_hook
|
84
|
+
# @return [NilClass]
|
85
|
+
# @return [Proc] If set, will call this in the gRPC health check. It is required to return a
|
86
|
+
# `::Grpc::Health::V1::HealthCheckResponse` object in this proc to indicate the health of the server.
|
80
87
|
VALID_CONFIG_KEYS = {
|
81
88
|
root_path: '',
|
82
89
|
server_binding_url: '0.0.0.0:9001',
|
@@ -84,7 +91,7 @@ module Gruf
|
|
84
91
|
interceptors: nil,
|
85
92
|
hooks: nil,
|
86
93
|
default_channel_credentials: nil,
|
87
|
-
default_client_host: '',
|
94
|
+
default_client_host: '0.0.0.0:9001',
|
88
95
|
use_ssl: false,
|
89
96
|
ssl_crt_file: '',
|
90
97
|
ssl_key_file: '',
|
@@ -101,6 +108,8 @@ module Gruf
|
|
101
108
|
use_exception_message: true,
|
102
109
|
internal_error_message: 'Internal Server Error',
|
103
110
|
event_listener_proc: nil,
|
111
|
+
health_check_enabled: false,
|
112
|
+
health_check_hook: nil,
|
104
113
|
synchronized_client_internal_cache_expiry: 60,
|
105
114
|
rpc_server_options: {
|
106
115
|
pool_size: GRPC::RpcServer::DEFAULT_POOL_SIZE,
|
@@ -165,14 +174,15 @@ module Gruf
|
|
165
174
|
determine_loggers
|
166
175
|
self.ssl_crt_file = "#{root_path}config/ssl/#{environment}.crt"
|
167
176
|
self.ssl_key_file = "#{root_path}config/ssl/#{environment}.key"
|
168
|
-
|
177
|
+
cp = ::ENV.fetch('GRUF_CONTROLLERS_PATH', 'app/rpc').to_s
|
178
|
+
self.controllers_path = root_path.to_s.empty? ? cp : "#{root_path}/#{cp}"
|
169
179
|
self.backtrace_on_error = ::ENV.fetch('GRPC_BACKTRACE_ON_ERROR', 0).to_i.positive?
|
170
180
|
self.rpc_server_options = {
|
171
181
|
max_waiting_requests: ::ENV.fetch('GRPC_SERVER_MAX_WAITING_REQUESTS',
|
172
182
|
GRPC::RpcServer::DEFAULT_MAX_WAITING_REQUESTS).to_i,
|
173
183
|
pool_size: ::ENV.fetch('GRPC_SERVER_POOL_SIZE', GRPC::RpcServer::DEFAULT_POOL_SIZE).to_i,
|
174
|
-
pool_keep_alive: ::ENV.fetch('GRPC_SERVER_POOL_KEEP_ALIVE', GRPC::Pool::DEFAULT_KEEP_ALIVE),
|
175
|
-
poll_period: ::ENV.fetch('GRPC_SERVER_POLL_PERIOD', GRPC::RpcServer::DEFAULT_POLL_PERIOD),
|
184
|
+
pool_keep_alive: ::ENV.fetch('GRPC_SERVER_POOL_KEEP_ALIVE', GRPC::Pool::DEFAULT_KEEP_ALIVE).to_i,
|
185
|
+
poll_period: ::ENV.fetch('GRPC_SERVER_POLL_PERIOD', GRPC::RpcServer::DEFAULT_POLL_PERIOD).to_i,
|
176
186
|
connect_md_proc: nil,
|
177
187
|
server_args: {}
|
178
188
|
}
|
@@ -180,9 +190,17 @@ module Gruf
|
|
180
190
|
interceptors.use(::Gruf::Interceptors::ActiveRecord::ConnectionReset)
|
181
191
|
interceptors.use(::Gruf::Interceptors::Instrumentation::OutputMetadataTimer)
|
182
192
|
end
|
193
|
+
self.health_check_enabled = ::ENV.fetch('GRUF_HEALTH_CHECK_ENABLED', 0).to_i.positive?
|
183
194
|
options
|
184
195
|
end
|
185
196
|
|
197
|
+
##
|
198
|
+
# @return [Boolean]
|
199
|
+
#
|
200
|
+
def development?
|
201
|
+
environment == 'development'
|
202
|
+
end
|
203
|
+
|
186
204
|
private
|
187
205
|
|
188
206
|
##
|
@@ -194,7 +212,7 @@ module Gruf
|
|
194
212
|
if defined?(::Rails)
|
195
213
|
::Rails.env.to_s
|
196
214
|
else
|
197
|
-
(
|
215
|
+
ENV.fetch('RACK_ENV') { ENV.fetch('RAILS_ENV', 'development') }.to_s
|
198
216
|
end
|
199
217
|
end
|
200
218
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2017-present, BigCommerce Pty. Ltd. All rights reserved
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
6
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
7
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
8
|
+
# persons to whom the Software is furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
11
|
+
# Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
14
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
15
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
|
+
#
|
18
|
+
require 'concurrent/atomic/read_write_lock'
|
19
|
+
|
20
|
+
module Gruf
|
21
|
+
module Controllers
|
22
|
+
##
|
23
|
+
# Handles autoloading of Gruf controllers in the application path. This allows for code reloading on Gruf
|
24
|
+
# controllers.
|
25
|
+
#
|
26
|
+
class Autoloader
|
27
|
+
include ::Gruf::Loggable
|
28
|
+
|
29
|
+
# @!attribute [r] path
|
30
|
+
# @return [String] The path for this autoloader
|
31
|
+
attr_reader :path
|
32
|
+
|
33
|
+
##
|
34
|
+
# @param [String] path
|
35
|
+
# @param [Boolean] reloading
|
36
|
+
# @param [String] tag
|
37
|
+
#
|
38
|
+
def initialize(path:, reloading: nil, tag: nil)
|
39
|
+
super()
|
40
|
+
@path = path
|
41
|
+
@loader = ::Zeitwerk::Loader.new
|
42
|
+
@loader.tag = tag || 'gruf-controllers'
|
43
|
+
@setup = false
|
44
|
+
@reloading_enabled = reloading || ::Gruf.development?
|
45
|
+
setup!
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Reload all files managed by the autoloader, if reloading is enabled
|
50
|
+
#
|
51
|
+
def reload
|
52
|
+
return unless @reloading_enabled
|
53
|
+
|
54
|
+
reload_lock.with_write_lock do
|
55
|
+
@loader.reload
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def with_fresh_controller(controller_name)
|
60
|
+
return yield(controller_name.constantize) unless @reloading_enabled
|
61
|
+
|
62
|
+
::Gruf::Autoloaders.reload
|
63
|
+
reload_lock.with_read_lock do
|
64
|
+
yield(controller_name.constantize)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
##
|
71
|
+
# @return [Boolean]
|
72
|
+
#
|
73
|
+
def setup!
|
74
|
+
return true if @setup
|
75
|
+
|
76
|
+
return false unless File.directory?(@path)
|
77
|
+
|
78
|
+
@loader.enable_reloading if @reloading_enabled
|
79
|
+
@loader.ignore("#{@path}/**/*_pb.rb")
|
80
|
+
@loader.push_dir(@path)
|
81
|
+
@loader.setup
|
82
|
+
# always eager load RPC files, so that the service binder can bind the Gruf::Controller instantiation
|
83
|
+
# to the gRPC Service classes
|
84
|
+
@loader.eager_load
|
85
|
+
@setup = true
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Handle thread-safe access to the loader
|
90
|
+
#
|
91
|
+
def reload_lock
|
92
|
+
@reload_lock ||= Concurrent::ReadWriteLock.new
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -15,9 +15,6 @@
|
|
15
15
|
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
16
16
|
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
17
17
|
#
|
18
|
-
require_relative 'request'
|
19
|
-
require_relative 'service_binder'
|
20
|
-
|
21
18
|
module Gruf
|
22
19
|
module Controllers
|
23
20
|
##
|
@@ -67,11 +64,12 @@ module Gruf
|
|
67
64
|
#
|
68
65
|
def self.bind(service)
|
69
66
|
service_class = service.name.constantize
|
70
|
-
Gruf.
|
67
|
+
::Gruf.logger.debug "[gruf] Binding #{service_class} to #{name}"
|
68
|
+
::Gruf.services << service_class
|
71
69
|
# rubocop:disable ThreadSafety/InstanceVariableInClassMethod
|
72
70
|
@bound_service = service_class
|
73
71
|
# rubocop:enable ThreadSafety/InstanceVariableInClassMethod
|
74
|
-
ServiceBinder.
|
72
|
+
ServiceBinder.bind!(service: service_class, controller: self)
|
75
73
|
end
|
76
74
|
|
77
75
|
##
|