einhorn 0.8.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +10 -0
  3. data/{LICENSE → LICENSE.txt} +0 -0
  4. data/README.md +5 -36
  5. data/einhorn.gemspec +23 -21
  6. data/example/pool_worker.rb +2 -2
  7. data/example/thin_example +8 -8
  8. data/example/time_server +5 -5
  9. data/lib/einhorn/client.rb +8 -8
  10. data/lib/einhorn/command/interface.rb +92 -98
  11. data/lib/einhorn/command.rb +76 -85
  12. data/lib/einhorn/compat.rb +7 -7
  13. data/lib/einhorn/event/abstract_text_descriptor.rb +31 -35
  14. data/lib/einhorn/event/ack_timer.rb +2 -2
  15. data/lib/einhorn/event/command_server.rb +7 -9
  16. data/lib/einhorn/event/connection.rb +1 -3
  17. data/lib/einhorn/event/loop_breaker.rb +2 -1
  18. data/lib/einhorn/event/persistent.rb +2 -2
  19. data/lib/einhorn/event/timer.rb +4 -4
  20. data/lib/einhorn/event.rb +19 -19
  21. data/lib/einhorn/prctl.rb +2 -2
  22. data/lib/einhorn/prctl_linux.rb +13 -14
  23. data/lib/einhorn/safe_yaml.rb +17 -0
  24. data/lib/einhorn/version.rb +1 -1
  25. data/lib/einhorn/worker.rb +26 -30
  26. data/lib/einhorn/worker_pool.rb +9 -9
  27. data/lib/einhorn.rb +120 -125
  28. metadata +33 -117
  29. data/.gitignore +0 -17
  30. data/.travis.yml +0 -10
  31. data/CONTRIBUTORS +0 -6
  32. data/Gemfile +0 -11
  33. data/History.txt +0 -4
  34. data/README.md.in +0 -94
  35. data/Rakefile +0 -27
  36. data/test/_lib.rb +0 -12
  37. data/test/integration/_lib/fixtures/env_printer/env_printer.rb +0 -26
  38. data/test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb +0 -23
  39. data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
  40. data/test/integration/_lib/fixtures/pdeathsig_printer/pdeathsig_printer.rb +0 -29
  41. data/test/integration/_lib/fixtures/signal_timeout/sleepy_server.rb +0 -23
  42. data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -24
  43. data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -148
  44. data/test/integration/_lib/helpers.rb +0 -4
  45. data/test/integration/_lib.rb +0 -6
  46. data/test/integration/pdeathsig.rb +0 -26
  47. data/test/integration/startup.rb +0 -31
  48. data/test/integration/upgrading.rb +0 -204
  49. data/test/unit/_lib/bad_worker.rb +0 -7
  50. data/test/unit/_lib/sleep_worker.rb +0 -5
  51. data/test/unit/einhorn/client.rb +0 -88
  52. data/test/unit/einhorn/command/interface.rb +0 -49
  53. data/test/unit/einhorn/command.rb +0 -135
  54. data/test/unit/einhorn/event.rb +0 -89
  55. data/test/unit/einhorn/worker_pool.rb +0 -39
  56. data/test/unit/einhorn.rb +0 -96
@@ -2,7 +2,7 @@ module Einhorn::Event
2
2
  class Timer
3
3
  attr_reader :time
4
4
 
5
- def initialize(time, start=nil, &blk)
5
+ def initialize(time, start = nil, &blk)
6
6
  @time = time
7
7
  @start = start || Time.now
8
8
  @blk = blk
@@ -10,7 +10,7 @@ module Einhorn::Event
10
10
 
11
11
  # TODO: abstract into some interface
12
12
  def self.open(*args, &blk)
13
- instance = self.new(*args, &blk)
13
+ instance = new(*args, &blk)
14
14
  instance.register!
15
15
  instance
16
16
  end
@@ -27,12 +27,12 @@ module Einhorn::Event
27
27
  end
28
28
 
29
29
  def register!
30
- Einhorn.log_debug("Scheduling a new #{self.time}s timer")
30
+ Einhorn.log_debug("Scheduling a new #{time}s timer")
31
31
  Einhorn::Event.register_timer(self)
32
32
  end
33
33
 
34
34
  def deregister!
35
- Einhorn.log_debug("Nuking timer that expired #{Time.now - self.expires_at}s ago")
35
+ Einhorn.log_debug("Nuking timer that expired #{Time.now - expires_at}s ago")
36
36
  Einhorn::Event.deregister_timer(self)
