cloud-crowd 0.2.7 → 0.2.8
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/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
|