grpc_fork_safety 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +28 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/README.md +59 -0
- data/Rakefile +12 -0
- data/lib/grpc_fork_safety/init.rb +15 -0
- data/lib/grpc_fork_safety/no_patch.rb +140 -0
- data/lib/grpc_fork_safety/patch.rb +20 -0
- data/lib/grpc_fork_safety/version.rb +5 -0
- data/lib/grpc_fork_safety.rb +5 -0
- metadata +70 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cf32a7caf75ba7c10da05f6a07eb4fb071d8d1242947be0006d7c80d0789542e
|
4
|
+
data.tar.gz: d999967eb9c841a40ea919f507ed9e2c5eaaf17909ca6614ffe5686317b2bc25
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 630e682ecaaf7352ae3a7db2db4e3768f348e52fa06ed1d906743ae07caced67af02ec0d231846ea6016983bbe0dff3cb52f065da389c89c0ee8908c6ac2a573
|
7
|
+
data.tar.gz: 2f9c820ab4004b05dd5f752ca02557e3e92818dcbd959fe067758203d37dc4b10da04bd76070de1da298fb5ae9cc6507bd611a2830e65c3758f56253f29385df
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.1
|
3
|
+
SuggestExtensions: false
|
4
|
+
NewCops: enable
|
5
|
+
|
6
|
+
Style/StringLiterals:
|
7
|
+
EnforcedStyle: double_quotes
|
8
|
+
|
9
|
+
Style/StringLiteralsInInterpolation:
|
10
|
+
EnforcedStyle: double_quotes
|
11
|
+
|
12
|
+
Style/Documentation:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Style/IfUnlessModifier:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Gemspec/RequireMFA:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Metrics/MethodLength:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Metrics/AbcSize:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Metrics/ClassLength:
|
28
|
+
Enabled: false
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.3.3
|
data/CHANGELOG.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# GrpcForkSafety
|
2
|
+
|
3
|
+
Small gem that makes it easier to use the `grpc` gem in a fork safe way.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add the gem to your gemfile **before the `grpc` gem, or any gem that depend on `grpc`:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem "grpc_fork_safety"
|
11
|
+
gem "grpc"
|
12
|
+
gem "some-gem-that-depend-on-grpc"
|
13
|
+
```
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
There isn't anything particular to do, the gem will hook itself into Ruby and properly call the GRPC fork hooks when needed.
|
18
|
+
|
19
|
+
### `keep_disabled!`
|
20
|
+
|
21
|
+
However, when a process will need to fork repeatedly and won't need to use GRPC, you can optimize by calling `GrpcForkSafety.keep_disabled!`.
|
22
|
+
`grpc` will be enabled again in child process, but stay shutdown in the current process. This is useful for the main process of Puma or Unicorn
|
23
|
+
and for the mold process of Pitchfork, e.g.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
before_fork do
|
27
|
+
GrpcForkSafety.keep_disabled!
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
If for some reason you need to undo this, you can call `GrpcForkSafety.reenable!`
|
32
|
+
|
33
|
+
### Hooks
|
34
|
+
|
35
|
+
You can also register hooks to be called before GRPC is disabled and after it's re-enabled:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
GrpcForkSafety.before_disable do
|
39
|
+
ThreadPool.shutdown
|
40
|
+
end
|
41
|
+
|
42
|
+
GrpcForkSafety.after_enable do |in_child|
|
43
|
+
unless in_child
|
44
|
+
ThreadPool.start
|
45
|
+
end
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
Typically if you have background threads using GRPC, you should make sure to shut them down in `before_disable`.
|
50
|
+
|
51
|
+
## Development
|
52
|
+
|
53
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
54
|
+
|
55
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/grpc_fork_safety.
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "version"
|
4
|
+
|
5
|
+
if defined? GRPC::Core # ::GRPC may be loaded by bundler from `grpc.gemspec` so we check `GRPC::Core`
|
6
|
+
raise <<~ERROR
|
7
|
+
GRPC was loaded before `grpc_fork_safety` preventing to enable fork support
|
8
|
+
|
9
|
+
You may need to set `require: false` in your Gemfile where `grpc` is listed or
|
10
|
+
move `gem "grpc_fork_safety"` before `gem "grpc"`in your Gemfile.
|
11
|
+
ERROR
|
12
|
+
end
|
13
|
+
|
14
|
+
ENV["GRPC_ENABLE_FORK_SUPPORT"] = "1"
|
15
|
+
require "grpc"
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "grpc_fork_safety/init"
|
4
|
+
|
5
|
+
module GrpcForkSafety
|
6
|
+
Error = Class.new(StandardError)
|
7
|
+
|
8
|
+
class LifeCycle
|
9
|
+
def initialize(grpc = ::GRPC, process = ::Process)
|
10
|
+
@grpc = grpc
|
11
|
+
@process = process
|
12
|
+
@shutdown_by = nil
|
13
|
+
@keep_disabled = false
|
14
|
+
@before_disable = []
|
15
|
+
@after_enable = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_disable(&block)
|
19
|
+
raise Error, "No block given" unless block_given?
|
20
|
+
|
21
|
+
@before_disable << block
|
22
|
+
end
|
23
|
+
|
24
|
+
def after_enable(&block)
|
25
|
+
raise Error, "No block given" unless block_given?
|
26
|
+
|
27
|
+
@after_enable << block
|
28
|
+
end
|
29
|
+
|
30
|
+
def keep_disabled!
|
31
|
+
return if @keep_disabled
|
32
|
+
|
33
|
+
@keep_disabled = true
|
34
|
+
before_fork
|
35
|
+
end
|
36
|
+
|
37
|
+
def keep_disabled?
|
38
|
+
@keep_disabled
|
39
|
+
end
|
40
|
+
|
41
|
+
def reenable!
|
42
|
+
return unless @keep_disabled
|
43
|
+
|
44
|
+
@keep_disabled = false
|
45
|
+
after_fork
|
46
|
+
end
|
47
|
+
|
48
|
+
def before_fork
|
49
|
+
return if @shutdown_by
|
50
|
+
|
51
|
+
@before_disable.each(&:call)
|
52
|
+
|
53
|
+
@grpc.prefork
|
54
|
+
@shutdown_by = @process.pid
|
55
|
+
end
|
56
|
+
|
57
|
+
def after_fork
|
58
|
+
if @shutdown_by.nil?
|
59
|
+
# noop
|
60
|
+
elsif @shutdown_by == @process.pid # In parent
|
61
|
+
unless @keep_disabled
|
62
|
+
@grpc.postfork_parent
|
63
|
+
@shutdown_by = nil
|
64
|
+
|
65
|
+
@after_enable.each do |cb|
|
66
|
+
cb.call(false)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
@keep_disabled = false
|
71
|
+
@shutdown_by = nil
|
72
|
+
@grpc.postfork_child
|
73
|
+
@after_enable.each do |cb|
|
74
|
+
cb.call(true)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class NoopLifeCycle
|
81
|
+
def initialize
|
82
|
+
@keep_disabled = false
|
83
|
+
end
|
84
|
+
|
85
|
+
def keep_disabled!
|
86
|
+
@keep_disabled = true
|
87
|
+
end
|
88
|
+
|
89
|
+
def keep_disabled?
|
90
|
+
@keep_disabled
|
91
|
+
end
|
92
|
+
|
93
|
+
def reenable!
|
94
|
+
@keep_disabled = false
|
95
|
+
end
|
96
|
+
|
97
|
+
def before_fork; end
|
98
|
+
|
99
|
+
def after_fork; end
|
100
|
+
|
101
|
+
def before_disable; end
|
102
|
+
|
103
|
+
def after_enable; end
|
104
|
+
end
|
105
|
+
|
106
|
+
GRPC_FORK_SUPPORT = RUBY_PLATFORM.match?(/linux/i)
|
107
|
+
|
108
|
+
@lifecycle = if GRPC_FORK_SUPPORT
|
109
|
+
LifeCycle.new
|
110
|
+
else
|
111
|
+
NoopLifeCycle.new
|
112
|
+
end
|
113
|
+
|
114
|
+
class << self
|
115
|
+
def keep_disabled!
|
116
|
+
@lifecycle.keep_disabled!
|
117
|
+
end
|
118
|
+
|
119
|
+
def reenable!
|
120
|
+
@lifecycle.keep_disabled = false
|
121
|
+
@lifecycle.after_fork
|
122
|
+
end
|
123
|
+
|
124
|
+
def before_disable(&)
|
125
|
+
@lifecycle.before_disable(&)
|
126
|
+
end
|
127
|
+
|
128
|
+
def after_enable(&)
|
129
|
+
@lifecycle.after_enable(&)
|
130
|
+
end
|
131
|
+
|
132
|
+
def _before_fork_hook
|
133
|
+
@lifecycle.before_fork
|
134
|
+
end
|
135
|
+
|
136
|
+
def _after_fork_hook
|
137
|
+
@lifecycle.after_fork
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "grpc_fork_safety/no_patch"
|
4
|
+
|
5
|
+
module GrpcForkSafety
|
6
|
+
module ProcessExtension
|
7
|
+
def _fork
|
8
|
+
GrpcForkSafety._before_fork_hook
|
9
|
+
pid = super
|
10
|
+
GrpcForkSafety._after_fork_hook
|
11
|
+
pid
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def patch!
|
17
|
+
::Process.singleton_class.prepend(ProcessExtension)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grpc_fork_safety
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jean Boussier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-07-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: grpc
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.57.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.57.0
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- jean.boussier@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".rubocop.yml"
|
35
|
+
- ".ruby-version"
|
36
|
+
- CHANGELOG.md
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- lib/grpc_fork_safety.rb
|
40
|
+
- lib/grpc_fork_safety/init.rb
|
41
|
+
- lib/grpc_fork_safety/no_patch.rb
|
42
|
+
- lib/grpc_fork_safety/patch.rb
|
43
|
+
- lib/grpc_fork_safety/version.rb
|
44
|
+
homepage: https://github.com/Shopify/grpc_fork_safety
|
45
|
+
licenses: []
|
46
|
+
metadata:
|
47
|
+
allowed_push_host: https://rubygems.org
|
48
|
+
homepage_uri: https://github.com/Shopify/grpc_fork_safety
|
49
|
+
source_code_uri: https://github.com/Shopify/grpc_fork_safety
|
50
|
+
changelog_uri: https://github.com/Shopify/grpc_fork_safety/blob/main/CHANGELOG.md
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 3.1.0
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubygems_version: 3.5.11
|
67
|
+
signing_key:
|
68
|
+
specification_version: 4
|
69
|
+
summary: Simple gems to facilitate making the grpc gem fork safe
|
70
|
+
test_files: []
|