blue_green_process 0.1.3 → 0.1.4.2
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/.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:
|