queue_classic 0.2.0 → 0.2.1
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/lib/queue_classic/api.rb +14 -2
- data/lib/queue_classic/durable_array.rb +8 -30
- data/lib/queue_classic/job.rb +7 -2
- data/lib/queue_classic/queue.rb +4 -0
- data/readme.markdown +3 -3
- data/test/api_test.rb +24 -7
- data/test/durable_array_test.rb +4 -3
- data/test/helper.rb +16 -0
- data/test/job_test.rb +82 -0
- data/test/queue_test.rb +11 -0
- metadata +4 -3
- data/lib/notifier.rb +0 -7
data/lib/queue_classic/api.rb
CHANGED
|
@@ -6,7 +6,11 @@ module QC
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def enqueue(job,*params)
|
|
9
|
-
|
|
9
|
+
if job.respond_to?(:details) and job.respond_to?(:params)
|
|
10
|
+
queue.enqueue(job.signature, (job.params || []))
|
|
11
|
+
else
|
|
12
|
+
queue.enqueue(job,params)
|
|
13
|
+
end
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
def dequeue
|
|
@@ -17,6 +21,10 @@ module QC
|
|
|
17
21
|
queue.delete(job)
|
|
18
22
|
end
|
|
19
23
|
|
|
24
|
+
def delete_all
|
|
25
|
+
queue.delete_all
|
|
26
|
+
end
|
|
27
|
+
|
|
20
28
|
def queue_length
|
|
21
29
|
queue.length
|
|
22
30
|
end
|
|
@@ -26,7 +34,11 @@ module QC
|
|
|
26
34
|
method = job.method
|
|
27
35
|
params = job.params
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
if params.class == Array
|
|
38
|
+
klass.send(method,*params)
|
|
39
|
+
else
|
|
40
|
+
klass.send(method,params)
|
|
41
|
+
end
|
|
30
42
|
end
|
|
31
43
|
|
|
32
44
|
def logging_enabled?
|
|
@@ -3,9 +3,6 @@ module QC
|
|
|
3
3
|
|
|
4
4
|
def initialize(args={})
|
|
5
5
|
@db_string = args[:database]
|
|
6
|
-
@connection = connection
|
|
7
|
-
execute("SET client_min_messages TO 'warning'")
|
|
8
|
-
with_log("setup PG LISTEN") { execute("LISTEN jobs") }
|
|
9
6
|
end
|
|
10
7
|
|
|
11
8
|
def <<(details)
|
|
@@ -18,7 +15,7 @@ module QC
|
|
|
18
15
|
end
|
|
19
16
|
|
|
20
17
|
def delete(job)
|
|
21
|
-
|
|
18
|
+
execute("DELETE FROM jobs WHERE id = #{job.id}")
|
|
22
19
|
job
|
|
23
20
|
end
|
|
24
21
|
|
|
@@ -28,7 +25,7 @@ module QC
|
|
|
28
25
|
|
|
29
26
|
def lock_head
|
|
30
27
|
job = nil
|
|
31
|
-
|
|
28
|
+
connection.transaction do
|
|
32
29
|
if job = find_one {"SELECT * FROM jobs WHERE locked_at IS NULL ORDER BY id ASC LIMIT 1 FOR UPDATE"}
|
|
33
30
|
execute("UPDATE jobs SET locked_at = (CURRENT_TIMESTAMP) WHERE id = #{job.id} AND locked_at IS NULL")
|
|
34
31
|
end
|
|
@@ -40,37 +37,32 @@ module QC
|
|
|
40
37
|
if job = lock_head
|
|
41
38
|
job
|
|
42
39
|
else
|
|
43
|
-
|
|
40
|
+
execute("LISTEN jobs")
|
|
41
|
+
connection.wait_for_notify {|e,p,msg| job = lock_head if msg == "new-job" }
|
|
44
42
|
job
|
|
45
43
|
end
|
|
46
44
|
end
|
|
47
45
|
|
|
48
46
|
def each
|
|
49
47
|
execute("SELECT * FROM jobs ORDER BY id ASC").each do |r|
|
|
50
|
-
yield
|
|
48
|
+
yield Job.new(r)
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
def execute(sql)
|
|
55
|
-
|
|
53
|
+
connection.exec(sql)
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
def find_one
|
|
59
57
|
res = execute(yield)
|
|
60
58
|
if res.count > 0
|
|
61
|
-
res.map
|
|
62
|
-
Job.new(
|
|
63
|
-
"id" => r["id"],
|
|
64
|
-
"details" => JSON.parse( r["details"]),
|
|
65
|
-
"locked_at" => r["locked_at"]
|
|
66
|
-
)
|
|
67
|
-
end.pop
|
|
59
|
+
res.map {|r| Job.new(r)}.pop
|
|
68
60
|
end
|
|
69
61
|
end
|
|
70
62
|
|
|
71
63
|
def connection
|
|
72
64
|
db_params = URI.parse(@db_string)
|
|
73
|
-
if db_params.scheme == "postgres"
|
|
65
|
+
@connection ||= if db_params.scheme == "postgres"
|
|
74
66
|
PGconn.connect(
|
|
75
67
|
:dbname => db_params.path.gsub("/",""),
|
|
76
68
|
:user => db_params.user,
|
|
@@ -82,19 +74,5 @@ module QC
|
|
|
82
74
|
end
|
|
83
75
|
end
|
|
84
76
|
|
|
85
|
-
def with_log(msg)
|
|
86
|
-
res = yield
|
|
87
|
-
if QC.logging_enabled?
|
|
88
|
-
log(msg)
|
|
89
|
-
log(res.cmd_status) if res.respond_to?(:cmd_status)
|
|
90
|
-
log(res.result_error_message) if res.respond_to?(:result_error_message)
|
|
91
|
-
end
|
|
92
|
-
res
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def log(msg)
|
|
96
|
-
puts "| \t" + msg
|
|
97
|
-
end
|
|
98
|
-
|
|
99
77
|
end
|
|
100
78
|
end
|
data/lib/queue_classic/job.rb
CHANGED
|
@@ -4,19 +4,24 @@ module QC
|
|
|
4
4
|
|
|
5
5
|
def initialize(args={})
|
|
6
6
|
@id = args["id"]
|
|
7
|
-
@details = args["details"]
|
|
7
|
+
@details = JSON.parse(args["details"])
|
|
8
8
|
@locked_at = args["locked_at"]
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def klass
|
|
12
|
-
|
|
12
|
+
eval(details["job"].split(".").first)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def method
|
|
16
16
|
details["job"].split(".").last
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
def signature
|
|
20
|
+
details["job"]
|
|
21
|
+
end
|
|
22
|
+
|
|
19
23
|
def params
|
|
24
|
+
return [] unless details["params"]
|
|
20
25
|
params = details["params"]
|
|
21
26
|
if params.length == 1
|
|
22
27
|
return params[0]
|
data/lib/queue_classic/queue.rb
CHANGED
data/readme.markdown
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Queue Classic
|
|
2
|
-
__Beta 0.2.
|
|
2
|
+
__Beta 0.2.1__
|
|
3
3
|
|
|
4
|
-
__Queue Classic 0.2.
|
|
4
|
+
__Queue Classic 0.2.1 is in Beta.__ I have been using this library with 30-50 Heroku workers and have had great results. However, your mileage may vary.
|
|
5
5
|
|
|
6
6
|
I am using this in production applications and plan to maintain and support this library for a long time.
|
|
7
7
|
|
|
8
|
-
Queue Classic is an alternative queueing library for Ruby apps (Rails, Sinatra, Etc...) Queue Classic features
|
|
8
|
+
Queue Classic is an alternative queueing library for Ruby apps (Rails, Sinatra, Etc...) Queue Classic features a blocking dequeue, database maintained locks and
|
|
9
9
|
no ridiculous dependencies. As a matter of fact, Queue Classic only requires the __pg__ and __json__.
|
|
10
10
|
|
|
11
11
|
## Installation
|
data/test/api_test.rb
CHANGED
|
@@ -1,14 +1,31 @@
|
|
|
1
1
|
require File.expand_path("../helper.rb", __FILE__)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
context "QC::Api" do
|
|
4
|
+
setup { clean_database }
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
test "enqueue takes a job without params" do
|
|
7
|
+
QC.enqueue "Notifier.send"
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
assert_equal 1, QC.queue_length
|
|
9
|
+
job = QC.dequeue
|
|
10
|
+
assert_equal({"job" => "Notifier.send", "params" => [] }, job.details)
|
|
12
11
|
end
|
|
12
|
+
|
|
13
|
+
test "enqueue takes a hash" do
|
|
14
|
+
QC.enqueue "Notifier.send", {:arg => 1}
|
|
15
|
+
|
|
16
|
+
job = QC.dequeue
|
|
17
|
+
assert_equal({"job" => "Notifier.send", "params" => [{"arg" => 1}] }, job.details)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
test "enqueue takes a job" do
|
|
21
|
+
h = {"id" => 1, "details" => {"job" => 'Notifier.send'}.to_json, "locked_at" => nil}
|
|
22
|
+
job = QC::Job.new(h)
|
|
23
|
+
QC.enqueue(job)
|
|
24
|
+
|
|
25
|
+
job = QC.dequeue
|
|
26
|
+
assert_equal({"job" => "Notifier.send", "params" => []}, job.details)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
13
30
|
end
|
|
14
31
|
|
data/test/durable_array_test.rb
CHANGED
|
@@ -47,8 +47,10 @@ class DurableArrayTest < MiniTest::Unit::TestCase
|
|
|
47
47
|
job = array.first
|
|
48
48
|
|
|
49
49
|
assert_equal( {"job" => "one"}, job.details)
|
|
50
|
+
|
|
51
|
+
assert_equal(1,array.count)
|
|
50
52
|
array.delete(job)
|
|
51
|
-
|
|
53
|
+
assert_equal(0,array.count)
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def test_delete_returns_job_after_delete
|
|
@@ -61,7 +63,6 @@ class DurableArrayTest < MiniTest::Unit::TestCase
|
|
|
61
63
|
assert_equal({"job" => "one"}, job.details)
|
|
62
64
|
|
|
63
65
|
res = array.delete(job)
|
|
64
|
-
assert_nil(array.first)
|
|
65
66
|
assert_equal({"job" => "one"}, res.details)
|
|
66
67
|
end
|
|
67
68
|
|
|
@@ -72,7 +73,7 @@ class DurableArrayTest < MiniTest::Unit::TestCase
|
|
|
72
73
|
array << {"job" => "one"}
|
|
73
74
|
array << {"job" => "two"}
|
|
74
75
|
results = []
|
|
75
|
-
array.each {|v| results << v}
|
|
76
|
+
array.each {|v| results << v.details}
|
|
76
77
|
assert_equal([{"job" => "one"},{"job" => "two"}], results)
|
|
77
78
|
end
|
|
78
79
|
|
data/test/helper.rb
CHANGED
|
@@ -16,3 +16,19 @@ def set_data_store(store=nil)
|
|
|
16
16
|
)
|
|
17
17
|
)
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
def context(*args, &block)
|
|
21
|
+
return super unless (name = args.first) && block
|
|
22
|
+
klass = Class.new(MiniTest::Unit::TestCase) do
|
|
23
|
+
def self.test(name, &block)
|
|
24
|
+
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
|
|
25
|
+
end
|
|
26
|
+
def self.xtest(*args) end
|
|
27
|
+
def self.setup(&block) define_method(:setup, &block) end
|
|
28
|
+
def self.teardown(&block) define_method(:teardown, &block) end
|
|
29
|
+
end
|
|
30
|
+
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
|
31
|
+
|
|
32
|
+
klass.send :include, DatabaseHelpers
|
|
33
|
+
klass.class_eval &block
|
|
34
|
+
end
|
data/test/job_test.rb
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require File.expand_path("../helper.rb", __FILE__)
|
|
2
|
+
|
|
3
|
+
context "QC::Job" do
|
|
4
|
+
|
|
5
|
+
test "initialize takes details as JSON" do
|
|
6
|
+
job = QC::Job.new(
|
|
7
|
+
"id" => 1,
|
|
8
|
+
"details" => "{\"arg\":1}",
|
|
9
|
+
"locked_at" => nil
|
|
10
|
+
)
|
|
11
|
+
assert_equal({"arg" => 1}, job.details)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
test "signature returns the class and method" do
|
|
15
|
+
job = QC::Job.new(
|
|
16
|
+
"id" => 1,
|
|
17
|
+
"details" => {:job => "Class.method", :params => []}.to_json,
|
|
18
|
+
"locked_at" => nil
|
|
19
|
+
)
|
|
20
|
+
assert_equal "Class.method", job.signature
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
test "method returns the class method" do
|
|
24
|
+
job = QC::Job.new(
|
|
25
|
+
"id" => 1,
|
|
26
|
+
"details" => {:job => "Class.method", :params => []}.to_json,
|
|
27
|
+
"locked_at" => nil
|
|
28
|
+
)
|
|
29
|
+
assert_equal "method", job.method
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
test "klass returns the class" do
|
|
33
|
+
class WhoHa; end
|
|
34
|
+
job = QC::Job.new(
|
|
35
|
+
"id" => 1,
|
|
36
|
+
"details" => {:job => "WhoHa.method", :params => []}.to_json,
|
|
37
|
+
"locked_at" => nil
|
|
38
|
+
)
|
|
39
|
+
assert_equal WhoHa, job.klass
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
test "klass returns the class when scoped to module" do
|
|
43
|
+
module Mod
|
|
44
|
+
class K
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
job = QC::Job.new(
|
|
49
|
+
"id" => 1,
|
|
50
|
+
"details" => {:job => "Mod::K.method", :params => []}.to_json,
|
|
51
|
+
"locked_at" => nil
|
|
52
|
+
)
|
|
53
|
+
assert_equal Mod::K, job.klass
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "params returns empty array when nil" do
|
|
57
|
+
job = QC::Job.new(
|
|
58
|
+
"id" => 1,
|
|
59
|
+
"details" => {:job => "Mod::K.method", :params => nil}.to_json,
|
|
60
|
+
"locked_at" => nil
|
|
61
|
+
)
|
|
62
|
+
assert_equal [], job.params
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
test "params returns 1 items when there is 1 param" do
|
|
66
|
+
job = QC::Job.new(
|
|
67
|
+
"id" => 1,
|
|
68
|
+
"details" => {:job => "Mod::K.method", :params => ["arg"]}.to_json,
|
|
69
|
+
"locked_at" => nil
|
|
70
|
+
)
|
|
71
|
+
assert_equal "arg", job.params
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
test "params retuns many items when there are many params" do
|
|
75
|
+
job = QC::Job.new(
|
|
76
|
+
"id" => 1,
|
|
77
|
+
"details" => {:job => "Mod::K.method", :params => ["arg","arg"]}.to_json,
|
|
78
|
+
"locked_at" => nil
|
|
79
|
+
)
|
|
80
|
+
assert_equal ["arg","arg"], job.params
|
|
81
|
+
end
|
|
82
|
+
end
|
data/test/queue_test.rb
CHANGED
|
@@ -19,4 +19,15 @@ class QueueTest < MiniTest::Unit::TestCase
|
|
|
19
19
|
assert_equal 1, QC::Queue.instance.length
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
def test_queue_delete_all
|
|
23
|
+
QC::Queue.instance.setup :data_store => []
|
|
24
|
+
|
|
25
|
+
QC::Queue.instance.enqueue "job","params"
|
|
26
|
+
QC::Queue.instance.enqueue "job","params"
|
|
27
|
+
|
|
28
|
+
assert_equal 2, QC::Queue.instance.length
|
|
29
|
+
QC::Queue.instance.delete_all
|
|
30
|
+
assert_equal 0, QC::Queue.instance.length
|
|
31
|
+
end
|
|
32
|
+
|
|
22
33
|
end
|
metadata
CHANGED
|
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
|
5
5
|
segments:
|
|
6
6
|
- 0
|
|
7
7
|
- 2
|
|
8
|
-
-
|
|
9
|
-
version: 0.2.
|
|
8
|
+
- 1
|
|
9
|
+
version: 0.2.1
|
|
10
10
|
platform: ruby
|
|
11
11
|
authors:
|
|
12
12
|
- Ryan Smith
|
|
@@ -55,7 +55,6 @@ extra_rdoc_files: []
|
|
|
55
55
|
|
|
56
56
|
files:
|
|
57
57
|
- readme.markdown
|
|
58
|
-
- lib/notifier.rb
|
|
59
58
|
- lib/queue_classic/api.rb
|
|
60
59
|
- lib/queue_classic/durable_array.rb
|
|
61
60
|
- lib/queue_classic/job.rb
|
|
@@ -67,6 +66,7 @@ files:
|
|
|
67
66
|
- test/database_helpers.rb
|
|
68
67
|
- test/durable_array_test.rb
|
|
69
68
|
- test/helper.rb
|
|
69
|
+
- test/job_test.rb
|
|
70
70
|
- test/queue_test.rb
|
|
71
71
|
- test/worker_test.rb
|
|
72
72
|
has_rdoc: true
|
|
@@ -104,5 +104,6 @@ summary: Queue Classic is an alternative queueing library for Ruby apps (Rails,
|
|
|
104
104
|
test_files:
|
|
105
105
|
- test/api_test.rb
|
|
106
106
|
- test/durable_array_test.rb
|
|
107
|
+
- test/job_test.rb
|
|
107
108
|
- test/queue_test.rb
|
|
108
109
|
- test/worker_test.rb
|