cloud-crowd 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- data/cloud-crowd.gemspec +2 -2
- data/lib/cloud-crowd.rb +13 -8
- data/lib/cloud_crowd/models/job.rb +2 -2
- data/lib/cloud_crowd/models/work_unit.rb +25 -17
- data/test/unit/test_configuration.rb +14 -1
- metadata +2 -2
data/cloud-crowd.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'cloud-crowd'
|
3
|
-
s.version = '0.2.
|
4
|
-
s.date = '2009-10-
|
3
|
+
s.version = '0.2.8' # Keep version in sync with cloud-cloud.rb
|
4
|
+
s.date = '2009-10-27'
|
5
5
|
|
6
6
|
s.homepage = "http://wiki.github.com/documentcloud/cloud-crowd"
|
7
7
|
s.summary = "Parallel Processing for the Rest of Us"
|
data/lib/cloud-crowd.rb
CHANGED
@@ -44,7 +44,7 @@ module CloudCrowd
|
|
44
44
|
autoload :WorkUnit, 'cloud_crowd/models'
|
45
45
|
|
46
46
|
# Keep this version in sync with the gemspec.
|
47
|
-
VERSION = '0.2.
|
47
|
+
VERSION = '0.2.8'
|
48
48
|
|
49
49
|
# Increment the schema version when there's a backwards incompatible change.
|
50
50
|
SCHEMA_VERSION = 3
|
@@ -152,20 +152,25 @@ module CloudCrowd
|
|
152
152
|
# Actions, then install only those into the actions directory.
|
153
153
|
def actions
|
154
154
|
return @actions if @actions
|
155
|
-
@actions = {}
|
156
|
-
default_actions = Dir["#{ROOT}/actions/*.rb"]
|
157
|
-
installed_actions = Dir["#{@config_path}/actions/*.rb"]
|
158
|
-
custom_actions = Dir["#{CloudCrowd.config[:actions_path]}/*.rb"]
|
159
|
-
(default_actions + installed_actions + custom_actions).each do |path|
|
155
|
+
@actions = action_paths.inject({}) do |memo, path|
|
160
156
|
name = File.basename(path, File.extname(path))
|
161
157
|
require path
|
162
|
-
|
158
|
+
memo[name] = Module.const_get(Inflector.camelize(name))
|
159
|
+
memo
|
163
160
|
end
|
164
|
-
@actions
|
165
161
|
rescue NameError => e
|
166
162
|
adjusted_message = "One of your actions failed to load. Please ensure that the name of your action class can be deduced from the name of the file. ex: 'word_count.rb' => 'WordCount'\n#{e.message}"
|
167
163
|
raise NameError.new(adjusted_message, e.name)
|
168
164
|
end
|
165
|
+
|
166
|
+
# Retrieve the list of every installed Action for this node or server.
|
167
|
+
def action_paths
|
168
|
+
default_actions = Dir["#{ROOT}/actions/*.rb"]
|
169
|
+
installed_actions = Dir["#{@config_path}/actions/*.rb"]
|
170
|
+
custom_actions = CloudCrowd.config[:actions_path] ? Dir["#{CloudCrowd.config[:actions_path]}/*.rb"] : []
|
171
|
+
default_actions + installed_actions + custom_actions
|
172
|
+
end
|
173
|
+
|
169
174
|
end
|
170
175
|
|
171
176
|
end
|
@@ -100,7 +100,7 @@ module CloudCrowd
|
|
100
100
|
|
101
101
|
# This job is splittable if its Action has a +split+ method.
|
102
102
|
def splittable?
|
103
|
-
self.action_class.public_instance_methods.include?
|
103
|
+
self.action_class.public_instance_methods.map {|m| m.to_sym }.include? :split
|
104
104
|
end
|
105
105
|
|
106
106
|
# This job is done splitting if it's finished with its splitting work units.
|
@@ -110,7 +110,7 @@ module CloudCrowd
|
|
110
110
|
|
111
111
|
# This job is mergeable if its Action has a +merge+ method.
|
112
112
|
def mergeable?
|
113
|
-
self.processing? && self.action_class.public_instance_methods.include?(
|
113
|
+
self.processing? && self.action_class.public_instance_methods.map {|m| m.to_sym }.include?(:merge)
|
114
114
|
end
|
115
115
|
|
116
116
|
# Retrieve the class for this Job's Action.
|
@@ -11,6 +11,10 @@ module CloudCrowd
|
|
11
11
|
# The size of the maximum signed integer in MySQL -- SQLite has no limit.
|
12
12
|
MAX_RESERVATION = 2147483647
|
13
13
|
|
14
|
+
# We only reserve a certain number of WorkUnits in a single go, to avoid
|
15
|
+
# reserving the entire table.
|
16
|
+
RESERVATION_LIMIT = 25
|
17
|
+
|
14
18
|
belongs_to :job
|
15
19
|
belongs_to :node_record
|
16
20
|
|
@@ -28,32 +32,36 @@ module CloudCrowd
|
|
28
32
|
# distributed to multiple nodes by reserving it first. The algorithm used
|
29
33
|
# should be lock-free.
|
30
34
|
#
|
31
|
-
# We
|
32
|
-
# to Nodes that are capable of handling the Action.
|
33
|
-
# from the availability list when they are
|
34
|
-
# removed when they are busy or have the
|
35
|
+
# We reserve WorkUnits for this process in chunks of RESERVATION_LIMIT size,
|
36
|
+
# and try to match them to Nodes that are capable of handling the Action.
|
37
|
+
# WorkUnits get removed from the availability list when they are
|
38
|
+
# successfully sent, and Nodes get removed when they are busy or have the
|
39
|
+
# action in question disabled.
|
35
40
|
def self.distribute_to_nodes
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
if node.
|
42
|
-
|
43
|
-
|
41
|
+
begin
|
42
|
+
return unless reservation_number = WorkUnit.reserve_available(:limit => RESERVATION_LIMIT)
|
43
|
+
work_units = WorkUnit.reserved(reservation_number)
|
44
|
+
available_nodes = NodeRecord.available
|
45
|
+
while node = available_nodes.shift and unit = work_units.shift do
|
46
|
+
if node.actions.include? unit.action
|
47
|
+
if node.send_work_unit(unit)
|
48
|
+
available_nodes.push(node) unless node.busy?
|
49
|
+
next
|
50
|
+
end
|
44
51
|
end
|
52
|
+
work_units.push(unit)
|
45
53
|
end
|
46
|
-
work_units.
|
54
|
+
retry if work_units.empty? && !available_nodes.empty?
|
55
|
+
ensure
|
56
|
+
WorkUnit.cancel_reservations(reservation_number) if reservation_number
|
47
57
|
end
|
48
|
-
ensure
|
49
|
-
WorkUnit.cancel_reservations(reservation_number) if reservation_number
|
50
58
|
end
|
51
59
|
|
52
60
|
# Reserves all available WorkUnits for this process. Returns false if there
|
53
61
|
# were none available.
|
54
|
-
def self.reserve_available
|
62
|
+
def self.reserve_available(options={})
|
55
63
|
reservation_number = ActiveSupport::SecureRandom.random_number(MAX_RESERVATION)
|
56
|
-
any = WorkUnit.available.update_all("reservation = #{reservation_number}") > 0
|
64
|
+
any = WorkUnit.available.update_all("reservation = #{reservation_number}", nil, options) > 0
|
57
65
|
any && reservation_number
|
58
66
|
end
|
59
67
|
|
@@ -3,7 +3,9 @@ require 'test_helper'
|
|
3
3
|
class ConfigurationTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
context "CloudCrowd Configuration" do
|
6
|
-
|
6
|
+
|
7
|
+
setup { CloudCrowd.instance_variable_set("@actions", nil) }
|
8
|
+
|
7
9
|
should "have read in config.yml" do
|
8
10
|
assert CloudCrowd.config[:max_workers] == 10
|
9
11
|
assert CloudCrowd.config[:storage] == 'filesystem'
|
@@ -22,6 +24,17 @@ class ConfigurationTest < Test::Unit::TestCase
|
|
22
24
|
assert CloudCrowd.actions['process_pdfs'] == ProcessPdfs
|
23
25
|
assert CloudCrowd.actions['graphics_magick'] == GraphicsMagick
|
24
26
|
end
|
27
|
+
|
28
|
+
should "not find custom actions unless 'actions_path' is set" do
|
29
|
+
CloudCrowd.config[:actions_path] = nil
|
30
|
+
assert CloudCrowd.actions.keys.length == 4
|
31
|
+
end
|
32
|
+
|
33
|
+
should "find custom actions when 'actions_path' is set" do
|
34
|
+
CloudCrowd.config[:actions_path] = "#{CloudCrowd::ROOT}/test/config/actions/custom"
|
35
|
+
assert CloudCrowd.actions['echo_action'] == EchoAction
|
36
|
+
assert CloudCrowd.actions.keys.length == 5
|
37
|
+
end
|
25
38
|
|
26
39
|
end
|
27
40
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloud-crowd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Ashkenas
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-27 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|