37
37
  end
38
38
  end
data/lib/einhorn/event.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'set'
1
+ require "set"
2
2
 
3
3
  module Einhorn
4
4
  module Event
@@ -37,8 +37,8 @@ module Einhorn
37
37
 
38
38
  def self.persistent_descriptors
39
39
  descriptor_sets = @@readable.values + @@writeable.values + @@timers.values
40
- descriptors = descriptor_sets.inject {|a, b| a | b}
41
- descriptors.select {|descriptor| Einhorn::Event::Persistent.persistent?(descriptor)}
40
+ descriptors = descriptor_sets.inject { |a, b| a | b }
41
+ descriptors.select { |descriptor| Einhorn::Event::Persistent.persistent?(descriptor) }
42
42
  end
43
43
 
44
44
  def self.restore_persistent_descriptors(persistent_descriptors)
@@ -81,8 +81,8 @@ module Einhorn
81
81
 
82
82
  def self.writeable_fds
83
83
  writers = @@writeable.select do |io, writers|
84
- writers.any? {|writer| writer.write_pending?}
85
- end.map {|io, writers| io}
84
+ writers.any? { |writer| writer.write_pending? }
85
+ end.map { |io, writers| io }
86
86
  Einhorn.log_debug("Writeable fds are #{writers.inspect}")
87
87
  writers
88
88
  end
@@ -118,7 +118,7 @@ module Einhorn
118
118
 
119
119
  def self.timeout
120
120
  # (expires_at of the next timer) - now
121
- if expires_at = @@timers.keys.sort[0]
121
+ if (expires_at = @@timers.keys.min)
122
122
  expires_at - Time.now
123
123
  else
124
124
  @@default_timeout
@@ -130,7 +130,7 @@ module Einhorn
130
130
  # handlers. Since it's just an array we push to/shift from, we
131
131
  # can be sure there's no race (such as adding hash keys during
132
132
  # iteration.)
133
- while blk = @@signal_actions.shift
133
+ while (blk = @@signal_actions.shift)
134
134
  blk.call
135
135
  end
136
136
  end
@@ -143,25 +143,25 @@ module Einhorn
143
143
 
144
144
  readable, writeable, _ = IO.select(readable_fds, writeable_fds, nil, time)
145
145
  (readable || []).each do |io|
146
- @@readable[io].each {|reader| reader.notify_readable}
146
+ @@readable[io].each { |reader| reader.notify_readable }
147
147
  end
148
148
 
149
149
  (writeable || []).each do |io|
150
- @@writeable[io].each {|writer| writer.notify_writeable}
150
+ @@writeable[io].each { |writer| writer.notify_writeable }
151
151
  end
152
152
  end
153
153
 
154
154
  def self.run_timers
155
- @@timers.select {|expires_at, _| expires_at <= Time.now}.each do |expires_at, timers|
155
+ @@timers.select { |expires_at, _| expires_at <= Time.now }.each do |expires_at, timers|
156
156
  # Going to be modifying the set, so let's dup it.
157
- timers.dup.each {|timer| timer.ring!}
157
+ timers.dup.each { |timer| timer.ring! }
158
158
  end
159
159
  end
160
160
 
161
161
  def self.break_loop
162
162
  Einhorn.log_debug("Breaking the loop")
163
163
  begin
164
- @@loopbreak_writer.write_nonblock('a')
164
+ @@loopbreak_writer.write_nonblock("a")
165
165
  rescue Errno::EWOULDBLOCK, Errno::EAGAIN
166
166
  Einhorn.log_error("Loop break pipe is full -- probably means that we are quite backlogged")
167
167
  end
@@ -177,11 +177,11 @@ module Einhorn
177
177
  end
178
178
  end
179
179
 
180
- require 'einhorn/event/persistent'
181
- require 'einhorn/event/timer'
180
+ require "einhorn/event/persistent"
181
+ require "einhorn/event/timer"
182
182
 
183
- require 'einhorn/event/abstract_text_descriptor'
184
- require 'einhorn/event/ack_timer'
185
- require 'einhorn/event/command_server'
186
- require 'einhorn/event/connection'
187
- require 'einhorn/event/loop_breaker'
183
+ require "einhorn/event/abstract_text_descriptor"
184
+ require "einhorn/event/ack_timer"
185
+ require "einhorn/event/command_server"
186
+ require "einhorn/event/connection"
187
+ require "einhorn/event/loop_breaker"
data/lib/einhorn/prctl.rb CHANGED
@@ -13,9 +13,9 @@ module Einhorn
13
13
  # Deliberately empty; NotImplementedError is intended
