gruf 2.6.1 → 2.7.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 +4 -0
- data/README.md +40 -0
- data/lib/gruf.rb +3 -0
- data/lib/gruf/cli/executor.rb +26 -9
- data/lib/gruf/configuration.rb +2 -0
- data/lib/gruf/hooks/base.rb +34 -0
- data/lib/gruf/hooks/executor.rb +47 -0
- data/lib/gruf/hooks/registry.rb +159 -0
- data/lib/gruf/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fb4c36a5bd87553944bb4056fcca8875620cffe1d623f8757db6fa0e88f9aee
|
4
|
+
data.tar.gz: b3903a27dd46302ac99db4cbfa512b7ffc6b3ad9a4faafe83ac22fe83469ae29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba2ccb4ffe9915044fa8026eefa6e078b746d392af52401a779ad62bcd12ae2ae92cdfce73dc8680564d635bad0f2412b2ff3c740dd0fbfb5291a6a3c3fe70f6
|
7
|
+
data.tar.gz: c3db4263ae977cede3b9f3adb9cd90984e7906f72c1d005d3b61f977bb64eb3b442e8b4a092475f6fdd9af4ad5510d7e316255bbe59613c4f19d0d8231a48edf
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,10 @@ Changelog for the gruf gem. This includes internal history before the gem was ma
|
|
2
2
|
|
3
3
|
### Pending release
|
4
4
|
|
5
|
+
### 2.7.0
|
6
|
+
|
7
|
+
- Add hook support for executing code paths before a server is started, and after a server stops
|
8
|
+
|
5
9
|
### 2.6.1
|
6
10
|
|
7
11
|
- Add frozen_string_literal: true to files, update rubocop to 0.68
|
data/README.md
CHANGED
@@ -382,6 +382,46 @@ By default, the ActiveRecord Connection Reset interceptor and Output Metadata Ti
|
|
382
382
|
are loaded into gruf unless explicitly told not to via the `use_default_interceptors` configuration
|
383
383
|
parameter.
|
384
384
|
|
385
|
+
## Hooks
|
386
|
+
|
387
|
+
Hooks, unlike interceptors, are executed outside of the request chain, such as when a server starts
|
388
|
+
or stops. They run in FIFO order sequentially and do not wrap one another. They can be used to provide
|
389
|
+
custom boot sequences, external instrumentation support, or shutdown alerting.
|
390
|
+
|
391
|
+
You can create a hook by extending the `Gruf::Hooks::Base` class and defining the methods
|
392
|
+
on the hook you wish to implement:
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
class MyHook < Gruf::Hooks::Base
|
396
|
+
def before_server_start(server:)
|
397
|
+
# do my thing before the server starts
|
398
|
+
end
|
399
|
+
|
400
|
+
def after_server_stop(server:)
|
401
|
+
# do my thing after the server stops
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
# Then in an initializer:
|
406
|
+
|
407
|
+
Gruf.configure do |c|
|
408
|
+
c.hooks.use(MyHook, option_foo: 'value 123')
|
409
|
+
end
|
410
|
+
```
|
411
|
+
|
412
|
+
Exceptions raised in hooks will halt the execution chain and bubble up the stack appropriately.
|
413
|
+
|
414
|
+
### Available Hook Insertion Points
|
415
|
+
|
416
|
+
Current hook insertion points are:
|
417
|
+
|
418
|
+
* `before_server_start` - Right before the gRPC server starts
|
419
|
+
* `after_server_stop` - Right after the gRPC server is shutdown
|
420
|
+
|
421
|
+
Note that exceptions raised in `before_server_start` will halt the execution chain for the remaining
|
422
|
+
`before_server_start` hooks, but will still execute the `after_server_stop` hooks as expected. Exceptions raised
|
423
|
+
in `after_server_stop` will prevent further `after_server_stop` hooks from running.
|
424
|
+
|
385
425
|
## Instrumentation
|
386
426
|
|
387
427
|
gruf comes out of the box with a couple of instrumentation interceptors packed in:
|
data/lib/gruf.rb
CHANGED
@@ -30,6 +30,9 @@ require_relative 'gruf/controllers/base'
|
|
30
30
|
require_relative 'gruf/outbound/request_context'
|
31
31
|
require_relative 'gruf/interceptors/registry'
|
32
32
|
require_relative 'gruf/interceptors/base'
|
33
|
+
require_relative 'gruf/hooks/registry'
|
34
|
+
require_relative 'gruf/hooks/executor'
|
35
|
+
require_relative 'gruf/hooks/base'
|
33
36
|
require_relative 'gruf/timer'
|
34
37
|
require_relative 'gruf/response'
|
35
38
|
require_relative 'gruf/error'
|
data/lib/gruf/cli/executor.rb
CHANGED
@@ -26,22 +26,39 @@ module Gruf
|
|
26
26
|
##
|
27
27
|
# @param [Hash|ARGV]
|
28
28
|
#
|
29
|
-
def initialize(
|
29
|
+
def initialize(
|
30
|
+
args = ARGV,
|
31
|
+
server: nil,
|
32
|
+
services: nil,
|
33
|
+
hook_executor: nil,
|
34
|
+
logger: nil
|
35
|
+
)
|
30
36
|
@args = args
|
31
|
-
setup!
|
37
|
+
setup! # ensure we set some defaults from CLI here so we can allow configuration
|
38
|
+
@services = services || Gruf.services
|
39
|
+
@hook_executor = hook_executor || Gruf::Hooks::Executor.new(hooks: Gruf.hooks&.prepare)
|
40
|
+
@server = server || Gruf::Server.new(Gruf.server_options)
|
41
|
+
@logger = logger || Gruf.logger || ::Logger.new(STDERR)
|
32
42
|
end
|
33
43
|
|
34
44
|
##
|
35
45
|
# Run the server
|
36
46
|
#
|
37
47
|
def run
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
48
|
+
exception = nil
|
49
|
+
begin
|
50
|
+
@services.each { |s| @server.add_service(s) }
|
51
|
+
@hook_executor.call(:before_server_start, server: @server)
|
52
|
+
@server.start!
|
53
|
+
rescue StandardError => e
|
54
|
+
exception = e
|
55
|
+
# Catch the exception here so that we always ensure the post hook runs
|
56
|
+
# This allows systems wanting to provide external server instrumentation
|
57
|
+
# the ability to properly handle server failures
|
58
|
+
@logger.fatal("FATAL ERROR: #{e.message} #{e.backtrace.join("\n")}")
|
59
|
+
end
|
60
|
+
@hook_executor.call(:after_server_stop, server: @server)
|
61
|
+
raise exception if exception
|
45
62
|
end
|
46
63
|
|
47
64
|
private
|
data/lib/gruf/configuration.rb
CHANGED
@@ -25,6 +25,7 @@ module Gruf
|
|
25
25
|
server_binding_url: '0.0.0.0:9001',
|
26
26
|
server_options: {},
|
27
27
|
interceptors: nil,
|
28
|
+
hooks: nil,
|
28
29
|
default_client_host: '',
|
29
30
|
use_ssl: false,
|
30
31
|
ssl_crt_file: '',
|
@@ -99,6 +100,7 @@ module Gruf
|
|
99
100
|
send((k.to_s + '='), v)
|
100
101
|
end
|
101
102
|
self.interceptors = Gruf::Interceptors::Registry.new
|
103
|
+
self.hooks = Gruf::Hooks::Registry.new
|
102
104
|
self.root_path = Rails.root.to_s.chomp('/') if defined?(Rails)
|
103
105
|
if defined?(Rails) && Rails.logger
|
104
106
|
self.logger = Rails.logger
|
@@ -0,0 +1,34 @@
|
|
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
|
+
module Hooks
|
20
|
+
##
|
21
|
+
# Base class for a hook that allows execution at various points of Gruf server processes
|
22
|
+
#
|
23
|
+
class Base
|
24
|
+
include Gruf::Loggable
|
25
|
+
|
26
|
+
##
|
27
|
+
# @param [Hash] options
|
28
|
+
#
|
29
|
+
def initialize(options: nil)
|
30
|
+
@options = options || {}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,47 @@
|
|
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
|
+
module Hooks
|
20
|
+
##
|
21
|
+
# Base class for a hook that allows execution at various points of gRPC server processes
|
22
|
+
#
|
23
|
+
class Executor
|
24
|
+
include Gruf::Loggable
|
25
|
+
|
26
|
+
def initialize(hooks: nil)
|
27
|
+
@hooks = hooks || Gruf.hooks&.prepare || []
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Execute a hook point for each registered hook in the registry
|
32
|
+
#
|
33
|
+
# @param [Symbol] name
|
34
|
+
# @param [Hash] arguments
|
35
|
+
#
|
36
|
+
def call(name, arguments = {})
|
37
|
+
name = name.to_sym
|
38
|
+
|
39
|
+
@hooks.each do |hook|
|
40
|
+
next unless hook.respond_to?(name)
|
41
|
+
|
42
|
+
hook.send(name, arguments)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,159 @@
|
|
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
|
+
module Hooks
|
20
|
+
##
|
21
|
+
# Handles registration of hooks
|
22
|
+
#
|
23
|
+
class Registry
|
24
|
+
class HookNotFoundError < StandardError; end
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@registry = []
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Add a hook to the registry
|
32
|
+
#
|
33
|
+
# @param [Class] hook_class The class of the hook to add
|
34
|
+
# @param [Hash] options A hash of options to pass into the hook during initialization
|
35
|
+
#
|
36
|
+
def use(hook_class, options = {})
|
37
|
+
hooks_mutex do
|
38
|
+
@registry << {
|
39
|
+
klass: hook_class,
|
40
|
+
options: options
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
##
|
46
|
+
# Remove a hook from the registry
|
47
|
+
#
|
48
|
+
# @param [Class] hook_class The hook class to remove
|
49
|
+
# @raise [HookNotFoundError] if the hook is not found
|
50
|
+
#
|
51
|
+
def remove(hook_class)
|
52
|
+
pos = @registry.find_index { |opts| opts.fetch(:klass, '') == hook_class }
|
53
|
+
raise HookNotFoundError if pos.nil?
|
54
|
+
|
55
|
+
@registry.delete_at(pos)
|
56
|
+
end
|
57
|
+
|
58
|
+
##
|
59
|
+
# Insert a hook before another specified hook
|
60
|
+
#
|
61
|
+
# @param [Class] before_class The hook to insert before
|
62
|
+
# @param [Class] hook_class The class of the hook to add
|
63
|
+
# @param [Hash] options A hash of options to pass into the hook during initialization
|
64
|
+
# @raise [HookNotFoundError] if the before hook is not found
|
65
|
+
#
|
66
|
+
def insert_before(before_class, hook_class, options = {})
|
67
|
+
hooks_mutex do
|
68
|
+
pos = @registry.find_index { |opts| opts.fetch(:klass, '') == before_class }
|
69
|
+
raise HookNotFoundError if pos.nil?
|
70
|
+
|
71
|
+
@registry.insert(
|
72
|
+
pos,
|
73
|
+
klass: hook_class,
|
74
|
+
options: options
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Insert a hook after another specified hook
|
81
|
+
#
|
82
|
+
# @param [Class] after_class The hook to insert after
|
83
|
+
# @param [Class] hook_class The class of the hook to add
|
84
|
+
# @param [Hash] options A hash of options to pass into the hook during initialization
|
85
|
+
# @raise [HookNotFoundError] if the after hook is not found
|
86
|
+
#
|
87
|
+
def insert_after(after_class, hook_class, options = {})
|
88
|
+
hooks_mutex do
|
89
|
+
pos = @registry.find_index { |opts| opts.fetch(:klass, '') == after_class }
|
90
|
+
raise HookNotFoundError if pos.nil?
|
91
|
+
|
92
|
+
@registry.insert(
|
93
|
+
(pos + 1),
|
94
|
+
klass: hook_class,
|
95
|
+
options: options
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Return a list of the hook classes in the registry in their execution order
|
102
|
+
#
|
103
|
+
# @return [Array<Class>]
|
104
|
+
#
|
105
|
+
def list
|
106
|
+
hooks_mutex do
|
107
|
+
@registry.map { |h| h[:klass] }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Lazily load and return all hooks for the given request
|
113
|
+
#
|
114
|
+
# @return [Array<Gruf::Hooks::Base>]
|
115
|
+
#
|
116
|
+
def prepare
|
117
|
+
is = []
|
118
|
+
hooks_mutex do
|
119
|
+
@registry.each do |o|
|
120
|
+
is << o[:klass].new(options: o[:options])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
is
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Clear the registry
|
128
|
+
#
|
129
|
+
def clear
|
130
|
+
hooks_mutex do
|
131
|
+
@registry = []
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# @return [Integer] The number of hooks currently loaded
|
137
|
+
#
|
138
|
+
def count
|
139
|
+
hooks_mutex do
|
140
|
+
@registry ||= []
|
141
|
+
@registry.count
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
##
|
148
|
+
# Handle mutations to the hook registry in a thread-safe manner
|
149
|
+
#
|
150
|
+
def hooks_mutex(&block)
|
151
|
+
@hooks_mutex ||= begin
|
152
|
+
require 'monitor'
|
153
|
+
Monitor.new
|
154
|
+
end
|
155
|
+
@hooks_mutex.synchronize(&block)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/gruf/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gruf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shaun McCormick
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -148,6 +148,9 @@ files:
|
|
148
148
|
- lib/gruf/errors/debug_info.rb
|
149
149
|
- lib/gruf/errors/field.rb
|
150
150
|
- lib/gruf/errors/helpers.rb
|
151
|
+
- lib/gruf/hooks/base.rb
|
152
|
+
- lib/gruf/hooks/executor.rb
|
153
|
+
- lib/gruf/hooks/registry.rb
|
151
154
|
- lib/gruf/instrumentable_grpc_server.rb
|
152
155
|
- lib/gruf/integrations/rails/railtie.rb
|
153
156
|
- lib/gruf/interceptors/active_record/connection_reset.rb
|
@@ -193,8 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
196
|
- !ruby/object:Gem::Version
|
194
197
|
version: '0'
|
195
198
|
requirements: []
|
196
|
-
|
197
|
-
rubygems_version: 2.7.7
|
199
|
+
rubygems_version: 3.0.2
|
198
200
|
signing_key:
|
199
201
|
specification_version: 4
|
200
202
|
summary: gRPC Ruby Framework
|