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.
@@ -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 => 60,
65
+ :iteration_length => 10,
71
66
  :servers => ['127.0.0.1:11211']
72
- }
73
- options.merge!(config)
67
+ }.merge(config)
74
68
 
75
- self.group_name = name
76
- self.iteration_length = options[:iteration_length]
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 = @uri
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
- initialize_buckets
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, @uri, iteration_length)
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 && @uri == leader_uri
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 = "#{self.group_name}-#{local_ip}-#{$$}"
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 Worker, "when processing bucket" do
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 Worker, "when handling a bucket request" do
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.9
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-05-30 00:00:00 -07:00
13
+ date: 2009-06-29 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency