gruf 2.13.1 → 2.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
##
|