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.
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'cloud-crowd'
3
- s.version = '0.2.7' # Keep version in sync with cloud-cloud.rb
4
- s.date = '2009-10-19'
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"
@@ -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.7'
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
- @actions[name] = Module.const_get(Inflector.camelize(name))
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? 'split'
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?('merge')
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 loop over the WorkUnits reserved by this process and try to match them
32
- # to Nodes that are capable of handling the Action. WorkUnits get removed
33
- # from the availability list when they are successfully sent, and Nodes get
34
- # removed when they are busy or have the action in question disabled.
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
- return unless reservation_number = WorkUnit.reserve_available
37
- work_units = WorkUnit.reserved(reservation_number)
38
- available_nodes = NodeRecord.available
39
- while node = available_nodes.shift and unit = work_units.shift do
40
- if node.actions.include? unit.action
41
- if node.send_work_unit(unit)
42
- available_nodes.push(node) unless node.busy?
43
- next
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.push(unit)
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.7
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-19 00:00:00 -04:00
12
+ date: 2009-10-27 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency