einhorn 0.8.2 → 1.0.0
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 +10 -0
- data/{LICENSE → LICENSE.txt} +0 -0
- data/README.md +5 -36
- 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 +76 -85
- data/lib/einhorn/compat.rb +7 -7
- data/lib/einhorn/event/abstract_text_descriptor.rb +31 -35
- 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 +19 -19
- 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 +33 -117
- 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/lib/einhorn/command.rb
CHANGED
@@ -1,27 +1,25 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require "pp"
|
2
|
+
require "set"
|
3
|
+
require "tmpdir"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
5
|
+
require "einhorn/command/interface"
|
6
|
+
require "einhorn/prctl"
|
7
7
|
|
8
8
|
module Einhorn
|
9
9
|
module Command
|
10
10
|
def self.reap
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Einhorn::Event.break_loop
|
18
|
-
end
|
19
|
-
rescue Errno::ECHILD
|
11
|
+
loop do
|
12
|
+
Einhorn.log_debug("Going to reap a child process")
|
13
|
+
pid = Process.wait(-1, Process::WNOHANG)
|
14
|
+
return unless pid
|
15
|
+
cleanup(pid)
|
16
|
+
Einhorn::Event.break_loop
|
20
17
|
end
|
18
|
+
rescue Errno::ECHILD
|
21
19
|
end
|
22
20
|
|
23
21
|
def self.cleanup(pid)
|
24
|
-
unless spec = Einhorn::State.children[pid]
|
22
|
+
unless (spec = Einhorn::State.children[pid])
|
25
23
|
Einhorn.log_error("Could not find any config for exited child #{pid.inspect}! This probably indicates a bug in Einhorn.")
|
26
24
|
return
|
27
25
|
end
|
@@ -31,7 +29,7 @@ module Einhorn
|
|
31
29
|
# Unacked worker
|
32
30
|
if spec[:type] == :worker && !spec[:acked]
|
33
31
|
Einhorn::State.consecutive_deaths_before_ack += 1
|
34
|
-
extra =
|
32
|
+
extra = " before it was ACKed"
|
35
33
|
else
|
36
34
|
extra = nil
|
37
35
|
end
|
@@ -47,7 +45,7 @@ module Einhorn
|
|
47
45
|
end
|
48
46
|
|
49
47
|
def self.register_ping(pid, request_id)
|
50
|
-
unless spec = Einhorn::State.children[pid]
|
48
|
+
unless (spec = Einhorn::State.children[pid])
|
51
49
|
Einhorn.log_error("Could not find state for PID #{pid.inspect}; ignoring ACK.")
|
52
50
|
return
|
53
51
|
end
|
@@ -84,7 +82,7 @@ module Einhorn
|
|
84
82
|
end
|
85
83
|
|
86
84
|
def self.register_ack(pid)
|
87
|
-
unless spec = Einhorn::State.children[pid]
|
85
|
+
unless (spec = Einhorn::State.children[pid])
|
88
86
|
Einhorn.log_error("Could not find state for PID #{pid.inspect}; ignoring ACK.")
|
89
87
|
return
|
90
88
|
end
|
@@ -94,10 +92,8 @@ module Einhorn
|
|
94
92
|
return
|
95
93
|
end
|
96
94
|
|
97
|
-
if Einhorn::State.consecutive_deaths_before_ack > 0
|
98
|
-
|
99
|
-
else
|
100
|
-
extra = nil
|
95
|
+
extra = if Einhorn::State.consecutive_deaths_before_ack > 0
|
96
|
+
", breaking the streak of #{Einhorn::State.consecutive_deaths_before_ack} consecutive unacked workers dying"
|
101
97
|
end
|
102
98
|
Einhorn::State.consecutive_deaths_before_ack = 0
|
103
99
|
|
@@ -107,14 +103,14 @@ module Einhorn
|
|
107
103
|
Einhorn::Event.break_loop
|
108
104
|
end
|
109
105
|
|
110
|
-
def self.signal_all(signal, children=nil, record=true)
|
106
|
+
def self.signal_all(signal, children = nil, record = true)
|
111
107
|
children ||= Einhorn::WorkerPool.workers
|
112
108
|
signaled = {}
|
113
109
|
|
114
110
|
Einhorn.log_info("Sending #{signal} to #{children.inspect}", :upgrade)
|
115
111
|
|
116
112
|
children.each do |child|
|
117
|
-
unless spec = Einhorn::State.children[child]
|
113
|
+
unless (spec = Einhorn::State.children[child])
|
118
114
|
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
115
|
next
|
120
116
|
end
|
@@ -150,10 +146,10 @@ module Einhorn
|
|
150
146
|
|
151
147
|
Einhorn.log_info("Child #{child.inspect} is still active after #{Einhorn::State.signal_timeout}s. Sending SIGKILL.")
|
152
148
|
begin
|
153
|
-
Process.kill(
|
149
|
+
Process.kill("KILL", child)
|
154
150
|
rescue Errno::ESRCH
|
155
151
|
end
|
156
|
-
spec[:signaled].add(
|
152
|
+
spec[:signaled].add("KILL")
|
157
153
|
end
|
158
154
|
end
|
159
155
|
|
@@ -161,20 +157,19 @@ module Einhorn
|
|
161
157
|
end
|
162
158
|
end
|
163
159
|
|
164
|
-
|
165
160
|
def self.increment
|
166
161
|
Einhorn::Event.break_loop
|
167
162
|
old = Einhorn::State.config[:number]
|
168
163
|
new = (Einhorn::State.config[:number] += 1)
|
169
164
|
output = "Incrementing number of workers from #{old} -> #{new}"
|
170
|
-
|
165
|
+
warn(output)
|
171
166
|
output
|
172
167
|
end
|
173
168
|
|
174
169
|
def self.decrement
|
175
170
|
if Einhorn::State.config[:number] <= 1
|
176
171
|
output = "Can't decrease number of workers (already at #{Einhorn::State.config[:number]}). Run kill #{$$} if you really want to kill einhorn."
|
177
|
-
|
172
|
+
warn(output)
|
178
173
|
return output
|
179
174
|
end
|
180
175
|
|
@@ -182,7 +177,7 @@ module Einhorn
|
|
182
177
|
old = Einhorn::State.config[:number]
|
183
178
|
new = (Einhorn::State.config[:number] -= 1)
|
184
179
|
output = "Decrementing number of workers from #{old} -> #{new}"
|
185
|
-
|
180
|
+
warn(output)
|
186
181
|
output
|
187
182
|
end
|
188
183
|
|
@@ -195,12 +190,12 @@ module Einhorn
|
|
195
190
|
old = Einhorn::State.config[:number]
|
196
191
|
Einhorn::State.config[:number] = new
|
197
192
|
output = "Altering worker count, #{old} -> #{new}. Will "
|
198
|
-
if old < new
|
199
|
-
|
193
|
+
output << if old < new
|
194
|
+
"spin up additional workers."
|
200
195
|
else
|
201
|
-
|
196
|
+
"gracefully terminate workers."
|
202
197
|
end
|
203
|
-
|
198
|
+
warn(output)
|
204
199
|
output
|
205
200
|
end
|
206
201
|
|
@@ -211,8 +206,8 @@ module Einhorn
|
|
211
206
|
end
|
212
207
|
|
213
208
|
{
|
214
|
-
:
|
215
|
-
:
|
209
|
+
state: global_state,
|
210
|
+
persistent_descriptors: descriptor_state
|
216
211
|
}
|
217
212
|
end
|
218
213
|
|
@@ -257,8 +252,8 @@ module Einhorn
|
|
257
252
|
|
258
253
|
begin
|
259
254
|
Einhorn.initialize_reload_environment
|
260
|
-
respawn_commandline = Einhorn.upgrade_commandline([
|
261
|
-
respawn_commandline << {
|
255
|
+
respawn_commandline = Einhorn.upgrade_commandline(["--with-state-fd", read.fileno.to_s])
|
256
|
+
respawn_commandline << {close_others: false}
|
262
257
|
Einhorn.log_info("About to re-exec einhorn master as #{respawn_commandline.inspect}", :reload)
|
263
258
|
Einhorn::Compat.exec(*respawn_commandline)
|
264
259
|
rescue SystemCallError => e
|
@@ -275,16 +270,16 @@ module Einhorn
|
|
275
270
|
end
|
276
271
|
end
|
277
272
|
|
278
|
-
def self.spinup(cmd=nil)
|
273
|
+
def self.spinup(cmd = nil)
|
279
274
|
cmd ||= Einhorn::State.cmd
|
280
275
|
index = next_index
|
281
276
|
expected_ppid = Process.pid
|
282
|
-
if Einhorn::State.preloaded
|
283
|
-
|
277
|
+
pid = if Einhorn::State.preloaded
|
278
|
+
fork do
|
284
279
|
Einhorn::TransientState.whatami = :worker
|
285
280
|
prepare_child_process
|
286
281
|
|
287
|
-
Einhorn.log_info(
|
282
|
+
Einhorn.log_info("About to tear down Einhorn state and run einhorn_main")
|
288
283
|
Einhorn::Command::Interface.uninit
|
289
284
|
Einhorn::Event.close_all_for_worker
|
290
285
|
Einhorn.set_argv(cmd, true)
|
@@ -297,7 +292,7 @@ module Einhorn
|
|
297
292
|
einhorn_main
|
298
293
|
end
|
299
294
|
else
|
300
|
-
|
295
|
+
fork do
|
301
296
|
Einhorn::TransientState.whatami = :worker
|
302
297
|
prepare_child_process
|
303
298
|
|
@@ -314,20 +309,20 @@ module Einhorn
|
|
314
309
|
setup_parent_watch(expected_ppid)
|
315
310
|
|
316
311
|
prepare_child_environment(index)
|
317
|
-
Einhorn::Compat.exec(cmd[0], cmd[1..-1], :
|
312
|
+
Einhorn::Compat.exec(cmd[0], cmd[1..-1], close_others: false)
|
318
313
|
end
|
319
314
|
end
|
320
315
|
|
321
316
|
Einhorn.log_info("===> Launched #{pid} (index: #{index})", :upgrade)
|
322
317
|
Einhorn::State.last_spinup = Time.now
|
323
318
|
Einhorn::State.children[pid] = {
|
324
|
-
:
|
325
|
-
:
|
326
|
-
:
|
327
|
-
:
|
328
|
-
:
|
329
|
-
:
|
330
|
-
:
|
319
|
+
type: :worker,
|
320
|
+
version: Einhorn::State.version,
|
321
|
+
acked: false,
|
322
|
+
signaled: Set.new,
|
323
|
+
last_signaled_at: nil,
|
324
|
+
index: index,
|
325
|
+
spinup_time: Einhorn::State.last_spinup
|
331
326
|
}
|
332
327
|
|
333
328
|
# Set up whatever's needed for ACKing
|
@@ -336,6 +331,7 @@ module Einhorn
|
|
336
331
|
when :timer
|
337
332
|
Einhorn::Event::ACKTimer.open(ack_mode[:timeout], pid)
|
338
333
|
when :manual
|
334
|
+
# nothing to do
|
339
335
|
else
|
340
336
|
Einhorn.log_error("Unrecognized ACK mode #{type.inspect}")
|
341
337
|
end
|
@@ -343,24 +339,18 @@ module Einhorn
|
|
343
339
|
|
344
340
|
def self.prepare_child_environment(index)
|
345
341
|
# This is run from the child
|
346
|
-
ENV[
|
347
|
-
ENV[
|
342
|
+
ENV["EINHORN_MASTER_PID"] = Process.ppid.to_s
|
343
|
+
ENV["EINHORN_SOCK_PATH"] = Einhorn::Command::Interface.socket_path
|
348
344
|
if Einhorn::State.command_socket_as_fd
|
349
345
|
socket = UNIXSocket.open(Einhorn::Command::Interface.socket_path)
|
350
346
|
Einhorn::TransientState.socket_handles << socket
|
351
|
-
ENV[
|
347
|
+
ENV["EINHORN_SOCK_FD"] = socket.fileno.to_s
|
352
348
|
end
|
353
349
|
|
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
|
350
|
+
ENV["EINHORN_FD_COUNT"] = Einhorn::State.bind_fds.length.to_s
|
351
|
+
Einhorn::State.bind_fds.each_with_index { |fd, i| ENV["EINHORN_FD_#{i}"] = fd.to_s }
|
358
352
|
|
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(' ')
|
353
|
+
ENV["EINHORN_CHILD_INDEX"] = index.to_s
|
364
354
|
end
|
365
355
|
|
366
356
|
# Reseed common ruby random number generators.
|
@@ -383,11 +373,11 @@ module Einhorn
|
|
383
373
|
|
384
374
|
# reseed OpenSSL::Random if it's loaded
|
385
375
|
if defined?(OpenSSL::Random)
|
386
|
-
if defined?(Random)
|
387
|
-
|
376
|
+
seed = if defined?(Random)
|
377
|
+
Random.new_seed
|
388
378
|
else
|
389
379
|
# Ruby 1.8
|
390
|
-
|
380
|
+
rand
|
391
381
|
end
|
392
382
|
OpenSSL::Random.seed(seed.to_s)
|
393
383
|
end
|
@@ -399,14 +389,14 @@ module Einhorn
|
|
399
389
|
end
|
400
390
|
|
401
391
|
def self.setup_parent_watch(expected_ppid)
|
402
|
-
if Einhorn::State.kill_children_on_exit
|
392
|
+
if Einhorn::State.kill_children_on_exit
|
403
393
|
begin
|
404
394
|
# NB: Having the USR2 signal handler set to terminate (the default) at
|
405
395
|
# this point is required. If it's set to a ruby handler, there are
|
406
396
|
# race conditions that could cause the worker to leak.
|
407
397
|
|
408
398
|
Einhorn::Prctl.set_pdeathsig("USR2")
|
409
|
-
if Process.ppid != expected_ppid
|
399
|
+
if Process.ppid != expected_ppid
|
410
400
|
Einhorn.log_error("Parent process died before we set pdeathsig; cowardly refusing to exec child process.")
|
411
401
|
exit(1)
|
412
402
|
end
|
@@ -424,18 +414,19 @@ module Einhorn
|
|
424
414
|
# upgrade, bring up all the new workers and don't cull any old workers
|
425
415
|
# until they're all up.
|
426
416
|
#
|
427
|
-
def self.full_upgrade(options={})
|
428
|
-
options = {:
|
417
|
+
def self.full_upgrade(options = {})
|
418
|
+
options = {smooth: false}.merge(options)
|
429
419
|
|
430
420
|
Einhorn::State.smooth_upgrade = options.fetch(:smooth)
|
431
421
|
reload_for_upgrade
|
432
422
|
end
|
433
423
|
|
434
424
|
def self.full_upgrade_smooth
|
435
|
-
full_upgrade(:
|
425
|
+
full_upgrade(smooth: true)
|
436
426
|
end
|
427
|
+
|
437
428
|
def self.full_upgrade_fleet
|
438
|
-
full_upgrade(:
|
429
|
+
full_upgrade(smooth: false)
|
439
430
|
end
|
440
431
|
|
441
432
|
def self.reload_for_upgrade
|
@@ -448,8 +439,8 @@ module Einhorn
|
|
448
439
|
Einhorn.log_info("Currently upgrading (#{Einhorn::WorkerPool.ack_count} / #{Einhorn::WorkerPool.ack_target} ACKs; bumping version and starting over)...", :upgrade)
|
449
440
|
else
|
450
441
|
Einhorn::State.upgrading = true
|
451
|
-
u_type = Einhorn::State.smooth_upgrade ?
|
452
|
-
Einhorn.log_info("Starting #{u_type} upgrade from version"
|
442
|
+
u_type = Einhorn::State.smooth_upgrade ? "smooth" : "fleet"
|
443
|
+
Einhorn.log_info("Starting #{u_type} upgrade from version" \
|
453
444
|
" #{Einhorn::State.version}...", :upgrade)
|
454
445
|
end
|
455
446
|
|
@@ -496,7 +487,7 @@ module Einhorn
|
|
496
487
|
end
|
497
488
|
|
498
489
|
if unsignaled > target
|
499
|
-
excess = Einhorn::WorkerPool.unsignaled_modern_workers_with_priority[0...(unsignaled-target)]
|
490
|
+
excess = Einhorn::WorkerPool.unsignaled_modern_workers_with_priority[0...(unsignaled - target)]
|
500
491
|
Einhorn.log_info("Have too many workers at the current version, so killing off #{excess.length} of them.")
|
501
492
|
signal_all("USR2", excess)
|
502
493
|
end
|
@@ -507,13 +498,13 @@ module Einhorn
|
|
507
498
|
|
508
499
|
def self.kill_expired_signaled_workers
|
509
500
|
now = Time.now
|
510
|
-
children = Einhorn::State.children.select do |_,c|
|
501
|
+
children = Einhorn::State.children.select do |_, c|
|
511
502
|
# Only interested in USR2 signaled workers
|
512
503
|
next unless c[:signaled] && c[:signaled].length > 0
|
513
|
-
next unless c[:signaled].include?(
|
504
|
+
next unless c[:signaled].include?("USR2")
|
514
505
|
|
515
506
|
# Ignore processes that have received KILL since it can't be trapped.
|
516
|
-
next if c[:signaled].include?(
|
507
|
+
next if c[:signaled].include?("KILL")
|
517
508
|
|
518
509
|
# Filter out those children that have not reached signal_timeout yet.
|
519
510
|
next unless c[:last_signaled_at]
|
@@ -527,12 +518,12 @@ module Einhorn
|
|
527
518
|
children.each do |pid, child|
|
528
519
|
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
520
|
begin
|
530
|
-
Process.kill(
|
521
|
+
Process.kill("KILL", pid)
|
531
522
|
rescue Errno::ESRCH
|
532
523
|
Einhorn.log_debug("Attempted to SIGKILL child #{pid.inspect} but the process does not exist.")
|
533
524
|
end
|
534
525
|
|
535
|
-
child[:signaled].add(
|
526
|
+
child[:signaled].add("KILL")
|
536
527
|
child[:last_signaled_at] = Time.now
|
537
528
|
end
|
538
529
|
end
|
@@ -559,7 +550,7 @@ module Einhorn
|
|
559
550
|
return
|
560
551
|
end
|
561
552
|
Einhorn.log_info("Launching #{missing} new workers")
|
562
|
-
missing.times {spinup}
|
553
|
+
missing.times { spinup }
|
563
554
|
end
|
564
555
|
|
565
556
|
# Unbounded exponential backoff is not a thing: we run into problems if
|
@@ -568,7 +559,7 @@ module Einhorn
|
|
568
559
|
# don't wait until the heat death of the universe to spin up new capacity.
|
569
560
|
MAX_SPINUP_INTERVAL = 30.0
|
570
561
|
|
571
|
-
def self.replenish_gradually(max_unacked=nil)
|
562
|
+
def self.replenish_gradually(max_unacked = nil)
|
572
563
|
return if Einhorn::TransientState.has_outstanding_spinup_timer
|
573
564
|
return unless Einhorn::WorkerPool.missing_worker_count > 0
|
574
565
|
|
@@ -591,7 +582,7 @@ module Einhorn
|
|
591
582
|
|
592
583
|
# Exponentially backoff automated spinup if we're just having
|
593
584
|
# things die before ACKing
|
594
|
-
spinup_interval = Einhorn::State.config[:seconds] * (1.5
|
585
|
+
spinup_interval = Einhorn::State.config[:seconds] * (1.5**Einhorn::State.consecutive_deaths_before_ack)
|
595
586
|
spinup_interval = [spinup_interval, MAX_SPINUP_INTERVAL].min
|
596
587
|
seconds_ago = (Time.now - Einhorn::State.last_spinup).to_f
|
597
588
|
|
@@ -618,14 +609,14 @@ module Einhorn
|
|
618
609
|
end
|
619
610
|
end
|
620
611
|
|
621
|
-
def self.quieter(log=true)
|
612
|
+
def self.quieter(log = true)
|
622
613
|
Einhorn::State.verbosity += 1 if Einhorn::State.verbosity < 2
|
623
614
|
output = "Verbosity set to #{Einhorn::State.verbosity}"
|
624
615
|
Einhorn.log_info(output) if log
|
625
616
|
output
|
626
617
|
end
|
627
618
|
|
628
|
-
def self.louder(log=true)
|
619
|
+
def self.louder(log = true)
|
629
620
|
Einhorn::State.verbosity -= 1 if Einhorn::State.verbosity > 0
|
630
621
|
output = "Verbosity set to #{Einhorn::State.verbosity}"
|
631
622
|
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)
|
@@ -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
|