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.
@@ -3,15 +3,17 @@ class ResqueJobsTree::Job
3
3
  class << self
4
4
 
5
5
  def perform *args
6
- node, resources = tree_node_and_resources(args)
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 = tree_node_and_resources(args)
14
- unless node.root?
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 = tree_node_and_resources args
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.cleanup node, resources
30
+ ResqueJobsTree::Storage.failure_cleanup node, resources
29
31
  end
30
32
  else
31
- ResqueJobsTree::Storage.cleanup node, resources, global: true
33
+ ResqueJobsTree::Storage.failure_cleanup node, resources, global: true
32
34
  raise exception
33
35
  end
34
36
  end
35
37
 
36
- def tree_node_and_resources args
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
@@ -82,4 +82,8 @@ class ResqueJobsTree::Node
82
82
  node_childs+node_childs.map(&:nodes)
83
83
  end
84
84
 
85
+ def inspect
86
+ "<ResqueJobsTree::Node @name=#{name}>"
87
+ end
88
+
85
89
  end
@@ -2,32 +2,35 @@
2
2
  module ResqueJobsTree::Storage
3
3
  extend self
4
4
 
5
- PARENTS_KEY = "JobsTree:Node:Parents"
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
- Resque.redis.hset PARENTS_KEY, node_key, parent_key
11
+ redis.hset PARENTS_KEY, node_key, parent_key
11
12
  childs_key = childs_key parent, parent_resources
12
- unless Resque.redis.sadd childs_key, node_key
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
- lock_with parent_lock_key(node, resources) do
20
+ lock node, resources do
20
21
  siblings_key = siblings_key node, resources
21
- Resque.redis.srem siblings_key, key(node, resources)
22
- yield if Resque.redis.scard(siblings_key) == 0 && block_given?
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 cleanup node, resources, option={}
27
+ def failure_cleanup node, resources, options={}
27
28
  cleanup_childs node, resources
28
- unless node.root?
29
+ if node.root?
30
+ release_launch node.tree, resources
31
+ else
29
32
  remove_from_siblings node, resources
30
- option[:global] ? cleanup_parent(node, resources) : remove_parent_key(node, resources)
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
- "JobsTree:Node:#{job_args.to_json}"
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
- Resque.redis.hget PARENTS_KEY, node_key
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 lock_with key
65
- while !Resque.redis.setnx(key, 'locked')
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
- Resque.redis.del key
82
+ redis.del key
71
83
  end
72
84
 
73
85
  def cleanup_childs *node_info
74
86
  key = childs_key *node_info
75
- Resque.redis.smembers(key).each do |child_key|
76
- cleanup *node_info_from_key(child_key)
87
+ redis.smembers(key).each do |child_key|
88
+ failure_cleanup *node_info_from_key(child_key)
77
89
  end
78
- Resque.redis.del key
90
+ redis.del key
79
91
  end
80
92
 
81
- def cleanup_parent *node_info
82
- parent, parent_resources = node_info_from_key(parent_key(*node_info))
83
- remove_parent_key *node_info
84
- cleanup parent, parent_resources
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
- Resque.redis.hdel PARENTS_KEY, key(*node_info)
100
+ redis.hdel PARENTS_KEY, key(*node_info)
89
101
  end
90
102
 
91
103
  def remove_from_siblings *node_info
92
- Resque.redis.srem siblings_key(*node_info), key(*node_info)
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(/JobsTree:Node:/, '')
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
- @root.launch resources
19
- enqueue_leaves_jobs
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
@@ -1,3 +1,3 @@
1
1
  module ResqueJobsTree
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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(:tree_node_and_resources, @args)
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque_jobs_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: