blue_green_process 0.1.3 → 0.1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/README.md +24 -3
- data/lib/blue_green_process/master_process.rb +48 -5
- data/lib/blue_green_process/version.rb +1 -1
- data/lib/blue_green_process/worker_process.rb +8 -2
- data/lib/blue_green_process.rb +31 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 864e404c1e622e26d8689695a3791b4cf6c84709127617a5d778b550cb03527c
|
4
|
+
data.tar.gz: 002bba05528b78c7be7184c74bbbf0b94418c12d01d3c93cda5883043b6c376b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 511d89c74a5a24331cb3d4cc3e72aacf92d8eaed3cc2abcfc4e8964949fa2760fb311d3dd265f2ef185a35c53af70042fa2106b0c6297548793a4565fbc2de9b
|
7
|
+
data.tar.gz: ab4c37316936239229d153bc4c54747cb184319bd04438d16c81100c0dfc8346c27273727b57112cc9f8f764b91ca4daa28fea8f00d848f93774d216fcde9cbb
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## [0.1.4.2] - 2022-12-25
|
2
|
+
* プロセス終了時にDRb.stop_serviceを呼ぶのをやめました
|
3
|
+
|
4
|
+
## [0.1.4.1] - 2022-12-13
|
5
|
+
* プロセス終了時にDRb.stop_serviceを呼ぶようにしました
|
6
|
+
|
7
|
+
## [0.1.4] - 2022-12-12
|
8
|
+
* BlueGreenProcess.terminate_workers_immediately を呼び出すことで、シグナル経由で終了できるようになりました
|
9
|
+
|
1
10
|
## [0.1.3] - 2022-9-5
|
2
11
|
* 単一プロセスでの実行を延長するとGC.startを実行しなくなりました
|
3
12
|
* 長時間にわたって延長する時は呼び出し側でGC.startを実行してください
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,29 @@
|
|
2
2
|
|
3
3
|
A library that solves GC bottlenecks with multi-process.
|
4
4
|
|
5
|
+
```mermaid
|
6
|
+
sequenceDiagram
|
7
|
+
autonumber
|
8
|
+
loop
|
9
|
+
Master->>+Blue: be active
|
10
|
+
Blue->>-Master: response
|
11
|
+
Master->>+Blue: work
|
12
|
+
Blue->>-Master: response
|
13
|
+
Master->>+Blue: be inactive
|
14
|
+
Blue->>-Master: response
|
15
|
+
Blue-->>Blue: GC.start
|
16
|
+
Master->>+Green: be active
|
17
|
+
Green->>-Master: response
|
18
|
+
Master->>+Green: work
|
19
|
+
Green->>-Master: response
|
20
|
+
Master->>+Green: be inactive
|
21
|
+
Green->>-Master: response
|
22
|
+
Green->>Green: GC.start
|
23
|
+
end
|
24
|
+
|
25
|
+
```
|
26
|
+
|
27
|
+
|
5
28
|
## Installation
|
6
29
|
|
7
30
|
Install the gem and add to the application's Gemfile by executing:
|
@@ -31,6 +54,7 @@ end
|
|
31
54
|
sleep(1)
|
32
55
|
|
33
56
|
process.shutdown
|
57
|
+
# or BlueGreenProcess.terminate_workers_immediately
|
34
58
|
Process.waitall
|
35
59
|
```
|
36
60
|
|
@@ -148,6 +172,3 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
148
172
|
|
149
173
|
## TODO
|
150
174
|
* shutdownしないでプロセスを停止したときにSIGINTを受け取りたい
|
151
|
-
* runしている間にsignalをもらったらすぐにdieを送りたい
|
152
|
-
* inactiveからactiveへの切り替えになる時間を測定したい
|
153
|
-
* GCが長引いてactiveプロセスが処理開始に時間がかかるので
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "English"
|
3
4
|
module BlueGreenProcess
|
4
5
|
class ErrorWrapper < StandardError
|
5
6
|
attr_accessor :error_class, :message
|
@@ -24,10 +25,18 @@ module BlueGreenProcess
|
|
24
25
|
@max_work = max_work
|
25
26
|
end
|
26
27
|
|
28
|
+
# @return [Array<Integer>]
|
29
|
+
# 削除予定
|
27
30
|
def pids
|
28
31
|
@processes.map(&:pid)
|
29
32
|
end
|
30
33
|
|
34
|
+
# @return [Array<Integer>]
|
35
|
+
def worker_pids
|
36
|
+
@processes.map(&:pid)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [BlueGreenProcess::WorkerProcess]
|
31
40
|
def fork_process(label:, worker_instance:)
|
32
41
|
child_read, parent_write = IO.pipe
|
33
42
|
parent_read, child_write = IO.pipe
|
@@ -39,10 +48,12 @@ module BlueGreenProcess
|
|
39
48
|
parent_write.close
|
40
49
|
parent_read.close
|
41
50
|
process_status = :inactive
|
51
|
+
handle_signal(pipes: [child_read, child_write])
|
42
52
|
|
43
53
|
loop do
|
44
|
-
data = child_read.gets
|
45
|
-
|
54
|
+
next unless (data = child_read.gets)
|
55
|
+
|
56
|
+
json = JSON.parse(data.strip)
|
46
57
|
command = json["c"]
|
47
58
|
case command
|
48
59
|
when BlueGreenProcess::PROCESS_COMMAND_DIE, nil, ""
|
@@ -60,9 +71,7 @@ module BlueGreenProcess
|
|
60
71
|
data: BlueGreenProcess::SharedVariable.data }.to_json)
|
61
72
|
::GC.start unless BlueGreenProcess::SharedVariable.extend_run_on_this_process
|
62
73
|
when BlueGreenProcess::PROCESS_COMMAND_WORK
|
63
|
-
if process_status == BlueGreenProcess::PROCESS_STATUS_INACTIVE
|
64
|
-
warn "Should not be able to run in this status"
|
65
|
-
end
|
74
|
+
warn "Should not be able to run in this status" if process_status == BlueGreenProcess::PROCESS_STATUS_INACTIVE
|
66
75
|
|
67
76
|
begin
|
68
77
|
worker_instance.work(*label)
|
@@ -76,6 +85,8 @@ module BlueGreenProcess
|
|
76
85
|
puts "unknown. from #{label}(#{$PROCESS_ID})"
|
77
86
|
exit 1
|
78
87
|
end
|
88
|
+
rescue IOError # NOTE: シグナル経由でpipeが破棄された時にこれが発生する
|
89
|
+
exit 127
|
79
90
|
end
|
80
91
|
|
81
92
|
exit 0
|
@@ -87,6 +98,7 @@ module BlueGreenProcess
|
|
87
98
|
BlueGreenProcess::WorkerProcess.new(pid, label, parent_read, parent_write)
|
88
99
|
end
|
89
100
|
|
101
|
+
# @return [void]
|
90
102
|
def work
|
91
103
|
active_process do |process|
|
92
104
|
@max_work.times do
|
@@ -99,6 +111,7 @@ module BlueGreenProcess
|
|
99
111
|
raise eval(e.error_class), e.message
|
100
112
|
end
|
101
113
|
|
114
|
+
# @return [void]
|
102
115
|
def shutdown
|
103
116
|
@processes.each(&:shutdown)
|
104
117
|
Process.waitall
|
@@ -106,6 +119,7 @@ module BlueGreenProcess
|
|
106
119
|
|
107
120
|
private
|
108
121
|
|
122
|
+
# @return [void]
|
109
123
|
def active_process
|
110
124
|
active_process = nil
|
111
125
|
@stage[!@stage_state].be_inactive
|
@@ -126,5 +140,34 @@ module BlueGreenProcess
|
|
126
140
|
|
127
141
|
true
|
128
142
|
end
|
143
|
+
|
144
|
+
# @return [void]
|
145
|
+
# シグナルを受け取ってpipeをcloseする
|
146
|
+
def handle_signal(pipes:)
|
147
|
+
Thread.new do
|
148
|
+
self_read, self_write = IO.pipe
|
149
|
+
%w[INT TERM].each do |sig|
|
150
|
+
trap sig do
|
151
|
+
self_write.puts(sig)
|
152
|
+
end
|
153
|
+
rescue ArgumentError
|
154
|
+
warn("Signal #{sig} not supported")
|
155
|
+
end
|
156
|
+
|
157
|
+
begin
|
158
|
+
while (readable_io = IO.select([self_read]))
|
159
|
+
signal = readable_io.first[0].gets.strip
|
160
|
+
case signal
|
161
|
+
when "TERM"
|
162
|
+
raise Interrupt
|
163
|
+
when "INT"
|
164
|
+
BlueGreenProcess.logger.warn "[BLUE_GREEN_PROCESS][#{$PROCESS_ID}] INTシグナルは無視します"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
rescue Interrupt
|
168
|
+
pipes.each(&:close)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
129
172
|
end
|
130
173
|
end
|
@@ -47,7 +47,9 @@ module BlueGreenProcess
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def wait_response
|
50
|
-
|
50
|
+
return unless (text = read)
|
51
|
+
|
52
|
+
response = JSON.parse(text.strip)
|
51
53
|
BlueGreenProcess::SharedVariable.instance.restore(response["data"])
|
52
54
|
case response["c"]
|
53
55
|
when BlueGreenProcess::RESPONSE_OK
|
@@ -60,11 +62,15 @@ module BlueGreenProcess
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def read
|
63
|
-
rpipe.gets
|
65
|
+
rpipe.gets
|
66
|
+
rescue IOError
|
67
|
+
# NOTE: シグナル経由でpipeが破棄された時にこれが発生する
|
64
68
|
end
|
65
69
|
|
66
70
|
def write(token, args = {})
|
67
71
|
wpipe.puts({ c: token }.merge!(args).to_json)
|
72
|
+
rescue Errno::EPIPE
|
73
|
+
# NOTE: シグナル経由でpipeが破棄された時にこれが発生する
|
68
74
|
end
|
69
75
|
|
70
76
|
def enforce_to_be_active
|
data/lib/blue_green_process.rb
CHANGED
@@ -13,6 +13,8 @@ require "json"
|
|
13
13
|
require "singleton"
|
14
14
|
|
15
15
|
module BlueGreenProcess
|
16
|
+
PID_PATH = "/tmp/pbm_blue_green_process_pids"
|
17
|
+
|
16
18
|
PROCESS_STATUS_ACTIVE = :active
|
17
19
|
PROCESS_STATUS_INACTIVE = :inactive
|
18
20
|
|
@@ -25,7 +27,9 @@ module BlueGreenProcess
|
|
25
27
|
RESPONSE_ERROR = "ERR"
|
26
28
|
|
27
29
|
def self.new(worker_instance:, max_work:)
|
28
|
-
BlueGreenProcess::MasterProcess.new(worker_instance: worker_instance, max_work: max_work)
|
30
|
+
master_process = BlueGreenProcess::MasterProcess.new(worker_instance: worker_instance, max_work: max_work)
|
31
|
+
File.write(PID_PATH, master_process.worker_pids.join(","))
|
32
|
+
master_process
|
29
33
|
end
|
30
34
|
|
31
35
|
def self.configure
|
@@ -50,4 +54,30 @@ module BlueGreenProcess
|
|
50
54
|
@config = Config.new
|
51
55
|
@performance = Performance.new
|
52
56
|
end
|
57
|
+
|
58
|
+
# @return [void]
|
59
|
+
def self.terminate_workers_immediately
|
60
|
+
BlueGreenProcess.logger.warn "[BLUE_GREEN_PROCESS][#{$PROCESS_ID}] TERMシグナルを送信します"
|
61
|
+
|
62
|
+
worker_pids = nil
|
63
|
+
begin
|
64
|
+
worker_pids = File.read(PID_PATH).split(",").map(&:to_i)
|
65
|
+
rescue Errno::ENOENT
|
66
|
+
warn("#{PID_PATH}にファイルがありませんでした")
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
worker_pids.each do |worker_pid|
|
71
|
+
Process.kill "TERM", worker_pid
|
72
|
+
rescue Errno::ESRCH => e
|
73
|
+
BlueGreenProcess.logger.warn("[BLUE_GREEN_PROCESS][#{$PROCESS_ID}] workerプロセス(#{worker_pid})の終了に失敗しました。#{e.message}")
|
74
|
+
end
|
75
|
+
|
76
|
+
begin
|
77
|
+
Process.wait
|
78
|
+
rescue Errno::ECHILD => e
|
79
|
+
BlueGreenProcess.logger.warn("[BLUE_GREEN_PROCESS][#{$PROCESS_ID}] Process.wait(#{e.message})に失敗しました")
|
80
|
+
end
|
81
|
+
BlueGreenProcess.logger.warn "[BLUE_GREEN_PROCESS][#{$PROCESS_ID}] TERMシグナルを送信しました"
|
82
|
+
end
|
53
83
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blue_green_process
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jiikko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A library that solves GC bottlenecks with multi-process.
|
14
14
|
email:
|