ost-bin 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +12 -2
  3. data/bin/ost +103 -12
  4. data/lib/ost-bin/version.rb +1 -1
  5. data/test/bin.rb +109 -39
  6. metadata +6 -15
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Mzg5YjIyZTAxMTliOTFjYTE4NWI2MGUwNTMxNWU4MjY2ZjExMDU0YQ==
5
+ data.tar.gz: !binary |-
6
+ MzI1ZWRhNmE2NDJlOWZkMTZiZjBlZWUwOGU4MjBjMDNiYzY0YmM4NA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MWJhNGNhYzZlNzBhMWI2OTk2OGYwN2U4Y2IzODA4ODM5ZGJhZjcwMWZmNTJh
10
+ M2E2ZTI2YmZmMjhhN2IxNjA0NzFkMDQ0MmMyNjRkNGY4ZjliODBlMzA2ZDRi
11
+ OTc5NWM0OWI0M2Q4NDNkOTkyOTliZjYxYzA1ZTBlZWE3NTUyYzg=
12
+ data.tar.gz: !binary |-
13
+ MzQ3MDUxYWQ1YTIzN2QwNjA5NGNlMzMyZTk5NDEzNzc5YTFlZjlhYTE4ZGJk
14
+ YmJlMWMyYTM4NjkzMmViM2NlNTkzNGQwYTk0M2M2MWFlMWVjNWJiZWY2MTYy
15
+ OWY0NmQ4OGVlNGExMTMwY2ViMjhjMDI4ZDM3YjY1ZDk3NzhmOWY=
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
- 0.0.3 - 2012-07-05
1
+ # 0.1.0 - 2014-05-26
2
2
 
3
- * Fix graceful handling of `SIGTERM`.
3
+ * Moved to a threaded model.
4
+
5
+ * Added `Ostfile` to specify which workers you want to run.
6
+
7
+ * New `-p` switch to specify the path where the PID file should be stored.
8
+
9
+ * `ost(1)` now accepts `start` and `stop` commands.
10
+
11
+ # 0.0.3 - 2012-07-05
12
+
13
+ * Fixed graceful handling of `SIGTERM`.
data/bin/ost CHANGED
@@ -11,28 +11,119 @@ end
11
11
  trap(:INT, &stop)
12
12
  trap(:TERM, &stop)
13
13
 
14
+ usage = <<-EOS
15
+ Usage:
16
+
17
+ ost start [-r <require>] [-d] [-p <pid-path>]
18
+ ost stop [-p <pid-path>]
19
+
20
+ EOS
21
+
14
22
  require "clap"
15
23
  require "ost"
16
24
 
17
- opts = {}
25
+ opts = {
26
+ requires: []
27
+ }
18
28
 
19
- args = Clap.run ARGV,
29
+ command, _ = Clap.run ARGV,
20
30
  "-d" => -> {
21
31
  opts[:daemonize] = true
32
+ },
33
+ "-p" => -> path {
34
+ opts[:pid_path] = path
35
+ },
36
+ "-s" => -> size {
37
+ opts[:pool_size] = Integer(size)
38
+ },
39
+ "-r" => -> file {
40
+ opts[:requires] << file
41
+ },
42
+ "-v" => -> {
43
+ require_relative "../lib/ost-bin/version"
44
+
45
+ puts Ost::Bin::VERSION
46
+
47
+ exit 0
48
+ },
49
+ "-h" => -> {
50
+ puts(usage)
51
+ exit 0
22
52
  }
23
53
 
24
- worker = args.shift
25
- worker_path = File.expand_path("workers/#{worker}")
54
+ opts[:pid_path] = File.expand_path("ost.pid") unless opts.include?(:pid_path)
26
55
 
27
- $stdout.sync = true
56
+ opts[:requires].each do |file|
57
+ require(file)
58
+ end
28
59
 
29
- if opts[:daemonize]
30
- pid_path = File.expand_path("#{worker}.pid", File.dirname(worker_path))
31
- Process.daemon(true)
32
- File.open(pid_path, File::RDWR|File::EXCL|File::CREAT, 0600) { |io| io.write(Process.pid) }
33
- at_exit do
34
- File.delete(pid_path) if File.exists?(pid_path)
60
+ module Ost
61
+ def self.run(worker)
62
+ workers[worker] = worker.new
63
+ end
64
+
65
+ def self.workers
66
+ @workers ||= {}
35
67
  end
36
68
  end
37
69
 
