multi_headed_greek_monster 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2010 3M. All rights reserved.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,9 @@
1
+ Yet another parallel processing utility. because I felt like it.
2
+
3
+ To run the test:
4
+ ruby -Ilib test/multi_headed_greek_monster_test.rb
5
+
6
+
7
+ Copyright (c) 2008-2010 3M. All rights reserved. Released under the MIT license.
8
+
9
+ Authored by Jacob Burkhart.
@@ -0,0 +1,107 @@
1
+ class MultiHeadedGreekMonster
2
+
3
+ def initialize(progress = nil, worker_count = 3, on_port = 23121, &block)
4
+ @action = block
5
+ @on_port = on_port
6
+ @worker_count = worker_count
7
+ @progress = progress
8
+ start_service
9
+ start_workers
10
+ end
11
+
12
+ def feed(thing)
13
+ @service_manager.give(thing)
14
+ end
15
+
16
+ def wait(for_min_q_size = 5, &block)
17
+ while(@service_manager.q_size > for_min_q_size)
18
+ sleep(1)
19
+ if block_given?
20
+ yield
21
+ end
22
+ end
23
+ end
24
+
25
+ def finish
26
+ @service_manager.done!
27
+ while(!@service_manager.done?)
28
+ sleep(1)
29
+ end
30
+ @worker_pids.each do |pid|
31
+ Process.wait(pid)
32
+ end
33
+ Process.kill("KILL", @server_pid)
34
+ end
35
+
36
+ class ServiceManager
37
+ def initialize(progress)
38
+ @progress = progress
39
+ @things = []
40
+ @done = false
41
+ end
42
+ def give(thing)
43
+ @things << thing
44
+ end
45
+ def take
46
+ @things && @things.pop
47
+ end
48
+ def q_size
49
+ @things && @things.size || 0
50
+ end
51
+ def done?
52
+ @done && @things && @things.empty?
53
+ end
54
+ def done!
55
+ @done = true
56
+ end
57
+ def tick
58
+ @progress.tick if @progress
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def start_service
65
+ require 'drb'
66
+
67
+ @server_pid = fork do
68
+ if defined?(ActiveRecord)
69
+ ActiveRecord::Base.clear_all_connections!
70
+ end
71
+ at_exit { exit! }
72
+ DRb.start_service "druby://localhost:#{@on_port}", ServiceManager.new(@progress)
73
+ DRb.thread.join
74
+ end
75
+
76
+ @service_manager = DRbObject.new nil, "druby://localhost:#{@on_port}"
77
+ sleep 0.2 # FIXME
78
+ end
79
+
80
+ def start_workers
81
+ @worker_pids = []
82
+ @worker_count.times do |i|
83
+ @worker_pids << fork do
84
+ if defined?(ActiveRecord)
85
+ ActiveRecord::Base.clear_all_connections!
86
+ end
87
+ sleep(i)
88
+ at_exit { exit! }
89
+ work = DRbObject.new nil, "druby://localhost:#{@on_port}"
90
+ waits = 0
91
+ while(!work.done?)
92
+ if got = work.take
93
+ @action.call(got, work)
94
+ waits = 0
95
+ else
96
+ if waits > 10
97
+ puts "waiting on work to do from #{Process.pid} (#{waits})"
98
+ end
99
+ sleep(1)
100
+ waits += 1
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,45 @@
1
+ require 'multi_headed_greek_monster'
2
+ require 'test/unit'
3
+ require 'forwardable'
4
+ require 'cubbyhole/base'
5
+
6
+ class Face < Cubbyhole::Base
7
+ def self.find_in_batches
8
+ batch = []
9
+ self.all.each_with_index do |item, index|
10
+ batch << item
11
+ if index % 10 == 9
12
+ yield batch
13
+ batch = []
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class MultiHeadedGreekMonsterTest < Test::Unit::TestCase
20
+
21
+ def test_should_work
22
+ 100.times{ Face.create(:size => rand.to_s) }
23
+ # puts "accounts created"
24
+ monster = MultiHeadedGreekMonster.new(nil, 3, 28371) do |face, work|
25
+ puts "working on #{face.id} from #{Process.pid}"
26
+ face.size = face.size + " improved"
27
+ face.save!
28
+ puts "face renamed to " + face.size.inspect
29
+ end
30
+ total = Face.all.size
31
+ # progress = Progress.new(total, 20)
32
+ Face.find_in_batches do |batch_of_things|
33
+ batch_of_things.each do |thing|
34
+ # progress.tick
35
+ puts "feed #{thing.id}"
36
+ monster.feed(thing)
37
+ end
38
+ puts "waiting..."
39
+ monster.wait
40
+ end
41
+ puts "finishing..."
42
+ monster.finish
43
+ end
44
+
45
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multi_headed_greek_monster
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jacob
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cubbyhole
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description:
31
+ email: jacob@engineyard.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - lib/multi_headed_greek_monster.rb
37
+ - MIT-LICENSE
38
+ - README.rdoc
39
+ - test/multi_headed_greek_monster_test.rb
40
+ homepage: https://github.com/engineyard/multi_headed_greek_monster
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: 1.3.6
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.24
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: parallelize stuff
64
+ test_files:
65
+ - test/multi_headed_greek_monster_test.rb