blue_green_process 0.1.1 → 0.1.4

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: 52701d853a73bdee8a68d84b64f395590122f426d429c16d02429ea39ee04872
4
- data.tar.gz: b2b537c0446067c400e58a578baa3c6c2fab48db0222864c65abb8066bebd040
3
+ metadata.gz: c8905db7b63e35a80a3fa1272553fa4f84002e06747501b5749110a6edfed79e
4
+ data.tar.gz: 467fbb4d348d6fae9cfc70a013f88a560cc69a97690451d0b53908585d312e20
5
5
  SHA512:
6
- metadata.gz: ae61b9553e9a7e4af24b2d0371ba8ed60273e177b62f6ecc92115e643852bf02d67c4f5b5c32ad4f849cb37fc8b0116004299988654bb80f7335075d216e7889
7
- data.tar.gz: 9965a927dca526abc3143d7f471eb87ba5ffc987abe3c4d66a740c4621c5a62d431db249ce05eacf5129c0032e4cb81618ae179961a2db96844c6054605b6895
6
+ metadata.gz: f042564e95f91038d87faaae43d891d798204784a27f0b2fb1a038df195717c58755c7a65531216d97f4477296dee1775119ba1c01560c7fb067a336c1b613aa
7
+ data.tar.gz: 28d937c6280847b821ef49f4c247649549a13bf4a5c0f62d444cb6d5c391d88c491ac2d2fcca38a56947e4eb66b5583200885b4cc4e4f40c63cd61c2a8ac5bc1
data/.rubocop.yml CHANGED
@@ -25,3 +25,18 @@ Metrics/AbcSize:
25
25
 
26
26
  Metrics/MethodLength:
27
27
  Enabled: false
28
+
29
+ Metrics/CyclomaticComplexity:
30
+ Enabled: false
31
+
32
+ Security/Eval:
33
+ Enabled: false
34
+
35
+ Lint/MissingSuper:
36
+ Enabled: false
37
+
38
+ Metrics/ClassLength:
39
+ Enabled: false
40
+
41
+ Lint/AmbiguousBlockAssociation:
42
+ Enabled: false
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ ## [0.1.4] - 2022-12-
2
+ * BlueGreenProcess.terminate_workers_immediately を呼び出すことで、シグナル経由で終了できるようになりました
3
+
4
+ ## [0.1.3] - 2022-9-5
5
+ * 単一プロセスでの実行を延長するとGC.startを実行しなくなりました
6
+ * 長時間にわたって延長する時は呼び出し側でGC.startを実行してください
7
+
8
+ ## [0.1.2] - 2022-9-5
9
+ - プロセス間での値の共有で値の共有ができるようになりました
10
+ - 単一プロセスでの実行を延長できるようになりました
11
+ - プロセスをforkした時に実行するコールバックをblockで設定できるようになりました
12
+ - プロセスの切り替え時にかかった時間を取得できるようになりました
13
+ - BlueGreenProcess.performance.process_switching_time_before_work
14
+
15
+ ## [0.1] - 2022-06-17
16
+
17
+ - Initial release
data/Gemfile CHANGED
@@ -6,6 +6,6 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "pry"
9
- gem "rake", "~> 13.0"
10
- gem "rspec", "~> 3.0"
9
+ gem "rake"
10
+ gem "rspec"
11
11
  gem "rubocop", "~> 1.21"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- blue_green_process (0.1.1)