14
14
  end
15
15
 
16
- if RUBY_PLATFORM =~ /linux/ then
16
+ if RUBY_PLATFORM.match?(/linux/)
17
17
  begin
18
- require 'einhorn/prctl_linux'
18
+ require "einhorn/prctl_linux"
19
19
  Prctl = PrctlLinux
20
20
  rescue LoadError
21
21
  Prctl = PrctlUnimplemented
@@ -1,11 +1,11 @@
1
- require 'fiddle'
2
- require 'fiddle/import'
1
+ require "fiddle"
2
+ require "fiddle/import"
3
3
 
4
4
  module Einhorn
5
5
  module PrctlRaw
6
6
  extend Fiddle::Importer
7
7
  dlload Fiddle.dlopen(nil) # libc
8
- extern 'int prctl(int, unsigned long, unsigned long, unsigned long, unsigned long)'
8
+ extern "int prctl(int, unsigned long, unsigned long, unsigned long, unsigned long)"
9
9
 
10
10
  # From linux/prctl.h
11
11
  SET_PDEATHSIG = 1
@@ -14,34 +14,33 @@ module Einhorn
14
14
 
15
15
  class PrctlLinux < PrctlAbstract
16
16
  # Reading integers is hard with fiddle. :(
17
- IntStruct = Fiddle::CStructBuilder.create(Fiddle::CStruct, [Fiddle::TYPE_INT], ['i'])
17
+ IntStruct = Fiddle::CStructBuilder.create(Fiddle::CStruct, [Fiddle::TYPE_INT], ["i"])
18
18
 
19
19
  def self.get_pdeathsig
20
20
  out = IntStruct.malloc
21
21
  out.i = 0
22
- if PrctlRaw.prctl(PrctlRaw::GET_PDEATHSIG, out.to_i, 0, 0, 0) != 0 then
22
+ if PrctlRaw.prctl(PrctlRaw::GET_PDEATHSIG, out.to_i, 0, 0, 0) != 0
23
23
  raise SystemCallError.new("get_pdeathsig", Fiddle.last_error)
24
24
  end
25
25
 
26
26
  signo = out.i
27
- if signo == 0 then
27
+ if signo == 0
28
28
  return nil
29
29
  end
30
30
 
31
- return Signal.signame(signo)
31
+ Signal.signame(signo)
32
32
  end
33
33
 
34
34
  def self.set_pdeathsig(signal)
35
- case
36
- when signal == nil
37
- signo = 0
38
- when signal.instance_of?(String)
39
- signo = Signal.list.fetch(signal)
35
+ signo = if signal.nil?
36
+ 0
37
+ elsif signal.instance_of?(String)
38
+ Signal.list.fetch(signal)
40
39
  else
41
- signo = signal
40
+ signal
42
41
  end
43
42
 
44
- if PrctlRaw.prctl(PrctlRaw::SET_PDEATHSIG, signo, 0, 0, 0) != 0 then
43
+ if PrctlRaw.prctl(PrctlRaw::SET_PDEATHSIG, signo, 0, 0, 0) != 0
45
44
  raise SystemCallError.new("set_pdeathsig(#{signal})", Fiddle.last_error)
46
45
  end
47
46
  end
@@ -0,0 +1,17 @@
1
+ require "yaml"
2
+
3
+ module Einhorn
4
+ module SafeYAML
5
+ begin
6
+ YAML.safe_load("---", permitted_classes: [])
7
+ rescue ArgumentError
8
+ def self.load(payload)
9
+ YAML.safe_load(payload, [Set, Symbol, Time], [], true)
10
+ end
11
+ else
12
+ def self.load(payload) # rubocop:disable Lint/DuplicateMethods
13
+ YAML.safe_load(payload, permitted_classes: [Set, Symbol, Time], aliases: true)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Einhorn
2
- VERSION = '0.8.2'
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,23 +1,21 @@
1
- require 'einhorn/client'
2
- require 'einhorn/command/interface'
1
+ require "einhorn/client"
2
+ require "einhorn/command/interface"
3
3
 
4
4
  module Einhorn
5
5
  module Worker
6
6
  class WorkerError < RuntimeError; end
7
7
 
8
8
  def self.is_worker?
9
- begin
10
- ensure_worker!
11
- rescue WorkerError
12
- false
13
- else
14
- true
15
- end
9
+ ensure_worker!
10
+ rescue WorkerError
11
+ false
12
+ else
13
+ true
16
14
  end
