ost-bin 0.0.3 → 0.1.0

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 (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: []