einhorn 0.8.2 → 1.0.1
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/Changes.md +15 -0
- data/README.md +7 -38
- data/einhorn.gemspec +23 -21
- data/example/pool_worker.rb +2 -2
- data/example/thin_example +8 -8
- data/example/time_server +5 -5
- data/lib/einhorn/client.rb +8 -8
- data/lib/einhorn/command/interface.rb +92 -98
- data/lib/einhorn/command.rb +75 -85
- data/lib/einhorn/compat.rb +7 -7
- data/lib/einhorn/event/abstract_text_descriptor.rb +32 -36
- data/lib/einhorn/event/ack_timer.rb +2 -2
- data/lib/einhorn/event/command_server.rb +7 -9
- data/lib/einhorn/event/connection.rb +1 -3
- data/lib/einhorn/event/loop_breaker.rb +2 -1
- data/lib/einhorn/event/persistent.rb +2 -2
- data/lib/einhorn/event/timer.rb +4 -4
- data/lib/einhorn/event.rb +20 -20
- data/lib/einhorn/prctl.rb +2 -2
- data/lib/einhorn/prctl_linux.rb +13 -14
- data/lib/einhorn/safe_yaml.rb +17 -0
- data/lib/einhorn/version.rb +1 -1
- data/lib/einhorn/worker.rb +26 -30
- data/lib/einhorn/worker_pool.rb +9 -9
- data/lib/einhorn.rb +120 -125
- metadata +37 -110
- data/.gitignore +0 -17
- data/.travis.yml +0 -10
- data/CONTRIBUTORS +0 -6
- data/Gemfile +0 -11
- data/History.txt +0 -4
- data/README.md.in +0 -94
- data/Rakefile +0 -27
- data/test/_lib.rb +0 -12
- data/test/integration/_lib/fixtures/env_printer/env_printer.rb +0 -26
- data/test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb +0 -23
- data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
- data/test/integration/_lib/fixtures/pdeathsig_printer/pdeathsig_printer.rb +0 -29
- data/test/integration/_lib/fixtures/signal_timeout/sleepy_server.rb +0 -23
- data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -24
- data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -148
- data/test/integration/_lib/helpers.rb +0 -4
- data/test/integration/_lib.rb +0 -6
- data/test/integration/pdeathsig.rb +0 -26
- data/test/integration/startup.rb +0 -31
- data/test/integration/upgrading.rb +0 -204
- data/test/unit/_lib/bad_worker.rb +0 -7
- data/test/unit/_lib/sleep_worker.rb +0 -5
- data/test/unit/einhorn/client.rb +0 -88
- data/test/unit/einhorn/command/interface.rb +0 -49
- data/test/unit/einhorn/command.rb +0 -135
- data/test/unit/einhorn/event.rb +0 -89
- data/test/unit/einhorn/worker_pool.rb +0 -39
- data/test/unit/einhorn.rb +0 -96
- /data/{LICENSE → LICENSE.txt} +0 -0
data/lib/einhorn/command.rb
CHANGED
@@ -1,27 +1,24 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require 'tmpdir'
|
1
|
+
require "set"
|
2
|
+
require "tmpdir"
|
4
3
|
|
5
|
-
require
|
6
|
-
require
|
4
|
+
require "einhorn/command/interface"
|
5
|
+
require "einhorn/prctl"
|
7
6
|
|
8
7
|
module Einhorn
|
9
8
|
module Command
|
10
9
|
def self.reap
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Einhorn::Event.break_loop
|
18
|
-
end
|
19
|
-
rescue Errno::ECHILD
|
10
|
+
loop do
|
11
|
+
Einhorn.log_debug("Going to reap a child process")
|
12
|
+
pid = Process.wait(-1, Process::WNOHANG)
|
13
|
+
return unless pid
|
14
|
+
cleanup(pid)
|
15
|
+
Einhorn::Event.break_loop
|
20
16
|
end
|
17
|
+
rescue Errno::ECHILD
|
21
18
|
end
|
22
19
|
|
23
20
|
def self.cleanup(pid)
|
24
|
-
unless spec = Einhorn::State.children[pid]
|
21
|
+
unless (spec = Einhorn::State.children[pid])
|
25
22
|
Einhorn.log_error("Could not find any config for exited child #{pid.inspect}! This probably indicates a bug in Einhorn.")
|
26
23
|
return
|
27
24
|
end
|
@@ -31,7 +28,7 @@ module Einhorn
|
|
31
28
|
# Unacked worker
|
32
29
|
if spec[:type] == :worker && !spec[:acked]
|
33
30
|
Einhorn::State.consecutive_deaths_before_ack += 1
|
34
|
-
extra =
|
31
|
+
extra = " before it was ACKed"
|
35
32
|
else
|
36
33
|
extra = nil
|
37
34
|
end
|
@@ -47,7 +44,7 @@ module Einhorn
|
|
47
44
|
end
|
48
45
|
|
49
46
|
def self.register_ping(pid, request_id)
|
50
|
-
unless spec = Einhorn::State.children[pid]
|
47
|
+
unless (spec = Einhorn::State.children[pid])
|
51
48
|
Einhorn.log_error("Could not find state for PID #{pid.inspect}; ignoring ACK.")
|
52
49
|
return
|
53
50
|
end
|
@@ -84,7 +81,7 @@ module Einhorn
|
|
84
81
|
end
|
85
82
|
|
86
83
|
def self.register_ack(pid)
|
87
|
-
unless spec = Einhorn::State.children[pid]
|
84
|
+
unless (spec = Einhorn::State.children[pid])
|
88
85
|
Einhorn.log_error("Could not find state for PID #{pid.inspect}; ignoring ACK.")
|
89
86
|
return
|
90
87
|
end
|
@@ -94,10 +91,8 @@ module Einhorn
|
|
94
91
|
return
|
95
92
|
end
|
96
93
|
|
97
|
-
if Einhorn::State.consecutive_deaths_before_ack > 0
|
98
|
-
|
99
|
-
else
|
100
|
-
extra = nil
|
94
|
+
extra = if Einhorn::State.consecutive_deaths_before_ack > 0
|
95
|
+
", breaking the streak of #{Einhorn::State.consecutive_deaths_before_ack} consecutive unacked workers dying"
|
101
96
|
end
|
102
97
|
Einhorn::State.consecutive_deaths_before_ack = 0
|
103
98
|
|
@@ -107,14 +102,14 @@ module Einhorn
|
|
107
102
|
Einhorn::Event.break_loop
|
108
103
|
end
|
109
104
|
|
110
|
-
def self.signal_all(signal, children=nil, record=true)
|
105
|
+
def self.signal_all(signal, children = nil, record = true)
|
111
106
|
children ||= Einhorn::WorkerPool.workers
|
112
107
|
signaled = {}
|
113
108
|
|
114
109
|
Einhorn.log_info("Sending #{signal} to #{children.inspect}", :upgrade)
|
115
110
|
|
116
111
|
children.each do |child|
|
117
|
-
unless spec = Einhorn::State.children[child]
|
112
|
+
unless (spec = Einhorn::State.children[child])
|
118
113
|
Einhorn.log_error("Trying to send #{signal} to dead child #{child.inspect}. The fact we tried this probably indicates a bug in Einhorn.", :upgrade)
|
119
114
|
next
|
120
115
|
end
|
@@ -150,10 +145,10 @@ module Einhorn
|
|
150
145
|
|
151
146
|
Einhorn.log_info("Child #{child.inspect} is still active after #{Einhorn::State.signal_timeout}s. Sending SIGKILL.")
|
152
147
|
begin
|
153
|
-
Process.kill(
|
148
|
+
Process.kill("KILL", child)
|
154
149
|
rescue Errno::ESRCH
|
155
150
|
end
|
156
|
-
spec[:signaled].add(
|
151
|
+
spec[:signaled].add("KILL")
|
157
152
|
end
|
158
153
|
end
|
159
154
|
|
@@ -161,20 +156,19 @@ module Einhorn
|
|
161
156
|
end
|
162
157
|
end
|
163
158
|
|
164
|
-
|
165
159
|
def self.increment
|
166
160
|
Einhorn::Event.break_loop
|
167
161
|
old = Einhorn::State.config[:number]
|
168
162
|
new = (Einhorn::State.config[:number] += 1)
|
169
163
|
output = "Incrementing number of workers from #{old} -> #{new}"
|
170
|
-
|
164
|
+
warn(output)
|
171
165
|
output
|
172
166
|
end
|
173
167
|
|
174
168
|
def self.decrement
|
175
169
|
if Einhorn::State.config[:number] <= 1
|
176
170
|
output = "Can't decrease number of workers (already at #{Einhorn::State.config[:number]}). Run kill #{$$} if you really want to kill einhorn."
|
177
|
-
|
171
|
+
warn(output)
|
178
172
|
return output
|
179
173
|
end
|
180
174
|
|
@@ -182,7 +176,7 @@ module Einhorn
|
|
182
176
|
old = Einhorn::State.config[:number]
|
183
177
|
new = (Einhorn::State.config[:number] -= 1)
|
184
178
|
output = "Decrementing number of workers from #{old} -> #{new}"
|
185
|
-
|
179
|
+
warn(output)
|
186
180
|
output
|
187
181
|
end
|
188
182
|
|
@@ -195,12 +189,12 @@ module Einhorn
|
|
195
189
|
old = Einhorn::State.config[:number]
|
196
190
|
Einhorn::State.config[:number] = new
|
197
191
|
output = "Altering worker count, #{old} -> #{new}. Will "
|
198
|
-
if old < new
|
199
|
-
|
192
|
+
output << if old < new
|
193
|
+
"spin up additional workers."
|
200
194
|
else
|
201
|
-
|
195
|
+
"gracefully terminate workers."
|
202
196
|
end
|
203
|
-
|
197
|
+
warn(output)
|
204
198
|
output
|
205
199
|
end
|
206
200
|
|
@@ -211,8 +205,8 @@ module Einhorn
|
|
211
205
|
end
|
212
206
|
|
213
207
|
{
|
214
|
-
:
|
215
|
-
:
|
208
|
+
state: global_state,
|
209
|
+
persistent_descriptors: descriptor_state
|
216
210
|
}
|
217
211
|
end
|
218
212
|
|
@@ -257,8 +251,8 @@ module Einhorn
|
|
257
251
|
|
258
252
|
begin
|
259
253
|
Einhorn.initialize_reload_environment
|
260
|
-
respawn_commandline = Einhorn.upgrade_commandline([
|
261
|
-
respawn_commandline << {
|
254
|
+
respawn_commandline = Einhorn.upgrade_commandline(["--with-state-fd", read.fileno.to_s])
|
255
|
+
respawn_commandline << {close_others: false}
|
262
256
|
Einhorn.log_info("About to re-exec einhorn master as #{respawn_commandline.inspect}", :reload)
|
263
257
|
Einhorn::Compat.exec(*respawn_commandline)
|
264
258
|
rescue SystemCallError => e
|
@@ -275,16 +269,16 @@ module Einhorn
|
|
275
269
|
end
|
276
270
|
end
|
277
271
|
|
278
|
-
def self.spinup(cmd=nil)
|
272
|
+
def self.spinup(cmd = nil)
|
279
273
|
cmd ||= Einhorn::State.cmd
|
280
274
|
index = next_index
|
281
275
|
expected_ppid = Process.pid
|
282
|
-
if Einhorn::State.preloaded
|
283
|
-
|
276
|
+
pid = if Einhorn::State.preloaded
|
277
|
+
fork do
|
284
278
|
Einhorn::TransientState.whatami = :worker
|
285
279
|
prepare_child_process
|
286
280
|
|
287
|
-
Einhorn.log_info(
|
281
|
+
Einhorn.log_info("About to tear down Einhorn state and run einhorn_main")
|
288
282
|
Einhorn::Command::Interface.uninit
|
289
283
|
Einhorn::Event.close_all_for_worker
|
290
284
|
Einhorn.set_argv(cmd, true)
|
@@ -297,7 +291,7 @@ module Einhorn
|
|
297
291
|
einhorn_main
|
298
292
|
end
|
299
293
|
else
|
300
|
-
|
294
|
+
fork do
|
301
295
|
Einhorn::TransientState.whatami = :worker
|
302
296
|
prepare_child_process
|
303
297
|
|
@@ -314,20 +308,20 @@ module Einhorn
|
|
314
308
|
setup_parent_watch(expected_ppid)
|
315
309
|
|
316
310
|
prepare_child_environment(index)
|
317
|
-
Einhorn::Compat.exec(cmd[0], cmd[1..-1], :
|
311
|
+
Einhorn::Compat.exec(cmd[0], cmd[1..-1], close_others: false)
|
318
312
|
end
|
319
313
|
end
|
320
314
|
|
321
315
|
Einhorn.log_info("===> Launched #{pid} (index: #{index})", :upgrade)
|
322
316
|
Einhorn::State.last_spinup = Time.now
|
323
317
|
Einhorn::State.children[pid] = {
|
324
|
-
:
|
325
|
-
:
|
326
|
-
:
|
327
|
-
:
|
328
|
-
:
|
329
|
-
:
|
330
|
-
:
|
318
|
+
type: :worker,
|
319
|
+
version: Einhorn::State.version,
|
320
|
+
acked: false,
|
321
|
+
signaled: Set.new,
|
322
|
+
last_signaled_at: nil,
|
323
|
+
index: index,
|
324
|
+
spinup_time: Einhorn::State.last_spinup
|
331
325
|
}
|
332
326
|
|
333
327
|
# Set up whatever's needed for ACKing
|
@@ -336,6 +330,7 @@ module Einhorn
|
|
336
330
|
when :timer
|
337
331
|
Einhorn::Event::ACKTimer.open(ack_mode[:timeout], pid)
|
338
332
|
when :manual
|
333
|
+
# nothing to do
|
339
334
|
else
|
340
335
|
Einhorn.log_error("Unrecognized ACK mode #{type.inspect}")
|
341
336
|
end
|
@@ -343,24 +338,18 @@ module Einhorn
|
|
343
338
|
|
344
339
|
def self.prepare_child_environment(index)
|
345
340
|
# This is run from the child
|
346
|
-
ENV[
|
347
|
-
ENV[
|
341
|
+
ENV["EINHORN_MASTER_PID"] = Process.ppid.to_s
|
342
|
+
ENV["EINHORN_SOCK_PATH"] = Einhorn::Command::Interface.socket_path
|
348
343
|
if Einhorn::State.command_socket_as_fd
|
349
344
|
socket = UNIXSocket.open(Einhorn::Command::Interface.socket_path)
|
350
345
|
Einhorn::TransientState.socket_handles << socket
|
351
|
-
ENV[
|
346
|
+
ENV["EINHORN_SOCK_FD"] = socket.fileno.to_s
|
352
347
|
end
|
353
348
|
|
354
|
-
ENV[
|
355
|
-
Einhorn::State.bind_fds.each_with_index {|fd, i| ENV["EINHORN_FD_#{i}"] = fd.to_s}
|
356
|
-
|
357
|
-
ENV['EINHORN_CHILD_INDEX'] = index.to_s
|
349
|
+
ENV["EINHORN_FD_COUNT"] = Einhorn::State.bind_fds.length.to_s
|
350
|
+
Einhorn::State.bind_fds.each_with_index { |fd, i| ENV["EINHORN_FD_#{i}"] = fd.to_s }
|
358
351
|
|
359
|
-
|
360
|
-
# match Upstart's nominal internal support for space-separated
|
361
|
-
# FD lists, but nobody uses that in practice, and it makes
|
362
|
-
# finding individual FDs more difficult
|
363
|
-
ENV['EINHORN_FDS'] = Einhorn::State.bind_fds.map(&:to_s).join(' ')
|
352
|
+
ENV["EINHORN_CHILD_INDEX"] = index.to_s
|
364
353
|
end
|
365
354
|
|
366
355
|
# Reseed common ruby random number generators.
|
@@ -383,11 +372,11 @@ module Einhorn
|
|
383
372
|
|
384
373
|
# reseed OpenSSL::Random if it's loaded
|
385
374
|
if defined?(OpenSSL::Random)
|
386
|
-
if defined?(Random)
|
387
|
-
|
375
|
+
seed = if defined?(Random)
|
376
|
+
Random.new_seed
|
388
377
|
else
|
389
378
|
# Ruby 1.8
|
390
|
-
|
379
|
+
rand
|
391
380
|
end
|
392
381
|
OpenSSL::Random.seed(seed.to_s)
|
393
382
|
end
|
@@ -399,14 +388,14 @@ module Einhorn
|
|
399
388
|
end
|
400
389
|
|
401
390
|
def self.setup_parent_watch(expected_ppid)
|
402
|
-
if Einhorn::State.kill_children_on_exit
|
391
|
+
if Einhorn::State.kill_children_on_exit
|
403
392
|
begin
|
404
393
|
# NB: Having the USR2 signal handler set to terminate (the default) at
|
405
394
|
# this point is required. If it's set to a ruby handler, there are
|
406
395
|
# race conditions that could cause the worker to leak.
|
407
396
|
|
408
397
|
Einhorn::Prctl.set_pdeathsig("USR2")
|
409
|
-
if Process.ppid != expected_ppid
|
398
|
+
if Process.ppid != expected_ppid
|
410
399
|
Einhorn.log_error("Parent process died before we set pdeathsig; cowardly refusing to exec child process.")
|
411
400
|
exit(1)
|
412
401
|
end
|
@@ -424,18 +413,19 @@ module Einhorn
|
|
424
413
|
# upgrade, bring up all the new workers and don't cull any old workers
|
425
414
|
# until they're all up.
|
426
415
|
#
|
427
|
-
def self.full_upgrade(options={})
|
428
|
-
options = {:
|
416
|
+
def self.full_upgrade(options = {})
|
417
|
+
options = {smooth: false}.merge(options)
|
429
418
|
|
430
419
|
Einhorn::State.smooth_upgrade = options.fetch(:smooth)
|
431
420
|
reload_for_upgrade
|
432
421
|
end
|
433
422
|
|
434
423
|
def self.full_upgrade_smooth
|
435
|
-
full_upgrade(:
|
424
|
+
full_upgrade(smooth: true)
|
436
425
|
end
|
426
|
+
|
437
427
|
def self.full_upgrade_fleet
|
438
|
-
full_upgrade(:
|
428
|
+
full_upgrade(smooth: false)
|
439
429
|
end
|
440
430
|
|
441
431
|
def self.reload_for_upgrade
|
@@ -448,8 +438,8 @@ module Einhorn
|
|
448
438
|
Einhorn.log_info("Currently upgrading (#{Einhorn::WorkerPool.ack_count} / #{Einhorn::WorkerPool.ack_target} ACKs; bumping version and starting over)...", :upgrade)
|
449
439
|
else
|
450
440
|
Einhorn::State.upgrading = true
|
451
|
-
u_type = Einhorn::State.smooth_upgrade ?
|
452
|
-
Einhorn.log_info("Starting #{u_type} upgrade from version"
|
441
|
+
u_type = Einhorn::State.smooth_upgrade ? "smooth" : "fleet"
|
442
|
+
Einhorn.log_info("Starting #{u_type} upgrade from version" \
|
453
443
|
" #{Einhorn::State.version}...", :upgrade)
|
454
444
|
end
|
455
445
|
|
@@ -496,7 +486,7 @@ module Einhorn
|
|
496
486
|
end
|
497
487
|
|
498
488
|
if unsignaled > target
|
499
|
-
excess = Einhorn::WorkerPool.unsignaled_modern_workers_with_priority[0...(unsignaled-target)]
|
489
|
+
excess = Einhorn::WorkerPool.unsignaled_modern_workers_with_priority[0...(unsignaled - target)]
|
500
490
|
Einhorn.log_info("Have too many workers at the current version, so killing off #{excess.length} of them.")
|
501
491
|
signal_all("USR2", excess)
|
502
492
|
end
|
@@ -507,13 +497,13 @@ module Einhorn
|
|
507
497
|
|
508
498
|
def self.kill_expired_signaled_workers
|
509
499
|
now = Time.now
|
510
|
-
children = Einhorn::State.children.select do |_,c|
|
500
|
+
children = Einhorn::State.children.select do |_, c|
|
511
501
|
# Only interested in USR2 signaled workers
|
512
502
|
next unless c[:signaled] && c[:signaled].length > 0
|
513
|
-
next unless c[:signaled].include?(
|
503
|
+
next unless c[:signaled].include?("USR2")
|
514
504
|
|
515
505
|
# Ignore processes that have received KILL since it can't be trapped.
|
516
|
-
next if c[:signaled].include?(
|
506
|
+
next if c[:signaled].include?("KILL")
|
517
507
|
|
518
508
|
# Filter out those children that have not reached signal_timeout yet.
|
519
509
|
next unless c[:last_signaled_at]
|
@@ -527,12 +517,12 @@ module Einhorn
|
|
527
517
|
children.each do |pid, child|
|
528
518
|
Einhorn.log_info("Child #{pid.inspect} was signaled #{(child[:last_signaled_at] - now).abs.to_i}s ago. Sending SIGKILL as it is still active after #{Einhorn::State.signal_timeout}s timeout.", :upgrade)
|
529
519
|
begin
|
530
|
-
Process.kill(
|
520
|
+
Process.kill("KILL", pid)
|
531
521
|
rescue Errno::ESRCH
|
532
522
|
Einhorn.log_debug("Attempted to SIGKILL child #{pid.inspect} but the process does not exist.")
|
533
523
|
end
|
534
524
|
|
535
|
-
child[:signaled].add(
|
525
|
+
child[:signaled].add("KILL")
|
536
526
|
child[:last_signaled_at] = Time.now
|
537
527
|
end
|
538
528
|
end
|
@@ -559,7 +549,7 @@ module Einhorn
|
|
559
549
|
return
|
560
550
|
end
|
561
551
|
Einhorn.log_info("Launching #{missing} new workers")
|
562
|
-
missing.times {spinup}
|
552
|
+
missing.times { spinup }
|
563
553
|
end
|
564
554
|
|
565
555
|
# Unbounded exponential backoff is not a thing: we run into problems if
|
@@ -568,7 +558,7 @@ module Einhorn
|
|
568
558
|
# don't wait until the heat death of the universe to spin up new capacity.
|
569
559
|
MAX_SPINUP_INTERVAL = 30.0
|
570
560
|
|
571
|
-
def self.replenish_gradually(max_unacked=nil)
|
561
|
+
def self.replenish_gradually(max_unacked = nil)
|
572
562
|
return if Einhorn::TransientState.has_outstanding_spinup_timer
|
573
563
|
return unless Einhorn::WorkerPool.missing_worker_count > 0
|
574
564
|
|
@@ -591,7 +581,7 @@ module Einhorn
|
|
591
581
|
|
592
582
|
# Exponentially backoff automated spinup if we're just having
|
593
583
|
# things die before ACKing
|
594
|
-
spinup_interval = Einhorn::State.config[:seconds] * (1.5
|
584
|
+
spinup_interval = Einhorn::State.config[:seconds] * (1.5**Einhorn::State.consecutive_deaths_before_ack)
|
595
585
|
spinup_interval = [spinup_interval, MAX_SPINUP_INTERVAL].min
|
596
586
|
seconds_ago = (Time.now - Einhorn::State.last_spinup).to_f
|
597
587
|
|
@@ -618,14 +608,14 @@ module Einhorn
|
|
618
608
|
end
|
619
609
|
end
|
620
610
|
|
621
|
-
def self.quieter(log=true)
|
611
|
+
def self.quieter(log = true)
|
622
612
|
Einhorn::State.verbosity += 1 if Einhorn::State.verbosity < 2
|
623
613
|
output = "Verbosity set to #{Einhorn::State.verbosity}"
|
624
614
|
Einhorn.log_info(output) if log
|
625
615
|
output
|
626
616
|
end
|
627
617
|
|
628
|
-
def self.louder(log=true)
|
618
|
+
def self.louder(log = true)
|
629
619
|
Einhorn::State.verbosity -= 1 if Einhorn::State.verbosity > 0
|
630
620
|
output = "Verbosity set to #{Einhorn::State.verbosity}"
|
631
621
|
Einhorn.log_info(output) if log
|
data/lib/einhorn/compat.rb
CHANGED
@@ -11,10 +11,10 @@ module Einhorn
|
|
11
11
|
|
12
12
|
def self.cloexec!(fd, enable)
|
13
13
|
original = fd.fcntl(Fcntl::F_GETFD)
|
14
|
-
if enable
|
15
|
-
|
14
|
+
new = if enable
|
15
|
+
original | Fcntl::FD_CLOEXEC
|
16
16
|
else
|
17
|
-
|
17
|
+
original & (-Fcntl::FD_CLOEXEC - 1)
|
18
18
|
end
|
19
19
|
fd.fcntl(Fcntl::F_SETFD, new)
|
20
20
|
end
|
@@ -24,7 +24,7 @@ module Einhorn
|
|
24
24
|
end
|
25
25
|
|
26
26
|
# Opts are ignored in Ruby 1.8
|
27
|
-
def self.exec(script, args, opts={})
|
27
|
+
def self.exec(script, args, opts = {})
|
28
28
|
cmd = [script, script]
|
29
29
|
begin
|
30
30
|
Kernel.exec(cmd, *(args + [opts]))
|
@@ -53,18 +53,18 @@ module Einhorn
|
|
53
53
|
|
54
54
|
# linux / friends
|
55
55
|
begin
|
56
|
-
return File.read(
|
56
|
+
return File.read("/proc/cpuinfo").scan(/^processor\s*:/).count
|
57
57
|
rescue Errno::ENOENT
|
58
58
|
end
|
59
59
|
|
60
60
|
# OS X
|
61
|
-
if RUBY_PLATFORM
|
61
|
+
if RUBY_PLATFORM.match?(/darwin/)
|
62
62
|
return Integer(`sysctl -n hw.logicalcpu`)
|
63
63
|
end
|
64
64
|
|
65
65
|
# windows / friends
|
66
66
|
begin
|
67
|
-
require
|
67
|
+
require "win32ole"
|
68
68
|
rescue LoadError
|
69
69
|
else
|
70
70
|
wmi = WIN32OLE.connect("winmgmts://")
|
@@ -6,7 +6,7 @@ module Einhorn::Event
|
|
6
6
|
@@instance_counter = 0
|
7
7
|
|
8
8
|
def self.open(sock)
|
9
|
-
|
9
|
+
new(sock)
|
10
10
|
end
|
11
11
|
|
12
12
|
def initialize(sock)
|
@@ -16,7 +16,7 @@ module Einhorn::Event
|
|
16
16
|
@client_id = "#{@@instance_counter}:#{sock.fileno}"
|
17
17
|
|
18
18
|
@read_buffer = ""
|
19
|
-
@write_buffer = ""
|
19
|
+
@write_buffer = +""
|
20
20
|
|
21
21
|
@closed = false
|
22
22
|
|
@@ -40,24 +40,22 @@ module Einhorn::Event
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def notify_readable
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
process_read_buffer
|
60
|
-
end
|
43
|
+
loop do
|
44
|
+
return if @closed
|
45
|
+
chunk = @socket.read_nonblock(1024)
|
46
|
+
rescue Errno::EAGAIN
|
47
|
+
break
|
48
|
+
rescue EOFError, Errno::EPIPE, Errno::ECONNRESET
|
49
|
+
close
|
50
|
+
break
|
51
|
+
rescue => e
|
52
|
+
log_error("Caught unrecognized error while reading from socket: #{e} (#{e.class})")
|
53
|
+
close
|
54
|
+
break
|
55
|
+
else
|
56
|
+
log_debug("read #{chunk.length} bytes (#{chunk.inspect[0..20]})")
|
57
|
+
@read_buffer << chunk
|
58
|
+
process_read_buffer
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -72,19 +70,17 @@ module Einhorn::Event
|
|
72
70
|
end
|
73
71
|
|
74
72
|
def notify_writeable
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@write_buffer = @write_buffer[written..-1]
|
87
|
-
end
|
73
|
+
return if @closed
|
74
|
+
written = @socket.write_nonblock(@write_buffer)
|
75
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
|
76
|
+
rescue Errno::EPIPE, Errno::ECONNRESET
|
77
|
+
close
|
78
|
+
rescue => e
|
79
|
+
log_error("Caught unrecognized error while writing to socket: #{e} (#{e.class})")
|
80
|
+
close
|
81
|
+
else
|
82
|
+
log_debug("wrote #{written} bytes")
|
83
|
+
@write_buffer = @write_buffer[written..-1]
|
88
84
|
end
|
89
85
|
|
90
86
|
def to_io
|
@@ -102,9 +98,9 @@ module Einhorn::Event
|
|
102
98
|
end
|
103
99
|
|
104
100
|
def process_read_buffer
|
105
|
-
|
101
|
+
loop do
|
106
102
|
if @read_buffer.length > 0
|
107
|
-
break unless split = parse_record
|
103
|
+
break unless (split = parse_record)
|
108
104
|
record, remainder = split
|
109
105
|
log_debug("Read a record of #{record.length} bytes.")
|
110
106
|
@read_buffer = remainder
|
@@ -117,7 +113,7 @@ module Einhorn::Event
|
|
117
113
|
|
118
114
|
# Override in subclass. This lets you do streaming reads.
|
119
115
|
def parse_record
|
120
|
-
[@read_buffer,
|
116
|
+
[@read_buffer, ""]
|
121
117
|
end
|
122
118
|
|
123
119
|
def consume_record(record)
|
@@ -2,7 +2,7 @@ module Einhorn::Event
|
|
2
2
|
class ACKTimer < Timer
|
3
3
|
include Persistent
|
4
4
|
|
5
|
-
def initialize(time, pid, start=nil)
|
5
|
+
def initialize(time, pid, start = nil)
|
6
6
|
super(time, start) do
|
7
7
|
Einhorn::Command.register_timer_ack(time, pid)
|
8
8
|
end
|
@@ -10,7 +10,7 @@ module Einhorn::Event
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def to_state
|
13
|
-
{:
|
13
|
+
{class: self.class.to_s, time: @time, start: @start, pid: @pid}
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.from_state(state)
|
@@ -3,7 +3,7 @@ module Einhorn::Event
|
|
3
3
|
include Persistent
|
4
4
|
|
5
5
|
def self.open(server)
|
6
|
-
|
6
|
+
new(server)
|
7
7
|
end
|
8
8
|
|
9
9
|
def initialize(server)
|
@@ -15,14 +15,12 @@ module Einhorn::Event
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def notify_readable
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Connection.open(sock)
|
23
|
-
end
|
24
|
-
rescue Errno::EAGAIN
|
18
|
+
loop do
|
19
|
+
return if @closed
|
20
|
+
sock = Einhorn::Compat.accept_nonblock(@server)
|
21
|
+
Connection.open(sock)
|
25
22
|
end
|
23
|
+
rescue Errno::EAGAIN
|
26
24
|
end
|
27
25
|
|
28
26
|
def to_io
|
@@ -30,7 +28,7 @@ module Einhorn::Event
|
|
30
28
|
end
|
31
29
|
|
32
30
|
def to_state
|
33
|
-
{:
|
31
|
+
{class: self.class.to_s, server: @server.fileno}
|
34
32
|
end
|
35
33
|
|
36
34
|
def self.from_state(state)
|
@@ -11,8 +11,6 @@ module Einhorn::Event
|
|
11
11
|
split = @read_buffer.split("\n", 2)
|
12
12
|
if split.length > 1
|
13
13
|
split
|
14
|
-
else
|
15
|
-
nil
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
@@ -21,7 +19,7 @@ module Einhorn::Event
|
|
21
19
|
end
|
22
20
|
|
23
21
|
def to_state
|
24
|
-
state = {:
|
22
|
+
state = {class: self.class.to_s, socket: @socket.fileno}
|
25
23
|
# Don't include by default because it's not that pretty
|
26
24
|
state[:read_buffer] = @read_buffer if @read_buffer.length > 0
|
27
25
|
state[:write_buffer] = @write_buffer if @write_buffer.length > 0
|
@@ -8,7 +8,7 @@ module Einhorn::Event
|
|
8
8
|
|
9
9
|
def self.from_state(state)
|
10
10
|
klass_name = state[:class]
|
11
|
-
if klass = @@persistent[klass_name]
|
11
|
+
if (klass = @@persistent[klass_name])
|
12
12
|
klass.from_state(state)
|
13
13
|
else
|
14
14
|
Einhorn.log_error("Unrecognized persistent descriptor class #{klass_name.inspect}. Ignoring. This most likely indicates that your Einhorn version has upgraded. Everything should still be working, but it may be worth a restart.", :upgrade)
|
@@ -17,7 +17,7 @@ module Einhorn::Event
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.persistent?(descriptor)
|
20
|
-
@@persistent.values.any? {|klass| descriptor.
|
20
|
+
@@persistent.values.any? { |klass| descriptor.is_a?(klass) }
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|