infopark-politics 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.rbenv-version +1 -1
- data/README.md +9 -0
- data/infopark-politics.gemspec +5 -6
- data/lib/politics/static_queue_worker.rb +10 -77
- data/lib/politics.rb +2 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/static_queue_worker_spec.rb +3 -33
- metadata +98 -156
- data/History.rdoc +0 -31
- data/README.rdoc +0 -49
- data/VERSION +0 -1
- data/examples/queue_worker_example.rb +0 -38
- data/examples/token_worker_example.rb +0 -36
- data/lib/init.rb +0 -5
- data/lib/politics/discoverable_node.rb +0 -138
- data/lib/politics/token_worker.rb +0 -175
- data/test/static_queue_worker_test.rb +0 -43
- data/test/test_helper.rb +0 -20
- data/test/token_worker_test.rb +0 -79
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NjRmYzE1MmU4OGM0YmE4YTZhZTA2N2NjYjc3NjI4MjJiM2VlYTJjMQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTliMGY5NzcyNDk3ZjliZjBmZjY1NGM5MGE3MzkzZmM3NDg5ZDljZQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MzYzMjdmYzgxZTgyNjgxODhiYzg0NWVmZmI4YmY5N2JkZmMxZTZjN2Q4OGRh
|
10
|
+
MDIxYzg1NTc0MTk5MDA2ZTI3N2VmMjkzODQ4YzMxYzM3MTZiNWViZDRhM2I1
|
11
|
+
Yzc0ZDM3MWU2N2FlNzlhMDFmODMwYjZkMWMzMWIxYzRhNGY4ZjQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjkxNGRkMzFkNTlhYTY3Yzk4MDBhMzE4OGRkMzA3YzM2ZTZlZjc2MDczNzIz
|
14
|
+
N2JjMWFjYmRmNzM2ODE1NGUxOWI2MDE5NmQ2MTA0NmRkYmY1ODM5ODY4ODI2
|
15
|
+
Yzc1Yzk3MzZiOTdkN2NlYzIzZGRkYzdmODNiNDU2Yzk0MGY0M2I=
|
data/.rbenv-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.9.3-p448
|
data/README.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Infopark-Politics
|
2
|
+
=================
|
3
|
+
|
4
|
+
Infopark-Politics was derived from mperham-politics. All parts except of the StaticQueueWorker had
|
5
|
+
eventually been dropped. The StaticQueueWorker remained for breaking up work into a large amount of
|
6
|
+
pieces (called buckets) which then are distributed on n-1 worker nodes where n ist the total number
|
7
|
+
of nodes.
|
8
|
+
|
9
|
+
Actually there is no further documentation available…
|
data/infopark-politics.gemspec
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
requirement = Gem::Requirement.new(">= 1.
|
2
|
+
requirement = Gem::Requirement.new(">= 1.8")
|
3
3
|
unless requirement.satisfied_by?(Gem::Version.new(Gem::VERSION))
|
4
4
|
raise "You need at RubyGems in Version #{requirement} to build this gem."
|
5
5
|
end
|
6
6
|
|
7
7
|
Gem::Specification.new do |gem|
|
8
8
|
gem.name = "infopark-politics"
|
9
|
-
gem.version = "0.
|
9
|
+
gem.version = "0.4.0"
|
10
10
|
gem.summary = "Algorithms and Tools for Distributed Computing in Ruby."
|
11
11
|
gem.description = ""
|
12
12
|
gem.authors = ["Mike Perham", "Tilo Prütz"]
|
13
13
|
gem.email = "tilo@infopark.de"
|
14
14
|
gem.homepage = "http://github.com/infopark/politics"
|
15
15
|
|
16
|
+
gem.required_ruby_version = Gem::Requirement.new('>= 1.9')
|
16
17
|
gem.required_rubygems_version = Gem::Requirement.new(">= 1.8")
|
17
18
|
gem.extra_rdoc_files = [
|
18
|
-
"History.rdoc",
|
19
19
|
"LICENSE",
|
20
|
-
"README.
|
20
|
+
"README.md"
|
21
21
|
]
|
22
22
|
gem.files = `git ls-files`.split("\n")
|
23
23
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -30,7 +30,6 @@ Gem::Specification.new do |gem|
|
|
30
30
|
gem.add_development_dependency "rubygems-tasks", ">=0.2"
|
31
31
|
|
32
32
|
gem.add_runtime_dependency "memcache-client", ">= 1.5.0"
|
33
|
-
gem.add_runtime_dependency "starling
|
34
|
-
gem.add_runtime_dependency "net-mdns", ">= 0.4"
|
33
|
+
gem.add_runtime_dependency "starling", ">= 0.9.8"
|
35
34
|
end
|
36
35
|
|
@@ -4,15 +4,7 @@ require 'ipaddr'
|
|
4
4
|
require 'uri'
|
5
5
|
require 'drb'
|
6
6
|
require 'set'
|
7
|
-
|
8
|
-
begin
|
9
|
-
require 'net/dns/mdns-sd'
|
10
|
-
require 'net/dns/resolv-mdns'
|
11
|
-
require 'net/dns/resolv-replace'
|
12
|
-
rescue LoadError => e
|
13
|
-
puts "Unable to load net-mdns, please run `sudo gem install net-mdns`: #{e.message}"
|
14
|
-
exit(1)
|
15
|
-
end
|
7
|
+
require 'logger'
|
16
8
|
|
17
9
|
begin
|
18
10
|
require 'memcache'
|
@@ -22,42 +14,6 @@ rescue LoadError => e
|
|
22
14
|
end
|
23
15
|
|
24
16
|
module Politics
|
25
|
-
|
26
|
-
# The StaticQueueWorker mixin allows a processing daemon to "lease" or checkout
|
27
|
-
# a portion of a problem space to ensure no other process is processing that same
|
28
|
-
# space at the same time. The processing space is cut into N "buckets", each of
|
29
|
-
# which is placed in a queue. Processes then fetch entries from the queue
|
30
|
-
# and process them. It is up to the application to map the bucket number onto its
|
31
|
-
# specific problem space.
|
32
|
-
#
|
33
|
-
# Note that memcached is used for leader election. The leader owns the queue during
|
34
|
-
# the iteration period and other peers fetch buckets from the current leader during the
|
35
|
-
# iteration.
|
36
|
-
#
|
37
|
-
# The leader hands out buckets in order. Once all the buckets have been processed, the
|
38
|
-
# leader returns nil to the processors which causes them to sleep until the end of the
|
39
|
-
# iteration. Then everyone wakes up, a new leader is elected, and the processing starts
|
40
|
-
# all over again.
|
41
|
-
#
|
42
|
-
# DRb and mDNS are used for peer discovery and communication.
|
43
|
-
#
|
44
|
-
# Example usage:
|
45
|
-
#
|
46
|
-
# class Analyzer
|
47
|
-
# include Politics::StaticQueueWorker
|
48
|
-
# TOTAL_BUCKETS = 16
|
49
|
-
#
|
50
|
-
# def start
|
51
|
-
# register_worker(self.class.name, TOTAL_BUCKETS)
|
52
|
-
# process_bucket do |bucket|
|
53
|
-
# puts "Analyzing bucket #{bucket} of #{TOTAL_BUCKETS}"
|
54
|
-
# sleep 5
|
55
|
-
# end
|
56
|
-
# end
|
57
|
-
# end
|
58
|
-
#
|
59
|
-
# Note: process_bucket never returns i.e. this should be the main loop of your processing daemon.
|
60
|
-
#
|
61
17
|
module StaticQueueWorker
|
62
18
|
attr_reader :group_name, :iteration_length, :dictatorship_length, :uri, :buckets
|
63
19
|
|
@@ -72,17 +28,15 @@ module Politics
|
|
72
28
|
@iteration_length = options[:iteration_length]
|
73
29
|
@memcache_client = client_for(Array(options[:servers]))
|
74
30
|
@nominated_at = Time.now
|
75
|
-
# FIXME: Tests
|
76
|
-
@domain = options[:domain]
|
77
31
|
@dictatorship_length = options[:dictatorship_length]
|
78
32
|
|
79
33
|
@buckets = []
|
80
34
|
@bucket_count = bucket_count
|
81
35
|
@followers_to_stop = Set.new
|
82
36
|
|
83
|
-
|
37
|
+
start_druby_service
|
84
38
|
log.progname = uri
|
85
|
-
log.info { "Registered in group #{group_name} at
|
39
|
+
log.info { "Registered in group #{group_name} at #{uri}" }
|
86
40
|
at_exit do
|
87
41
|
cleanup
|
88
42
|
end
|
@@ -213,18 +167,7 @@ module Politics
|
|
213
167
|
end
|
214
168
|
|
215
169
|
def find_workers
|
216
|
-
|
217
|
-
browser = Net::DNS::MDNSSD.browse(mdns_type) do |reply|
|
218
|
-
worker_uri = reply.name.gsub(/#/, '.')
|
219
|
-
workers << worker_uri unless worker_uri == uri
|
220
|
-
end
|
221
|
-
sleep 5
|
222
|
-
browser.stop
|
223
|
-
workers
|
224
|
-
end
|
225
|
-
|
226
|
-
def hostname
|
227
|
-
nil
|
170
|
+
raise "Please provide a method ”find_workers” returning a list of all other worker URIs"
|
228
171
|
end
|
229
172
|
|
230
173
|
private
|
@@ -328,26 +271,16 @@ module Politics
|
|
328
271
|
Time.now - a
|
329
272
|
end
|
330
273
|
|
331
|
-
def
|
332
|
-
"_#{group_name}._tcp"
|
274
|
+
def hostname
|
333
275
|
end
|
334
276
|
|
335
|
-
def
|
277
|
+
def register_service
|
278
|
+
end
|
279
|
+
|
280
|
+
def start_druby_service
|
336
281
|
server = DRb.start_service("druby://#{hostname || ""}:0", self)
|
337
282
|
@uri = DRb.uri
|
338
|
-
|
339
|
-
|
340
|
-
# Register our DRb server with Bonjour.
|
341
|
-
name = @uri.gsub(/\./, '#')
|
342
|
-
domain = "local"
|
343
|
-
log.debug "register service #{name} of type #{mdns_type} within domain #{domain} at port #{@port}"
|
344
|
-
handle = Net::DNS::MDNSSD.register(name, mdns_type, domain, @port) do |reply|
|
345
|
-
log.debug "registered as #{reply.fullname}"
|
346
|
-
if reply.name != name
|
347
|
-
log.debug "Registered name #{reply.name} differs from requested name #{name} … exiting."
|
348
|
-
handle.stop
|
349
|
-
end
|
350
|
-
end
|
283
|
+
register_service
|
351
284
|
end
|
352
285
|
end
|
353
286
|
end
|
data/lib/politics.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
require '
|
4
|
-
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
5
|
-
require File.dirname(__FILE__) + '/../lib/init'
|
6
|
-
Politics::log.level = Logger::FATAL
|
2
|
+
|
3
|
+
require 'politics'
|
7
4
|
|
8
5
|
class UninitializedWorker
|
9
6
|
include Politics::StaticQueueWorker
|
@@ -56,7 +53,7 @@ describe UninitializedWorker do
|
|
56
53
|
end
|
57
54
|
|
58
55
|
it "should not have a hostname" do
|
59
|
-
worker.hostname.should be_nil
|
56
|
+
worker.send(:hostname).should be_nil
|
60
57
|
end
|
61
58
|
|
62
59
|
context "when it has a hostname" do
|
@@ -477,33 +474,6 @@ describe Worker do
|
|
477
474
|
end
|
478
475
|
end
|
479
476
|
|
480
|
-
describe "when finding workers" do
|
481
|
-
before do
|
482
|
-
Net::DNS::MDNSSD.stub(:browse).
|
483
|
-
and_yield(double('response1', :name => 'w1')).
|
484
|
-
and_yield(double('response2', :name => 'w2')).
|
485
|
-
and_yield(double('response3', :name => 'w3')).
|
486
|
-
and_yield(double('response4', :name => 'w4')).
|
487
|
-
and_return(@browser = double('browser', :stop => nil))
|
488
|
-
worker.stub(:sleep)
|
489
|
-
end
|
490
|
-
|
491
|
-
it "should browse mdns group and return workers found" do
|
492
|
-
worker.find_workers.should == %w(w1 w2 w3 w4)
|
493
|
-
end
|
494
|
-
|
495
|
-
it "should not add itself to the result list" do
|
496
|
-
worker.stub(:uri).and_return('w3')
|
497
|
-
worker.find_workers.should_not include('w3')
|
498
|
-
end
|
499
|
-
|
500
|
-
it "should stop browser thread after five seconds" do
|
501
|
-
worker.should_receive(:sleep).with(5).ordered
|
502
|
-
@browser.should_receive(:stop)
|
503
|
-
worker.find_workers
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
477
|
describe "when populating followers_to_stop" do
|
508
478
|
before do
|
509
479
|
worker.stub(:find_workers).and_return(%w(a b c))
|
metadata
CHANGED
@@ -1,203 +1,145 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: infopark-politics
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 3
|
9
|
-
- 3
|
10
|
-
version: 0.3.3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Mike Perham
|
14
|
-
-
|
8
|
+
- Tilo Prütz
|
15
9
|
autorequire:
|
16
10
|
bindir: bin
|
17
11
|
cert_chain: []
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
dependencies:
|
22
|
-
- !ruby/object:Gem::Dependency
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
-
none: false
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 31
|
29
|
-
segments:
|
30
|
-
- 2
|
31
|
-
- 14
|
32
|
-
version: "2.14"
|
12
|
+
date: 2013-11-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
33
15
|
name: rspec
|
34
|
-
|
35
|
-
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '2.14'
|
36
21
|
type: :development
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ">="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
hash: 23
|
44
|
-
segments:
|
45
|
-
- 10
|
46
|
-
version: "10"
|
47
|
-
name: rake
|
48
|
-
version_requirements: *id002
|
49
22
|
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '2.14'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '10'
|
50
35
|
type: :development
|
51
|
-
- !ruby/object:Gem::Dependency
|
52
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
-
none: false
|
54
|
-
requirements:
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
hash: 21
|
58
|
-
segments:
|
59
|
-
- 3
|
60
|
-
- 9
|
61
|
-
version: "3.9"
|
62
|
-
name: rdoc
|
63
|
-
version_requirements: *id003
|
64
36
|
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '10'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rdoc
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.9'
|
65
49
|
type: :development
|
66
|
-
- !ruby/object:Gem::Dependency
|
67
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
|
-
requirements:
|
70
|
-
- - ">="
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
hash: 15
|
73
|
-
segments:
|
74
|
-
- 0
|
75
|
-
- 2
|
76
|
-
version: "0.2"
|
77
|
-
name: rubygems-tasks
|
78
|
-
version_requirements: *id004
|
79
50
|
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.9'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rubygems-tasks
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.2'
|
80
63
|
type: :development
|
81
|
-
- !ruby/object:Gem::Dependency
|
82
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
|
-
requirements:
|
85
|
-
- - ">="
|
86
|
-
- !ruby/object:Gem::Version
|
87
|
-
hash: 3
|
88
|
-
segments:
|
89
|
-
- 1
|
90
|
-
- 5
|
91
|
-
- 0
|
92
|
-
version: 1.5.0
|
93
|
-
name: memcache-client
|
94
|
-
version_requirements: *id005
|
95
64
|
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.2'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: memcache-client
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 1.5.0
|
96
77
|
type: :runtime
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
99
|
-
none: false
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
hash: 43
|
104
|
-
segments:
|
105
|
-
- 0
|
106
|
-
- 9
|
107
|
-
- 8
|
108
|
-
version: 0.9.8
|
109
|
-
name: starling-starling
|
110
|
-
version_requirements: *id006
|
111
78
|
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ! '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.5.0
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: starling
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.9.8
|
112
91
|
type: :runtime
|
113
|
-
- !ruby/object:Gem::Dependency
|
114
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
115
|
-
none: false
|
116
|
-
requirements:
|
117
|
-
- - ">="
|
118
|
-
- !ruby/object:Gem::Version
|
119
|
-
hash: 3
|
120
|
-
segments:
|
121
|
-
- 0
|
122
|
-
- 4
|
123
|
-
version: "0.4"
|
124
|
-
name: net-mdns
|
125
|
-
version_requirements: *id007
|
126
92
|
prerelease: false
|
127
|
-
|
128
|
-
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 0.9.8
|
98
|
+
description: ''
|
129
99
|
email: tilo@infopark.de
|
130
100
|
executables: []
|
131
|
-
|
132
101
|
extensions: []
|
133
|
-
|
134
|
-
extra_rdoc_files:
|
135
|
-
- History.rdoc
|
102
|
+
extra_rdoc_files:
|
136
103
|
- LICENSE
|
137
|
-
- README.
|
138
|
-
files:
|
104
|
+
- README.md
|
105
|
+
files:
|
139
106
|
- .gitignore
|
140
107
|
- .rbenv-version
|
141
108
|
- .rspec
|
142
109
|
- Gemfile
|
143
|
-
- History.rdoc
|
144
110
|
- LICENSE
|
145
111
|
- Manifest
|
146
|
-
- README.
|
112
|
+
- README.md
|
147
113
|
- Rakefile
|
148
|
-
- VERSION
|
149
114
|
- dcc_config.rb
|
150
|
-
- examples/queue_worker_example.rb
|
151
|
-
- examples/token_worker_example.rb
|
152
115
|
- infopark-politics.gemspec
|
153
|
-
- lib/init.rb
|
154
116
|
- lib/politics.rb
|
155
|
-
- lib/politics/discoverable_node.rb
|
156
117
|
- lib/politics/static_queue_worker.rb
|
157
|
-
- lib/politics/token_worker.rb
|
158
118
|
- spec/spec_helper.rb
|
159
119
|
- spec/static_queue_worker_spec.rb
|
160
|
-
- test/static_queue_worker_test.rb
|
161
|
-
- test/test_helper.rb
|
162
|
-
- test/token_worker_test.rb
|
163
|
-
has_rdoc: true
|
164
120
|
homepage: http://github.com/infopark/politics
|
165
121
|
licenses: []
|
166
|
-
|
122
|
+
metadata: {}
|
167
123
|
post_install_message:
|
168
124
|
rdoc_options: []
|
169
|
-
|
170
|
-
require_paths:
|
125
|
+
require_paths:
|
171
126
|
- lib
|
172
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
none: false
|
183
|
-
requirements:
|
184
|
-
- - ">="
|
185
|
-
- !ruby/object:Gem::Version
|
186
|
-
hash: 31
|
187
|
-
segments:
|
188
|
-
- 1
|
189
|
-
- 8
|
190
|
-
version: "1.8"
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ! '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.9'
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ! '>='
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '1.8'
|
191
137
|
requirements: []
|
192
|
-
|
193
138
|
rubyforge_project:
|
194
|
-
rubygems_version: 1.
|
139
|
+
rubygems_version: 2.1.10
|
195
140
|
signing_key:
|
196
|
-
specification_version:
|
141
|
+
specification_version: 4
|
197
142
|
summary: Algorithms and Tools for Distributed Computing in Ruby.
|
198
|
-
test_files:
|
143
|
+
test_files:
|
199
144
|
- spec/spec_helper.rb
|
200
145
|
- spec/static_queue_worker_spec.rb
|
201
|
-
- test/static_queue_worker_test.rb
|
202
|
-
- test/test_helper.rb
|
203
|
-
- test/token_worker_test.rb
|
data/History.rdoc
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
= Changelog
|
2
|
-
|
3
|
-
== 0.2.5 (2009-02-04)
|
4
|
-
|
5
|
-
* Gracefully handle MemCache::MemCacheErrors. Just sleep until memcached comes back.
|
6
|
-
|
7
|
-
== 0.2.4 (2009-01-28)
|
8
|
-
|
9
|
-
* Reduce leader token expiration time to discourage a get/set race condition. (Brian Dainton)
|
10
|
-
|
11
|
-
== 0.2.3 (2009-01-12)
|
12
|
-
|
13
|
-
* Fix invalid result check in previous change. (Brian Dainton)
|
14
|
-
|
15
|
-
== 0.2.2 (2009-01-07)
|
16
|
-
|
17
|
-
* Fix invalid leader? logic in TokenWorker which could allow
|
18
|
-
two workers to become leader at the same time. (Brian Dainton)
|
19
|
-
|
20
|
-
== 0.2.1 (2008-11-04)
|
21
|
-
|
22
|
-
* Cleanup and prepare for public release for RubyConf 2008.
|
23
|
-
* Election Day. Politics. Get it? Hee hee.
|
24
|
-
|
25
|
-
== 0.2.0 (2008-10-24)
|
26
|
-
|
27
|
-
* Remove BucketWorker based on initial feedback. Add StaticQueueWorker as a more reliable replacement.
|
28
|
-
|
29
|
-
== 0.1.0 (2008-10-07)
|
30
|
-
|
31
|
-
* Add BucketWorker and TokenWorker mixins.
|
data/README.rdoc
DELETED
@@ -1,49 +0,0 @@
|
|
1
|
-
= Politics
|
2
|
-
|
3
|
-
Politics is a Ruby library providing utilities and algorithms for solving common distributed
|
4
|
-
computing problems. Distributed Computing and Politics have a number of things in common:
|
5
|
-
1) they can be beautiful in theory but get really ugly in reality; 2) after working with
|
6
|
-
either for a few weeks/months/years (depending on your moral flexibility) you'll find yourself
|
7
|
-
intellectually devoid, a hollow shell of a man/woman/cybernetic killing machine.
|
8
|
-
|
9
|
-
So the name is to be taken tongue in cheek. Onto the real details.
|
10
|
-
|
11
|
-
== Common Problems in Distributed Computing
|
12
|
-
|
13
|
-
Ruby services are often deployed as a cloud of many processes across several machines,
|
14
|
-
for fault tolerance. This introduces the problem of coordination between those processes.
|
15
|
-
Specifically, how do you keep those processes from stepping on each other's electronic
|
16
|
-
toes? There are several answers:
|
17
|
-
|
18
|
-
1. Break the processing into N 'buckets'. Have an individual process fetch a bucket,
|
19
|
-
work on it, and ask for another. This is a very scalable solution as it allows N workers
|
20
|
-
to work on different parts of the same task concurrently. See the +StaticQueueWorker+ mixin.
|
21
|
-
1. Elect a leader for a short period of time. The leader is the process which performs the
|
22
|
-
actual processing. After a length of time, a new leader is elected from the group. This
|
23
|
-
is fault tolerant but not as scalable, as only one process is performing the task at a given
|
24
|
-
point in time. See the +TokenWorker+ mixin.
|
25
|
-
|
26
|
-
== Installation
|
27
|
-
|
28
|
-
sudo gem install mperham-politics -s http://gems.github.com
|
29
|
-
|
30
|
-
== Dependencies
|
31
|
-
|
32
|
-
StaticQueueWorker mixin
|
33
|
-
* memcached - the mechanism to elect a leader amongst a set of peers.
|
34
|
-
* DRb - the mechanism to communicate between peers.
|
35
|
-
* mDNS - the mechanism to discover peers.
|
36
|
-
|
37
|
-
TokenWorker mixin
|
38
|
-
* memcached - the mechanism to elect a leader amongst a set of peers.
|
39
|
-
|
40
|
-
|
41
|
-
= Author
|
42
|
-
|
43
|
-
Name:: Mike Perham
|
44
|
-
Email:: mailto:mperham@gmail.com
|
45
|
-
Twitter:: http://twitter.com/mperham
|
46
|
-
Homepage:: http://mikeperham.com/
|
47
|
-
|
48
|
-
This software is free for you to use as you'd like. If you find it useful, please consider giving
|
49
|
-
me a recommendation at {Working with Rails}[http://workingwithrails.com/person/10797-mike-perham].
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.3.2
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#gem 'mperham-politics'
|
3
|
-
require 'politics'
|
4
|
-
require 'politics/static_queue_worker'
|
5
|
-
|
6
|
-
# Test this example by starting memcached locally and then in two irb sessions, run this:
|
7
|
-
#
|
8
|
-
=begin
|
9
|
-
require 'queue_worker_example'
|
10
|
-
p = Politics::QueueWorkerExample.new
|
11
|
-
p.start
|
12
|
-
=end
|
13
|
-
#
|
14
|
-
# You can then watch as one of them is elected leader. You can kill the leader and verify
|
15
|
-
# the backup process is elected after approximately iteration_length seconds.
|
16
|
-
#
|
17
|
-
module Politics
|
18
|
-
class QueueWorkerExample
|
19
|
-
include Politics::StaticQueueWorker
|
20
|
-
TOTAL_BUCKETS = 20
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
register_worker 'queue-example', TOTAL_BUCKETS, :iteration_length => 60, :servers => memcached_servers
|
24
|
-
end
|
25
|
-
|
26
|
-
def start
|
27
|
-
process_bucket do |bucket|
|
28
|
-
puts "PID #{$$} processing bucket #{bucket}/#{TOTAL_BUCKETS} at #{Time.now}..."
|
29
|
-
sleep 1.5
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def memcached_servers
|
34
|
-
['127.0.0.1:11211']
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|
38
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
#gem 'mperham-politics'
|
3
|
-
require 'politics'
|
4
|
-
require 'politics/token_worker'
|
5
|
-
|
6
|
-
# Test this example by starting memcached locally and then in two irb sessions, run this:
|
7
|
-
#
|
8
|
-
=begin
|
9
|
-
require 'token_worker_example'
|
10
|
-
p = Politics::TokenWorkerExample.new
|
11
|
-
p.start
|
12
|
-
=end
|
13
|
-
#
|
14
|
-
# You can then watch as one of them is elected leader. You can kill the leader and verify
|
15
|
-
# the backup process is elected after approximately iteration_length seconds.
|
16
|
-
#
|
17
|
-
module Politics
|
18
|
-
class TokenWorkerExample
|
19
|
-
include Politics::TokenWorker
|
20
|
-
|
21
|
-
def initialize
|
22
|
-
register_worker 'token-example', :iteration_length => 10, :servers => memcached_servers
|
23
|
-
end
|
24
|
-
|
25
|
-
def start
|
26
|
-
process do
|
27
|
-
puts "PID #{$$} processing at #{Time.now}..."
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def memcached_servers
|
32
|
-
['localhost:11211']
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
data/lib/init.rb
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'socket'
|
3
|
-
require 'ipaddr'
|
4
|
-
require 'uri'
|
5
|
-
require 'drb'
|
6
|
-
|
7
|
-
require 'net/dns/mdns-sd'
|
8
|
-
require 'net/dns/resolv-mdns'
|
9
|
-
require 'net/dns/resolv-replace'
|
10
|
-
|
11
|
-
=begin
|
12
|
-
IRB setup:
|
13
|
-
require 'lib/politics'
|
14
|
-
require 'lib/politics/discoverable_node'
|
15
|
-
require 'lib/politics/convention'
|
16
|
-
Object.send(:include, Election::Candidate)
|
17
|
-
p = Object.new
|
18
|
-
p.register
|
19
|
-
=end
|
20
|
-
|
21
|
-
module Politics
|
22
|
-
|
23
|
-
# A module to solve the Group Membership problem in distributed computing.
|
24
|
-
# The "group" is the cloud of processes which are replicas and need to coordinate.
|
25
|
-
# Handling group membership is the first step in solving distributed computing
|
26
|
-
# problems. There are two issues:
|
27
|
-
# 1) replica discovery
|
28
|
-
# 2) controlling and maintaining a consistent group of replicas in each replica
|
29
|
-
#
|
30
|
-
# Peer discovery is implemented using Bonjour for local network auto-discovery.
|
31
|
-
# Each process registers itself on the network as a process of a given type.
|
32
|
-
# Each process then queries the network for other replicas of the same type.
|
33
|
-
#
|
34
|
-
# The replicas then run the Multi-Paxos algorithm to provide consensus on a given
|
35
|
-
# replica set. The algorithm is robust in the face of crash failures, but not
|
36
|
-
# Byzantine failures.
|
37
|
-
module DiscoverableNode
|
38
|
-
|
39
|
-
attr_accessor :group
|
40
|
-
attr_accessor :coordinator
|
41
|
-
|
42
|
-
def register(group='foo')
|
43
|
-
self.group = group
|
44
|
-
start_drb
|
45
|
-
register_with_bonjour(group)
|
46
|
-
Politics::log.info { "Registered #{self} in group #{group} with RID #{rid}" }
|
47
|
-
sleep 0.5
|
48
|
-
find_replicas(0)
|
49
|
-
end
|
50
|
-
|
51
|
-
def replicas
|
52
|
-
@replicas ||= {}
|
53
|
-
end
|
54
|
-
|
55
|
-
def find_replicas(count)
|
56
|
-
replicas.clear if count % 5 == 0
|
57
|
-
return if count > 10 # Guaranteed to terminate, but not successfully :-(
|
58
|
-
|
59
|
-
#puts "Finding replicas"
|
60
|
-
peer_set = []
|
61
|
-
bonjour_scan do |replica|
|
62
|
-
(his_rid, his_peers) = replica.hello(rid)
|
63
|
-
unless replicas.has_key?(his_rid)
|
64
|
-
replicas[his_rid] = replica
|
65
|
-
end
|
66
|
-
his_peers.each do |peer|
|
67
|
-
peer_set << peer unless peer_set.include? peer
|
68
|
-
end
|
69
|
-
end
|
70
|
-
#p [peer_set.sort, replicas.keys.sort]
|
71
|
-
if peer_set.sort != replicas.keys.sort
|
72
|
-
# Recursively call ourselves until the network has settled down and all
|
73
|
-
# peers have reached agreement on the peer group membership.
|
74
|
-
sleep 0.2
|
75
|
-
find_replicas(count + 1)
|
76
|
-
end
|
77
|
-
Politics::log.info { "Found #{replicas.size} peers: #{replicas.keys.sort.inspect}" } if count == 0
|
78
|
-
replicas
|
79
|
-
end
|
80
|
-
|
81
|
-
# Called for one peer to introduce itself to another peer. The caller
|
82
|
-
# sends his RID, the responder sends his RID and his list of current peer
|
83
|
-
# RIDs.
|
84
|
-
def hello(remote_rid)
|
85
|
-
[rid, replicas.keys]
|
86
|
-
end
|
87
|
-
|
88
|
-
# A process's Replica ID is its PID + a random 16-bit value. We don't want
|
89
|
-
# weigh solely based on PID or IP as that may unduly load one machine.
|
90
|
-
def rid
|
91
|
-
@rid ||= begin
|
92
|
-
rand(65536) + $$
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
private
|
97
|
-
|
98
|
-
def register_with_bonjour(group)
|
99
|
-
# Register our DRb server with Bonjour.
|
100
|
-
handle = Net::DNS::MDNSSD.register("#{self.group}-#{local_ip}-#{$$}",
|
101
|
-
"_#{self.group}._tcp", 'local', @port)
|
102
|
-
|
103
|
-
['INT', 'TERM'].each { |signal|
|
104
|
-
trap(signal) { handle.stop }
|
105
|
-
}
|
106
|
-
end
|
107
|
-
|
108
|
-
def start_drb
|
109
|
-
server = DRb.start_service(nil, self)
|
110
|
-
@port = URI.parse(DRb.uri).port
|
111
|
-
['INT', 'TERM'].each { |signal|
|
112
|
-
trap(signal) { server.stop_service }
|
113
|
-
}
|
114
|
-
end
|
115
|
-
|
116
|
-
def bonjour_scan
|
117
|
-
Net::DNS::MDNSSD.browse("_#{@group}._tcp") do |b|
|
118
|
-
Net::DNS::MDNSSD.resolve(b.name, b.type) do |r|
|
119
|
-
drburl = "druby://#{r.target}:#{r.port}"
|
120
|
-
replica = DRbObject.new(nil, drburl)
|
121
|
-
yield replica
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
# http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
|
127
|
-
def local_ip
|
128
|
-
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
129
|
-
|
130
|
-
UDPSocket.open do |s|
|
131
|
-
s.connect '64.233.187.99', 1
|
132
|
-
IPAddr.new(s.addr.last).to_i
|
133
|
-
end
|
134
|
-
ensure
|
135
|
-
Socket.do_not_reverse_lookup = orig
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
@@ -1,175 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
begin
|
3
|
-
require 'memcache'
|
4
|
-
rescue LoadError => e
|
5
|
-
puts "Unable to load memcache client, please run `sudo gem install memcache-client`: #{e.message}"
|
6
|
-
exit(1)
|
7
|
-
end
|
8
|
-
|
9
|
-
module Politics
|
10
|
-
|
11
|
-
# An algorithm to provide leader election between a set of identical processing daemons.
|
12
|
-
#
|
13
|
-
# Each TokenWorker is an instance which needs to perform some processing.
|
14
|
-
# The worker instance must obtain the leader token before performing some task.
|
15
|
-
# We use a memcached server as a central token authority to provide a shared,
|
16
|
-
# network-wide view for all processors. This reliance on a single resource means
|
17
|
-
# if your memcached server goes down, so do the processors. Oftentimes,
|
18
|
-
# this is an acceptable trade-off since many high-traffic web sites would
|
19
|
-
# not be useable without memcached running anyhow.
|
20
|
-
#
|
21
|
-
# Essentially each TokenWorker attempts to elect itself every +:iteration_length+
|
22
|
-
# seconds by simply setting a key in memcached to its own name. Memcached tracks
|
23
|
-
# which name got there first. The key expires after +:iteration_length+ seconds.
|
24
|
-
#
|
25
|
-
# Example usage:
|
26
|
-
# class Analyzer
|
27
|
-
# include Politics::TokenWorker
|
28
|
-
#
|
29
|
-
# def initialize
|
30
|
-
# register_worker 'analyzer', :iteration_length => 120, :servers => ['localhost:11211']
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# def start
|
34
|
-
# process do
|
35
|
-
# # do analysis here, will only be done when this process
|
36
|
-
# # is actually elected leader, otherwise it will sleep for
|
37
|
-
# # iteration_length seconds.
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
#
|
42
|
-
# Notes:
|
43
|
-
# * This will not work with multiple instances in the same Ruby process.
|
44
|
-
# The library is only designed to elect a leader from a set of processes, not instances within
|
45
|
-
# a single process.
|
46
|
-
# * The algorithm makes no attempt to keep the same leader during the next iteration.
|
47
|
-
# This can often times be quite beneficial (e.g. leveraging a warm cache from the last iteration)
|
48
|
-
# for performance but is left to the reader to implement.
|
49
|
-
module TokenWorker
|
50
|
-
|
51
|
-
def self.included(model) #:nodoc:
|
52
|
-
model.class_eval do
|
53
|
-
attr_accessor :memcache_client, :token, :iteration_length, :worker_name
|
54
|
-
class << self
|
55
|
-
attr_accessor :worker_instance #:nodoc:
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Register this instance as a worker.
|
61
|
-
#
|
62
|
-
# Options:
|
63
|
-
# +:iteration_length+:: The length of a processing iteration, in seconds. The
|
64
|
-
# leader's 'reign' lasts for this length of time.
|
65
|
-
# +:servers+:: An array of memcached server strings
|
66
|
-
def register_worker(name, config={})
|
67
|
-
# track the latest instance of this class, there's really only supposed to be
|
68
|
-
# a single TokenWorker instance per process.
|
69
|
-
self.class.worker_instance = self
|
70
|
-
|
71
|
-
options = { :iteration_length => 60, :servers => ['localhost:11211'] }
|
72
|
-
options.merge!(config)
|
73
|
-
|
74
|
-
self.token = "#{name}_token"
|
75
|
-
self.memcache_client = client_for(Array(options[:servers]))
|
76
|
-
self.iteration_length = options[:iteration_length]
|
77
|
-
self.worker_name = "#{Socket.gethostname}:#{$$}"
|
78
|
-
|
79
|
-
cleanup
|
80
|
-
end
|
81
|
-
|
82
|
-
def process(*args, &block)
|
83
|
-
verify_registration
|
84
|
-
|
85
|
-
begin
|
86
|
-
# Try to add our name as the worker with the master token.
|
87
|
-
# If another process got there first, this is a noop.
|
88
|
-
# We add an expiry so that the master token will constantly
|
89
|
-
# need to be refreshed (in case the current leader dies).
|
90
|
-
time = 0
|
91
|
-
begin
|
92
|
-
nominate
|
93
|
-
|
94
|
-
if leader?
|
95
|
-
Politics::log.info { "#{worker_name} elected leader at #{Time.now}" }
|
96
|
-
# If we are the master worker, do the work.
|
97
|
-
time = time_for do
|
98
|
-
result = block.call(*args)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
rescue MemCache::MemCacheError => me
|
102
|
-
Politics::log.error("Error from memcached, pausing until the next iteration...")
|
103
|
-
Politics::log.error(me.message)
|
104
|
-
Politics::log.error(me.backtrace.join("\n"))
|
105
|
-
self.memcache_client.reset
|
106
|
-
end
|
107
|
-
|
108
|
-
pause_until_expiry(time)
|
109
|
-
reset_state
|
110
|
-
end while loop?
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
def reset_state
|
116
|
-
@leader = nil
|
117
|
-
end
|
118
|
-
|
119
|
-
def verify_registration
|
120
|
-
unless self.class.worker_instance
|
121
|
-
raise ArgumentError, "Cannot call process without first calling register_worker"
|
122
|
-
end
|
123
|
-
unless self.class.worker_instance == self
|
124
|
-
raise SecurityError, "Only one instance of #{self.class} per process. Another instance was created after this one."
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def loop?
|
129
|
-
true
|
130
|
-
end
|
131
|
-
|
132
|
-
def cleanup
|
133
|
-
at_exit do
|
134
|
-
memcache_client.delete(token) if leader?
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def pause_until_expiry(elapsed)
|
139
|
-
pause_time = (iteration_length - elapsed).to_f
|
140
|
-
if pause_time > 0
|
141
|
-
relax(pause_time)
|
142
|
-
else
|
143
|
-
raise ArgumentError, "Negative iteration time left. Assuming the worst and exiting... #{iteration_length}/#{elapsed}"
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def relax(time)
|
148
|
-
sleep time
|
149
|
-
end
|
150
|
-
|
151
|
-
# Nominate ourself as leader by contacting the memcached server
|
152
|
-
# and attempting to add the token with our name attached.
|
153
|
-
# The result will tell us if memcached stored our value and therefore
|
154
|
-
# if we are now leader.
|
155
|
-
def nominate
|
156
|
-
result = memcache_client.add(token, worker_name, (iteration_length * 0.9).to_i)
|
157
|
-
@leader = (result =~ /\ASTORED/)
|
158
|
-
end
|
159
|
-
|
160
|
-
def leader?
|
161
|
-
@leader
|
162
|
-
end
|
163
|
-
|
164
|
-
# Easy to mock or monkey-patch if another MemCache client is preferred.
|
165
|
-
def client_for(servers)
|
166
|
-
MemCache.new(servers)
|
167
|
-
end
|
168
|
-
|
169
|
-
def time_for(&block)
|
170
|
-
a = Time.now
|
171
|
-
yield
|
172
|
-
Time.now - a
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require File.dirname(__FILE__) + '/test_helper'
|
3
|
-
|
4
|
-
Thread.abort_on_exception = true
|
5
|
-
|
6
|
-
class Worker
|
7
|
-
include Politics::StaticQueueWorker
|
8
|
-
def initialize
|
9
|
-
register_worker 'worker', 10, :iteration_length => 10
|
10
|
-
end
|
11
|
-
|
12
|
-
def start
|
13
|
-
process_bucket do |bucket|
|
14
|
-
sleep 1
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
class StaticQueueWorkerTest < Test::Unit::TestCase
|
20
|
-
|
21
|
-
context "nodes" do
|
22
|
-
setup do
|
23
|
-
@nodes = []
|
24
|
-
5.times do
|
25
|
-
@nodes << nil
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
should "start up" do
|
30
|
-
processes = @nodes.map do
|
31
|
-
fork do
|
32
|
-
['INT', 'TERM'].each { |signal|
|
33
|
-
trap(signal) { exit(0) }
|
34
|
-
}
|
35
|
-
Worker.new.start
|
36
|
-
end
|
37
|
-
end
|
38
|
-
sleep 10
|
39
|
-
puts "Terminating"
|
40
|
-
Process.kill('INT', *processes)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'rubygems'
|
3
|
-
require 'test/unit'
|
4
|
-
|
5
|
-
begin
|
6
|
-
gem 'thoughtbot-shoulda', '>=2.0.2'
|
7
|
-
require 'shoulda'
|
8
|
-
rescue LoadError => e
|
9
|
-
puts "Please install shoulda: `sudo gem install thoughtbot-shoulda -s http://gems.github.com`"
|
10
|
-
end
|
11
|
-
|
12
|
-
begin
|
13
|
-
require 'mocha'
|
14
|
-
rescue LoadError => e
|
15
|
-
puts "Please install mocha: `sudo gem install mocha`"
|
16
|
-
end
|
17
|
-
|
18
|
-
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
19
|
-
require File.dirname(__FILE__) + '/../lib/init'
|
20
|
-
Politics::log.level = Logger::WARN
|
data/test/token_worker_test.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'test_helper'
|
3
|
-
|
4
|
-
class TokenWorkerTest < Test::Unit::TestCase
|
5
|
-
|
6
|
-
context "token workers" do
|
7
|
-
setup do
|
8
|
-
@harness = Class.new
|
9
|
-
@harness.send(:include, Politics::TokenWorker)
|
10
|
-
@harness.any_instance.stubs(:cleanup)
|
11
|
-
@harness.any_instance.stubs(:loop?).returns(false)
|
12
|
-
@harness.any_instance.stubs(:pause_until_expiry)
|
13
|
-
@harness.any_instance.stubs(:relax)
|
14
|
-
|
15
|
-
@worker = @harness.new
|
16
|
-
end
|
17
|
-
|
18
|
-
should "test_instance_property_accessors" do
|
19
|
-
assert @worker.iteration_length = 20
|
20
|
-
assert_equal 20, @worker.iteration_length
|
21
|
-
end
|
22
|
-
|
23
|
-
should 'test_tracks_a_registered_singleton' do
|
24
|
-
assert_nil @worker.class.worker_instance
|
25
|
-
@worker.register_worker('testing')
|
26
|
-
assert_equal @worker.class.worker_instance, @worker
|
27
|
-
end
|
28
|
-
|
29
|
-
should 'not process if they are not leader' do
|
30
|
-
@worker.expects(:nominate)
|
31
|
-
@worker.expects(:leader?).returns(false)
|
32
|
-
@worker.register_worker('testing')
|
33
|
-
@worker.process do
|
34
|
-
assert false
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
should 'handle unexpected MemCache errors' do
|
39
|
-
@worker.expects(:nominate)
|
40
|
-
@worker.expects(:leader?).raises(MemCache::MemCacheError)
|
41
|
-
Politics::log.expects(:error).times(3)
|
42
|
-
|
43
|
-
@worker.register_worker('testing')
|
44
|
-
@worker.process do
|
45
|
-
assert false
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
should 'process if they are leader' do
|
50
|
-
@worker.expects(:nominate)
|
51
|
-
@worker.expects(:leader?).returns(true)
|
52
|
-
@worker.register_worker('testing')
|
53
|
-
|
54
|
-
worked = 0
|
55
|
-
@worker.process do
|
56
|
-
worked += 1
|
57
|
-
end
|
58
|
-
|
59
|
-
assert_equal 1, worked
|
60
|
-
end
|
61
|
-
|
62
|
-
should 'not allow processing without registration' do
|
63
|
-
assert_raises ArgumentError do
|
64
|
-
@worker.process
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
should 'not allow processing by old instances' do
|
69
|
-
@worker.register_worker('testing')
|
70
|
-
|
71
|
-
foo = @worker.class.new
|
72
|
-
foo.register_worker('testing')
|
73
|
-
|
74
|
-
assert_raises SecurityError do
|
75
|
-
@worker.process
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|