38
- require worker_path
70
+ case command
71
+ when "start"
72
+ if opts[:daemonize]
73
+ Process.daemon(true)
74
+
75
+ File.open(opts[:pid_path], File::RDWR|File::EXCL|File::CREAT, 0600) do |io|
76
+ io.write(Process.pid)
77
+ end
78
+
79
+ at_exit do
80
+ File.delete(opts[:pid_path]) if File.exists?(opts[:pid_path])
81
+ end
82
+ end
83
+
84
+ load "./Ostfile"
85
+
86
+ opts[:pool_size] = Ost.workers.size unless opts.include?(:pool_size)
87
+
88
+ threads_per_worker = opts[:pool_size] / Ost.workers.size
89
+
90
+ if threads_per_worker == 0
91
+ abort("Not enough threads for your workers (found #{Ost.workers.size} workers).")
92
+ end
93
+
94
+ pool = Ost.workers.each_with_object([]) do |(queue, handler), accum|
95
+ accum.concat(Array.new(threads_per_worker) do
96
+ Thread.new(queue, handler) do |q, h|
97
+ queue = Ost[q]
98
+
99
+ queue.each do |item|
100
+ handler.call(item)
101
+ end
102
+ end
103
+ end)
104
+ end
105
+
106
+ pool.each(&:join)
107
+
108
+ when "stop"
109
+ pid = Integer(File.read(opts[:pid_path]).chomp)
110
+
111
+ running = true
112
+
113
+ Process.kill(:TERM, pid)
114
+
115
+ while running
116
+ begin
117
+ Process.kill(0, pid)
118
+ running = true
119
+ rescue Errno::ESRCH
120
+ running = false
121
+ end
122
+ end
123
+
124
+ else
125
+ $stderr.puts("Unkown command #{command.inspect}.")
126
+ $stderr.puts(usage)
127
+
128
+ exit 2
129
+ end
@@ -1,5 +1,5 @@
1
1
  require "ost"
2
2
 
3
3
  module Ost::Bin
4
- VERSION = "0.0.3"
4
+ VERSION = "0.1.0"
5
5
  end
data/test/bin.rb CHANGED
@@ -1,65 +1,88 @@
1
1
  require "cutest"
2
2
  require "redis"
3
+ require "timeout"
3
4
 
4
5
  at_exit {
5
6
  Process.waitall
6
7
  }
7
8
 
8
9
  def wait_for_pid(pid)
9
- running = true
10
+ wait_for { !running?(pid) }
11
+ end
12
+
13
+ def wait_for_child(pid)
14
+ Timeout.timeout(5) do
15
+ Process.wait(pid)
16
+ end
17
+ end
10
18
 
11
- while running
12
- begin
13
- Process.kill(0, pid)
14
- rescue Errno::ESRCH
15
- running = false
19
+ def wait_for
20
+ Timeout.timeout(5) do
21
+ until value = yield
22
+ sleep 0.1
16
23
  end
24
+
25
+ return value
17
26
  end
18
27
  end
19
28
 
29
+ def running?(pid)
30
+ begin
31
+ Process.kill(0, pid)
32
+ true
33
+ rescue Errno::ESRCH
34
+ false
35
+ end
36
+ end
37
+
38
+ def read_pid_file(path)
39
+ wait_for { File.exist?(path) && File.size(path) > 0 }
40
+
41
+ Integer(File.read(path))
42
+ end
43
+
20
44
  def root(path)
21
45
  File.expand_path("../#{path}", File.dirname(__FILE__))
22
46
  end
23
47
 
24
48
  redis = Redis.connect
25
49
 
26
- test "daemon" do
27
- r, w = IO.pipe
50
+ prepare do
51
+ redis.flushdb
52
+ Dir["test/workers/**/*.pid"].each { |file| File.delete(file) }
53
+ end
54
+
55
+ test "start" do
28
56
  pid = nil
29
57
 
30
58
  begin
31
59
  redis.flushdb
32
60
 
33
- pid = spawn("#{root("bin/ost")} echo", out: w, chdir: "test")
61
+ pid = spawn("#{root("bin/ost")} start", chdir: "test/workers/echo")
34
62
 
35
- redis.rpush("ost:echo", 1)
63
+ redis.rpush("ost:Echo", 2)
36
64
 
37
- assert_equal "1\n", r.gets
65
+ value = wait_for { redis.get("Echo:result") }
66
+
67
+ assert_equal "2", value
38
68
  ensure
39
69
  Process.kill(:INT, pid) if pid
40
70
  end
41
71
  end
42
72
 
43
73
  test "daemonizes" do
44
- r, w = IO.pipe
45
74
  pid, detached_pid = nil
46
75
 
47
- redis.flushdb
76
+ pid_path = "./test/workers/echo/ost.pid"
48
77
 
49
78
  begin
50
- pid = spawn("#{root("bin/ost")} -d echo", out: w, chdir: "test")
51
-
52
- sleep 1
53
-
54
- state = `ps -p #{pid} -o state`.lines.to_a.last[/(\w+)/, 1]
79
+ pid = spawn("#{root("bin/ost")} -d start", chdir: "test/workers/echo")
55
80
 
56
- assert_equal "Z", state
81
+ assert wait_for {
82
+ `ps -p #{pid} -o state`.lines.to_a.last[/(\w+)/, 1] == "Z"
83
+ }
57
84
 
58
- pid_path = "./test/workers/echo.pid"
59
-
60
- assert File.exist?(pid_path)
61
-
62
- detached_pid = File.read(pid_path).to_i
85
+ detached_pid = read_pid_file(pid_path)
63
86
 
