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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +15 -0
  3. data/README.md +7 -38
  4. data/einhorn.gemspec +23 -21
  5. data/example/pool_worker.rb +2 -2
  6. data/example/thin_example +8 -8
  7. data/example/time_server +5 -5
  8. data/lib/einhorn/client.rb +8 -8
  9. data/lib/einhorn/command/interface.rb +92 -98
  10. data/lib/einhorn/command.rb +75 -85
  11. data/lib/einhorn/compat.rb +7 -7
  12. data/lib/einhorn/event/abstract_text_descriptor.rb +32 -36
  13. data/lib/einhorn/event/ack_timer.rb +2 -2
  14. data/lib/einhorn/event/command_server.rb +7 -9
  15. data/lib/einhorn/event/connection.rb +1 -3
  16. data/lib/einhorn/event/loop_breaker.rb +2 -1
  17. data/lib/einhorn/event/persistent.rb +2 -2
  18. data/lib/einhorn/event/timer.rb +4 -4
  19. data/lib/einhorn/event.rb +20 -20
  20. data/lib/einhorn/prctl.rb +2 -2
  21. data/lib/einhorn/prctl_linux.rb +13 -14
  22. data/lib/einhorn/safe_yaml.rb +17 -0
  23. data/lib/einhorn/version.rb +1 -1
  24. data/lib/einhorn/worker.rb +26 -30
  25. data/lib/einhorn/worker_pool.rb +9 -9
  26. data/lib/einhorn.rb +120 -125
  27. metadata +37 -110
  28. data/.gitignore +0 -17
  29. data/.travis.yml +0 -10
  30. data/CONTRIBUTORS +0 -6
  31. data/Gemfile +0 -11
  32. data/History.txt +0 -4
  33. data/README.md.in +0 -94
  34. data/Rakefile +0 -27
  35. data/test/_lib.rb +0 -12
  36. data/test/integration/_lib/fixtures/env_printer/env_printer.rb +0 -26
  37. data/test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb +0 -23
  38. data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
  39. data/test/integration/_lib/fixtures/pdeathsig_printer/pdeathsig_printer.rb +0 -29
  40. data/test/integration/_lib/fixtures/signal_timeout/sleepy_server.rb +0 -23
  41. data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -24
  42. data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -148
  43. data/test/integration/_lib/helpers.rb +0 -4
  44. data/test/integration/_lib.rb +0 -6
  45. data/test/integration/pdeathsig.rb +0 -26
  46. data/test/integration/startup.rb +0 -31
  47. data/test/integration/upgrading.rb +0 -204
  48. data/test/unit/_lib/bad_worker.rb +0 -7
  49. data/test/unit/_lib/sleep_worker.rb +0 -5
  50. data/test/unit/einhorn/client.rb +0 -88
  51. data/test/unit/einhorn/command/interface.rb +0 -49
  52. data/test/unit/einhorn/command.rb +0 -135
  53. data/test/unit/einhorn/event.rb +0 -89
  54. data/test/unit/einhorn/worker_pool.rb +0 -39
  55. data/test/unit/einhorn.rb +0 -96
  56. /data/{LICENSE → LICENSE.txt} +0 -0
@@ -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,32 +143,32 @@ 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
168
168
  end
169
169
 
170
170
  def self.default_timeout=(val)
171
- @@default_timeout = val.to_i == 0 ? nil : val.to_i
171
+ @@default_timeout = (val.to_i == 0) ? nil : val.to_i
172
172
  end
173
173
 
174
174
  def self.default_timeout
@@ -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.1"
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