timberline 0.1.2 → 0.2.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/Rakefile +0 -1
- data/lib/timberline.rb +101 -6
- data/lib/timberline/config.rb +4 -7
- data/lib/timberline/queue.rb +87 -6
- data/lib/timberline/version.rb +1 -1
- data/test/test_helper.rb +10 -1
- data/test/unit/test_config.rb +31 -26
- data/test/unit/test_envelope.rb +17 -15
- data/test/unit/test_queue.rb +54 -15
- data/test/unit/test_timberline.rb +124 -46
- data/timberline.gemspec +3 -0
- metadata +32 -13
- data/lib/timberline/queue_manager.rb +0 -84
- data/test/unit/test_queue_manager.rb +0 -154
data/Rakefile
CHANGED
data/lib/timberline.rb
CHANGED
@@ -1,22 +1,18 @@
|
|
1
|
-
require 'forwardable'
|
2
1
|
require 'json'
|
3
2
|
require 'logger'
|
4
3
|
require 'yaml'
|
5
4
|
|
6
5
|
require 'redis'
|
7
6
|
require 'redis-namespace'
|
7
|
+
require 'redis-expiring-set/monkeypatch'
|
8
8
|
|
9
9
|
require_relative "timberline/version"
|
10
10
|
require_relative "timberline/config"
|
11
11
|
require_relative "timberline/queue"
|
12
12
|
require_relative "timberline/envelope"
|
13
|
-
require_relative "timberline/queue_manager"
|
14
13
|
|
15
14
|
class Timberline
|
16
15
|
class << self
|
17
|
-
extend Forwardable
|
18
|
-
|
19
|
-
def_delegators :@queue_manager, :error_job, :retry_job, :watch, :push
|
20
16
|
|
21
17
|
attr_reader :config
|
22
18
|
|
@@ -38,9 +34,54 @@ class Timberline
|
|
38
34
|
if @redis.nil?
|
39
35
|
self.redis = Redis.new(@config.redis_config)
|
40
36
|
end
|
37
|
+
|
41
38
|
@redis
|
42
39
|
end
|
43
40
|
|
41
|
+
def all_queues
|
42
|
+
Timberline.redis.smembers("timberline_queue_names").map { |name| queue(name) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def error_queue
|
46
|
+
@error_queue ||= Queue.new("timberline_errors")
|
47
|
+
end
|
48
|
+
|
49
|
+
def queue(queue_name)
|
50
|
+
if @queue_list[queue_name].nil?
|
51
|
+
@queue_list[queue_name] = Queue.new(queue_name)
|
52
|
+
end
|
53
|
+
@queue_list[queue_name]
|
54
|
+
end
|
55
|
+
|
56
|
+
def push(queue_name, data, metadata={})
|
57
|
+
queue(queue_name).push(data, metadata)
|
58
|
+
end
|
59
|
+
|
60
|
+
def retry_item(item)
|
61
|
+
if (item.retries < max_retries)
|
62
|
+
item.retries += 1
|
63
|
+
item.last_tried_at = Time.now.to_f
|
64
|
+
queue(item.origin_queue).push(item)
|
65
|
+
raise ItemRetried
|
66
|
+
else
|
67
|
+
error_item(item)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def error_item(item)
|
72
|
+
item.fatal_error_at = Time.now.to_f
|
73
|
+
error_queue.push(item)
|
74
|
+
raise ItemErrored
|
75
|
+
end
|
76
|
+
|
77
|
+
def pause(queue_name)
|
78
|
+
queue(queue_name).pause
|
79
|
+
end
|
80
|
+
|
81
|
+
def unpause(queue_name)
|
82
|
+
queue(queue_name).unpause
|
83
|
+
end
|
84
|
+
|
44
85
|
def config(&block)
|
45
86
|
initialize_if_necessary
|
46
87
|
yield @config
|
@@ -51,6 +92,36 @@ class Timberline
|
|
51
92
|
@config.max_retries
|
52
93
|
end
|
53
94
|
|
95
|
+
def stat_timeout
|
96
|
+
initialize_if_necessary
|
97
|
+
@config.stat_timeout
|
98
|
+
end
|
99
|
+
|
100
|
+
def stat_timeout_seconds
|
101
|
+
initialize_if_necessary
|
102
|
+
@config.stat_timeout * 60
|
103
|
+
end
|
104
|
+
|
105
|
+
def watch(queue_name, &block)
|
106
|
+
queue = queue(queue_name)
|
107
|
+
while(true)
|
108
|
+
item = queue.pop
|
109
|
+
fix_binding(block)
|
110
|
+
item.started_processing_at = Time.now.to_f
|
111
|
+
|
112
|
+
begin
|
113
|
+
block.call(item, self)
|
114
|
+
rescue ItemRetried
|
115
|
+
queue.add_retry_stat(item)
|
116
|
+
rescue ItemErrored
|
117
|
+
queue.add_error_stat(item)
|
118
|
+
else
|
119
|
+
item.finished_processing_at = Time.now.to_f
|
120
|
+
queue.add_success_stat(item)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
54
125
|
private
|
55
126
|
# Don't know if I like doing this, but we want the configuration to be
|
56
127
|
# lazy-loaded so as to be sure and give users a chance to set up their
|
@@ -58,8 +129,32 @@ class Timberline
|
|
58
129
|
def initialize_if_necessary
|
59
130
|
@config ||= Config.new
|
60
131
|
end
|
132
|
+
|
133
|
+
# Hacky-hacky. I like the idea of calling retry_item(item) and
|
134
|
+
# error_item(item)
|
135
|
+
# directly from the watch block, but this seems ugly. There may be a better
|
136
|
+
# way to do this.
|
137
|
+
def fix_binding(block)
|
138
|
+
binding = block.binding
|
139
|
+
binding.eval <<-HERE
|
140
|
+
def retry_item(item)
|
141
|
+
Timberline.retry_item(item)
|
142
|
+
raise Timberline::ItemRetried
|
143
|
+
end
|
144
|
+
|
145
|
+
def error_item(item)
|
146
|
+
Timberline.error_item(item)
|
147
|
+
raise Timberline::ItemErrored
|
148
|
+
end
|
149
|
+
HERE
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class ItemRetried < Exception
|
61
154
|
end
|
62
155
|
|
156
|
+
class ItemErrored < Exception
|
157
|
+
end
|
63
158
|
end
|
64
159
|
|
65
|
-
Timberline.instance_variable_set("@
|
160
|
+
Timberline.instance_variable_set("@queue_list", {})
|
data/lib/timberline/config.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Timberline
|
2
2
|
class Config
|
3
|
-
attr_accessor :database, :host, :port, :timeout, :password, :logger, :namespace, :max_retries
|
3
|
+
attr_accessor :database, :host, :port, :timeout, :password, :logger, :namespace, :max_retries, :stat_timeout
|
4
4
|
|
5
5
|
def initialize
|
6
6
|
if defined? TIMBERLINE_YAML
|
@@ -15,14 +15,11 @@ class Timberline
|
|
15
15
|
load_from_yaml(config_file)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def namespace
|
21
|
-
@namespace ||= 'timberline'
|
22
|
-
end
|
23
18
|
|
24
|
-
|
19
|
+
# load defaults
|
20
|
+
@namespace ||= 'timberline'
|
25
21
|
@max_retries ||= 5
|
22
|
+
@stat_timeout ||= 60
|
26
23
|
end
|
27
24
|
|
28
25
|
def redis_config
|
data/lib/timberline/queue.rb
CHANGED
@@ -6,6 +6,15 @@ class Timberline
|
|
6
6
|
@queue_name = queue_name
|
7
7
|
@read_timeout = read_timeout
|
8
8
|
@redis = Timberline.redis
|
9
|
+
@redis.sadd "timberline_queue_names", queue_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def delete!
|
13
|
+
@redis.del @queue_name
|
14
|
+
@redis.keys("#{@queue_name}:*").each do |key|
|
15
|
+
@redis.del key
|
16
|
+
end
|
17
|
+
@redis.srem "timberline_queue_names", @queue_name
|
9
18
|
end
|
10
19
|
|
11
20
|
def length
|
@@ -13,6 +22,8 @@ class Timberline
|
|
13
22
|
end
|
14
23
|
|
15
24
|
def pop
|
25
|
+
block_while_paused
|
26
|
+
|
16
27
|
br_tuple = @redis.brpop(@queue_name, read_timeout)
|
17
28
|
envelope_string = br_tuple.nil? ? nil : br_tuple[1]
|
18
29
|
if envelope_string.nil?
|
@@ -22,21 +33,91 @@ class Timberline
|
|
22
33
|
end
|
23
34
|
end
|
24
35
|
|
25
|
-
def push(
|
26
|
-
case
|
36
|
+
def push(contents, metadata = {})
|
37
|
+
case contents
|
27
38
|
when Envelope
|
28
|
-
@redis.lpush @queue_name,
|
39
|
+
@redis.lpush @queue_name, contents
|
29
40
|
else
|
30
|
-
@redis.lpush @queue_name, wrap(
|
41
|
+
@redis.lpush @queue_name, wrap(contents, metadata)
|
31
42
|
end
|
32
43
|
end
|
33
44
|
|
45
|
+
def pause
|
46
|
+
@redis[attr("paused")] = "true"
|
47
|
+
end
|
48
|
+
|
49
|
+
def unpause
|
50
|
+
@redis[attr("paused")] = "false"
|
51
|
+
end
|
52
|
+
|
53
|
+
def paused?
|
54
|
+
@redis[attr("paused")] == "true"
|
55
|
+
end
|
56
|
+
|
57
|
+
def attr(key)
|
58
|
+
"#{@queue_name}:#{key}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def number_errors
|
62
|
+
Timberline.redis.xcard attr("error_stats")
|
63
|
+
end
|
64
|
+
|
65
|
+
def number_retries
|
66
|
+
Timberline.redis.xcard attr("retry_stats")
|
67
|
+
end
|
68
|
+
|
69
|
+
def number_successes
|
70
|
+
Timberline.redis.xcard attr("success_stats")
|
71
|
+
end
|
72
|
+
|
73
|
+
def average_execution_time
|
74
|
+
successes = Timberline.redis.xmembers(attr("success_stats")).map { |item| Envelope.from_json(item)}
|
75
|
+
times = successes.map { |item| item.finished_processing_at.to_f - item.started_processing_at.to_f }
|
76
|
+
times.inject(0, :+) / times.size.to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_retry_stat(item)
|
80
|
+
add_stat_for_key(attr("retry_stats"), item)
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_error_stat(item)
|
84
|
+
add_stat_for_key(attr("success_stats"), item)
|
85
|
+
end
|
86
|
+
|
87
|
+
def add_success_stat(item)
|
88
|
+
add_stat_for_key(attr("success_stats"), item)
|
89
|
+
end
|
90
|
+
|
34
91
|
private
|
35
92
|
|
36
|
-
def
|
93
|
+
def add_stat_for_key(key, item)
|
94
|
+
Timberline.redis.xadd key, item, Time.now + Timberline.stat_timeout_seconds
|
95
|
+
end
|
96
|
+
|
97
|
+
def next_id
|
98
|
+
Timberline.redis.incr attr("id_seq")
|
99
|
+
end
|
100
|
+
|
101
|
+
def wrap(contents, metadata)
|
37
102
|
envelope = Envelope.new
|
38
|
-
envelope.contents =
|
103
|
+
envelope.contents = contents
|
104
|
+
metadata.each do |key, value|
|
105
|
+
envelope.send("#{key.to_s}=", value)
|
106
|
+
end
|
107
|
+
|
108
|
+
# default metadata
|
109
|
+
envelope.item_id = next_id
|
110
|
+
envelope.retries = 0
|
111
|
+
envelope.submitted_at = Time.now.to_f
|
112
|
+
envelope.origin_queue = @queue_name
|
113
|
+
|
39
114
|
envelope
|
40
115
|
end
|
116
|
+
|
117
|
+
def block_while_paused
|
118
|
+
while(self.paused?)
|
119
|
+
sleep(1)
|
120
|
+
end
|
121
|
+
end
|
41
122
|
end
|
42
123
|
end
|
data/lib/timberline/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'shoulda'
|
4
4
|
|
5
5
|
# include the gem
|
6
6
|
require 'timberline'
|
7
7
|
|
8
|
+
def reset_timberline
|
9
|
+
Timberline.redis = nil
|
10
|
+
Timberline.instance_variable_set("@config", nil)
|
11
|
+
clear_test_db
|
12
|
+
Timberline.redis = nil
|
13
|
+
Timberline.instance_variable_set("@queue_list", {})
|
14
|
+
end
|
15
|
+
|
8
16
|
# Use database 15 for testing, so we don't risk overwriting any data that's
|
9
17
|
# actually useful
|
10
18
|
def clear_test_db
|
@@ -13,3 +21,4 @@ def clear_test_db
|
|
13
21
|
end
|
14
22
|
Timberline.redis.flushdb
|
15
23
|
end
|
24
|
+
|
data/test/unit/test_config.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class ConfigTest < Test::Unit::TestCase
|
4
|
+
context "Without any preset YAML configs" do
|
5
|
+
setup do
|
6
6
|
@config = Timberline::Config.new
|
7
7
|
end
|
8
8
|
|
9
|
-
|
9
|
+
should "build a proper config hash for Redis" do
|
10
10
|
@logger = Logger.new STDERR
|
11
11
|
|
12
12
|
@config.host = "localhost"
|
@@ -27,7 +27,7 @@ describe Timberline::Config do
|
|
27
27
|
|
28
28
|
end
|
29
29
|
|
30
|
-
|
30
|
+
should "reads configuration from a YAML config file" do
|
31
31
|
base_dir = File.dirname(File.path(__FILE__))
|
32
32
|
yaml_file = File.join(base_dir, "..", "test_config.yaml")
|
33
33
|
@config.load_from_yaml(yaml_file)
|
@@ -40,34 +40,39 @@ describe Timberline::Config do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
RAILS_ROOT = File.join(File.dirname(File.path(__FILE__)), "..", "gibberish")
|
43
|
+
context "when in a Rails app without a config file" do
|
44
|
+
setup do
|
45
|
+
Object::RAILS_ROOT = File.join(File.dirname(File.path(__FILE__)), "..", "gibberish")
|
46
46
|
@config = Timberline::Config.new
|
47
47
|
end
|
48
48
|
|
49
|
-
|
49
|
+
teardown do
|
50
50
|
Object.send(:remove_const, :RAILS_ROOT)
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
["database","host","port","timeout","password","logger"
|
53
|
+
should "load successfully without any configs." do
|
54
|
+
["database","host","port","timeout","password","logger"].each do |setting|
|
55
55
|
assert_equal nil, @config.instance_variable_get("@#{setting}")
|
56
56
|
end
|
57
|
+
|
58
|
+
# check defaults
|
59
|
+
assert_equal "timberline", @config.namespace
|
60
|
+
assert_equal 5, @config.max_retries
|
61
|
+
assert_equal 60, @config.stat_timeout
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
60
|
-
|
61
|
-
|
62
|
-
RAILS_ROOT = File.join(File.dirname(File.path(__FILE__)), "..", "fake_rails")
|
65
|
+
context "when in a Rails app with a config file" do
|
66
|
+
setup do
|
67
|
+
Object::RAILS_ROOT = File.join(File.dirname(File.path(__FILE__)), "..", "fake_rails")
|
63
68
|
@config = Timberline::Config.new
|
64
69
|
end
|
65
70
|
|
66
|
-
|
71
|
+
teardown do
|
67
72
|
Object.send(:remove_const, :RAILS_ROOT)
|
68
73
|
end
|
69
74
|
|
70
|
-
|
75
|
+
should "load the config/timberline.yaml file" do
|
71
76
|
assert_equal "localhost", @config.host
|
72
77
|
assert_equal 12345, @config.port
|
73
78
|
assert_equal 10, @config.timeout
|
@@ -77,17 +82,17 @@ describe Timberline::Config do
|
|
77
82
|
end
|
78
83
|
end
|
79
84
|
|
80
|
-
|
81
|
-
|
82
|
-
TIMBERLINE_YAML = File.join(File.dirname(File.path(__FILE__)), "..", "test_config.yaml")
|
85
|
+
context "when TIMBERLINE_YAML is defined" do
|
86
|
+
setup do
|
87
|
+
Object::TIMBERLINE_YAML = File.join(File.dirname(File.path(__FILE__)), "..", "test_config.yaml")
|
83
88
|
@config = Timberline::Config.new
|
84
89
|
end
|
85
90
|
|
86
|
-
|
91
|
+
teardown do
|
87
92
|
Object.send(:remove_const, :TIMBERLINE_YAML)
|
88
93
|
end
|
89
94
|
|
90
|
-
|
95
|
+
should "load the specified yaml file" do
|
91
96
|
assert_equal "localhost", @config.host
|
92
97
|
assert_equal 12345, @config.port
|
93
98
|
assert_equal 10, @config.timeout
|
@@ -97,16 +102,16 @@ describe Timberline::Config do
|
|
97
102
|
end
|
98
103
|
end
|
99
104
|
|
100
|
-
|
101
|
-
|
102
|
-
TIMBERLINE_YAML = File.join(File.dirname(File.path(__FILE__)), "..", "fake_config.yaml")
|
105
|
+
context "when TIMBERLINE_YAML is defined, but doesn't exist" do
|
106
|
+
setup do
|
107
|
+
Object::TIMBERLINE_YAML = File.join(File.dirname(File.path(__FILE__)), "..", "fake_config.yaml")
|
103
108
|
end
|
104
109
|
|
105
|
-
|
110
|
+
teardown do
|
106
111
|
Object.send(:remove_const, :TIMBERLINE_YAML)
|
107
112
|
end
|
108
113
|
|
109
|
-
|
114
|
+
should "raise an exception" do
|
110
115
|
assert_raises RuntimeError do
|
111
116
|
@config = Timberline::Config.new
|
112
117
|
end
|
data/test/unit/test_envelope.rb
CHANGED
@@ -1,51 +1,53 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'date'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class EnvelopeTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "newly instantiated" do
|
7
|
+
setup do
|
8
|
+
@envelope = Timberline::Envelope.new
|
9
|
+
end
|
8
10
|
|
9
|
-
|
10
|
-
it "raises a MissingContentException when to_s is called because the contents are nil" do
|
11
|
+
should "raises a MissingContentException when to_s is called because the contents are nil" do
|
11
12
|
assert_raises Timberline::MissingContentException do
|
12
13
|
@envelope.to_s
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
|
17
|
+
should "has an empty hash for metadata" do
|
17
18
|
assert_equal({}, @envelope.metadata)
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
+
should "allows for the reading of attributes via method_missing magic" do
|
21
22
|
@envelope.metadata["original_queue"] = "test_queue"
|
22
23
|
assert_equal "test_queue", @envelope.original_queue
|
23
24
|
end
|
24
25
|
|
25
|
-
|
26
|
+
should "allows for the setting of attributes via method_missing magic" do
|
26
27
|
@envelope.original_queue = "test_queue"
|
27
28
|
assert_equal "test_queue", @envelope.metadata["original_queue"]
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
|
32
|
+
context "with contents" do
|
33
|
+
setup do
|
34
|
+
@envelope = Timberline::Envelope.new
|
33
35
|
@envelope.contents = "Test data"
|
34
36
|
end
|
35
37
|
|
36
|
-
|
38
|
+
should "returns a JSON string when to_s is called" do
|
37
39
|
json_string = @envelope.to_s
|
38
40
|
json_data = JSON.parse(json_string)
|
39
41
|
assert_equal "Test data", json_data["contents"]
|
40
42
|
end
|
41
43
|
|
42
|
-
|
44
|
+
should "only includes a 'contents' parameter by default" do
|
43
45
|
json_string = @envelope.to_s
|
44
46
|
json_data = JSON.parse(json_string)
|
45
47
|
assert_equal 1, json_data.keys.size
|
46
48
|
end
|
47
49
|
|
48
|
-
|
50
|
+
should "also includes metadata, if provided" do
|
49
51
|
time = DateTime.now
|
50
52
|
time_s = DateTime.now.to_s
|
51
53
|
@envelope.first_posted = time
|
@@ -57,7 +59,7 @@ describe Timberline::Envelope do
|
|
57
59
|
assert_equal time_s, json_data["first_posted"]
|
58
60
|
end
|
59
61
|
|
60
|
-
|
62
|
+
should "parses itself back correctly using from_json" do
|
61
63
|
json_string = @envelope.to_s
|
62
64
|
new_envelope = Timberline::Envelope.from_json(json_string)
|
63
65
|
assert_equal @envelope.contents, new_envelope.contents
|
data/test/unit/test_queue.rb
CHANGED
@@ -1,36 +1,51 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class QueueTest < Test::Unit::TestCase
|
4
|
+
context "newly instantiated" do
|
5
|
+
setup do
|
6
6
|
clear_test_db
|
7
7
|
@queue = Timberline::Queue.new("test_queue")
|
8
8
|
end
|
9
9
|
|
10
|
-
|
10
|
+
should "saves the passed-in string as its queue name" do
|
11
11
|
assert_equal "test_queue", @queue.queue_name
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
should "saves its existence in timberline_queue_names" do
|
15
|
+
assert_equal true, Timberline.redis.sismember("timberline_queue_names", "test_queue")
|
16
|
+
end
|
17
|
+
|
18
|
+
should "has a length of 0" do
|
15
19
|
assert_equal 0, @queue.length
|
16
20
|
end
|
17
21
|
|
18
|
-
|
22
|
+
should "starts in an unpaused state" do
|
23
|
+
assert_equal false, @queue.paused?
|
24
|
+
end
|
25
|
+
|
26
|
+
should "allows itself to be paused and unpaused" do
|
27
|
+
@queue.pause
|
28
|
+
assert_equal true, @queue.paused?
|
29
|
+
@queue.unpause
|
30
|
+
assert_equal false, @queue.paused?
|
31
|
+
end
|
32
|
+
|
33
|
+
should "has a default read_timeout of 0" do
|
19
34
|
assert_equal 0, @queue.read_timeout
|
20
35
|
end
|
21
36
|
|
22
|
-
|
37
|
+
should "responds nil to a pop request after the read_timeout occurs" do
|
23
38
|
# Let's set the read_timeout to 1 in order for this test to return
|
24
39
|
@queue.instance_variable_set("@read_timeout", 1)
|
25
40
|
assert_equal nil, @queue.pop
|
26
41
|
end
|
27
42
|
|
28
|
-
|
43
|
+
should "puts an item on the queue when that item is pushed" do
|
29
44
|
test_item = "Test Queue Item"
|
30
45
|
assert_equal 1, @queue.push(test_item)
|
31
46
|
end
|
32
47
|
|
33
|
-
|
48
|
+
should "wraps an item in an envelope when that item is pushed" do
|
34
49
|
test_item = "Test Queue Item"
|
35
50
|
assert_equal 1, @queue.push(test_item)
|
36
51
|
data = @queue.pop
|
@@ -38,7 +53,7 @@ describe Timberline::Queue do
|
|
38
53
|
assert_equal test_item, data.contents
|
39
54
|
end
|
40
55
|
|
41
|
-
|
56
|
+
should "doesn't wrap an envelope that gets pushed in another envelope" do
|
42
57
|
test_item = "Test Queue Item"
|
43
58
|
env = Timberline::Envelope.new
|
44
59
|
env.contents = test_item
|
@@ -47,25 +62,49 @@ describe Timberline::Queue do
|
|
47
62
|
assert_kind_of Timberline::Envelope, data
|
48
63
|
assert_equal test_item, data.contents
|
49
64
|
end
|
65
|
+
|
66
|
+
should "removes everything associated with the queue when delete! is called" do
|
67
|
+
test_item = "Test Queue Item"
|
68
|
+
assert_equal 1, @queue.push(test_item)
|
69
|
+
Timberline.redis[@queue.attr("test")] = "test"
|
70
|
+
Timberline.redis[@queue.attr("foo")] = "foo"
|
71
|
+
Timberline.redis[@queue.attr("bar")] = "bar"
|
72
|
+
|
73
|
+
@queue.delete!
|
74
|
+
assert_equal nil, Timberline.redis[@queue.attr("test")]
|
75
|
+
assert_equal nil, Timberline.redis[@queue.attr("test")]
|
76
|
+
assert_equal nil, Timberline.redis[@queue.attr("test")]
|
77
|
+
assert_equal nil, Timberline.redis[@queue.queue_name]
|
78
|
+
assert_equal false, Timberline.redis.sismember("timberline_queue_names","test_queue")
|
79
|
+
end
|
80
|
+
|
81
|
+
should "uses any passed-in metadata to push when building the envelope" do
|
82
|
+
@queue.push("Howdy kids.", { :special_notes => "Super-awesome."})
|
83
|
+
assert_equal 1, @queue.length
|
84
|
+
data = @queue.pop
|
85
|
+
assert_equal "Howdy kids.", data.contents
|
86
|
+
assert_equal "Super-awesome.", data.special_notes
|
87
|
+
end
|
88
|
+
|
50
89
|
end
|
51
90
|
|
52
|
-
|
53
|
-
|
91
|
+
context "with one item" do
|
92
|
+
setup do
|
54
93
|
clear_test_db
|
55
94
|
@test_item = "Test Queue Item"
|
56
95
|
@queue = Timberline::Queue.new("test_queue")
|
57
96
|
@queue.push(@test_item)
|
58
97
|
end
|
59
98
|
|
60
|
-
|
99
|
+
should "has a length of 1" do
|
61
100
|
assert_equal 1, @queue.length
|
62
101
|
end
|
63
102
|
|
64
|
-
|
103
|
+
should "responds to pop with the one item" do
|
65
104
|
assert_equal @test_item, @queue.pop.contents
|
66
105
|
end
|
67
106
|
|
68
|
-
|
107
|
+
should "responds nil to a second pop" do
|
69
108
|
# Let's set the read_timeout to 1 in order for this test to return
|
70
109
|
@queue.instance_variable_set("@read_timeout", 1)
|
71
110
|
assert_equal @test_item, @queue.pop.contents
|
@@ -1,65 +1,143 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
3
|
+
class TimberlineTest < Test::Unit::TestCase
|
4
|
+
context "Freshly set up" do
|
5
|
+
setup do
|
6
|
+
reset_timberline
|
7
|
+
end
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
9
|
+
should "creates a new queue when asked if it doesn't exist" do
|
10
|
+
queue = Timberline.queue("test_queue")
|
11
|
+
assert_kind_of Timberline::Queue, queue
|
12
|
+
assert_equal queue, Timberline.instance_variable_get("@queue_list")["test_queue"]
|
13
|
+
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
15
|
+
should "doesn't create a new queue if a queue by the same name already exists" do
|
16
|
+
queue = Timberline.queue("test_queue")
|
17
|
+
new_queue = Timberline.queue("test_queue")
|
18
|
+
assert_equal queue, new_queue
|
19
|
+
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
should "creates a new queue as necessary when 'push' is called and pushes the item" do
|
22
|
+
Timberline.push("test_queue", "Howdy kids.")
|
23
|
+
queue = Timberline.queue("test_queue")
|
24
|
+
assert_equal 1, queue.length
|
25
|
+
assert_equal "Howdy kids.", queue.pop.contents
|
26
|
+
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
should "logs the existence of the queue so that other managers can see it" do
|
29
|
+
queue = Timberline.queue("test_queue")
|
30
|
+
assert_equal 1, Timberline.all_queues.size
|
31
|
+
assert_equal "test_queue", Timberline.all_queues.first.queue_name
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
should "saves a passed-in redis namespace" do
|
35
|
+
redis = Redis.new
|
36
|
+
redisns = Redis::Namespace.new("timberline", redis)
|
37
|
+
Timberline.redis = redisns
|
38
|
+
assert_equal redisns, Timberline.redis
|
35
39
|
end
|
36
40
|
|
37
|
-
|
38
|
-
|
41
|
+
should "Converts a standard redis server into a namespace" do
|
42
|
+
redis = Redis.new
|
43
|
+
Timberline.redis = redis
|
44
|
+
assert_equal Redis::Namespace, Timberline.redis.class
|
45
|
+
assert_equal redis, Timberline.redis.instance_variable_get("@redis")
|
46
|
+
end
|
39
47
|
|
40
|
-
|
41
|
-
|
42
|
-
c.namespace = "skyline"
|
48
|
+
should "generates a redis namespace on request if one isn't present" do
|
49
|
+
assert_equal Redis::Namespace, Timberline.redis.class
|
43
50
|
end
|
44
51
|
|
45
|
-
|
46
|
-
|
52
|
+
should "uses a default namespace of 'timberline'" do
|
53
|
+
assert_equal "timberline", Timberline.redis.namespace
|
54
|
+
end
|
55
|
+
|
56
|
+
should "can be configured" do
|
57
|
+
Timberline.config do |c|
|
58
|
+
c.database = 3
|
59
|
+
end
|
60
|
+
|
61
|
+
assert_equal 3, Timberline.instance_variable_get("@config").database
|
62
|
+
end
|
47
63
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
c.logger = @logger
|
64
|
+
should "uses a pre-defined namespace name if configured" do
|
65
|
+
Timberline.config do |c|
|
66
|
+
c.namespace = "skyline"
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_equal "skyline", Timberline.redis.namespace
|
55
70
|
end
|
56
71
|
|
57
|
-
|
72
|
+
should "properly configures Redis" do
|
73
|
+
@logger = Logger.new STDERR
|
74
|
+
Timberline.config do |c|
|
75
|
+
c.host = "localhost"
|
76
|
+
c.timeout = 10
|
77
|
+
c.database = 3
|
78
|
+
c.logger = @logger
|
79
|
+
end
|
80
|
+
|
81
|
+
redis = Timberline.redis
|
82
|
+
|
83
|
+
assert_equal "localhost", redis.client.host
|
84
|
+
assert_equal 10, redis.client.timeout
|
85
|
+
assert_equal 3, redis.client.db
|
86
|
+
assert_equal @logger, redis.client.logger
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
should "allows you to retry a job that has failed" do
|
91
|
+
Timberline.push("test_queue", "Howdy kids.")
|
92
|
+
queue = Timberline.queue("test_queue")
|
93
|
+
data = queue.pop
|
94
|
+
|
95
|
+
assert_equal 0, queue.length
|
96
|
+
|
97
|
+
assert_raises Timberline::ItemRetried do
|
98
|
+
Timberline.retry_item(data)
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_equal 1, queue.length
|
58
102
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
103
|
+
data = queue.pop
|
104
|
+
assert_equal 1, data.retries
|
105
|
+
assert_kind_of Time, Time.at(data.last_tried_at)
|
106
|
+
end
|
107
|
+
|
108
|
+
should "will continue retrying until we pass the max retries (defaults to 5)" do
|
109
|
+
Timberline.push("test_queue", "Howdy kids.")
|
110
|
+
queue = Timberline.queue("test_queue")
|
111
|
+
data = queue.pop
|
112
|
+
|
113
|
+
5.times do |i|
|
114
|
+
assert_raises Timberline::ItemRetried do
|
115
|
+
Timberline.retry_item(data)
|
116
|
+
end
|
117
|
+
assert_equal 1, queue.length
|
118
|
+
data = queue.pop
|
119
|
+
assert_equal i + 1, data.retries
|
120
|
+
end
|
121
|
+
|
122
|
+
assert_raises Timberline::ItemErrored do
|
123
|
+
Timberline.retry_item(data)
|
124
|
+
end
|
125
|
+
assert_equal 0, queue.length
|
126
|
+
assert_equal 1, Timberline.error_queue.length
|
127
|
+
end
|
128
|
+
|
129
|
+
should "will allow you to directly error out a job" do
|
130
|
+
Timberline.push("test_queue", "Howdy kids.")
|
131
|
+
queue = Timberline.queue("test_queue")
|
132
|
+
data = queue.pop
|
133
|
+
|
134
|
+
assert_raises Timberline::ItemErrored do
|
135
|
+
Timberline.error_item(data)
|
136
|
+
end
|
137
|
+
assert_equal 1, Timberline.error_queue.length
|
138
|
+
data = Timberline.error_queue.pop
|
139
|
+
assert_kind_of Time, Time.at(data.fatal_error_at)
|
140
|
+
end
|
63
141
|
|
64
142
|
end
|
65
143
|
end
|
data/timberline.gemspec
CHANGED
@@ -23,6 +23,9 @@ Gem::Specification.new do |s|
|
|
23
23
|
# s.add_runtime_dependency "rest-client"
|
24
24
|
s.add_runtime_dependency "redis"
|
25
25
|
s.add_runtime_dependency "redis-namespace"
|
26
|
+
s.add_runtime_dependency "redis-expiring-set"
|
26
27
|
s.add_runtime_dependency "trollop"
|
27
28
|
s.add_runtime_dependency "daemons"
|
29
|
+
|
30
|
+
s.add_development_dependency "shoulda"
|
28
31
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timberline
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &70354056089500 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70354056089500
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: redis-namespace
|
27
|
-
requirement: &
|
27
|
+
requirement: &70354056077200 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,21 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70354056077200
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: redis-expiring-set
|
38
|
+
requirement: &70354056075820 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70354056075820
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: trollop
|
38
|
-
requirement: &
|
49
|
+
requirement: &70354056073880 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
52
|
- - ! '>='
|
@@ -43,10 +54,10 @@ dependencies:
|
|
43
54
|
version: '0'
|
44
55
|
type: :runtime
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *70354056073880
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: daemons
|
49
|
-
requirement: &
|
60
|
+
requirement: &70354056071600 !ruby/object:Gem::Requirement
|
50
61
|
none: false
|
51
62
|
requirements:
|
52
63
|
- - ! '>='
|
@@ -54,7 +65,18 @@ dependencies:
|
|
54
65
|
version: '0'
|
55
66
|
type: :runtime
|
56
67
|
prerelease: false
|
57
|
-
version_requirements: *
|
68
|
+
version_requirements: *70354056071600
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: shoulda
|
71
|
+
requirement: &70354056070520 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *70354056070520
|
58
80
|
description: Timberline is a simple and extensible queuing system built in Ruby and
|
59
81
|
backed by Redis. It makes as few assumptions as possible about how you want to interact
|
60
82
|
with your queues while also allowing for some functionality that should be universally
|
@@ -75,7 +97,6 @@ files:
|
|
75
97
|
- lib/timberline/config.rb
|
76
98
|
- lib/timberline/envelope.rb
|
77
99
|
- lib/timberline/queue.rb
|
78
|
-
- lib/timberline/queue_manager.rb
|
79
100
|
- lib/timberline/version.rb
|
80
101
|
- test/fake_rails/config/timberline.yaml
|
81
102
|
- test/partial_minispec.rb
|
@@ -84,7 +105,6 @@ files:
|
|
84
105
|
- test/unit/test_config.rb
|
85
106
|
- test/unit/test_envelope.rb
|
86
107
|
- test/unit/test_queue.rb
|
87
|
-
- test/unit/test_queue_manager.rb
|
88
108
|
- test/unit/test_timberline.rb
|
89
109
|
- timberline.gemspec
|
90
110
|
homepage: http://github.com/duwanis/timberline
|
@@ -120,5 +140,4 @@ test_files:
|
|
120
140
|
- test/unit/test_config.rb
|
121
141
|
- test/unit/test_envelope.rb
|
122
142
|
- test/unit/test_queue.rb
|
123
|
-
- test/unit/test_queue_manager.rb
|
124
143
|
- test/unit/test_timberline.rb
|
@@ -1,84 +0,0 @@
|
|
1
|
-
class Timberline
|
2
|
-
class QueueManager
|
3
|
-
attr_reader :queue_list
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@queue_list = {}
|
7
|
-
end
|
8
|
-
|
9
|
-
def error_queue
|
10
|
-
@error_queue ||= Queue.new("timberline_errors")
|
11
|
-
end
|
12
|
-
|
13
|
-
def queue(queue_name)
|
14
|
-
if @queue_list[queue_name].nil?
|
15
|
-
@queue_list[queue_name] = Queue.new(queue_name)
|
16
|
-
end
|
17
|
-
@queue_list[queue_name]
|
18
|
-
end
|
19
|
-
|
20
|
-
def push(queue_name, data, metadata={})
|
21
|
-
data = wrap(data, metadata.merge({:origin_queue => queue_name}))
|
22
|
-
queue(queue_name).push(data)
|
23
|
-
end
|
24
|
-
|
25
|
-
def retry_job(job)
|
26
|
-
if (job.retries < Timberline.max_retries)
|
27
|
-
job.retries += 1
|
28
|
-
job.last_tried_at = DateTime.now
|
29
|
-
queue(job.origin_queue).push(job)
|
30
|
-
else
|
31
|
-
error_job(job)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def error_job(job)
|
36
|
-
job.fatal_error_at = DateTime.now
|
37
|
-
error_queue.push(job)
|
38
|
-
end
|
39
|
-
|
40
|
-
def watch(queue_name, &block)
|
41
|
-
queue = queue(queue_name)
|
42
|
-
while(true)
|
43
|
-
job = queue.pop
|
44
|
-
fix_binding(block)
|
45
|
-
block.call(job, self)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
def wrap(contents, metadata)
|
51
|
-
env = Envelope.new
|
52
|
-
env.contents = contents
|
53
|
-
metadata.each do |key, value|
|
54
|
-
env.send("#{key.to_s}=", value)
|
55
|
-
end
|
56
|
-
|
57
|
-
env.job_id = next_id_for_queue(metadata[:origin_queue])
|
58
|
-
env.retries = 0
|
59
|
-
env.submitted_at = DateTime.now
|
60
|
-
|
61
|
-
env
|
62
|
-
end
|
63
|
-
|
64
|
-
def next_id_for_queue(queue_name)
|
65
|
-
Timberline.redis.incr "#{queue_name}_id_seq"
|
66
|
-
end
|
67
|
-
|
68
|
-
# Hacky-hacky. I like the idea of calling retry_job(job) and error_job(job)
|
69
|
-
# directly from the watch block, but this seems ugly. There may be a better
|
70
|
-
# way to do this.
|
71
|
-
def fix_binding(block)
|
72
|
-
binding = block.binding
|
73
|
-
binding.eval <<-HERE
|
74
|
-
def retry_job(job)
|
75
|
-
Timberline.retry_job(job)
|
76
|
-
end
|
77
|
-
|
78
|
-
def error_job(job)
|
79
|
-
Timberline.error_job(job)
|
80
|
-
end
|
81
|
-
HERE
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,154 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
describe Timberline::QueueManager do
|
4
|
-
describe "newly instantiated" do
|
5
|
-
before do
|
6
|
-
clear_test_db
|
7
|
-
@qm = Timberline::QueueManager.new
|
8
|
-
end
|
9
|
-
|
10
|
-
it "has an empty queue list" do
|
11
|
-
assert_equal 0, @qm.queue_list.size
|
12
|
-
end
|
13
|
-
|
14
|
-
it "instantiates an error_queue" do
|
15
|
-
assert_kind_of Timberline::Queue, @qm.error_queue
|
16
|
-
assert_equal "timberline_errors", @qm.error_queue.queue_name
|
17
|
-
end
|
18
|
-
|
19
|
-
it "creates a new queue when asked if it doesn't exist" do
|
20
|
-
queue = @qm.queue("test_queue")
|
21
|
-
assert_kind_of Timberline::Queue, queue
|
22
|
-
assert_equal 1, @qm.queue_list.size
|
23
|
-
assert_equal queue, @qm.queue_list["test_queue"]
|
24
|
-
end
|
25
|
-
|
26
|
-
it "doesn't create a new queue if a queue by the same name already exists" do
|
27
|
-
queue = @qm.queue("test_queue")
|
28
|
-
new_queue = @qm.queue("test_queue")
|
29
|
-
assert_equal queue, new_queue
|
30
|
-
assert_equal 1, @qm.queue_list.size
|
31
|
-
end
|
32
|
-
|
33
|
-
it "creates a new queue as necessary when 'push' is called and pushes the item" do
|
34
|
-
@qm.push("test_queue", "Howdy kids.")
|
35
|
-
assert_equal 1, @qm.queue_list.size
|
36
|
-
queue = @qm.queue("test_queue")
|
37
|
-
assert_equal 1, queue.length
|
38
|
-
assert_equal "Howdy kids.", queue.pop.contents
|
39
|
-
end
|
40
|
-
|
41
|
-
it "uses any passed-in metadata to push when building the envelope" do
|
42
|
-
@qm.push("test_queue", "Howdy kids.", { :special_notes => "Super-awesome."})
|
43
|
-
queue = @qm.queue("test_queue")
|
44
|
-
assert_equal 1, queue.length
|
45
|
-
data = queue.pop
|
46
|
-
assert_equal "Howdy kids.", data.contents
|
47
|
-
assert_equal "Super-awesome.", data.special_notes
|
48
|
-
end
|
49
|
-
|
50
|
-
it "includes some default information in the metadata" do
|
51
|
-
@qm.push("test_queue", "Howdy kids.")
|
52
|
-
queue = @qm.queue("test_queue")
|
53
|
-
data = queue.pop
|
54
|
-
assert_equal "Howdy kids.", data.contents
|
55
|
-
assert_kind_of DateTime, DateTime.parse(data.submitted_at)
|
56
|
-
assert_equal "test_queue", data.origin_queue
|
57
|
-
assert_equal 0, data.retries
|
58
|
-
assert_equal 1, data.job_id
|
59
|
-
end
|
60
|
-
|
61
|
-
it "allows you to retry a job that has failed" do
|
62
|
-
@qm.push("test_queue", "Howdy kids.")
|
63
|
-
queue = @qm.queue("test_queue")
|
64
|
-
data = queue.pop
|
65
|
-
|
66
|
-
assert_equal 0, queue.length
|
67
|
-
|
68
|
-
@qm.retry_job(data)
|
69
|
-
|
70
|
-
assert_equal 1, queue.length
|
71
|
-
|
72
|
-
data = queue.pop
|
73
|
-
assert_equal 1, data.retries
|
74
|
-
assert_kind_of DateTime, DateTime.parse(data.last_tried_at)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "will continue retrying until we pass the max retries (defaults to 5)" do
|
78
|
-
@qm.push("test_queue", "Howdy kids.")
|
79
|
-
queue = @qm.queue("test_queue")
|
80
|
-
data = queue.pop
|
81
|
-
|
82
|
-
5.times do |i|
|
83
|
-
@qm.retry_job(data)
|
84
|
-
assert_equal 1, queue.length
|
85
|
-
data = queue.pop
|
86
|
-
assert_equal i + 1, data.retries
|
87
|
-
end
|
88
|
-
|
89
|
-
@qm.retry_job(data)
|
90
|
-
assert_equal 0, queue.length
|
91
|
-
assert_equal 1, @qm.error_queue.length
|
92
|
-
end
|
93
|
-
|
94
|
-
it "will allow you to directly error out a job" do
|
95
|
-
@qm.push("test_queue", "Howdy kids.")
|
96
|
-
queue = @qm.queue("test_queue")
|
97
|
-
data = queue.pop
|
98
|
-
|
99
|
-
@qm.error_job(data)
|
100
|
-
assert_equal 1, @qm.error_queue.length
|
101
|
-
data = @qm.error_queue.pop
|
102
|
-
assert_kind_of DateTime, DateTime.parse(data.fatal_error_at)
|
103
|
-
end
|
104
|
-
|
105
|
-
it "will allow you to watch a queue" do
|
106
|
-
@qm.push("test_queue", "Howdy kids.")
|
107
|
-
|
108
|
-
assert_raises RuntimeError do
|
109
|
-
@qm.watch "test_queue" do |job|
|
110
|
-
if "Howdy kids." == job.contents
|
111
|
-
raise "This works."
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
assert_equal 0, @qm.queue("test_queue").length
|
117
|
-
end
|
118
|
-
|
119
|
-
it "will allow you to retry from a queue watcher" do
|
120
|
-
@qm.push("test_queue", "Howdy kids.")
|
121
|
-
|
122
|
-
assert_raises RuntimeError do
|
123
|
-
@qm.watch "test_queue" do |job|
|
124
|
-
if "Howdy kids." == job.contents
|
125
|
-
retry_job job
|
126
|
-
raise "Job retried."
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
assert_equal 1, @qm.queue("test_queue").length
|
132
|
-
data = @qm.queue("test_queue").pop
|
133
|
-
assert_equal 1, data.retries
|
134
|
-
end
|
135
|
-
|
136
|
-
it "will allow you to error from a queue watcher" do
|
137
|
-
@qm.push("test_queue", "Howdy kids.")
|
138
|
-
|
139
|
-
assert_raises RuntimeError do
|
140
|
-
@qm.watch "test_queue" do |job|
|
141
|
-
if "Howdy kids." == job.contents
|
142
|
-
error_job job
|
143
|
-
raise "Job errored."
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
assert_equal 1, @qm.error_queue.length
|
149
|
-
data = @qm.error_queue.pop
|
150
|
-
assert_kind_of DateTime, DateTime.parse(data.fatal_error_at)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|