infopark-politics 0.3.1 → 0.3.2pg55d4620

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ pkg
2
+ Gemfile.lock
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 1.8.7-p374
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Manifest ADDED
@@ -0,0 +1,17 @@
1
+ examples/queue_worker_example.rb
2
+ examples/token_worker_example.rb
3
+ History.rdoc
4
+ lib/init.rb
5
+ lib/politics/discoverable_node.rb
6
+ lib/politics/static_queue_worker.rb
7
+ lib/politics/token_worker.rb
8
+ lib/politics/version.rb
9
+ lib/politics.rb
10
+ LICENSE
11
+ Manifest
12
+ politics.gemspec
13
+ Rakefile
14
+ README.rdoc
15
+ test/static_queue_worker_test.rb
16
+ test/test_helper.rb
17
+ test/token_worker_test.rb
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ require 'rdoc/task'
3
+ require 'rake/testtask'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubygems/tasks'
6
+
7
+ Gem::Tasks.new
8
+
9
+ desc "Run tests"
10
+ Rake::TestTask.new do |t|
11
+ t.libs << 'test' << 'lib'
12
+ t.test_files = FileList['test/*_test.rb']
13
+ end
14
+
15
+ desc "Run specs"
16
+ RSpec::Core::RakeTask.new
17
+
18
+ desc "Create rdoc"
19
+ Rake::RDocTask.new do |rd|
20
+ rd.main = "README.rdoc"
21
+ rd.rdoc_files.include("README.rdoc", "History.rdoc", "lib/**/*.rb")
22
+ end
23
+
24
+
25
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.2
data/dcc_config.rb ADDED
@@ -0,0 +1,5 @@
1
+ send_notifications_to "tilo@infopark.de"
2
+
3
+ buckets "test" do
4
+ bucket(:specs).performs_rake_tasks("spec")
5
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  #gem 'mperham-politics'
2
3
  require 'politics'
3
4
  require 'politics/static_queue_worker'
@@ -21,14 +22,14 @@ module Politics
21
22
  def initialize
22
23
  register_worker 'queue-example', TOTAL_BUCKETS, :iteration_length => 60, :servers => memcached_servers
23
24
  end
24
-
25
+
25
26
  def start
26
27
  process_bucket do |bucket|
27
28
  puts "PID #{$$} processing bucket #{bucket}/#{TOTAL_BUCKETS} at #{Time.now}..."
28
29
  sleep 1.5
29
30
  end
30
31
  end
31
-
32
+
32
33
  def memcached_servers
33
34
  ['127.0.0.1:11211']
34
35
  end
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  #gem 'mperham-politics'
2
3
  require 'politics'
3
4
  require 'politics/token_worker'
@@ -16,17 +17,17 @@ require 'politics/token_worker'
16
17
  module Politics
17
18
  class TokenWorkerExample
18
19
  include Politics::TokenWorker
19
-
20
+
20
21
  def initialize
21
22
  register_worker 'token-example', :iteration_length => 10, :servers => memcached_servers
22
23
  end
23
-
24
+
24
25
  def start
25
26
  process do
26
27
  puts "PID #{$$} processing at #{Time.now}..."
27
28
  end
28
29
  end
29
-
30
+
30
31
  def memcached_servers
31
32
  ['localhost:11211']
32
33
  end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+ requirement = Gem::Requirement.new(">= 1.6.2")
3
+ unless requirement.satisfied_by?(Gem::Version.new(Gem::VERSION))
4
+ raise "You need at RubyGems in Version #{requirement} to build this gem."
5
+ end
6
+
7
+ version = `cd #{File.dirname(__FILE__)} && git describe --tags`.strip.gsub(/^v/, "").gsub(/-.*-/, "p")
8
+
9
+ Gem::Specification.new do |gem|
10
+ gem.name = "infopark-politics"
11
+ gem.version = version
12
+ gem.summary = "Algorithms and Tools for Distributed Computing in Ruby."
13
+ gem.description = ""
14
+ gem.authors = ["Mike Perham", "Tilo Prütz"]
15
+ gem.email = "tilo@infopark.de"
16
+ gem.homepage = "http://github.com/infopark/politics"
17
+
18
+ gem.required_rubygems_version = Gem::Requirement.new(">= 1.8")
19
+ gem.extra_rdoc_files = [
20
+ "History.rdoc",
21
+ "LICENSE",
22
+ "README.rdoc"
23
+ ]
24
+ gem.files = `git ls-files`.split("\n")
25
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ gem.require_paths = ["lib"]
28
+
29
+ gem.add_development_dependency "rspec", ">= 2.14"
30
+ gem.add_development_dependency "rake", ">= 10"
31
+ gem.add_development_dependency "rdoc", ">= 3.9"
32
+ gem.add_development_dependency "rubygems-tasks", ">=0.2"
33
+
34
+ gem.add_runtime_dependency "memcache-client", ">= 1.5.0"
35
+ gem.add_runtime_dependency "starling-starling", ">= 0.9.8"
36
+ gem.add_runtime_dependency "net-mdns", ">= 0.4"
37
+ end
38
+
data/lib/init.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'politics'
2
3
  require 'politics/token_worker'
3
4
  require 'politics/static_queue_worker'
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'socket'
2
3
  require 'ipaddr'
3
4
  require 'uri'
@@ -34,10 +35,10 @@ module Politics
34
35
  # replica set. The algorithm is robust in the face of crash failures, but not
35
36
  # Byzantine failures.
36
37
  module DiscoverableNode
37
-
38
+
38
39
  attr_accessor :group
39
40
  attr_accessor :coordinator
40
-
41
+
41
42
  def register(group='foo')
42
43
  self.group = group
43
44
  start_drb
@@ -46,11 +47,11 @@ module Politics
46
47
  sleep 0.5
47
48
  find_replicas(0)
48
49
  end
49
-
50
+
50
51
  def replicas
51
52
  @replicas ||= {}
52
53
  end
53
-
54
+
54
55
  def find_replicas(count)
55
56
  replicas.clear if count % 5 == 0
56
57
  return if count > 10 # Guaranteed to terminate, but not successfully :-(
@@ -76,14 +77,14 @@ module Politics
76
77
  Politics::log.info { "Found #{replicas.size} peers: #{replicas.keys.sort.inspect}" } if count == 0
77
78
  replicas
78
79
  end
79
-
80
+
80
81
  # Called for one peer to introduce itself to another peer. The caller
81
82
  # sends his RID, the responder sends his RID and his list of current peer
82
83
  # RIDs.
83
84
  def hello(remote_rid)
84
85
  [rid, replicas.keys]
85
86
  end
86
-
87
+
87
88
  # A process's Replica ID is its PID + a random 16-bit value. We don't want
88
89
  # weigh solely based on PID or IP as that may unduly load one machine.
89
90
  def rid
@@ -93,25 +94,25 @@ module Politics
93
94
  end
94
95
 
95
96
  private
96
-
97
+
97
98
  def register_with_bonjour(group)
98
99
  # Register our DRb server with Bonjour.
99
- handle = Net::DNS::MDNSSD.register("#{self.group}-#{local_ip}-#{$$}",
100
+ handle = Net::DNS::MDNSSD.register("#{self.group}-#{local_ip}-#{$$}",
100
101
  "_#{self.group}._tcp", 'local', @port)
101
-
102
- ['INT', 'TERM'].each { |signal|
102
+
103
+ ['INT', 'TERM'].each { |signal|
103
104
  trap(signal) { handle.stop }
104
105
  }
105
106
  end
106
-
107
+
107
108
  def start_drb
108
109
  server = DRb.start_service(nil, self)
109
110
  @port = URI.parse(DRb.uri).port
110
- ['INT', 'TERM'].each { |signal|
111
+ ['INT', 'TERM'].each { |signal|
111
112
  trap(signal) { server.stop_service }
112
113
  }
113
114
  end
114
-
115
+
115
116
  def bonjour_scan
116
117
  Net::DNS::MDNSSD.browse("_#{@group}._tcp") do |b|
117
118
  Net::DNS::MDNSSD.resolve(b.name, b.type) do |r|
@@ -121,17 +122,17 @@ module Politics
121
122
  end
122
123
  end
123
124
  end
124
-
125
+
125
126
  # http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
126
127
  def local_ip
127
128
  orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
128
-
129
+
129
130
  UDPSocket.open do |s|
130
131
  s.connect '64.233.187.99', 1
131
132
  IPAddr.new(s.addr.last).to_i
132
133
  end
133
134
  ensure
134
135
  Socket.do_not_reverse_lookup = orig
135
- end
136
+ end
136
137
  end
137
- end
138
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  require 'socket'
2
3
  require 'ipaddr'
3
4
  require 'uri'
@@ -222,6 +223,10 @@ module Politics
222
223
  workers
223
224
  end
224
225
 
226
+ def hostname
227
+ nil
228
+ end
229
+
225
230
  private
226
231
 
227
232
  def restart_wanted?
@@ -328,7 +333,7 @@ module Politics
328
333
  end
329
334
 
330
335
  def register_with_bonjour
331
- server = DRb.start_service(nil, self)
336
+ server = DRb.start_service("druby://#{hostname || ""}:0", self)
332
337
  @uri = DRb.uri
333
338
  @port = URI.parse(DRb.uri).port
334
339
 
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  begin
2
3
  require 'memcache'
3
4
  rescue LoadError => e
@@ -13,8 +14,8 @@ module Politics
13
14
  # The worker instance must obtain the leader token before performing some task.
14
15
  # We use a memcached server as a central token authority to provide a shared,
15
16
  # network-wide view for all processors. This reliance on a single resource means
16
- # if your memcached server goes down, so do the processors. Oftentimes,
17
- # this is an acceptable trade-off since many high-traffic web sites would
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
18
19
  # not be useable without memcached running anyhow.
19
20
  #
20
21
  # Essentially each TokenWorker attempts to elect itself every +:iteration_length+
@@ -46,7 +47,7 @@ module Politics
46
47
  # This can often times be quite beneficial (e.g. leveraging a warm cache from the last iteration)
47
48
  # for performance but is left to the reader to implement.
48
49
  module TokenWorker
49
-
50
+
50
51
  def self.included(model) #:nodoc:
51
52
  model.class_eval do
52
53
  attr_accessor :memcache_client, :token, :iteration_length, :worker_name
@@ -59,7 +60,7 @@ module Politics
59
60
  # Register this instance as a worker.
60
61
  #
61
62
  # Options:
62
- # +:iteration_length+:: The length of a processing iteration, in seconds. The
63
+ # +:iteration_length+:: The length of a processing iteration, in seconds. The
63
64
  # leader's 'reign' lasts for this length of time.
64
65
  # +:servers+:: An array of memcached server strings
65
66
  def register_worker(name, config={})
@@ -77,7 +78,7 @@ module Politics
77
78
 
78
79
  cleanup
79
80
  end
80
-
81
+
81
82
  def process(*args, &block)
82
83
  verify_registration
83
84
 
@@ -103,18 +104,18 @@ module Politics
103
104
  Politics::log.error(me.backtrace.join("\n"))
104
105
  self.memcache_client.reset
105
106
  end
106
-
107
+
107
108
  pause_until_expiry(time)
108
109
  reset_state
109
110
  end while loop?
110
111
  end
111
112
 
112
113
  private
113
-
114
+
114
115
  def reset_state
115
116
  @leader = nil
116
117
  end
117
-
118
+
118
119
  def verify_registration
119
120
  unless self.class.worker_instance
120
121
  raise ArgumentError, "Cannot call process without first calling register_worker"
@@ -123,30 +124,30 @@ module Politics
123
124
  raise SecurityError, "Only one instance of #{self.class} per process. Another instance was created after this one."
124
125
  end
125
126
  end
126
-
127
+
127
128
  def loop?
128
129
  true
129
130
  end
130
-
131
+
131
132
  def cleanup
132
133
  at_exit do
133
134
  memcache_client.delete(token) if leader?
134
135
  end
135
136
  end
136
-
137
+
137
138
  def pause_until_expiry(elapsed)
138
139
  pause_time = (iteration_length - elapsed).to_f
139
140
  if pause_time > 0
140
- relax(pause_time)
141
+ relax(pause_time)
141
142
  else
142
143
  raise ArgumentError, "Negative iteration time left. Assuming the worst and exiting... #{iteration_length}/#{elapsed}"
143
144
  end
144
145
  end
145
-
146
+
146
147
  def relax(time)
147
148
  sleep time
148
149
  end
149
-
150
+
150
151
  # Nominate ourself as leader by contacting the memcached server
151
152
  # and attempting to add the token with our name attached.
152
153
  # The result will tell us if memcached stored our value and therefore
data/lib/politics.rb CHANGED
@@ -1,9 +1,10 @@
1
+ # encoding: utf-8
1
2
  module Politics
2
3
 
3
4
  def self.log=(value)
4
5
  @log = log
5
6
  end
6
-
7
+
7
8
  def self.log
8
9
  @log ||= if defined?(RAILS_DEFAULT_LOGGER)
9
10
  RAILS_DEFAULT_LOGGER
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end