serverengine 1.5.4 → 1.5.5

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.
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - rbx-19mode
7
+
8
+ branches:
9
+ only:
10
+ - master
11
+
12
+ script: bundle exec rake
data/Changelog CHANGED
@@ -1,4 +1,15 @@
1
1
 
2
+ 2013-09-17 version 1.5.5:
3
+
4
+ * worker_type=thread and embedded show uncaught errors caused in Worker#stop
5
+ and Worker#reload interface
6
+ * ProcessManager: enables child process heartbeat only if enable_heartbeat
7
+ option is true
8
+ * ProcessManager: doesn't call fcntl on pipe pairs if F_SETFD or FD_CLOEXEC
9
+ is not defined
10
+ * ProcessManager: added #spawn(*args) method
11
+
12
+
2
13
  2013-09-10 version 1.5.4:
3
14
 
4
15
  * SignalThread: fixed "Unexpected error can't be called from trap context"
@@ -27,19 +27,37 @@ module ServerEngine
27
27
 
28
28
  def stop(stop_graceful)
29
29
  super
30
- Thread.new { @worker.stop }
30
+ Thread.new do
31
+ begin
32
+ @worker.stop
33
+ rescue => e
34
+ ServerEngine.dump_uncaught_error(e)
35
+ end
36
+ end
31
37
  nil
32
38
  end
33
39
 
34
40
  def restart(stop_graceful)
35
41
  super
36
- Thread.new { @worker.stop }
42
+ Thread.new do
43
+ begin
44
+ @worker.stop
45
+ rescue => e
46
+ ServerEngine.dump_uncaught_error(e)
47
+ end
48
+ end
37
49
  nil
38
50
  end
39
51
 
40
52
  def reload
41
53
  super
42
- Thread.new { @worker.reload }
54
+ Thread.new do
55
+ begin
56
+ @worker.reload
57
+ rescue => e
58
+ ServerEngine.dump_uncaught_error(e)
59
+ end
60
+ end
43
61
  nil
44
62
  end
45
63
  end
@@ -23,6 +23,7 @@ module ServerEngine
23
23
  auto_tick: false,
24
24
  graceful_kill_signal: Daemon::Signals::GRACEFUL_STOP,
25
25
  immediate_kill_signal: Daemon::Signals::IMMEDIATE_STOP,
26
+ enable_heartbeat: true,
26
27
  auto_heartbeat: true,
27
28
  on_heartbeat_error: Proc.new do
28
29
  @logger.fatal "parent process unexpectedly terminated"
@@ -69,7 +70,7 @@ module ServerEngine
69
70
  $0 = @worker_process_name % [wid] if @worker_process_name
70
71
  w.install_signal_handlers
71
72
 
72
- Daemon.change_privilege(@chumask, @chgroup)
73
+ Daemon.change_privilege(@chuser, @chgroup)
73
74
  File.umask(@chumask) if @chumask
74
75
 
75
76
  ## recreate the logger created at Server#main
@@ -40,12 +40,24 @@ module ServerEngine
40
40
  end
41
41
 
42
42
  def send_stop(stop_graceful)
43
- Thread.new { @worker.stop }
43
+ Thread.new do
44
+ begin
45
+ @worker.stop
46
+ rescue => e
47
+ ServerEngine.dump_uncaught_error(e)
48
+ end
49
+ end
44
50
  nil
45
51
  end
46
52
 
47
53
  def send_reload
48
- Thread.new { @worker.reload }
54
+ Thread.new do
55
+ begin
56
+ @worker.reload
57
+ rescue => e
58
+ ServerEngine.dump_uncaught_error(e)
59
+ end
60
+ end
49
61
  end
50
62
 
51
63
  def join
@@ -31,9 +31,10 @@ module ServerEngine
31
31
  @immediate_kill_signal = config[:immediate_kill_signal] || :QUIT
32
32
 
33
33
  @auto_tick = config.fetch(:auto_tick, true)
34
- @tick_interval = config[:tick_interval] || 1
34
+ @auto_tick_interval = config[:auto_tick_interval] || 1
35
35
 