17
15
 
18
16
  def self.ensure_worker!
19
17
  # Make sure that EINHORN_MASTER_PID is my parent
20
- if ppid_s = ENV['EINHORN_MASTER_PID']
18
+ if (ppid_s = ENV["EINHORN_MASTER_PID"])
21
19
  ppid = ppid_s.to_i
22
20
  raise WorkerError.new("EINHORN_MASTER_PID environment variable is #{ppid_s.inspect}, but my parent's pid is #{Process.ppid.inspect}. This probably means that I am a subprocess of an Einhorn worker, but am not one myself.") unless Process.ppid == ppid
23
21
  true
@@ -27,10 +25,8 @@ module Einhorn
27
25
  end
28
26
 
29
27
  def self.ack(*args)
30
- begin
31
- ack!(*args)
32
- rescue WorkerError
33
- end
28
+ ack!(*args)
29
+ rescue WorkerError
34
30
  end
35
31
 
36
32
  # Returns the index of this Einhorn child process.
@@ -42,7 +38,7 @@ module Einhorn
42
38
  # Returns nil if not running in Einhorn, or running on a version
43
39
  # of Einhorn that does not support indexing children.
44
40
  def self.einhorn_child_index
45
- index = ENV['EINHORN_CHILD_INDEX']
41
+ index = ENV["EINHORN_CHILD_INDEX"]
46
42
  if index.nil? || index !~ /\A \d+ \z/x
47
43
  index
48
44
  else
@@ -65,9 +61,9 @@ module Einhorn
65
61
  # TODO: add a :fileno option? Easy to implement; not sure if it'd
66
62
  # be useful for anything. Maybe if it's always fd 3, because then
67
63
  # the user wouldn't have to provide an arg.
68
- def self.ack!(discovery=:env, arg=nil)
64
+ def self.ack!(discovery = :env, arg = nil)
69
65
  handle_command_socket(discovery, arg) do |client|
70
- client.send_command('command' => 'worker:ack', 'pid' => $$)
66
+ client.send_command("command" => "worker:ack", "pid" => $$)
71
67
  end
72
68
  end
73
69
 
@@ -84,21 +80,21 @@ module Einhorn
84
80
  # Then @arg being true causes the FD to be left open after ACK;
85
81
  # otherwise it is closed.
86
82
  # :direct: Provide the path to the command socket in @arg.
87
- def self.ping!(request_id, discovery=:env, arg=nil)
83
+ def self.ping!(request_id, discovery = :env, arg = nil)
88
84
  handle_command_socket(discovery, arg) do |client|
89
- client.send_command('command' => 'worker:ping', 'pid' => $$, 'request_id' => request_id)
85
+ client.send_command("command" => "worker:ping", "pid" => $$, "request_id" => request_id)
90
86
  end
91
87
  end
92
88
 
93
- def self.socket(number=nil)
89
+ def self.socket(number = nil)
94
90
  number ||= 0
95
91
  einhorn_fd(number)
96
92
  end
97
93
 
98
- def self.socket!(number=nil)
94
+ def self.socket!(number = nil)
99
95
  number ||= 0
100
96
 
101
- unless count = einhorn_fd_count
97
+ unless (count = einhorn_fd_count)
102
98
  raise "No EINHORN_FD_COUNT provided in environment. Are you running under Einhorn?"
103
99
  end
104
100
 
@@ -106,7 +102,7 @@ module Einhorn
106
102
  raise "Only #{count} FDs available, but FD #{number} was requested"
107
103
  end
108
104
 
109
- unless fd = einhorn_fd(number)
105
+ unless (fd = einhorn_fd(number))
110
106
  raise "No EINHORN_FD_#{number} provided in environment. That's pretty weird"
111
107
  end
112
108
 
@@ -114,14 +110,14 @@ module Einhorn
114
110
  end
115
111
 
116
112
  def self.einhorn_fd(n)
117
- unless raw_fd = ENV["EINHORN_FD_#{n}"]
113
+ unless (raw_fd = ENV["EINHORN_FD_#{n}"])
118
114
  return nil
119
115
  end
120
116
  Integer(raw_fd)
121
117
  end
122
118
 
123
119
  def self.einhorn_fd_count
124
- unless raw_count = ENV['EINHORN_FD_COUNT']
120
+ unless (raw_count = ENV["EINHORN_FD_COUNT"])
125
121
  return 0
126
122
  end