4
+ blue_green_process (0.1.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -57,8 +57,8 @@ PLATFORMS
57
57
  DEPENDENCIES
58
58
  blue_green_process!
59
59
  pry
60
- rake (~> 13.0)
61
- rspec (~> 3.0)
60
+ rake
61
+ rspec
62
62
  rubocop (~> 1.21)
63
63
 
64
64
  BUNDLED WITH
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:
@@ -16,7 +39,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
16
39
 
17
40
  ```ruby
18
41
  BlueGreenProcess.configure do |config|
19
- config.after_fork = ->{ puts 'forked!' }
42
+ config.after_fork = ->{ puts 'forked!' }
20
43
  end
21
44
 
22
45
  process = BlueGreenProcess.new(
@@ -31,14 +54,99 @@ end
31
54
  sleep(1)
32
55
 
33
56
  process.shutdown
34
- loop do
35
- result = Process.waitall
36
- if result.empty?
37
- break
38
- else
39
- sleep(0.01)
57
+ # or BlueGreenProcess.terminate_workers_immediately
58
+ Process.waitall
59
+ ```
60
+
61
+ ### プロセス間での値の共有
62
+ * Hashが入っている'BlueGreenProcess::SharedVariable.data' の値はmaster process, work processで共有します.
63
+ * 共有するHashのキーは `config.shared_variables` で許可する必要があります
64
+ * プロセスを入れ替えるタイミングで値の復元とダンプを行います
65
+ * JSONでシリアライズしているので共有できるオブジェクトはプリミティブ型に限定されます
66
+ * GCの時間を軽減するために整数型だけを共有するとパフォーマンスに良さそう
67
+ * `config.shared_variables` に最初から入っている `extend_run_on_this_process` は消すことができません
68
+
69
+ ```ruby
70
+ BlueGreenProcess.configure do |config|
71
+ config.shared_variables = [:count]
72
+ end
73
+
74
+ worker_class = Class.new(BlueGreenProcess::BaseWorker) do
75
+ def initialize(*); end
76
+
77
+ def work(label)
78
+ BlueGreenProcess::SharedVariable.data['count'] += 1
79
+ puts "#{label}'s data['count'] is #{BlueGreenProcess::SharedVariable.data['count']}"
40
80
  end
41
81
  end
82
+
83
+ BlueGreenProcess::SharedVariable.data['count'] = 0
84
+ process = BlueGreenProcess.new(worker_instance: worker_class.new, max_work: 3)
85
+ process.work # blue
86
+ process.work # green
87
+ process.work # blue
88
+ BlueGreenProcess::SharedVariable.data['count']
89
+ ```
90
+
91
+ outputs
92
+
93
+ ```
94
+ blue's data['count'] is 1
95
+ blue's data['count'] is 2
96
+ blue's data['count'] is 3
97
+ green's data['count'] is 4
98
+ green's data['count'] is 5
99
+ green's data['count'] is 6
100
+ blue's data['count'] is 7
101
+ blue's data['count'] is 8
102
+ blue's data['count'] is 9
103
+ 9
104
+ ```
105
+
106
+ ### 単一プロセスでの実行を延長する
107
+ * workerクラスの中で、`BlueGreenProcess::SharedVariable.extend_run_on_this_process`にtrueをセットするともう一度同じプロセスで処理を行います
108
+ * 次の実行でtrueを明示しない限りはプロセスを切り替えます
109
+ * 単一プロセスでの実行を延長するとGC.startを実行しなくなります
110
+ * 長時間にわたって延長する時は呼び出し側でGC.startを実行してください
111
+
112
+ ```ruby
113
+ BlueGreenProcess.configure do |config|
114
+ config.shared_variables = [:count]
115
+ end
116
+
117
+ worker_class = Class.new(BlueGreenProcess::BaseWorker) do
118
+ def initialize(*); end
119
+
120
+ def work(label)
121
+ BlueGreenProcess::SharedVariable.data['count'] += 1
122
+ BlueGreenProcess::SharedVariable.extend_run_on_this_process = true
123
+ puts "#{label}'s data['count'] is #{BlueGreenProcess::SharedVariable.data['count']}"
124
+ end
125
+ end
126
+
127
+ BlueGreenProcess::SharedVariable.data['count'] = 0
128
+ process = BlueGreenProcess.new(worker_instance: worker_class.new, max_work: 3)
129
+ process.work # blue
130
+ process.work # blue
131
+ process.work # blue
132
+ process.work # blue
133
+ ```
134
+
135
+ ### Metrics
136
+ パフォーマンスの解析に使えます
137
+
138
+ ##### BlueGreenProcess.performance.process_switching_time_before_work
139
+ * プロセスを最後に入れ替えた時にかかった時間を返す
140
+
141
+ ### Callbacks
142
+ #### after_fork
143
+
144
+ プロセスをforkした時に実行する
145
+
146
+ ```ruby
147
+ BlueGreenProcess.configure do |config|
148
+ config.after_fork = ->{ puts 'forked!' }
149
+ end
42
150
  ```
43
151
 
44
152
  ## Development
@@ -56,14 +164,11 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERN
56
164
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
57
165
 
58
166
  ## NOTE
59
- * 前回の処理が終わっていないのにqueuingしない
60
- * workerの処理内容は固定
167
+ * 処理は直列で行う
168
+ * processが行う処理内容は引数を含めて固定
169
+ * A processがinactiveになった時に行うGC.startに時間がかかると、次にA processがbe_activeになったらレスポンスが遅れる. 構造上の仕様.
170
+ * これが起きる場合はオブジェクトの生成を減らすとか、blue, greenではなくプロセスのプールを作ってプロセスがGCに時間を費やせるようにする
171
+ * workerプロセスでエラーが起きたらmasterプロセスにそのエラーが伝わり、workerプロセスは終了します
61
172
 
62
173
  ## TODO
63
- * runしている間にsignalをもらったらすぐにdieを送りたい
64
- * プロセスを入れ替えるときに変数を受け渡しをする
65
- * queueしてからのdequeueするまでの時間を測定したい
66
- * webサーバでよくあるqueued timeみたいな扱い
67
- * これが伸びると致命的なのでチューニングできるようにしたいため
68
- * inactiveからactiveへの切り替えになる時間を測定したい
69
- * GCが長引いてactiveプロセスが処理開始に時間がかかるので
174
+ * shutdownしないでプロセスを停止したときにSIGINTを受け取りたい
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
4
+
3
5
  module BlueGreenProcess
4
6
  class Config
7
+ attr_writer :logger
8
+
5
9
  def after_fork=(block)
6
10
  @after_fork_block = block
7
11
  end
@@ -10,8 +14,20 @@ module BlueGreenProcess
10
14
  @after_fork_block || -> {}
11
15
  end
12
16
 
13
- def reset
14
- @after_fork_block = nil
17
+ def logger
18
+ @logger ||= Logger.new("/dev/null")
19
+ end
20
+
21
+ def shared_variables
22
+ @shared_variables ||= []
23
+ @shared_variables.push(:extend_run_on_this_process)
24
+ @shared_variables.uniq
25
+ end
26
+
27
+ def shared_variables=(value)
28
+ @shared_variables = value.map(&:to_s)
29
+ @shared_variables.push("extend_run_on_this_process")
30
+ @shared_variables.uniq
15
31
  end
16
32
  end
17
33
  end
@@ -1,6 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BlueGreenProcess
4
+ class ErrorWrapper < StandardError
5
+ attr_accessor :error_class, :message
6
+
7
+ def initialize(error_class, error_message)
8
+ self.error_class = error_class
9
+ self.message = error_message
10
+ end
11
+ end
12
+
4
13
  class MasterProcess
5
14
  def initialize(worker_instance:, max_work:)
6
15
  blue = fork_process(label: :blue, worker_instance: worker_instance)
@@ -15,47 +24,70 @@ module BlueGreenProcess
15
24
  @max_work = max_work
16
25
  end
17
26
 
27
+ # @return [Array<Integer>]
28
+ # 削除予定
29
+ def pids
30
+ @processes.map(&:pid)
31
+ end
32
+
33
+ # @return [Array<Integer>]
34
+ def worker_pids
35
+ @processes.map(&:pid)
36
+ end
37
+
38
+ # @return [BlueGreenProcess::WorkerProcess]
18
39
  def fork_process(label:, worker_instance:)
19
40
  child_read, parent_write = IO.pipe
20
41
  parent_read, child_write = IO.pipe
21
42
 
22
43
  pid = fork do
23
44
  BlueGreenProcess.config.after_fork.call
45
+ ::GC.disable
24
46
 
25
47
  parent_write.close
26
48
  parent_read.close
27
49
  process_status = :inactive
50
+ handle_signal(pipes: [child_read, child_write])
28
51
 
29
52
  loop do
30
- data = child_read.gets&.strip
31
- case data
53
+ next unless (data = child_read.gets)
54
+
55
+ json = JSON.parse(data.strip)
56
+ command = json["c"]
57
+ case command
32
58
  when BlueGreenProcess::PROCESS_COMMAND_DIE, nil, ""
33
- BlueGreenProcess.debug_log "#{label}'ll die(#{$PROCESS_ID})"
59
+ BlueGreenProcess.logger.debug "[BLUE_GREEN_PROCESS] #{label} will die(#{$PROCESS_ID})"
34
60
  exit 0
35
61
  when BlueGreenProcess::PROCESS_COMMAND_BE_ACTIVE
36
62
  process_status = BlueGreenProcess::PROCESS_STATUS_ACTIVE
37
- BlueGreenProcess.debug_log "#{label}'ll be active(#{$PROCESS_ID})"
38
- child_write.puts BlueGreenProcess::PROCESS_RESPONSE
39
- ::GC.disable
63
+ BlueGreenProcess::SharedVariable.instance.restore(json["data"])
64
+ BlueGreenProcess.logger.debug "[BLUE_GREEN_PROCESS] #{label} has become active(#{$PROCESS_ID})"
65
+ child_write.puts({ c: BlueGreenProcess::RESPONSE_OK }.to_json)
40
66
  when BlueGreenProcess::PROCESS_COMMAND_BE_INACTIVE
41
67
  process_status = BlueGreenProcess::PROCESS_STATUS_INACTIVE
42
- BlueGreenProcess.debug_log "#{label}'ll be inactive(#{$PROCESS_ID})"
43
- child_write.puts BlueGreenProcess::PROCESS_RESPONSE
44
- ::GC.enable
45
- ::GC.start
68
+ BlueGreenProcess.logger.debug "[BLUE_GREEN_PROCESS] #{label} has become inactive(#{$PROCESS_ID})"
69
+ child_write.puts({ c: BlueGreenProcess::RESPONSE_OK,
70
+ data: BlueGreenProcess::SharedVariable.data }.to_json)
71
+ ::GC.start unless BlueGreenProcess::SharedVariable.extend_run_on_this_process
46
72
  when BlueGreenProcess::PROCESS_COMMAND_WORK
47
73
  if process_status == BlueGreenProcess::PROCESS_STATUS_INACTIVE
48
74
  warn "Should not be able to run in this status"
49
75
  end
50
- # too verbose
51
- # BlueGreenProcess.debug_log "#{label}'ll work(#{$PROCESS_ID})"
52
- worker_instance.work(*label)
53
- child_write.puts BlueGreenProcess::PROCESS_RESPONSE
76
+
77
+ begin
78
+ worker_instance.work(*label)
79
+ child_write.puts({ c: BlueGreenProcess::RESPONSE_OK }.to_json)
80
+ rescue StandardError => e
81
+ child_write.puts({ c: BlueGreenProcess::RESPONSE_ERROR, err_class: e.class.name,
82
+ err_message: e.message }.to_json)
83
+ end
54
84
  else
55
85
  child_write.puts "NG"
56
86
  puts "unknown. from #{label}(#{$PROCESS_ID})"
57
87
  exit 1
58
88
  end
89
+ rescue IOError # NOTE: シグナル経由でpipeが破棄された時にこれが発生する
90
+ exit 127
59
91
  end
60
92
 
61
93
  exit 0
@@ -67,29 +99,74 @@ module BlueGreenProcess
67
99
  BlueGreenProcess::WorkerProcess.new(pid, label, parent_read, parent_write)
68
100
  end
69
101
 
102
+ # @return [void]
70
103
  def work
71
104
  active_process do |process|
72
105
  @max_work.times do
73
106
  process.work
74
107
  end
75
108
  end
76
-
77
- true
109
+ rescue BlueGreenProcess::ErrorWrapper => e
110
+ shutdown
111
+ BlueGreenProcess.logger.error "[BLUE_GREEN_PROCESS] #{e.error_class}: #{e.message}"
112
+ raise eval(e.error_class), e.message
78
113
  end
79
114
 
115
+ # @return [void]
80
116
  def shutdown
81
- @processes.each do |process|
82
- process.wpipe.puts(BlueGreenProcess::PROCESS_COMMAND_DIE)
83
- end
117
+ @processes.each(&:shutdown)
118
+ Process.waitall
84
119
  end
85
120
 
86
121
  private
87
122
 
123
+ # @return [void]
88
124
  def active_process
89
- active_process = @stage[@stage_state].be_active
125
+ active_process = nil
90
126
  @stage[!@stage_state].be_inactive
127
+ process_switching_time = Benchmark.realtime do
128
+ active_process = @stage[@stage_state].be_active
129
+ end
130
+ BlueGreenProcess.performance.process_switching_time_before_work = process_switching_time
131
+
91
132
  yield(active_process)
92
- @stage_state = !@stage_state
133
+
134
+ active_process.be_inactive
135
+ if BlueGreenProcess::SharedVariable.extend_run_on_this_process
136
+ BlueGreenProcess::SharedVariable.extend_run_on_this_process = false
137
+ active_process.be_active
138
+ else
139
+ @stage_state = !@stage_state
140
+ end
141
+
142
+ true
143
+ end
144
+
145
+ # @return [void]
146
+ # シグナルを受け取ってpipeをcloseする
147
+ def handle_signal(pipes:)
148
+ Thread.new do
149
+ self_read, self_write = IO.pipe
150
+ %w[INT TERM].each do |sig|
151
+ trap sig do
152
+ self_write.puts(sig)
153
+ end
154
+ rescue ArgumentError
155
+ warn("Signal #{sig} not supported")
156
+ end
157
+
158
+ begin
159
+ while (readable_io = IO.select([self_read]))
160
+ signal = readable_io.first[0].gets.strip
161
+ case signal
162
+ when "INT", "TERM"
163
+ raise Interrupt
164
+ end
165
+ end
166
+ rescue Interrupt
167
+ pipes.each(&:close)
168
+ end
169
+ end
93
170
  end
94
171
  end
95
172
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BlueGreenProcess
4
+ class Performance
5
+ attr_writer :process_switching_time_before_work
6
+
7
+ def process_switching_time_before_work
8
+ @process_switching_time_before_work || 0
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "singleton"
4
+
5
+ module BlueGreenProcess
6
+ class SharedVariable
7
+ include Singleton
8
+
9
+ attr_writer :data
10
+
11
+ def self.data
12
+ instance.data
13
+ end
14
+
15
+ def self.data=(value)
16
+ instance.data = value
17
+ end
18
+
19
+ def self.extend_run_on_this_process
20
+ instance.extend_run_on_this_process
21
+ end
22
+
23
+ def self.extend_run_on_this_process=(value)
24
+ instance.extend_run_on_this_process = (value)
25
+ end
26
+
27
+ # @return [Hash]
28
+ def data
29
+ @data ||= {}
30
+ end
31
+
32
+ # @return [Boolean]
33
+ def extend_run_on_this_process
34
+ @data["extend_run_on_this_process"] ||= false
35
+ end
36
+
37
+ # @return [Boolean]
38
+ def extend_run_on_this_process=(value)
39
+ @data["extend_run_on_this_process"] = value
40
+ end
41
+
42
+ # @return [Hash]
43
+ def restore(json)
44
+ return if json.nil?
45
+
46
+ self.data = json.slice(*BlueGreenProcess.config.shared_variables)
47
+ end
48
+
49
+ # @return [Hash]
50
+ def dump
51
+ data.slice(*BlueGreenProcess.config.shared_variables)
52
+ end
53
+
54
+ # @return [NilClass]
55
+ def reset
56
+ @data = nil
57
+ end
58
+ end
59
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BlueGreenProcess
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.4"
5
5
  end
@@ -15,7 +15,8 @@ module BlueGreenProcess
15
15
  def be_active
16
16
  return self if status == BlueGreenProcess::PROCESS_STATUS_ACTIVE
17
17
 
18
- write_and_await_until_read(BlueGreenProcess::PROCESS_COMMAND_BE_ACTIVE)
18
+ write_and_await_until_read(BlueGreenProcess::PROCESS_COMMAND_BE_ACTIVE,
19
+ { data: BlueGreenProcess::SharedVariable.data })
19
20
  self.status = BlueGreenProcess::PROCESS_STATUS_ACTIVE
20
21
  self
21
22
  end
@@ -34,24 +35,42 @@ module BlueGreenProcess
34
35
  write_and_await_until_read(BlueGreenProcess::PROCESS_COMMAND_WORK)
35
36
  end
36
37
 
38
+ def shutdown
39
+ write(BlueGreenProcess::PROCESS_COMMAND_DIE)
40
+ end
41
+
37
42
  private
38
43
 
39
- def write_and_await_until_read(command)
40
- write(command)
44
+ def write_and_await_until_read(command, args = {})
45
+ write(command, args)
41
46
  wait_response
42
47
  end
43
48
 
44
49
  def wait_response
45
- response = read
46
- raise "invalid response." unless response == BlueGreenProcess::PROCESS_RESPONSE
50
+ return unless (text = read)
51
+
52
+ response = JSON.parse(text.strip)
53
+ BlueGreenProcess::SharedVariable.instance.restore(response["data"])
54
+ case response["c"]
55
+ when BlueGreenProcess::RESPONSE_OK
56
+ [BlueGreenProcess::SharedVariable.data, response]
57
+ when BlueGreenProcess::RESPONSE_ERROR
58
+ raise BlueGreenProcess::ErrorWrapper.new(response["err_class"], response["err_message"])
59
+ else
60
+ raise "invalid response."
61
+ end
47
62
  end
48
63
 
49
64
  def read
50
- rpipe.gets.strip
65
+ rpipe.gets
66
+ rescue IOError
67
+ # NOTE: シグナル経由でpipeが破棄された時にこれが発生する
51
68
  end
52
69
 
53
- def write(token)
54
- wpipe.puts token
70
+ def write(token, args = {})
71
+ wpipe.puts({ c: token }.merge!(args).to_json)
72
+ rescue Errno::EPIPE
73
+ # NOTE: シグナル経由でpipeが破棄された時にこれが発生する
55
74
  end
56
75
 
57
76
  def enforce_to_be_active
@@ -2,30 +2,34 @@
2
2
 
3
3
  require "English"
4
4
  require_relative "blue_green_process/version"
5
- require "blue_green_process/master_process"
6
- require "blue_green_process/worker_process"
7
- require "blue_green_process/base_worker"
8
- require "blue_green_process/config"
5
+ require_relative "blue_green_process/master_process"
6
+ require_relative "blue_green_process/worker_process"
7
+ require_relative "blue_green_process/base_worker"
8
+ require_relative "blue_green_process/config"
9
+ require_relative "blue_green_process/performance"
10
+ require_relative "blue_green_process/shared_variable"
11
+ require "benchmark"
12
+ require "json"
13
+ require "singleton"
9
14
 
10
15
  module BlueGreenProcess
16
+ PID_PATH = "/tmp/pbm_blue_green_process_pids"
17
+
11
18
  PROCESS_STATUS_ACTIVE = :active
12
19
  PROCESS_STATUS_INACTIVE = :inactive
13
20
 
14
21
  PROCESS_COMMAND_DIE = "die"
15
22
  PROCESS_COMMAND_BE_ACTIVE = "be_active"
16
- PROCESS_COMMAND_BE_INACTIVE = "work"
17
- PROCESS_COMMAND_WORK = "be_inactive"
23
+ PROCESS_COMMAND_BE_INACTIVE = "be_inactive"
24
+ PROCESS_COMMAND_WORK = "work"
18
25
 
19
- PROCESS_RESPONSE = "ACK"
26
+ RESPONSE_OK = "OK"
27
+ RESPONSE_ERROR = "ERR"
20
28
 
21
29
  def self.new(worker_instance:, max_work:)
22
- BlueGreenProcess::MasterProcess.new(worker_instance: worker_instance, max_work: max_work)
23
- end
24
-
25
- def self.debug_log(message)
26
- return unless ENV["VERBOSE"]
27
-
28
- puts message
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
@@ -37,4 +41,34 @@ module BlueGreenProcess
37
41
  def self.config
38
42
  @config ||= Config.new
39
43
  end
44
+
45
+ def self.logger
46
+ config.logger
47
+ end
48
+
49
+ def self.performance
50
+ @performance ||= Performance.new
51
+ end
52
+
53
+ def self.reset
54
+ @config = Config.new
55
+ @performance = Performance.new
56
+ end
57
+
58
+ # @return [void]
59
+ def self.terminate_workers_immediately
60
+ worker_pids = nil
61
+ begin
62
+ worker_pids = File.read(PID_PATH).split(",").map(&:to_i)
63
+ rescue Errno::ENOENT
64
+ warn("#{PID_PATH}にファイルがありませんでした")
65
+ return
66
+ end
67
+
68
+ worker_pids.each do |worker_pid|
69
+ Process.kill "TERM", worker_pid
70
+ rescue Errno::ESRCH => e
71
+ warn("BlueGreenProcess workerプロセス(#{worker_pid})の終了に失敗しました。", e.message)
72
+ end
73
+ end
40
74
  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.1
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - jiikko
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-02 00:00:00.000000000 Z
11
+ date: 2022-12-12 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A library that solves GC bottlenecks with multi-process.
14
14
  email:
@@ -19,16 +19,19 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".rspec"
21
21
  - ".rubocop.yml"
22
+ - ".ruby-version"
23
+ - CHANGELOG.md
22
24
  - Gemfile
23
25
  - Gemfile.lock
24
26
  - LICENSE.txt
25
27
  - README.md
26
28
  - Rakefile
27
- - blue_green_process.gemspec
28
29
  - lib/blue_green_process.rb
29
30
  - lib/blue_green_process/base_worker.rb
30
31
  - lib/blue_green_process/config.rb
31
32
  - lib/blue_green_process/master_process.rb
33
+ - lib/blue_green_process/performance.rb
34
+ - lib/blue_green_process/shared_variable.rb
32
35
  - lib/blue_green_process/version.rb
33
36
  - lib/blue_green_process/worker_process.rb
34
37
  - sig/blue_green_process.rbs
@@ -38,7 +41,7 @@ licenses:
38
41
  metadata:
39
42
  homepage_uri: https://github.com/splaplapla/blue_green_process
40
43
  source_code_uri: https://github.com/splaplapla/blue_green_process
41
- post_install_message:
44
+ post_install_message:
42
45
  rdoc_options: []
43
46
  require_paths:
44
47
  - lib
@@ -53,8 +56,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
53
56
  - !ruby/object:Gem::Version
54
57
  version: '0'
55
58
  requirements: []
56
- rubygems_version: 3.3.19
57
- signing_key:
59
+ rubygems_version: 3.0.3.1
60
+ signing_key:
58
61
  specification_version: 4
59
62
  summary: A library that solves GC bottlenecks with multi-process.
60
63
  test_files: []
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/blue_green_process/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "blue_green_process"
7
- spec.version = BlueGreenProcess::VERSION
8
- spec.authors = ["jiikko"]
9
- spec.email = ["n905i.1214@gmail.com"]
10
-
11
- spec.summary = "A library that solves GC bottlenecks with multi-process."
12
- spec.description = spec.summary
13
- spec.homepage = "https://github.com/splaplapla/blue_green_process"
14
- spec.license = "MIT"
15
- spec.required_ruby_version = ">= 2.5"
16
-
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = "https://github.com/splaplapla/blue_green_process"
19
-
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(__dir__) do
23
- `git ls-files -z`.split("\x0").reject do |f|
24
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
- end
26
- end
27
- spec.bindir = "exe"
28
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
- spec.require_paths = ["lib"]
30
-
31
- # Uncomment to register a new dependency of your gem
32
- # spec.add_dependency "example-gem", "~> 1.0"
33
-
34
- # For more information and examples about making a new gem, check out our
35
- # guide at: https://bundler.io/guides/creating_gem.html
36
- end