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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89de1bb6ab2d7f30cf4063f1f04411da882e1061550603ee8ca866839a62f9d0
4
- data.tar.gz: dc64126db9c11c6e03dfa80be32aed24e0a7142f463efd04915729f335233c68
3
+ metadata.gz: 864e404c1e622e26d8689695a3791b4cf6c84709127617a5d778b550cb03527c
4
+ data.tar.gz: 002bba05528b78c7be7184c74bbbf0b94418c12d01d3c93cda5883043b6c376b
5
5
  SHA512:
6
- metadata.gz: '00874bdfeba757a3e8cc5bba6cebdf8a8bfa63d2ab8ff4779f26a9ce5bd8a5afd9ac933e4833b8a0042ce55cefda62d69f70077396d8f6df9bb64961d2e12f70'
7
- data.tar.gz: c5de3504ad7ec738260fcf4969ea9cb1cba38539f582ad122c159f543e05666ac1cc3dc7d8f1111134a5d8ad8ae4392006db8d7c4893f4d1214ae9bcea98361f
6
+ metadata.gz: 511d89c74a5a24331cb3d4cc3e72aacf92d8eaed3cc2abcfc4e8964949fa2760fb311d3dd265f2ef185a35c53af70042fa2106b0c6297548793a4565fbc2de9b
7
+ data.tar.gz: ab4c37316936239229d153bc4c54747cb184319bd04438d16c81100c0dfc8346c27273727b57112cc9f8f764b91ca4daa28fea8f00d848f93774d216fcde9cbb
data/.rubocop.yml CHANGED
@@ -40,3 +40,6 @@ Metrics/ClassLength:
40
40
 
41
41
  Lint/AmbiguousBlockAssociation:
42
42
  Enabled: false
43
+
44
+ Layout/LineLength:
45
+ Enabled: false
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- blue_green_process (0.1.3)
4
+ blue_green_process (0.1.4.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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&.strip
45
- json = JSON.parse(data)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BlueGreenProcess
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4.2"
5
5
  end
@@ -47,7 +47,9 @@ module BlueGreenProcess
47
47
  end
48
48
 
49
49
  def wait_response
50
- response = JSON.parse(read)
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.strip
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
@@ -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.3
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-09-05 00:00:00.000000000 Z
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: