einhorn 0.5.6 → 0.5.7

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/Gemfile CHANGED
@@ -4,5 +4,8 @@
4
4
  eval(File.read(path), binding, path); break true
5
5
  end || source('https://rubygems.org/')
6
6
 
7
+ # Only needed for examples
8
+ gem 'thin-attach_socket'
9
+
7
10
  # Specify your gem's dependencies in einhorn.gemspec
8
11
  gemspec
data/README.md CHANGED
@@ -238,11 +238,8 @@ servers) to a wider array of applications.
238
238
  See https://stripe.com/blog/meet-einhorn for more background.
239
239
 
240
240
  Stripe currently uses Einhorn in production for a number of
241
- services. Our Thin + EventMachine servers currently require patches to
242
- both Thin and EventMachine (to support file-descriptor passing). You
243
- can obtain these patches from our public forks of the
244
- [respective](https://github.com/stripe/thin)
245
- [projects](https://github.com/stripe/eventmachine). Check out
241
+ services. You can use Conrad Irwin's thin-attach_socket gem along with
242
+ EventMachine-LE to support file-descriptor passing. Check out
246
243
  `example/thin_example` for an example of running Thin under Einhorn.
247
244
 
248
245
  ## Compatibility
@@ -9,11 +9,9 @@
9
9
  require 'rubygems'
10
10
  require 'einhorn'
11
11
 
12
- # Make sure we're using the patched versions.
13
- gem 'thin', '1.3.2.stripe.0'
14
- gem 'eventmachine', '1.0.0.beta.4.stripe.0'
15
-
12
+ require 'eventmachine-le'
16
13
  require 'thin'
14
+ require 'thin/attach_socket'
17
15
 
18
16
  class App
19
17
  def initialize(id)
@@ -42,7 +40,9 @@ def einhorn_main
42
40
  EventMachine.run do
43
41
  (0...fd_count).each do |i|
44
42
  sock = Einhorn::Worker.socket!(i)
45
- srv = Thin::Server.new(sock, App.new(i), :reuse => true)
43
+ srv = Thin::Server.new(App.new(i),
44
+ :backend => Thin::Backends::AttachSocket,
45
+ :socket => IO.for_fd(sock))
46
46
  srv.start
47
47
  end
48
48
  end
@@ -152,6 +152,24 @@ module Einhorn
152
152
  output
153
153
  end
154
154
 
155
+ def self.set_workers(new)
156
+ if new == Einhorn::State.config[:number]
157
+ return ""
158
+ end
159
+
160
+ Einhorn::Event.break_loop
161
+ old = Einhorn::State.config[:number]
162
+ Einhorn::State.config[:number] = new
163
+ output = "Altering worker count, #{old} -> #{new}. Will "
164
+ if old < new
165
+ output << "spin up additional workers."
166
+ else
167
+ output << "gracefully terminate workers."
168
+ end
169
+ $stderr.puts(output)
170
+ output
171
+ end
172
+
155
173
  def self.dumpable_state
156
174
  global_state = Einhorn::State.state
157
175
  descriptor_state = Einhorn::Event.persistent_descriptors.map do |descriptor|
@@ -220,8 +238,16 @@ module Einhorn
220
238
  end
221
239
  end
222
240
 
241
+ def self.next_index
242
+ all_indexes = Set.new(Einhorn::State.children.map { |k, st| st[:index] })
243
+ 0.upto(all_indexes.length) do |i|
244
+ return i unless all_indexes.include?(i)
245
+ end
246
+ end
247
+
223
248
  def self.spinup(cmd=nil)
224
249
  cmd ||= Einhorn::State.cmd
250
+ index = next_index
225
251
  if Einhorn::TransientState.preloaded
226
252
  pid = fork do
227
253
  Einhorn::TransientState.whatami = :worker
@@ -234,7 +260,7 @@ module Einhorn
234
260
 
235
261
  reseed_random
236
262
 
237
- prepare_child_environment
263
+ prepare_child_environment(index)
238
264
  einhorn_main
239
265
  end
240
266
  else
@@ -251,17 +277,18 @@ module Einhorn
251
277
  # Note that Ruby 1.9's close_others option is useful here.
252
278
  Einhorn::Event.close_all_for_worker
253
279
 
254
- prepare_child_environment
280
+ prepare_child_environment(index)
255
281
  Einhorn::Compat.exec(cmd[0], cmd[1..-1], :close_others => false)
256
282
  end
257
283
  end
258
284
 
259
- Einhorn.log_info("===> Launched #{pid}", :upgrade)
285
+ Einhorn.log_info("===> Launched #{pid} (index: #{index})", :upgrade)
260
286
  Einhorn::State.children[pid] = {
261
287
  :type => :worker,
262
288
  :version => Einhorn::State.version,
263
289
  :acked => false,
264
- :signaled => Set.new
290
+ :signaled => Set.new,
291
+ :index => index
265
292
  }
266
293
  Einhorn::State.last_spinup = Time.now
267
294
 
@@ -276,7 +303,7 @@ module Einhorn
276
303
  end
277
304
  end
278
305
 
279
- def self.prepare_child_environment
306
+ def self.prepare_child_environment(index)
280
307
  # This is run from the child
281
308
  ENV['EINHORN_MASTER_PID'] = Process.ppid.to_s
282
309
  ENV['EINHORN_SOCK_PATH'] = Einhorn::Command::Interface.socket_path
@@ -289,6 +316,8 @@ module Einhorn
289
316
  ENV['EINHORN_FD_COUNT'] = Einhorn::State.bind_fds.length.to_s
290
317
  Einhorn::State.bind_fds.each_with_index {|fd, i| ENV["EINHORN_FD_#{i}"] = fd.to_s}
291
318
 
319
+ ENV['EINHORN_CHILD_INDEX'] = index.to_s
320
+
292
321
  # EINHORN_FDS is deprecated. It was originally an attempt to
293
322
  # match Upstart's nominal internal support for space-separated
294
323
  # FD lists, but nobody uses that in practice, and it makes
@@ -328,6 +328,21 @@ EOF
328
328
  Einhorn::Command.decrement
329
329
  end
330
330
 
331
+ command 'set_workers', 'Set the number of Einhorn child processes' do |conn, request|
332
+ args = request['args']
333
+ if message = validate_args(args)
334
+ next message
335
+ end
336
+
337
+ count = args[0].to_i
338
+ if count < 1 || count > 100
339
+ # sancheck. 100 is kinda arbitrary.
340
+ next "Invalid count: '#{args[0]}'. Must be an integer in [1,100)."
341
+ end
342
+
343
+ Einhorn::Command.set_workers(count)
344
+ end
345
+
331
346
  command 'quieter', 'Decrease verbosity' do
332
347
  Einhorn::Command.quieter
333
348
  end
@@ -1,3 +1,3 @@
1
1
  module Einhorn
2
- VERSION = '0.5.6'
2
+ VERSION = '0.5.7'
3
3
  end
@@ -33,6 +33,23 @@ module Einhorn
33
33
  end
34
34
  end
35
35
 
36
+ # Returns the index of this Einhorn child process.
37
+ #
38
+ # If an Einhorn master has N children, this will be an integer in
39
+ # the range [0,N), and no two workers running concurrently will
40
+ # ever have the same index.
41
+ #
42
+ # Returns nil if not running in Einhorn, or running on a version
43
+ # of Einhorn that does not support indexing children.
44
+ def self.einhorn_child_index
45
+ index = ENV['EINHORN_CHILD_INDEX']
46
+ if index.nil? || index !~ /\A \d+ \z/x
47
+ index
48
+ else
49
+ index.to_i
50
+ end
51
+ end
52
+
36
53
  # Call this once your app is up and running in a good state.
37
54
  # Arguments:
38
55
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: einhorn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
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: 2014-04-03 00:00:00.000000000 Z
12
+ date: 2014-04-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -164,21 +164,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
164
164
  - - ! '>='
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- segments:
168
- - 0
169
- hash: -2873954184645788574
170
167
  required_rubygems_version: !ruby/object:Gem::Requirement
171
168
  none: false
172
169
  requirements:
173
170
  - - ! '>='
174
171
  - !ruby/object:Gem::Version
175
172
  version: '0'
176
- segments:
177
- - 0
178
- hash: -2873954184645788574
179
173
  requirements: []
180
174
  rubyforge_project:
181
- rubygems_version: 1.8.23
175
+ rubygems_version: 1.8.23.2
182
176
  signing_key:
183
177
  specification_version: 3
184
178
  summary: ! 'Einhorn: the language-independent shared socket manager'
@@ -190,3 +184,4 @@ test_files:
190
184
  - test/unit/einhorn/command/interface.rb
191
185
  - test/unit/einhorn/event.rb
192
186
  - test/unit/einhorn/worker_pool.rb
187
+ has_rdoc: