qs 0.2.0 → 0.3.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.
- data/bench/report.txt +2 -2
- data/lib/qs/client.rb +4 -0
- data/lib/qs/daemon.rb +6 -1
- data/lib/qs/io_pipe.rb +2 -3
- data/lib/qs/process.rb +10 -3
- data/lib/qs/version.rb +1 -1
- data/test/unit/client_tests.rb +7 -0
- data/test/unit/daemon_tests.rb +11 -2
- data/test/unit/error_handler_tests.rb +48 -62
- data/test/unit/io_pipe_tests.rb +16 -8
- data/test/unit/process_tests.rb +44 -0
- metadata +4 -4
data/bench/report.txt
CHANGED
@@ -5,7 +5,7 @@ Adding jobs
|
|
5
5
|
Running jobs
|
6
6
|
....................................................................................................
|
7
7
|
|
8
|
-
Adding 10000 Jobs Time: 1.
|
9
|
-
Running 10000 Jobs Time: 12.
|
8
|
+
Adding 10000 Jobs Time: 1.6133s
|
9
|
+
Running 10000 Jobs Time: 12.4769s
|
10
10
|
|
11
11
|
Done running benchmark report
|
data/lib/qs/client.rb
CHANGED
data/lib/qs/daemon.rb
CHANGED
@@ -71,7 +71,11 @@ module Qs
|
|
71
71
|
!!(@work_loop_thread && @work_loop_thread.alive?)
|
72
72
|
end
|
73
73
|
|
74
|
+
# * Ping redis to check that it can communicate with redis before running,
|
75
|
+
# this is friendlier than starting and continously erroring because it
|
76
|
+
# can't dequeue.
|
74
77
|
def start
|
78
|
+
@client.ping
|
75
79
|
@signal.set :start
|
76
80
|
@work_loop_thread ||= Thread.new{ work_loop }
|
77
81
|
end
|
@@ -160,7 +164,8 @@ module Qs
|
|
160
164
|
|
161
165
|
def wait_for_available_worker
|
162
166
|
if !@worker_pool.worker_available? && @signal.start?
|
163
|
-
@worker_available_io.wait
|
167
|
+
@worker_available_io.wait
|
168
|
+
@worker_available_io.read
|
164
169
|
end
|
165
170
|
end
|
166
171
|
|
data/lib/qs/io_pipe.rb
CHANGED
data/lib/qs/process.rb
CHANGED
@@ -9,6 +9,8 @@ module Qs
|
|
9
9
|
STOP = 'S'.freeze
|
10
10
|
RESTART = 'R'.freeze
|
11
11
|
|
12
|
+
WAIT_FOR_SIGNALS_TIMEOUT = 15
|
13
|
+
|
12
14
|
attr_reader :daemon, :name
|
13
15
|
attr_reader :pid_file, :signal_io, :restart_cmd
|
14
16
|
|
@@ -77,9 +79,14 @@ module Qs
|
|
77
79
|
end
|
78
80
|
|
79
81
|
def wait_for_signals(signal_io, daemon)
|
80
|
-
|
81
|
-
|
82
|
-
handle_signal(
|
82
|
+
loop do
|
83
|
+
ready = signal_io.wait(WAIT_FOR_SIGNALS_TIMEOUT)
|
84
|
+
handle_signal(signal_io.read, daemon) if ready
|
85
|
+
|
86
|
+
if !daemon.running?
|
87
|
+
log "Daemon crashed, restarting"
|
88
|
+
start_daemon(daemon)
|
89
|
+
end
|
83
90
|
end
|
84
91
|
end
|
85
92
|
|
data/lib/qs/version.rb
CHANGED
data/test/unit/client_tests.rb
CHANGED
@@ -127,6 +127,13 @@ module Qs::Client
|
|
127
127
|
assert_equal [@queue_redis_key], call.args
|
128
128
|
end
|
129
129
|
|
130
|
+
should "ping redis using `ping`" do
|
131
|
+
subject.ping
|
132
|
+
|
133
|
+
call = @connection_spy.redis_calls.last
|
134
|
+
assert_equal :ping, call.command
|
135
|
+
end
|
136
|
+
|
130
137
|
end
|
131
138
|
|
132
139
|
class QsClientTests < UnitTests
|
data/test/unit/daemon_tests.rb
CHANGED
@@ -218,6 +218,11 @@ module Qs::Daemon
|
|
218
218
|
@thread.join 0.1
|
219
219
|
end
|
220
220
|
|
221
|
+
should "ping redis" do
|
222
|
+
call = @client_spy.calls.first
|
223
|
+
assert_equal :ping, call.command
|
224
|
+
end
|
225
|
+
|
221
226
|
should "return the thread that is running the daemon" do
|
222
227
|
assert_instance_of Thread, @thread
|
223
228
|
assert_true @thread.alive?
|
@@ -228,8 +233,8 @@ module Qs::Daemon
|
|
228
233
|
end
|
229
234
|
|
230
235
|
should "clear the signals list in redis" do
|
231
|
-
call = @client_spy.calls.
|
232
|
-
|
236
|
+
call = @client_spy.calls.find{ |c| c.command == :clear }
|
237
|
+
assert_not_nil call
|
233
238
|
assert_equal [subject.signals_redis_key], call.args
|
234
239
|
end
|
235
240
|
|
@@ -710,6 +715,10 @@ module Qs::Daemon
|
|
710
715
|
@calls << Call.new(:clear, args)
|
711
716
|
end
|
712
717
|
|
718
|
+
def ping
|
719
|
+
@calls << Call.new(:ping)
|
720
|
+
end
|
721
|
+
|
713
722
|
Call = Struct.new(:command, :args)
|
714
723
|
end
|
715
724
|
|
@@ -11,8 +11,7 @@ class Qs::ErrorHandler
|
|
11
11
|
setup do
|
12
12
|
@exception = Factory.exception
|
13
13
|
@daemon_data = Qs::DaemonData.new
|
14
|
-
@
|
15
|
-
@queue_redis_key = Qs::Queue::RedisKey.new(@queue_name)
|
14
|
+
@queue_redis_key = Qs::Queue::RedisKey.new(Factory.string)
|
16
15
|
@context_hash = {
|
17
16
|
:daemon_data => @daemon_data,
|
18
17
|
:queue_redis_key => @queue_redis_key,
|
@@ -27,20 +26,20 @@ class Qs::ErrorHandler
|
|
27
26
|
|
28
27
|
end
|
29
28
|
|
30
|
-
class
|
29
|
+
class InitSetupTests < UnitTests
|
31
30
|
desc "when init"
|
32
31
|
setup do
|
33
|
-
|
34
|
-
|
35
|
-
@
|
36
|
-
@
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
second_error_proc = proc{ @second_called_at = (@call_count += 1) }
|
42
|
-
Assert.stub(@daemon_data, :error_procs){ [first_error_proc, second_error_proc] }
|
32
|
+
# always make sure there are multiple error procs or tests can be false
|
33
|
+
# positives
|
34
|
+
@error_proc_spies = (1..(Factory.integer(3) + 1)).map{ ErrorProcSpy.new }
|
35
|
+
Assert.stub(@daemon_data, :error_procs){ @error_proc_spies }
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
43
39
|
|
40
|
+
class InitTests < InitSetupTests
|
41
|
+
desc "when init"
|
42
|
+
setup do
|
44
43
|
@handler = @handler_class.new(@exception, @context_hash)
|
45
44
|
end
|
46
45
|
subject{ @handler }
|
@@ -55,7 +54,7 @@ class Qs::ErrorHandler
|
|
55
54
|
end
|
56
55
|
|
57
56
|
should "know its error procs" do
|
58
|
-
assert_equal @
|
57
|
+
assert_equal @error_proc_spies.reverse, subject.error_procs
|
59
58
|
end
|
60
59
|
|
61
60
|
end
|
@@ -66,66 +65,36 @@ class Qs::ErrorHandler
|
|
66
65
|
@handler.run
|
67
66
|
end
|
68
67
|
|
69
|
-
should "
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
should "call each of its error procs" do
|
76
|
-
assert_equal 1, @second_called_at
|
77
|
-
assert_equal 2, @first_called_at
|
68
|
+
should "call each of its procs" do
|
69
|
+
subject.error_procs.each_with_index do |spy, index|
|
70
|
+
assert_true spy.called
|
71
|
+
assert_equal subject.exception, spy.exception
|
72
|
+
assert_equal subject.context, spy.context
|
73
|
+
end
|
78
74
|
end
|
79
75
|
|
80
76
|
end
|
81
77
|
|
82
|
-
class
|
83
|
-
desc "run with
|
78
|
+
class RunWithErrorProcExceptionsTests < InitSetupTests
|
79
|
+
desc "and run with error procs that throw exceptions"
|
84
80
|
setup do
|
85
|
-
@
|
86
|
-
|
87
|
-
|
81
|
+
@proc_exceptions = @error_proc_spies.reverse.map do |spy|
|
82
|
+
exception = Factory.exception(RuntimeError, @error_proc_spies.index(spy).to_s)
|
83
|
+
spy.raise_exception = exception
|
84
|
+
exception
|
85
|
+
end
|
88
86
|
|
89
87
|
@handler = @handler_class.new(@exception, @context_hash).tap(&:run)
|
90
88
|
end
|
91
89
|
subject{ @handler }
|
92
90
|
|
93
|
-
should "
|
94
|
-
|
91
|
+
should "pass the previously raised exception to the next proc" do
|
92
|
+
exp = [@exception] + @proc_exceptions[0..-2]
|
93
|
+
assert_equal exp, subject.error_procs.map(&:exception)
|
95
94
|
end
|
96
95
|
|
97
|
-
|
98
|
-
|
99
|
-
class RunWithMultipleErrorProcsThatThrowExceptionsTests < UnitTests
|
100
|
-
desc "run with multiple error procs that throw an exception"
|
101
|
-
setup do
|
102
|
-
@first_caught_exception = nil
|
103
|
-
@second_caught_exception = nil
|
104
|
-
@third_caught_exception = nil
|
105
|
-
|
106
|
-
@third_proc_exception = Factory.exception
|
107
|
-
third_proc = proc do |exception, context|
|
108
|
-
@third_caught_exception = exception
|
109
|
-
raise @third_proc_exception
|
110
|
-
end
|
111
|
-
|
112
|
-
@second_proc_exception = Factory.exception
|
113
|
-
second_proc = proc do |exception, context|
|
114
|
-
@second_caught_exception = exception
|
115
|
-
raise @second_proc_exception
|
116
|
-
end
|
117
|
-
|
118
|
-
first_proc = proc{ |exception, context| @first_caught_exception = exception }
|
119
|
-
|
120
|
-
Assert.stub(@daemon_data, :error_procs){ [first_proc, second_proc, third_proc] }
|
121
|
-
@handler = @handler_class.new(@exception, @context_hash).tap(&:run)
|
122
|
-
end
|
123
|
-
subject{ @handler }
|
124
|
-
|
125
|
-
should "call each proc, passing the previously raised exception to the next" do
|
126
|
-
assert_equal @exception, @third_caught_exception
|
127
|
-
assert_equal @third_proc_exception, @second_caught_exception
|
128
|
-
assert_equal @second_proc_exception, @first_caught_exception
|
96
|
+
should "set its exception to the last exception thrown by the procs" do
|
97
|
+
assert_equal @proc_exceptions.last, subject.exception
|
129
98
|
end
|
130
99
|
|
131
100
|
end
|
@@ -160,4 +129,21 @@ class Qs::ErrorHandler
|
|
160
129
|
|
161
130
|
end
|
162
131
|
|
132
|
+
class ErrorProcSpy
|
133
|
+
attr_reader :called, :exception, :context
|
134
|
+
attr_accessor :raise_exception
|
135
|
+
|
136
|
+
def initialize
|
137
|
+
@called = false
|
138
|
+
end
|
139
|
+
|
140
|
+
def call(exception, context)
|
141
|
+
@called = true
|
142
|
+
@exception = exception
|
143
|
+
@context = context
|
144
|
+
|
145
|
+
raise self.raise_exception if self.raise_exception
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
163
149
|
end
|
data/test/unit/io_pipe_tests.rb
CHANGED
@@ -8,6 +8,14 @@ class Qs::IOPipe
|
|
8
8
|
class UnitTests < Assert::Context
|
9
9
|
desc "Qs::IOPipe"
|
10
10
|
setup do
|
11
|
+
# mimic how IO.select responds
|
12
|
+
@io_select_response = Factory.boolean ? [[NULL], [], []] : nil
|
13
|
+
@io_select_called_with = nil
|
14
|
+
Assert.stub(IO, :select) do |*args|
|
15
|
+
@io_select_called_with = args
|
16
|
+
@io_select_response
|
17
|
+
end
|
18
|
+
|
11
19
|
@io_pipe = Qs::IOPipe.new
|
12
20
|
end
|
13
21
|
subject{ @io_pipe }
|
@@ -60,15 +68,15 @@ class Qs::IOPipe
|
|
60
68
|
should "be able to wait until there is something to read" do
|
61
69
|
subject.setup
|
62
70
|
|
63
|
-
result =
|
64
|
-
|
65
|
-
|
66
|
-
assert_equal
|
71
|
+
result = subject.wait
|
72
|
+
exp = [[subject.reader], nil, nil, nil]
|
73
|
+
assert_equal exp, @io_select_called_with
|
74
|
+
assert_equal !!@io_select_response, result
|
67
75
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
assert_equal
|
76
|
+
timeout = Factory.integer
|
77
|
+
subject.wait(timeout)
|
78
|
+
exp = [[subject.reader], nil, nil, timeout]
|
79
|
+
assert_equal exp, @io_select_called_with
|
72
80
|
end
|
73
81
|
|
74
82
|
end
|
data/test/unit/process_tests.rb
CHANGED
@@ -13,6 +13,10 @@ class Qs::Process
|
|
13
13
|
end
|
14
14
|
subject{ @process_class }
|
15
15
|
|
16
|
+
should "know its wait for signals timeout" do
|
17
|
+
assert_equal 15, WAIT_FOR_SIGNALS_TIMEOUT
|
18
|
+
end
|
19
|
+
|
16
20
|
end
|
17
21
|
|
18
22
|
class InitTests < UnitTests
|
@@ -117,9 +121,20 @@ class Qs::Process
|
|
117
121
|
class RunTests < RunSetupTests
|
118
122
|
desc "and run"
|
119
123
|
setup do
|
124
|
+
@wait_timeout = nil
|
125
|
+
Assert.stub(@process.signal_io, :wait) do |timeout|
|
126
|
+
@wait_timeout = timeout
|
127
|
+
sleep 0.1
|
128
|
+
false
|
129
|
+
end
|
130
|
+
|
120
131
|
@thread = Thread.new{ @process.run }
|
121
132
|
@thread.join(0.1)
|
122
133
|
end
|
134
|
+
teardown do
|
135
|
+
# manually unstub or the process thread will hang forever
|
136
|
+
Assert.unstub(@process.signal_io, :wait)
|
137
|
+
end
|
123
138
|
|
124
139
|
should "not daemonize the process" do
|
125
140
|
assert_false @daemonize_called
|
@@ -143,6 +158,7 @@ class Qs::Process
|
|
143
158
|
end
|
144
159
|
|
145
160
|
should "sleep its thread waiting for signals" do
|
161
|
+
assert_equal WAIT_FOR_SIGNALS_TIMEOUT, @wait_timeout
|
146
162
|
assert_equal 'sleep', @thread.status
|
147
163
|
end
|
148
164
|
|
@@ -262,6 +278,30 @@ class Qs::Process
|
|
262
278
|
|
263
279
|
end
|
264
280
|
|
281
|
+
class RunWithDaemonCrashTests < RunSetupTests
|
282
|
+
desc "and run with the daemon crashing"
|
283
|
+
setup do
|
284
|
+
# lower the time it sleeps so it loops and restarts the daemon quicker
|
285
|
+
Assert.stub(@process.signal_io, :wait) do |timeout|
|
286
|
+
sleep 0.1
|
287
|
+
false
|
288
|
+
end
|
289
|
+
|
290
|
+
@thread = Thread.new{ @process.run }
|
291
|
+
@daemon_spy.start_called = false
|
292
|
+
@thread.join(0.1)
|
293
|
+
end
|
294
|
+
teardown do
|
295
|
+
# manually unstub or the process thread will hang forever
|
296
|
+
Assert.unstub(@process.signal_io, :wait)
|
297
|
+
end
|
298
|
+
|
299
|
+
should "re-start its daemon" do
|
300
|
+
assert_true @daemon_spy.start_called
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
265
305
|
class RunWithInvalidSignalTests < RunSetupTests
|
266
306
|
desc "and run with unsupported signals"
|
267
307
|
setup do
|
@@ -402,6 +442,10 @@ class Qs::Process
|
|
402
442
|
@halt_args = args
|
403
443
|
@halt_called = true
|
404
444
|
end
|
445
|
+
|
446
|
+
def running?
|
447
|
+
!!@start_called
|
448
|
+
end
|
405
449
|
end
|
406
450
|
|
407
451
|
class RestartCmdSpy
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kelly Redding
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2015-05-
|
19
|
+
date: 2015-05-05 00:00:00 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|