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 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: