infopark-politics 0.2.9 → 0.2.10
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/politics/static_queue_worker.rb +46 -31
- data/spec/static_queue_worker_spec.rb +71 -2
- metadata +2 -2
@@ -57,39 +57,32 @@ module Politics
|
|
57
57
|
# Note: process_bucket never returns i.e. this should be the main loop of your processing daemon.
|
58
58
|
#
|
59
59
|
module StaticQueueWorker
|
60
|
-
|
61
|
-
def self.included(model) #:nodoc:
|
62
|
-
model.class_eval do
|
63
|
-
attr_accessor :group_name, :iteration_length, :uri
|
64
|
-
end
|
65
|
-
end
|
60
|
+
attr_reader :group_name, :iteration_length, :dictatorship_length, :uri, :buckets
|
66
61
|
|
67
62
|
# Register this process as able to work on buckets.
|
68
|
-
def register_worker(name, bucket_count, config={})
|
63
|
+
def register_worker(name, bucket_count, config = {})
|
69
64
|
options = {
|
70
|
-
:iteration_length =>
|
65
|
+
:iteration_length => 10,
|
71
66
|
:servers => ['127.0.0.1:11211']
|
72
|
-
}
|
73
|
-
options.merge!(config)
|
67
|
+
}.merge(config)
|
74
68
|
|
75
|
-
|
76
|
-
|
69
|
+
@group_name = name
|
70
|
+
@iteration_length = options[:iteration_length]
|
77
71
|
@memcache_client = client_for(Array(options[:servers]))
|
78
72
|
@nominated_at = Time.now
|
79
73
|
# FIXME: Tests
|
80
74
|
@domain = options[:domain]
|
75
|
+
@dictatorship_length = options[:dictatorship_length]
|
81
76
|
|
82
77
|
@buckets = []
|
83
78
|
@bucket_count = bucket_count
|
84
79
|
|
85
80
|
register_with_bonjour
|
86
|
-
log.progname =
|
81
|
+
log.progname = uri
|
87
82
|
log.info { "Registered in group #{group_name} at port #{@port}" }
|
88
83
|
end
|
89
84
|
|
90
85
|
# Fetch a bucket out of the queue and pass it to the given block to be processed.
|
91
|
-
#
|
92
|
-
# +bucket+:: The bucket number to process, within the range 0...TOTAL_BUCKETS
|
93
86
|
def process_bucket(&block)
|
94
87
|
log.debug "start bucket processing"
|
95
88
|
raise ArgumentError, "process_bucket requires a block!" unless block_given?
|
@@ -104,20 +97,8 @@ module Politics
|
|
104
97
|
begin
|
105
98
|
nominate
|
106
99
|
if leader?
|
107
|
-
# Drb thread handles leader duties
|
108
100
|
log.info { "has been elected leader" }
|
109
|
-
|
110
|
-
# keeping leader state as long as buckets are available by renominating before
|
111
|
-
# nomination times out
|
112
|
-
while !@buckets.empty? do
|
113
|
-
log.debug { "relaxes half the time until next iteration" }
|
114
|
-
relax(until_next_iteration / 2)
|
115
|
-
log.debug { "renew nomination too keep the hat and finish the work" }
|
116
|
-
@memcache_client.set(token, @uri, iteration_length)
|
117
|
-
@nominated_at = Time.now
|
118
|
-
log.error { "tried to staying leader but failed" } unless leader?
|
119
|
-
end
|
120
|
-
relax until_next_iteration
|
101
|
+
perform_leader_duties
|
121
102
|
else
|
122
103
|
# Get a bucket from the leader and process it
|
123
104
|
begin
|
@@ -135,6 +116,37 @@ module Politics
|
|
135
116
|
end while loop?
|
136
117
|
end
|
137
118
|
|
119
|
+
def as_dictator(&block)
|
120
|
+
duration = dictatorship_length || (iteration_length * 10)
|
121
|
+
log.debug { "become dictator for up to #{duration} seconds" }
|
122
|
+
seize_leadership duration
|
123
|
+
yield
|
124
|
+
raise "lost leadership while being dictator for #{duration} seconds" unless leader?
|
125
|
+
seize_leadership
|
126
|
+
end
|
127
|
+
|
128
|
+
def seize_leadership(duration = nil)
|
129
|
+
@memcache_client.set(token, uri, duration || iteration_length)
|
130
|
+
@nominated_at = Time.now
|
131
|
+
end
|
132
|
+
|
133
|
+
def perform_leader_duties
|
134
|
+
# Drb thread handles requests to leader
|
135
|
+
as_dictator do
|
136
|
+
initialize_buckets
|
137
|
+
end
|
138
|
+
# keeping leader state as long as buckets are available by renominating before nomination
|
139
|
+
# times out
|
140
|
+
while !buckets.empty? do
|
141
|
+
log.debug { "relaxes half the time until next iteration" }
|
142
|
+
relax(until_next_iteration / 2)
|
143
|
+
as_dictator do
|
144
|
+
update_buckets
|
145
|
+
end
|
146
|
+
end
|
147
|
+
relax until_next_iteration
|
148
|
+
end
|
149
|
+
|
138
150
|
def bucket_request(requestor_uri)
|
139
151
|
if leader?
|
140
152
|
log.debug "delivering bucket request"
|
@@ -186,6 +198,9 @@ module Politics
|
|
186
198
|
@bucket_count.times { |idx| @buckets << idx }
|
187
199
|
end
|
188
200
|
|
201
|
+
def update_buckets
|
202
|
+
end
|
203
|
+
|
189
204
|
def replicas
|
190
205
|
@replicas ||= []
|
191
206
|
end
|
@@ -226,7 +241,7 @@ module Politics
|
|
226
241
|
def nominate
|
227
242
|
log.debug("try to nominate")
|
228
243
|
@nominated_at = Time.now
|
229
|
-
@memcache_client.add(token,
|
244
|
+
@memcache_client.add(token, uri, iteration_length)
|
230
245
|
@leader_uri = nil
|
231
246
|
end
|
232
247
|
|
@@ -237,7 +252,7 @@ module Politics
|
|
237
252
|
# Check to see if we are leader by looking at the process name
|
238
253
|
# associated with the token.
|
239
254
|
def leader?
|
240
|
-
until_next_iteration > 0 &&
|
255
|
+
until_next_iteration > 0 && uri == leader_uri
|
241
256
|
end
|
242
257
|
|
243
258
|
# Easy to mock or monkey-patch if another MemCache client is preferred.
|
@@ -258,7 +273,7 @@ module Politics
|
|
258
273
|
@port = URI.parse(DRb.uri).port
|
259
274
|
|
260
275
|
# Register our DRb server with Bonjour.
|
261
|
-
name = "#{
|
276
|
+
name = "#{group_name}-#{local_ip}-#{$$}"
|
262
277
|
type = "_#{group_name}._tcp"
|
263
278
|
domain = "local"
|
264
279
|
log.debug "register service #{name} of type #{type} within domain #{domain} at port #{@port}"
|
@@ -4,6 +4,8 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
4
4
|
require File.dirname(__FILE__) + '/../lib/init'
|
5
5
|
Politics::log.level = Logger::FATAL
|
6
6
|
|
7
|
+
@@memcache_client = nil
|
8
|
+
|
7
9
|
class Worker
|
8
10
|
include Politics::StaticQueueWorker
|
9
11
|
def initialize
|
@@ -16,10 +18,15 @@ class Worker
|
|
16
18
|
sleep 0.1
|
17
19
|
end
|
18
20
|
end
|
21
|
+
|
22
|
+
def client_for(servers)
|
23
|
+
@@memcache_client
|
24
|
+
end
|
19
25
|
end
|
20
26
|
|
21
27
|
describe Worker do
|
22
28
|
before do
|
29
|
+
@@memcache_client = mock('memcache', :set => nil, :get => nil)
|
23
30
|
@worker = Worker.new
|
24
31
|
end
|
25
32
|
|
@@ -40,7 +47,7 @@ describe Worker do
|
|
40
47
|
@worker.should be_alive
|
41
48
|
end
|
42
49
|
|
43
|
-
describe
|
50
|
+
describe "when processing bucket" do
|
44
51
|
before do
|
45
52
|
DRbObject.stub!(:new).with(nil, @worker.uri).
|
46
53
|
and_return(@worker_drb = mock('drb', :alive? => true))
|
@@ -74,6 +81,17 @@ describe Worker do
|
|
74
81
|
@worker.start
|
75
82
|
end
|
76
83
|
|
84
|
+
describe "as leader" do
|
85
|
+
before do
|
86
|
+
@worker.stub!(:leader?).and_return true
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should do leader duties" do
|
90
|
+
@worker.should_receive(:perform_leader_duties).exactly(4).times
|
91
|
+
@worker.start
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
77
95
|
describe "as follower" do
|
78
96
|
before do
|
79
97
|
@worker.stub!(:leader?).and_return false
|
@@ -90,7 +108,7 @@ describe Worker do
|
|
90
108
|
end
|
91
109
|
end
|
92
110
|
|
93
|
-
describe
|
111
|
+
describe "when handling a bucket request" do
|
94
112
|
describe "as leader" do
|
95
113
|
before do
|
96
114
|
@worker.stub!(:leader?).and_return true
|
@@ -112,4 +130,55 @@ describe Worker do
|
|
112
130
|
end
|
113
131
|
end
|
114
132
|
end
|
133
|
+
|
134
|
+
describe "when performing leader duties" do
|
135
|
+
before do
|
136
|
+
@worker.stub!(:until_next_iteration).and_return 0
|
137
|
+
@worker.stub!(:leader?).and_return true
|
138
|
+
@worker.stub!(:dictatorship_length).and_return 666
|
139
|
+
@worker.stub!(:iteration_length).and_return 5
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should initialize buckets as dictator" do
|
143
|
+
@worker.should_receive(:seize_leadership).with(666).ordered
|
144
|
+
@worker.should_receive(:initialize_buckets).ordered
|
145
|
+
@worker.should_receive(:seize_leadership).ordered
|
146
|
+
@worker.perform_leader_duties
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "as long as there are buckets" do
|
150
|
+
before do
|
151
|
+
@worker.stub!(:buckets).and_return([1], [2], [3], [4], [])
|
152
|
+
@worker.stub!(:relax)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should update buckets periodically" do
|
156
|
+
@worker.should_receive(:update_buckets).exactly(4).times
|
157
|
+
@worker.perform_leader_duties
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should relax half of the time to the next iteration" do
|
161
|
+
@worker.stub!(:until_next_iteration).and_return(6)
|
162
|
+
@worker.should_receive(:relax).with(3).exactly(4).times
|
163
|
+
@worker.perform_leader_duties
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should seize the leadership periodically" do
|
167
|
+
@worker.should_receive(:seize_leadership).at_least(4).times
|
168
|
+
@worker.perform_leader_duties
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "if there are no more buckets" do
|
173
|
+
before do
|
174
|
+
@worker.stub!(:buckets).and_return([])
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should relax until next iteration" do
|
178
|
+
@worker.stub!(:until_next_iteration).and_return(6)
|
179
|
+
@worker.should_receive(:relax).with(6).once
|
180
|
+
@worker.perform_leader_duties
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
115
184
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: infopark-politics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Perham
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-
|
13
|
+
date: 2009-06-29 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|