resque_jobs_tree 0.1.0 → 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/lib/resque_jobs_tree/job.rb +9 -7
- data/lib/resque_jobs_tree/node.rb +4 -0
- data/lib/resque_jobs_tree/storage.rb +45 -25
- data/lib/resque_jobs_tree/tree.rb +8 -2
- data/lib/resque_jobs_tree/version.rb +1 -1
- data/test/job_test.rb +1 -1
- data/test/node_test.rb +34 -0
- data/test/storage_test.rb +15 -0
- metadata +1 -1
data/lib/resque_jobs_tree/job.rb
CHANGED
@@ -3,15 +3,17 @@ class ResqueJobsTree::Job
|
|
3
3
|
class << self
|
4
4
|
|
5
5
|
def perform *args
|
6
|
-
node, resources =
|
6
|
+
node, resources = node_and_resources(args)
|
7
7
|
node.perform.call resources
|
8
8
|
end
|
9
9
|
|
10
10
|
private
|
11
11
|
|
12
12
|
def after_perform_enqueue_parent *args
|
13
|
-
node, resources =
|
14
|
-
|
13
|
+
node, resources = node_and_resources(args)
|
14
|
+
if node.root?
|
15
|
+
ResqueJobsTree::Storage.release_launch node.tree, resources
|
16
|
+
else
|
15
17
|
ResqueJobsTree::Storage.remove(node, resources) do
|
16
18
|
parent_job_args = ResqueJobsTree::Storage.parent_job_args node, resources
|
17
19
|
Resque.enqueue_to node.tree.name, ResqueJobsTree::Job, *parent_job_args
|
@@ -20,20 +22,20 @@ class ResqueJobsTree::Job
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def on_failure_cleanup exception, *args
|
23
|
-
node, resources =
|
25
|
+
node, resources = node_and_resources args
|
24
26
|
if node.options[:continue_on_failure]
|
25
27
|
begin
|
26
28
|
after_perform_enqueue_parent *args
|
27
29
|
ensure
|
28
|
-
ResqueJobsTree::Storage.
|
30
|
+
ResqueJobsTree::Storage.failure_cleanup node, resources
|
29
31
|
end
|
30
32
|
else
|
31
|
-
ResqueJobsTree::Storage.
|
33
|
+
ResqueJobsTree::Storage.failure_cleanup node, resources, global: true
|
32
34
|
raise exception
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
36
|
-
def
|
38
|
+
def node_and_resources args
|
37
39
|
tree_name , job_name = args[0..1]
|
38
40
|
tree = ResqueJobsTree::Factory.find_tree_by_name tree_name
|
39
41
|
node = tree.find_node_by_name job_name
|
@@ -2,32 +2,35 @@
|
|
2
2
|
module ResqueJobsTree::Storage
|
3
3
|
extend self
|
4
4
|
|
5
|
-
PARENTS_KEY = "
|
5
|
+
PARENTS_KEY = "ResqueJobsTree:Node:Parents"
|
6
|
+
LAUNCHED_TREES = "ResqueJobsTree:Tree:Launched"
|
6
7
|
|
7
8
|
def store node, resources, parent, parent_resources
|
8
9
|
node_key = key node, resources
|
9
10
|
parent_key = key parent, parent_resources
|
10
|
-
|
11
|
+
redis.hset PARENTS_KEY, node_key, parent_key
|
11
12
|
childs_key = childs_key parent, parent_resources
|
12
|
-
unless
|
13
|
+
unless redis.sadd childs_key, node_key
|
13
14
|
raise ResqueJobsTree::JobNotUniq,
|
14
15
|
"Job #{parent.name} already has the child #{node.name} with resources: #{resources}"
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
19
|
def remove node, resources
|
19
|
-
|
20
|
+
lock node, resources do
|
20
21
|
siblings_key = siblings_key node, resources
|
21
|
-
|
22
|
-
yield if
|
22
|
+
redis.srem siblings_key, key(node, resources)
|
23
|
+
yield if redis.scard(siblings_key) == 0 && block_given?
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
def
|
27
|
+
def failure_cleanup node, resources, options={}
|
27
28
|
cleanup_childs node, resources
|
28
|
-
|
29
|
+
if node.root?
|
30
|
+
release_launch node.tree, resources
|
31
|
+
else
|
29
32
|
remove_from_siblings node, resources
|
30
|
-
|
33
|
+
options[:global] ? cleanup_parent(node, resources, options) : remove_parent_key(node, resources)
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
@@ -35,13 +38,21 @@ module ResqueJobsTree::Storage
|
|
35
38
|
args_from_key parent_key(node, resources)
|
36
39
|
end
|
37
40
|
|
41
|
+
def track_launch *tree_info
|
42
|
+
yield if redis.sadd LAUNCHED_TREES, tree_reference(*tree_info)
|
43
|
+
end
|
44
|
+
|
45
|
+
def release_launch *tree_info
|
46
|
+
redis.srem LAUNCHED_TREES, tree_reference(*tree_info)
|
47
|
+
end
|
48
|
+
|
38
49
|
private
|
39
50
|
|
40
51
|
def key node, resources
|
41
52
|
job_args = ResqueJobsTree::ResourcesSerializer.to_args(resources)
|
42
53
|
job_args.unshift node.name
|
43
54
|
job_args.unshift node.tree.name
|
44
|
-
"
|
55
|
+
"ResqueJobsTree:Node:#{job_args.to_json}"
|
45
56
|
end
|
46
57
|
|
47
58
|
def childs_key node, resources
|
@@ -50,7 +61,7 @@ module ResqueJobsTree::Storage
|
|
50
61
|
|
51
62
|
def parent_key node, resources
|
52
63
|
node_key = key node, resources
|
53
|
-
|
64
|
+
redis.hget PARENTS_KEY, node_key
|
54
65
|
end
|
55
66
|
|
56
67
|
def siblings_key node, resources
|
@@ -61,45 +72,54 @@ module ResqueJobsTree::Storage
|
|
61
72
|
"#{parent_key node, resources}:lock"
|
62
73
|
end
|
63
74
|
|
64
|
-
def
|
65
|
-
|
75
|
+
def lock *node_info
|
76
|
+
key = parent_lock_key *node_info
|
77
|
+
while !redis.setnx(key, 'locked')
|
66
78
|
sleep 0.05 # 50 ms
|
67
79
|
end
|
68
80
|
yield
|
69
81
|
ensure
|
70
|
-
|
82
|
+
redis.del key
|
71
83
|
end
|
72
84
|
|
73
85
|
def cleanup_childs *node_info
|
74
86
|
key = childs_key *node_info
|
75
|
-
|
76
|
-
|
87
|
+
redis.smembers(key).each do |child_key|
|
88
|
+
failure_cleanup *node_info_from_key(child_key)
|
77
89
|
end
|
78
|
-
|
90
|
+
redis.del key
|
79
91
|
end
|
80
92
|
|
81
|
-
def cleanup_parent
|
82
|
-
parent, parent_resources = node_info_from_key(parent_key(
|
83
|
-
remove_parent_key
|
84
|
-
|
93
|
+
def cleanup_parent node, resources, options
|
94
|
+
parent, parent_resources = node_info_from_key(parent_key(node, resources))
|
95
|
+
remove_parent_key node, resources
|
96
|
+
failure_cleanup parent, parent_resources, options
|
85
97
|
end
|
86
98
|
|
87
99
|
def remove_parent_key *node_info
|
88
|
-
|
100
|
+
redis.hdel PARENTS_KEY, key(*node_info)
|
89
101
|
end
|
90
102
|
|
91
103
|
def remove_from_siblings *node_info
|
92
|
-
|
104
|
+
redis.srem siblings_key(*node_info), key(*node_info)
|
93
105
|
end
|
94
106
|
|
95
107
|
def args_from_key key
|
96
|
-
JSON.load key.gsub(/
|
108
|
+
JSON.load key.gsub(/ResqueJobsTree:Node:/, '')
|
97
109
|
end
|
98
110
|
|
99
111
|
def node_info_from_key key
|
100
|
-
tree_name, node_name, resources = args_from_key(key)
|
112
|
+
tree_name, node_name, *resources = args_from_key(key)
|
101
113
|
node = ResqueJobsTree::Factory.find_tree_by_name(tree_name).find_node_by_name(node_name)
|
102
114
|
[node, resources]
|
103
115
|
end
|
104
116
|
|
117
|
+
def redis
|
118
|
+
Resque.redis
|
119
|
+
end
|
120
|
+
|
121
|
+
def tree_reference tree, resources
|
122
|
+
[tree.name, ResqueJobsTree::ResourcesSerializer.to_args(resources)].to_json
|
123
|
+
end
|
124
|
+
|
105
125
|
end
|
@@ -15,8 +15,10 @@ class ResqueJobsTree::Tree
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def launch *resources
|
18
|
-
|
19
|
-
|
18
|
+
ResqueJobsTree::Storage.track_launch self, resources do
|
19
|
+
@root.launch resources
|
20
|
+
enqueue_leaves_jobs
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
def enqueue *job_args
|
@@ -37,6 +39,10 @@ class ResqueJobsTree::Tree
|
|
37
39
|
[root, root.nodes].flatten
|
38
40
|
end
|
39
41
|
|
42
|
+
def inspect
|
43
|
+
"<ResqueJobsTree::Tree @name=#{name}>"
|
44
|
+
end
|
45
|
+
|
40
46
|
private
|
41
47
|
|
42
48
|
def enqueue_leaves_jobs
|
data/test/job_test.rb
CHANGED
@@ -9,7 +9,7 @@ class JobTest < MiniTest::Unit::TestCase
|
|
9
9
|
|
10
10
|
def test_tree_node_and_resources
|
11
11
|
result = [@tree.find_node_by_name('job1'), [1, 2, 3]]
|
12
|
-
assert_equal result, ResqueJobsTree::Job.send(:
|
12
|
+
assert_equal result, ResqueJobsTree::Job.send(:node_and_resources, @args)
|
13
13
|
end
|
14
14
|
|
15
15
|
end
|
data/test/node_test.rb
CHANGED
@@ -137,6 +137,40 @@ class NodeTest < MiniTest::Unit::TestCase
|
|
137
137
|
assert_equal [], Resque.keys
|
138
138
|
end
|
139
139
|
|
140
|
+
def test_leaf_failure
|
141
|
+
tree = ResqueJobsTree::Factory.create :tree1 do
|
142
|
+
root :job1 do
|
143
|
+
perform {}
|
144
|
+
childs { [:job2] }
|
145
|
+
node :job2 do
|
146
|
+
perform { raise 'an unexpected failure' }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
resources = [1, 2, 3]
|
151
|
+
assert_raises RuntimeError, 'an unexpected failure' do
|
152
|
+
tree.launch *resources
|
153
|
+
end
|
154
|
+
assert_equal [], Resque.keys
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_root_failure
|
158
|
+
tree = ResqueJobsTree::Factory.create :tree1 do
|
159
|
+
root :job1 do
|
160
|
+
perform { raise 'an unexpected failure' }
|
161
|
+
childs { [:job2] }
|
162
|
+
node :job2 do
|
163
|
+
perform {}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
resources = [1, 2, 3]
|
168
|
+
assert_raises RuntimeError, 'an unexpected failure' do
|
169
|
+
tree.launch resources
|
170
|
+
end
|
171
|
+
assert_equal [], Resque.keys
|
172
|
+
end
|
173
|
+
|
140
174
|
private
|
141
175
|
|
142
176
|
def create_async_tree
|
data/test/storage_test.rb
CHANGED
@@ -50,6 +50,21 @@ class StorageTest < MiniTest::Unit::TestCase
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
def test_node_info_from_key
|
54
|
+
key = %Q{ResqueJobsTree:Node:["tree1","job1",1,2,3]}
|
55
|
+
result = [@root, [1, 2, 3]]
|
56
|
+
assert_equal result, ResqueJobsTree::Storage.send(:node_info_from_key, key)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_track_launch
|
60
|
+
resources = [1, 2, 3]
|
61
|
+
ResqueJobsTree::Storage.track_launch(@tree, resources) {}
|
62
|
+
key = ResqueJobsTree::Storage::LAUNCHED_TREES
|
63
|
+
assert_equal [[@tree.name, resources].to_json], redis.smembers(key)
|
64
|
+
ResqueJobsTree::Storage.release_launch(@tree, resources)
|
65
|
+
assert_equal [], redis.smembers(key)
|
66
|
+
end
|
67
|
+
|
53
68
|
private
|
54
69
|
|
55
70
|
def store
|