36
- @auto_heartbeat = config.fetch(:auto_heartbeat, true)
36
+ @enable_heartbeat = !!config[:enable_heartbeat]
37
+ @auto_heartbeat = !!config.fetch(:auto_heartbeat, true)
37
38
 
38
39
  case op = config[:on_heartbeat_error]
39
40
  when nil
@@ -60,6 +61,10 @@ module ServerEngine
60
61
 
61
62
  attr_accessor :cloexec_mode
62
63
 
64
+ attr_reader :graceful_kill_signal, :immediate_kill_signal
65
+ attr_reader :auto_tick, :auto_tick_interval
66
+ attr_reader :enable_heartbeat, :auto_heartbeat
67
+
63
68
  CONFIG_PARAMS = {
64
69
  heartbeat_interval: 1,
65
70
  heartbeat_timeout: 180,
@@ -71,8 +76,6 @@ module ServerEngine
71
76
  immediate_kill_timeout: 600,
72
77
  }
73
78
 
74
- attr_reader :graceful_kill_signal, :immediate_kill_signal
75
-
76
79
  CONFIG_PARAMS.each_pair do |key,default_value|
77
80
  attr_reader key
78
81
 
@@ -90,14 +93,14 @@ module ServerEngine
90
93
  end
91
94
 
92
95
  def fork(&block)
93
- rpipe, wpipe = new_pair
96
+ rpipe, wpipe = new_pipe_pair
94
97
 
95
98
  begin
96
99
  pid = Process.fork do
97
100
  self.close
98
101
  begin
99
102
  t = Target.new(wpipe)
100
- if @auto_heartbeat
103
+ if @enable_heartbeat && @auto_heartbeat
101
104
  HeartbeatThread.new(self, t, @heartbeat_error_proc)
102
105
  end
103
106
 
@@ -115,25 +118,68 @@ module ServerEngine
115
118
 
116
119
  @monitors << m
117
120
  @rpipes[rpipe] = m
121
+ rpipe = nil
118
122
 
119
123
  return m
120
124
 
121
125
  ensure
122
126
  wpipe.close
127
+ rpipe.close if rpipe
123
128
  end
124
129
  end
125
130
 
126
- def new_pair
127
- rpipe, wpipe = IO.pipe
131
+ def spawn(*args)
132
+ if args.first.is_a?(Hash)
133
+ env = args.shift.dup
134
+ else
135
+ env = {}
136
+ end
128
137
 
129
- case @cloexec_mode
130
- when :target_only
131
- wpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
132
- when :monitor_only
133
- rpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
138
+ if args.last.is_a?(Hash)
139
+ options = args.pop.dup
134
140
  else
135
- rpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
136
- wpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
141
+ options = {}
142
+ end
143
+
144
+ # pipe is necessary even if @enable_heartbeat == false because
145
+ # parent process detects shutdown of a child process using it
146
+ rpipe, wpipe = new_pipe_pair
147
+
148
+ begin
149
+ options[wpipe.fileno] = wpipe
150
+ if @enable_heartbeat
151
+ env['SERVERENGINE_HEARTBEAT_PIPE'] = wpipe.fileno.to_s
152
+ end
153
+
154
+ pid = Process.spawn(env, *args, options)
155
+
156
+ m = Monitor.new(self, pid)
157
+
158
+ @monitors << m
159
+ @rpipes[rpipe] = m
160
+ rpipe = nil
161
+
162
+ return m
163
+
164
+ ensure
165
+ wpipe.close
166
+ rpipe.close if rpipe
167
+ end
168
+ end
169
+
170
+ def new_pipe_pair
171
+ rpipe, wpipe = IO.pipe
172
+
173
+ if Fcntl.const_defined?(:F_SETFD) && Fcntl.const_defined?(:FD_CLOEXEC)
174
+ case @cloexec_mode
175
+ when :target_only
176
+ wpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
177
+ when :monitor_only
178
+ rpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
179
+ else
180
+ rpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
181
+ wpipe.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
182
+ end
137
183
  end
138
184
 
139
185
  rpipe.sync = true
@@ -159,26 +205,25 @@ module ServerEngine
159
205
  end
160
206
 
161
207
  ready_pipes, _, _ = IO.select(@rpipes.keys, nil, nil, blocking_timeout)