64
87
  ppid = `ps -p #{detached_pid} -o ppid`.lines.to_a.last[/(\d+)/, 1]
65
88
 
@@ -75,30 +98,77 @@ test "daemonizes" do
75
98
  end
76
99
 
77
100
  test "gracefully handles TERM signals" do
78
- r, w = IO.pipe
79
- pid, detached_pid = nil
101
+ redis.rpush("ost:Slow", 3)
80
102
 
81
- redis.flushdb
103
+ begin
104
+ spawn("#{root("bin/ost")} -d start", chdir: "test/workers/slow")
82
105
 
83
- pid_path = "./test/workers/slow.pid"
106
+ pid = read_pid_file("./test/workers/slow/ost.pid")
84
107
 
85
- begin
86
- redis.rpush("ost:slow", 5)
108
+ assert wait_for { redis.llen("ost:Slow") == 0 }
109
+ ensure
110
+ Process.kill(:TERM, pid)
111
+ end
87
112
 
88
- pid = spawn("#{root("bin/ost")} -d slow", out: w, chdir: "test")
113
+ wait_for_pid(pid)
89
114
 
90
- until File.exist?(pid_path)
91
- sleep 0.5
92
- end
115
+ assert_equal "3", redis.get("slow")
116
+ end
93
117
 
94
- detached_pid = File.read(pid_path).to_i
118
+ test "stop waits for workers to be done" do
119
+ spawn("#{root("bin/ost")} start -d", chdir: "test/workers/slow")
95
120
 
96
- Process.kill(:TERM, detached_pid)
121
+ pid = read_pid_file("./test/workers/slow/ost.pid")
122
+
123
+ stopper = spawn("#{root("bin/ost")} stop", chdir: "test/workers/slow")
124
+
125
+ # Let the stop command start.
126
+ wait_for { running?(stopper) }
127
+
128
+ # Let the stop command end.
129
+ wait_for_child(stopper)
130
+
131
+ # Immediately after the stop command exits,
132
+ # ost shouldn't be running and the pid file
133
+ # should be gone.
134
+
135
+ assert !running?(pid)
136
+ assert !File.exist?("./test/workers/slow/ost.pid")
137
+ end
138
+
139
+ test "use a specific path for the pid file" do
140
+ pid = nil
141
+ pid_path = "./test/workers/echo/foo.pid"
142
+
143
+ begin
144
+ spawn("#{root("bin/ost")} -d start -p foo.pid", chdir: "test/workers/echo")
145
+
146
+ pid = read_pid_file(pid_path)
147
+
148
+ assert pid
97
149
  ensure
98
- Process.kill(:INT, pid)
150
+ Process.kill(:INT, pid) if pid
99
151
  end
100
152
 
101
- wait_for_pid(detached_pid)
153
+ wait_for_pid(pid)
102
154
 
103
- assert_equal "5", redis.get("slow")
155
+ assert !File.exist?(pid_path)
156
+ end
157
+
158
+ test "load Ostfile" do
159
+ pid = nil
160
+
161
+ begin
162
+ redis.flushdb
163
+
164
+ pid = spawn("#{root("bin/ost")} start", chdir: "test/workers/echo")
165
+
166
+ redis.rpush("ost:Echo", 2)
167
+
168
+ value = wait_for { redis.get("Echo:result") }
169
+
170
+ assert_equal "2", value
171
+ ensure
172
+ Process.kill(:INT, pid) if pid
173
+ end
104
174
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ost-bin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Damian Janowski
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-07-05 00:00:00.000000000 Z
11
+ date: 2014-05-26 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: ost
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: clap
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: cutest
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
@@ -71,30 +64,28 @@ files:
71
64
  - CHANGELOG.md
72
65
  - lib/ost-bin/version.rb
73
66
  - test/bin.rb
74
- - !binary |-
75
- YmluL29zdA==
67
+ - bin/ost
76
68
  homepage: https://github.com/djanowski/ost-bin
77
69
  licenses: []
70
+ metadata: {}
78
71
  post_install_message:
79
72
  rdoc_options: []
80
73
  require_paths:
81
74
  - lib
82
75
  required_ruby_version: !ruby/object:Gem::Requirement
83
- none: false
84
76
  requirements:
85
77
  - - ! '>='
86
78
  - !ruby/object:Gem::Version
87
79
  version: '0'
88
80
  required_rubygems_version: !ruby/object:Gem::Requirement
89
- none: false
90
81
  requirements:
91
82
  - - ! '>='
92
83
  - !ruby/object:Gem::Version
93
84
  version: '0'
94
85
  requirements: []
95
86
  rubyforge_project:
96
- rubygems_version: 1.8.21
87
+ rubygems_version: 2.0.0
97
88
  signing_key:
98
- specification_version: 3
89
+ specification_version: 4
99
90
  summary: ost(1)
100
91
  test_files: []