127
123
  Integer(raw_count)
@@ -129,21 +125,19 @@ module Einhorn
129
125
 
130
126
  # Call this to handle graceful shutdown requests to your app.
131
127
  def self.graceful_shutdown(&blk)
132
- Signal.trap('USR2', &blk)
128
+ Signal.trap("USR2", &blk)
133
129
  end
134
130
 
135
- private
136
-
137
131
  def self.handle_command_socket(discovery, contextual_arg)
138
132
  ensure_worker!
139
133
  close_after_use = true
140
134
 
141
135
  case discovery
142
136
  when :env
143
- socket = ENV['EINHORN_SOCK_PATH']
137
+ socket = ENV["EINHORN_SOCK_PATH"]
144
138
  client = Einhorn::Client.for_path(socket)
145
139
  when :fd
146
- raise "No EINHORN_SOCK_FD provided in environment. Did you run einhorn with the -g flag?" unless fd_str = ENV['EINHORN_SOCK_FD']
140
+ raise "No EINHORN_SOCK_FD provided in environment. Did you run einhorn with the -g flag?" unless (fd_str = ENV["EINHORN_SOCK_FD"])
147
141
 
148
142
  fd = Integer(fd_str)
149
143
  client = Einhorn::Client.for_fd(fd)
@@ -160,11 +154,13 @@ module Einhorn
160
154
 
161
155
  true
162
156
  end
157
+ private_class_method :handle_command_socket
163
158
 
164
159
  def self.socket_from_filesystem(cmd_name)
165
160
  ppid = Process.ppid
166
161
  socket_path_file = Einhorn::Command::Interface.socket_path_file(ppid)
167
162
  File.read(socket_path_file)
168
163
  end
164
+ private_class_method :socket_from_filesystem
169
165
  end
170
166
  end
@@ -7,13 +7,13 @@ module Einhorn
7
7
  end
8
8
 
9
9
  def self.workers
10
- workers_with_state.map {|pid, _| pid}
10
+ workers_with_state.map { |pid, _| pid }
11
11
  end
12
12
 
13
13
  def self.unsignaled_workers
14
14
  workers_with_state.select do |pid, spec|
15
15
  spec[:signaled].length == 0
16
- end.map {|pid, _| pid}
16
+ end.map { |pid, _| pid }
17
17
  end
18
18
 
19
19
  def self.modern_workers_with_state
@@ -23,15 +23,15 @@ module Einhorn
23
23
  end
24
24
 
25
25
  def self.acked_modern_workers_with_state
26
- modern_workers_with_state.select {|pid, spec| spec[:acked]}
26
+ modern_workers_with_state.select { |pid, spec| spec[:acked] }
27
27
  end
28
28
 
29
29
  def self.modern_workers
30
- modern_workers_with_state.map {|pid, _| pid}
30
+ modern_workers_with_state.map { |pid, _| pid }
31
31
  end
32
32
 
33
33
  def self.acked_modern_workers
34
- acked_modern_workers_with_state.map {|pid, _| pid}
34
+ acked_modern_workers_with_state.map { |pid, _| pid }
35
35
  end
36
36
 
37
37
  def self.unsignaled_modern_workers_with_state
@@ -43,23 +43,23 @@ module Einhorn
43
43
  def self.acked_unsignaled_modern_workers
44
44
  acked_modern_workers_with_state.select do |_, spec|
45
45
  spec[:signaled].length == 0
46
- end.map {|pid, _| pid}
46
+ end.map { |pid, _| pid }
47
47
  end
48
48
 
49
49
  def self.unsignaled_modern_workers_with_priority
50
50
  unsignaled_modern_workers_with_state.sort_by do |pid, spec|
51
51
  spec[:acked] ? 1 : 0
52
- end.map {|pid, _| pid}
52
+ end.map { |pid, _| pid }
53
53
  end
54
54
 
55
55
  def self.unacked_unsignaled_modern_workers_with_state
56
- modern_workers_with_state.select {|pid, spec|
56
+ modern_workers_with_state.select { |pid, spec|
57
57
  !spec[:acked] && spec[:signaled].length == 0
58
58
  }
59
59
  end
60
60
 
61
61
  def self.unacked_unsignaled_modern_workers
62
- unacked_unsignaled_modern_workers_with_state.map {|pid, _| pid}
62
+ unacked_unsignaled_modern_workers_with_state.map { |pid, _| pid }
63
63
  end
64
64
 
65
65
  # Use the number of modern workers, rather than unsignaled modern