162
- unless ready_pipes
163
- return nil
164
- end
165
208
 
166
209
  time ||= Time.now
167
210
 
168
- ready_pipes.each do |r|
169
- begin
170
- r.read_nonblock(1024, @read_buffer)
171
- rescue Errno::EAGAIN, Errno::EINTR
172
- next
173
- rescue #EOFError
174
- m = @rpipes.delete(r)
175
- m.start_immediate_stop!
176
- r.close rescue nil
177
- next
178
- end
211
+ if ready_pipes
212
+ ready_pipes.each do |r|
213
+ begin
214
+ r.read_nonblock(1024, @read_buffer)
215
+ rescue Errno::EAGAIN, Errno::EINTR
216
+ next
217
+ rescue #EOFError
218
+ m = @rpipes.delete(r)
219
+ m.start_immediate_stop!
220
+ r.close rescue nil
221
+ next
222
+ end
179
223
 
180
- if m = @rpipes[r]
181
- m.last_heartbeat_time = time
224
+ if m = @rpipes[r]
225
+ m.last_heartbeat_time = time
226
+ end
182
227
  end
183
228
  end
184
229
 
@@ -189,7 +234,7 @@ module ServerEngine
189
234
  nil
190
235
  end
191
236
 
192
- def self.signal_name(n)
237
+ def self.format_signal_name(n)
193
238
  Signal.list.each_pair {|k,v|
194
239
  return "SIG#{k}" if n == v
195
240
  }
@@ -200,7 +245,7 @@ module ServerEngine
200
245
  case code
201
246
  when Process::Status
202
247
  if code.signaled?
203
- "signal #{signal_name(code.termsig)}"
248
+ "signal #{format_signal_name(code.termsig)}"
204
249
  else
205
250
  "status #{code.exitstatus}"
206
251
  end
@@ -301,10 +346,17 @@ module ServerEngine
301
346
  return false unless pid
302
347
 
303
348
  if !@immediate_kill_start_time
304
- # check escalation
305
- if heartbeat_delay >= @pm.heartbeat_timeout ||
306
- (@graceful_kill_start_time && @pm.graceful_kill_timeout >= 0 &&
307
- @graceful_kill_start_time < now - @pm.graceful_kill_timeout)
349
+ # check heartbeat timeout or escalation
350
+ if (
351
+ # heartbeat timeout
352
+ @pm.enable_heartbeat &&
353
+ heartbeat_delay >= @pm.heartbeat_timeout
354
+ ) || (
355
+ # escalation
356
+ @graceful_kill_start_time &&
357
+ @pm.graceful_kill_timeout >= 0 &&
358
+ @graceful_kill_start_time < now - @pm.graceful_kill_timeout
359
+ )
308
360
  # escalate to immediate kill
309
361
  @kill_count = 0
310
362
  @immediate_kill_start_time = now
@@ -362,7 +414,7 @@ module ServerEngine
362
414
 
363
415
  def main
364
416
  while true
365
- @pm.tick(@pm.tick_interval)
417
+ @pm.tick(@pm.auto_tick_interval)
366
418
  end
367
419
  nil
368
420
  rescue AlreadyClosedError
@@ -31,6 +31,7 @@ module ServerEngine
31
31
  auto_tick: false,
32
32
  graceful_kill_signal: Daemon::Signals::GRACEFUL_STOP,
33
33
  immediate_kill_signal: Daemon::Signals::IMMEDIATE_STOP,
34
+ enable_heartbeat: true,
34
35
  auto_heartbeat: true,
35
36
  )
36
37
 
@@ -1,3 +1,3 @@
1
1
  module ServerEngine
2
- VERSION = "1.5.4"
2
+ VERSION = "1.5.5"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serverengine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.4
4
+ version: 1.5.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-10 00:00:00.000000000 Z
12
+ date: 2013-09-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sigdump
@@ -67,6 +67,7 @@ extensions: []
67
67
  extra_rdoc_files: []
68
68
  files:
69
69
  - .gitignore
70
+ - .travis.yml
70
71
  - Changelog
71
72
  - Gemfile
72
73
  - LICENSE