einhorn 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
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: