sponges 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT LICENSE
2
+
3
+ Copyright (c) chatgris <jboyer@af83.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # sponges
2
+
3
+ When I build workers, I want them to be like an army of spongebobs, always
4
+ stressed and eager to work. `sponges` helps you build this army of sponges, to
5
+ control them, and, well, to kill them gracefully. Making them stressed and eager
6
+ to work is your job. :)
7
+
8
+ Internally, `sponges` strongly relies on Unix signals.
9
+
10
+ ## Is it any good?
11
+
12
+ [Yes.](http://news.ycombinator.com/item?id=3067434)
13
+
14
+ ## Production ready ?
15
+
16
+ Not yet, but soon.
17
+
18
+ ## Installation
19
+
20
+ Ruby 1.9.2 (or superior) and Redis are required.
21
+
22
+ Install it with rubygems:
23
+
24
+ gem install sponges
25
+
26
+ With bundler, add it to your `Gemfile`:
27
+
28
+ ``` ruby
29
+ gem "sponges"
30
+ ```
31
+
32
+ ## Usage
33
+ In a file called `example.rb`:
34
+
35
+ ``` ruby
36
+ # The worker class is the one you want to daemonize.
37
+ #
38
+ require 'sponges'
39
+
40
+ class Worker
41
+ def run
42
+ # Trap the HUP signal, set a boolean to true.
43
+ trap(:HUP) {
44
+ Sponges.logger.info "HUP signal trapped, clean stop."
45
+ @hup = true
46
+ }
47
+ Sponges.logger.info Process.pid
48
+ if @hup # is true, we need to shutdown this worker
49
+ Sponges.logger.info "HUP signal trapped, shutdown..."
50
+ exit 0 # everything's fine, we can exit
51
+ else # this worker can continue its work
52
+ sleep rand(20)
53
+ run
54
+ end
55
+ end
56
+ end
57
+
58
+ Sponges.configure do |config|
59
+ config.worker = Worker.new # mandatory
60
+ config.worker_name = "bob" # mandatory
61
+ config.worker_method = :run # mandatory
62
+ config.worker_args = {first: true} # mandatory
63
+ config.logger = MyCustomLogger.new # optionnal
64
+ config.redis = Redis.new # optionnal
65
+ end
66
+
67
+ Sponges.start
68
+ ```
69
+ See the help message :
70
+ ``` bash
71
+ ruby example.rb
72
+ ```
73
+
74
+ Start workers :
75
+ ``` bash
76
+ ruby example.rb start
77
+ ```
78
+
79
+ Start workers and daemonize them:
80
+ ``` bash
81
+ ruby example.rb start -d
82
+ ```
83
+
84
+ Start 8 instances of the worker and daemonize them:
85
+ ``` bash
86
+ ruby example.rb start -d -s 8
87
+ ```
88
+
89
+ Retart gracefully 4 instances of the worker and daemonize them:
90
+ ``` bash
91
+ ruby example.rb restart -g -d -s 4
92
+ ```
93
+
94
+ Stop workers with a `QUIT` signal :
95
+ ``` bash
96
+ ruby example.rb stop
97
+ ```
98
+
99
+ Stop workers with a `HUP` signal :
100
+ ``` bash
101
+ ruby example.rb stop -g
102
+ ```
103
+ In this case, you will have to trap the `HUP` signal, and handle a clean stop
104
+ from each workers. The point is to wait for a task to be done before quitting.
105
+
106
+ Show a list of workers and their children.
107
+ ``` bash
108
+ ruby example.rb list
109
+ ```
110
+
111
+ ## TODO
112
+
113
+ * Specing.
114
+ * Increment / decrement workers pool size with `TTIN` and `TTOUT`.
115
+ * Check on OSX.
116
+
117
+ ## Copyright
118
+
119
+ MIT. See LICENSE for further details.
data/lib/sponges/cli.rb CHANGED
@@ -1,19 +1,31 @@
1
1
  # encoding: utf-8
2
2
  module Sponges
3
+ # This class concern is to expose a nice CLI interface.
4
+ #
3
5
  class Cli < Boson::Runner
4
6
  option :daemonize, type: :boolean
5
7
  option :size, type: :numeric
6
8
  desc "Start workers"
7
9
  def start(options = {})
8
10
  Sponges::Runner.new(Sponges::Configuration.worker_name, options).
9
- work(Sponges::Configuration.worker, Sponges::Configuration.worker_method)
11
+ work(Sponges::Configuration.worker, Sponges::Configuration.worker_method,
12
+ Sponges::Configuration.worker_args)
10
13
  end
11
14
 
12
15
  option :gracefully, type: :boolean
13
16
  desc "Stop workers"
14
17
  def stop(options = {})
15
- Sponges::Runner.new(Sponges::Configuration.worker_name, options).
16
- rest
18
+ Sponges::Commander.new(Sponges::Configuration.worker_name, options).
19
+ stop
20
+ end
21
+
22
+ option :daemonize, type: :boolean
23
+ option :size, type: :numeric
24
+ option :gracefully, type: :boolean
25
+ desc "Restart workers"
26
+ def restart(options = {})
27
+ stop(options)
28
+ start(options)
17
29
  end
18
30
 
19
31
  desc "Show running processes"
@@ -28,13 +40,16 @@ module Sponges
28
40
  |_| |___/
29
41
  }.gsub(/^\n/, '') + "\n"
30
42
  puts "Workers:"
31
- Array(redis[:workers].smembers).each do |worker|
32
- puts worker.rjust(6)
33
- puts "supervisor".rjust(15)
34
- puts redis[:worker][worker][:supervisor].get.rjust(12)
35
- puts "children".rjust(13)
36
- Array(redis[:worker][worker][:pids].smembers).each do |pid|
37
- puts pid.rjust(12)
43
+ Array(redis[:hostnames].smembers).each do |hostname|
44
+ puts hostname.rjust(6)
45
+ Array(redis[hostname][:workers].smembers).each do |worker|
46
+ puts worker.rjust(6)
47
+ puts "supervisor".rjust(15)
48
+ puts redis[hostname][:worker][worker][:supervisor].get.rjust(12)
49
+ puts "children".rjust(13)
50
+ Array(redis[hostname][:worker][worker][:pids].smembers).each do |pid|
51
+ puts pid.rjust(12)
52
+ end
38
53
  end
39
54
  end
40
55
  puts "\n"
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ module Sponges
3
+ # This class concern is to send messages to supervisor. It's used to send
4
+ # messages like 'stop' or 'restart'
5
+ #
6
+ class Commander
7
+ def initialize(name, options = {})
8
+ @name, @options = name, options
9
+ @redis = Nest.new('sponges', Configuration.redis || Redis.new)[Socket.gethostname]
10
+ end
11
+
12
+ def stop
13
+ Sponges.logger.info "Runner #{@name} stop message received."
14
+ if pid = @redis[:worker][@name][:supervisor].get
15
+ begin
16
+ Process.kill gracefully? ? :HUP : :QUIT, pid.to_i
17
+ while alive?(pid.to_i) do
18
+ Sponges.logger.info "Supervisor #{pid} still alive"
19
+ sleep 0.5
20
+ end
21
+ Sponges.logger.info "Supervisor #{pid} has stopped."
22
+ rescue Errno::ESRCH => e
23
+ Sponges.logger.error e
24
+ end
25
+ else
26
+ Sponges.logger.info "No supervisor found."
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def alive?(pid)
33
+ begin
34
+ Process.kill 0, pid
35
+ true
36
+ rescue Errno::ESRCH => e
37
+ false
38
+ end
39
+ end
40
+
41
+ def gracefully?
42
+ !!@options[:gracefully]
43
+ end
44
+ end
45
+ end
@@ -1,8 +1,10 @@
1
1
  # encoding: utf-8
2
2
  module Sponges
3
+ # This class concern is to provide a global object for configuration needs.
4
+ #
3
5
  class Configuration
4
6
  class << self
5
- ACCESSOR = [:worker_name, :worker, :worker_method, :logger]
7
+ ACCESSOR = [:worker_name, :worker, :worker_method, :worker_args, :logger, :redis]
6
8
  attr_accessor *ACCESSOR
7
9
 
8
10
  def configure
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  class CpuInfo
3
+ # This class concern is to grab some informations about hardware.
4
+ #
3
5
  class << self
4
6
  def cores_size
5
7
  `grep -c processor /proc/cpuinfo`.to_i
@@ -1,16 +1,19 @@
1
1
  # encoding: utf-8
2
2
  module Sponges
3
+ # This class concern is to create a Supervisor, set some signals handlers and
4
+ # watch over the supervisor.
5
+ #
3
6
  class Runner
4
7
  def initialize(name, options = {})
5
8
  @name = name
6
9
  @options = default_options.merge options
7
- @redis = Nest.new('sponges')
10
+ @redis = Nest.new('sponges', Configuration.redis || Redis.new)
11
+ @redis[:hostnames].sadd Socket.gethostname
8
12
  end
9
13
 
10
14
  def work(worker, method, *args, &block)
11
15
  Sponges.logger.info "Runner #{@name} start message received."
12
16
  @supervisor = fork_supervisor(worker, method, *args, &block)
13
- @redis[:worker][@name][:supervisor].set @supervisor
14
17
  trap_signals
15
18
  Sponges.logger.info "Supervisor started with #{@supervisor} pid."
16
19
  if daemonize?
@@ -21,19 +24,6 @@ module Sponges
21
24
  end
22
25
  end
23
26
 
24
- def rest
25
- Sponges.logger.info "Runner #{@name} stop message received."
26
- if pid = @redis[:worker][@name][:supervisor].get
27
- begin
28
- Process.kill gracefully? ? :HUP : :QUIT, pid.to_i
29
- rescue Errno::ESRCH => e
30
- Sponges.logger.error e
31
- end
32
- else
33
- Sponges.logger.info "No supervisor found."
34
- end
35
- end
36
-
37
27
  private
38
28
 
39
29
  def trap_signals
@@ -63,9 +53,5 @@ module Sponges
63
53
  def daemonize?
64
54
  !!@options[:daemonize]
65
55
  end
66
-
67
- def gracefully?
68
- !!@options[:gracefully]
69
- end
70
56
  end
71
57
  end
@@ -4,8 +4,9 @@ module Sponges
4
4
  def initialize(name, options, worker, method, *args, &block)
5
5
  @name, @options = name, options
6
6
  @worker, @method, @args, @block = worker, method, args, block
7
- @redis = Nest.new('sponges')
7
+ @redis = Nest.new('sponges', Configuration.redis || Redis.new)[Socket.gethostname]
8
8
  @redis[:workers].sadd name
9
+ @redis[:worker][@name][:supervisor].set Process.pid
9
10
  @pids = @redis[:worker][name][:pids]
10
11
  @children_seen = 0
11
12
  end
@@ -15,6 +16,10 @@ module Sponges
15
16
  fork_children
16
17
  end
17
18
  trap_signals
19
+ at_exit do
20
+ Sponges.logger.info "Supervisor exits."
21
+ end
22
+ Sponges.logger.info "Supervisor started, waiting for messages."
18
23
  sleep
19
24
  end
20
25
 
@@ -50,7 +55,7 @@ module Sponges
50
55
  fork_children
51
56
  end
52
57
  rescue Errno::ECHILD => e
53
- Sponges.logger.error e
58
+ # Don't panic
54
59
  end
55
60
  end
56
61
  end
@@ -62,7 +67,10 @@ module Sponges
62
67
  Process.waitall
63
68
  Sponges.logger.info "Children shutdown complete."
64
69
  Sponges.logger.info "Supervisor shutdown. Exiting..."
65
- Process.kill :USR1, @redis[:worker][@name][:supervisor].to_i
70
+ pid = @redis[:worker][@name][:supervisor]
71
+ @redis[:worker][@name][:supervisor].del
72
+ @redis[:workers].srem @name
73
+ Process.kill :USR1, pid.to_i
66
74
  end
67
75
 
68
76
  def kill_them_all(signal)
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  module Sponges
2
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
3
4
  end
@@ -1,5 +1,8 @@
1
1
  # encoding: utf-8
2
2
  module Sponges
3
+ # This class concern is to build a worker instance, set some signals handlers
4
+ # and make it start its job.
5
+ #
3
6
  class WorkerBuilder
4
7
  def initialize(worker, method, *args, &block)
5
8
  @worker, @method, @args, @block = worker, method, args, block
@@ -7,6 +10,9 @@ module Sponges
7
10
 
8
11
  def start
9
12
  trap_signals
13
+ at_exit do
14
+ Sponges.logger.info "Child exits."
15
+ end
10
16
  @worker.send(@method, *@args, &@block)
11
17
  end
12
18
 
data/lib/sponges.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require 'boson/runner'
3
+ require 'socket'
3
4
  require 'logger'
4
5
  require 'nest'
5
6
  require_relative 'sponges/configuration'
@@ -7,6 +8,7 @@ require_relative 'sponges/cpu_infos'
7
8
  require_relative 'sponges/worker_builder'
8
9
  require_relative 'sponges/supervisor'
9
10
  require_relative 'sponges/runner'
11
+ require_relative 'sponges/commander'
10
12
  require_relative 'sponges/cli'
11
13
 
12
14
  module Sponges
@@ -17,8 +19,8 @@ module Sponges
17
19
  end
18
20
  module_function :configure
19
21
 
20
- def start
21
- Sponges::Cli.start
22
+ def start(options = ARGV)
23
+ Sponges::Cli.start(options)
22
24
  end
23
25
  module_function :start
24
26
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sponges
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
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: 2012-09-26 00:00:00.000000000 Z
12
+ date: 2012-10-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: boson
@@ -43,6 +43,38 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.10.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.10.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: sys-proctable
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
46
78
  description: When I build some worker, I want them to be like an army of spongebob,
47
79
  always stressed and eager to work. sponges helps you to build this army of sponge,
48
80
  to control them, and, well, kill them gracefully.
@@ -52,8 +84,11 @@ executables: []
52
84
  extensions: []
53
85
  extra_rdoc_files: []
54
86
  files:
87
+ - LICENSE
88
+ - README.md
55
89
  - lib/sponges.rb
56
90
  - lib/sponges/cli.rb
91
+ - lib/sponges/commander.rb
57
92
  - lib/sponges/configuration.rb
58
93
  - lib/sponges/cpu_infos.rb
59
94
  - lib/sponges/runner.rb