arya-pandemic 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/pandemic.rb +1 -0
- data/lib/pandemic/connection_pool.rb +1 -1
- data/lib/pandemic/server_side/client.rb +11 -10
- data/lib/pandemic/server_side/peer.rb +24 -24
- data/lib/pandemic/server_side/request.rb +8 -2
- data/lib/pandemic/server_side/server.rb +14 -14
- data/pandemic.gemspec +2 -2
- metadata +2 -2
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('pandemic', '0.5.
|
5
|
+
Echoe.new('pandemic', '0.5.1') do |p|
|
6
6
|
p.description = "A framework for distributing work for real-time services and offline tasks."
|
7
7
|
p.url = "https://github.com/arya/pandemic/"
|
8
8
|
p.author = "Arya Asemanfar"
|
data/lib/pandemic.rb
CHANGED
@@ -30,6 +30,7 @@ require 'pandemic/client_side/pandemize'
|
|
30
30
|
TCP_NO_DELAY_AVAILABLE =
|
31
31
|
RUBY_VERSION < '1.9' ? Socket.constants.include?('TCP_NODELAY') : Socket.constants.include?(:TCP_NODELAY)
|
32
32
|
|
33
|
+
MONITOR_TIMEOUT_AVAILABLE = (RUBY_VERSION < '1.9')
|
33
34
|
def epidemic!(options = {})
|
34
35
|
if $pandemic_logger.nil?
|
35
36
|
$pandemic_logger = Logger.new(options[:log_file] || "pandemic.log")
|
@@ -12,7 +12,7 @@ module Pandemic
|
|
12
12
|
@max_connections = options[:max_connections] || 10
|
13
13
|
@min_connections = options[:min_connections] || 1
|
14
14
|
@connect_at_define = options.include?(:connect_at_define) ? options[:connect_at_define] : true
|
15
|
-
@timeout = options[:timeout] || 3
|
15
|
+
@timeout = MONITOR_TIMEOUT_AVAILABLE ? options[:timeout] || 3 : nil
|
16
16
|
end
|
17
17
|
|
18
18
|
def add_connection!
|
@@ -2,6 +2,7 @@ module Pandemic
|
|
2
2
|
module ServerSide
|
3
3
|
class Client
|
4
4
|
REQUEST_FLAGS = {:async => 'a'}
|
5
|
+
EMPTY_STRING = ""
|
5
6
|
REQUEST_REGEXP = /^([0-9]+)(?: ([#{REQUEST_FLAGS.values.join('')}]*))?$/
|
6
7
|
class DisconnectClient < Exception; end
|
7
8
|
include Util
|
@@ -22,21 +23,21 @@ module Pandemic
|
|
22
23
|
@listener_thread = Thread.new do
|
23
24
|
begin
|
24
25
|
while @server.running
|
25
|
-
debug("Waiting for incoming request")
|
26
|
+
# debug("Waiting for incoming request")
|
26
27
|
request = @connection.gets
|
27
|
-
info("Received incoming request")
|
28
|
+
# info("Received incoming request")
|
28
29
|
@received_requests += 1
|
29
30
|
|
30
31
|
if request.nil?
|
31
|
-
debug("Incoming request is nil")
|
32
|
+
# debug("Incoming request is nil")
|
32
33
|
@connection.close
|
33
34
|
@connection = nil
|
34
35
|
break
|
35
36
|
elsif request.strip! =~ REQUEST_REGEXP
|
36
|
-
size, flags = $1.to_i, $2.to_s.split(
|
37
|
-
debug("Reading request body (size #{size})")
|
37
|
+
size, flags = $1.to_i, $2.to_s.split(EMPTY_STRING)
|
38
|
+
# debug("Reading request body (size #{size})")
|
38
39
|
body = @connection.read(size)
|
39
|
-
debug("Finished reading request body")
|
40
|
+
# debug("Finished reading request body")
|
40
41
|
if flags.include?(REQUEST_FLAGS[:async])
|
41
42
|
Thread.new do
|
42
43
|
handle_request(body)
|
@@ -45,18 +46,18 @@ module Pandemic
|
|
45
46
|
else
|
46
47
|
response = handle_request(body)
|
47
48
|
if response
|
48
|
-
debug("Writing response to client")
|
49
|
+
# debug("Writing response to client")
|
49
50
|
|
50
51
|
# the connection could be closed, we'll let it be rescued if it is.
|
51
52
|
@connection.write("#{response.size}\n#{response}")
|
52
53
|
@connection.flush
|
53
|
-
debug("Finished writing response to client")
|
54
|
+
# debug("Finished writing response to client")
|
54
55
|
else
|
55
|
-
debug("Writing error code to client")
|
56
|
+
# debug("Writing error code to client")
|
56
57
|
|
57
58
|
@connection.write("-1\n")
|
58
59
|
@connection.flush
|
59
|
-
debug("Finished writing error code to client")
|
60
|
+
# debug("Finished writing error code to client")
|
60
61
|
end
|
61
62
|
@responded_requests.inc
|
62
63
|
end
|
@@ -29,18 +29,18 @@ module Pandemic
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def client_request(request, body)
|
32
|
-
debug("Sending client's request to peer")
|
33
|
-
debug("Connection pool has #{@connection_pool.available_count} of #{@connection_pool.connections_count} connections available")
|
32
|
+
# debug("Sending client's request to peer")
|
33
|
+
# debug("Connection pool has #{@connection_pool.available_count} of #{@connection_pool.connections_count} connections available")
|
34
34
|
# TODO: Consider adding back threads here if it will be faster that way in Ruby 1.9
|
35
35
|
@connection_pool.with_connection do |connection|
|
36
36
|
if connection && !connection.closed?
|
37
37
|
@pending_requests.synchronize do
|
38
38
|
@pending_requests[request.hash] = request
|
39
39
|
end
|
40
|
-
debug("Writing client's request")
|
40
|
+
# debug("Writing client's request")
|
41
41
|
connection.write("PROCESS #{request.hash} #{body.size}\n#{body}")
|
42
42
|
connection.flush
|
43
|
-
debug("Finished writing client's request")
|
43
|
+
# debug("Finished writing client's request")
|
44
44
|
end # TODO: else? fail silently? reconnect?
|
45
45
|
end
|
46
46
|
end
|
@@ -55,15 +55,15 @@ module Pandemic
|
|
55
55
|
begin
|
56
56
|
debug("Incoming connection thread started")
|
57
57
|
while @server.running
|
58
|
-
debug("Listening for incoming requests")
|
58
|
+
# debug("Listening for incoming requests")
|
59
59
|
request = connection.gets
|
60
|
-
debug("Read incoming request from peer")
|
60
|
+
# debug("Read incoming request from peer")
|
61
61
|
|
62
62
|
if request.nil?
|
63
|
-
debug("Incoming connection request is nil")
|
63
|
+
# debug("Incoming connection request is nil")
|
64
64
|
break
|
65
65
|
else
|
66
|
-
debug("Received incoming (#{request.strip})")
|
66
|
+
# debug("Received incoming (#{request.strip})")
|
67
67
|
handle_incoming_request(request, connection) if request =~ /^PROCESS/
|
68
68
|
handle_incoming_response(request, connection) if request =~ /^RESPONSE/
|
69
69
|
end
|
@@ -114,23 +114,23 @@ module Pandemic
|
|
114
114
|
end
|
115
115
|
|
116
116
|
def handle_incoming_request(request, connection)
|
117
|
-
debug("Identified as request")
|
117
|
+
# debug("Identified as request")
|
118
118
|
if request.strip =~ /^PROCESS ([A-Za-z0-9]+) ([0-9]+)$/
|
119
119
|
hash = $1
|
120
120
|
size = $2.to_i
|
121
|
-
debug("Incoming request: #{hash} #{size}")
|
121
|
+
# debug("Incoming request: #{hash} #{size}")
|
122
122
|
begin
|
123
|
-
debug("Reading request body")
|
123
|
+
# debug("Reading request body")
|
124
124
|
request_body = connection.read(size)
|
125
|
-
debug("Finished reading request body")
|
125
|
+
# debug("Finished reading request body")
|
126
126
|
rescue EOFError, TruncatedDataError
|
127
|
-
debug("Failed to read request body")
|
127
|
+
# debug("Failed to read request body")
|
128
128
|
# TODO: what to do here?
|
129
129
|
return false
|
130
130
|
rescue Exception => e
|
131
131
|
warn("Unhandled exception in incoming request read:\n#{e.inspect}\n#{e.backtrace.join("\n")}")
|
132
132
|
end
|
133
|
-
debug("Processing body")
|
133
|
+
# debug("Processing body")
|
134
134
|
process_request(hash, request_body)
|
135
135
|
else
|
136
136
|
warn("Malformed incoming request: #{request.strip}")
|
@@ -143,13 +143,13 @@ module Pandemic
|
|
143
143
|
if response.strip =~ /^RESPONSE ([A-Za-z0-9]+) ([0-9]+)$/
|
144
144
|
hash = $1
|
145
145
|
size = $2.to_i
|
146
|
-
debug("Incoming response: #{hash} #{size}")
|
146
|
+
# debug("Incoming response: #{hash} #{size}")
|
147
147
|
begin
|
148
|
-
debug("Reading response body")
|
148
|
+
# debug("Reading response body")
|
149
149
|
response_body = connection.read(size)
|
150
|
-
debug("Finished reading response body")
|
150
|
+
# debug("Finished reading response body")
|
151
151
|
rescue EOFError, TruncatedDataError
|
152
|
-
debug("Failed to read response body")
|
152
|
+
# debug("Failed to read response body")
|
153
153
|
# TODO: what to do here?
|
154
154
|
return false
|
155
155
|
rescue Exception => e
|
@@ -167,14 +167,14 @@ module Pandemic
|
|
167
167
|
def process_request(hash, body)
|
168
168
|
Thread.new do
|
169
169
|
begin
|
170
|
-
debug("Starting processing thread (#{hash})")
|
170
|
+
# debug("Starting processing thread (#{hash})")
|
171
171
|
response = @server.process(body)
|
172
|
-
debug("Processing finished (#{hash})")
|
172
|
+
# debug("Processing finished (#{hash})")
|
173
173
|
@connection_pool.with_connection do |connection|
|
174
|
-
debug( "Sending response (#{hash})")
|
174
|
+
# debug( "Sending response (#{hash})")
|
175
175
|
connection.write("RESPONSE #{hash} #{response.size}\n#{response}")
|
176
176
|
connection.flush
|
177
|
-
debug( "Finished sending response (#{hash})")
|
177
|
+
# debug( "Finished sending response (#{hash})")
|
178
178
|
end
|
179
179
|
rescue Exception => e
|
180
180
|
warn("Unhandled exception in process request thread:\n#{e.inspect}\n#{e.backtrace.join("\n")}")
|
@@ -185,10 +185,10 @@ module Pandemic
|
|
185
185
|
def process_response(hash, body)
|
186
186
|
Thread.new do
|
187
187
|
begin
|
188
|
-
debug("Finding original request (#{hash})")
|
188
|
+
# debug("Finding original request (#{hash})")
|
189
189
|
original_request = @pending_requests.synchronize { @pending_requests.delete(hash) }
|
190
190
|
if original_request
|
191
|
-
debug("Found original request, adding response")
|
191
|
+
# debug("Found original request, adding response")
|
192
192
|
original_request.add_response(body)
|
193
193
|
else
|
194
194
|
warn("Original response not found (#{hash})")
|
@@ -32,10 +32,10 @@ module Pandemic
|
|
32
32
|
@@late_responses.inc
|
33
33
|
return
|
34
34
|
end
|
35
|
-
debug("Adding response")
|
35
|
+
# debug("Adding response")
|
36
36
|
@responses << response
|
37
37
|
if @max_responses && @responses.size >= @max_responses
|
38
|
-
debug("Hit max responses, waking up waiting thread")
|
38
|
+
# debug("Hit max responses, waking up waiting thread")
|
39
39
|
wakeup_waiting_thread
|
40
40
|
@complete = true
|
41
41
|
end
|
@@ -62,6 +62,12 @@ module Pandemic
|
|
62
62
|
return if @complete
|
63
63
|
if Config.response_timeout <= 0
|
64
64
|
@waiter.wait
|
65
|
+
elsif !MONITOR_TIMEOUT_AVAILABLE
|
66
|
+
Thread.new do
|
67
|
+
sleep Config.response_timeout
|
68
|
+
wakeup_waiting_thread
|
69
|
+
end
|
70
|
+
@waiter.wait
|
65
71
|
else
|
66
72
|
@waiter.wait(Config.response_timeout)
|
67
73
|
end
|
@@ -57,14 +57,14 @@ module Pandemic
|
|
57
57
|
@running = true
|
58
58
|
@running_since = Time.now
|
59
59
|
|
60
|
-
debug("Connecting to peers")
|
60
|
+
# debug("Connecting to peers")
|
61
61
|
@peers.values.each { |peer| peer.connect }
|
62
62
|
|
63
63
|
@listener_thread = Thread.new do
|
64
64
|
begin
|
65
65
|
while @running
|
66
66
|
begin
|
67
|
-
debug("Listening")
|
67
|
+
# debug("Listening")
|
68
68
|
conn = @listener.accept
|
69
69
|
Thread.new(conn) { |c| handle_connection(c) }
|
70
70
|
rescue Errno::ECONNABORTED, Errno::EINTR # TODO: what else can wrong here? this should be more robust.
|
@@ -95,15 +95,15 @@ module Pandemic
|
|
95
95
|
connection.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if TCP_NO_DELAY_AVAILABLE
|
96
96
|
|
97
97
|
identification = connection.gets.strip
|
98
|
-
info("Incoming connection from #{connection.peeraddr.values_at(3,1).join(":")} (#{identification})")
|
98
|
+
# info("Incoming connection from #{connection.peeraddr.values_at(3,1).join(":")} (#{identification})")
|
99
99
|
if identification =~ /^SERVER ([a-zA-Z0-9.]+:[0-9]+)$/
|
100
|
-
debug("Recognized as peer")
|
100
|
+
# debug("Recognized as peer")
|
101
101
|
host, port = host_port($1)
|
102
102
|
matching_peer = @peers.values.detect { |peer| [peer.host, peer.port] == [host, port] }
|
103
103
|
if matching_peer
|
104
|
-
debug("Found matching peer")
|
104
|
+
# debug("Found matching peer")
|
105
105
|
else
|
106
|
-
debug("Didn't find matching peer, adding it")
|
106
|
+
# debug("Didn't find matching peer, adding it")
|
107
107
|
matching_peer = @peers.synchronize do
|
108
108
|
hostport = "#{host}:#{port}"
|
109
109
|
@servers.push(hostport) unless @servers.include?(hostport)
|
@@ -112,13 +112,13 @@ module Pandemic
|
|
112
112
|
end
|
113
113
|
matching_peer.add_incoming_connection(connection)
|
114
114
|
elsif identification =~ /^CLIENT$/
|
115
|
-
debug("Recognized as client")
|
115
|
+
# debug("Recognized as client")
|
116
116
|
@clients_mutex.synchronize do
|
117
117
|
@clients << Client.new(connection, self).listen
|
118
118
|
@total_clients += 1
|
119
119
|
end
|
120
120
|
elsif identification =~ /^stats$/
|
121
|
-
debug("Stats request received")
|
121
|
+
# debug("Stats request received")
|
122
122
|
print_stats(connection)
|
123
123
|
else
|
124
124
|
debug("Unrecognized connection. Closing.")
|
@@ -130,10 +130,10 @@ module Pandemic
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def handle_client_request(request)
|
133
|
-
info("Handling client request")
|
133
|
+
# info("Handling client request")
|
134
134
|
map = @handler_instance.partition(request, connection_statuses)
|
135
135
|
request.max_responses = map.size
|
136
|
-
debug("Sending client request to #{map.size} handlers (#{request.hash})")
|
136
|
+
# debug("Sending client request to #{map.size} handlers (#{request.hash})")
|
137
137
|
|
138
138
|
map.each do |peer, body|
|
139
139
|
if @peers[peer]
|
@@ -142,7 +142,7 @@ module Pandemic
|
|
142
142
|
end
|
143
143
|
|
144
144
|
if map[signature]
|
145
|
-
debug("Processing #{request.hash}")
|
145
|
+
# debug("Processing #{request.hash}")
|
146
146
|
Thread.new do
|
147
147
|
begin
|
148
148
|
request.add_response(self.process(map[signature]))
|
@@ -154,10 +154,10 @@ module Pandemic
|
|
154
154
|
|
155
155
|
@requests_per_second.hit
|
156
156
|
|
157
|
-
debug("Waiting for responses")
|
157
|
+
# debug("Waiting for responses")
|
158
158
|
request.wait_for_responses
|
159
159
|
|
160
|
-
debug("Done waiting for responses, calling reduce")
|
160
|
+
# debug("Done waiting for responses, calling reduce")
|
161
161
|
@handler_instance.reduce(request)
|
162
162
|
end
|
163
163
|
|
@@ -173,7 +173,7 @@ module Pandemic
|
|
173
173
|
end
|
174
174
|
|
175
175
|
def signature
|
176
|
-
"#{@host}:#{@port}"
|
176
|
+
@signature ||= "#{@host}:#{@port}"
|
177
177
|
end
|
178
178
|
|
179
179
|
def connection_statuses
|
data/pandemic.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{pandemic}
|
5
|
-
s.version = "0.5.
|
5
|
+
s.version = "0.5.1"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Arya Asemanfar"]
|
9
|
-
s.date = %q{2009-08-
|
9
|
+
s.date = %q{2009-08-09}
|
10
10
|
s.description = %q{A framework for distributing work for real-time services and offline tasks.}
|
11
11
|
s.email = %q{aryaasemanfar@gmail.com}
|
12
12
|
s.extra_rdoc_files = ["lib/pandemic/client_side/cluster_connection.rb", "lib/pandemic/client_side/config.rb", "lib/pandemic/client_side/connection.rb", "lib/pandemic/client_side/connection_proxy.rb", "lib/pandemic/client_side/pandemize.rb", "lib/pandemic/connection_pool.rb", "lib/pandemic/mutex_counter.rb", "lib/pandemic/requests_per_second.rb", "lib/pandemic/server_side/client.rb", "lib/pandemic/server_side/config.rb", "lib/pandemic/server_side/handler.rb", "lib/pandemic/server_side/peer.rb", "lib/pandemic/server_side/processor.rb", "lib/pandemic/server_side/request.rb", "lib/pandemic/server_side/server.rb", "lib/pandemic/util.rb", "lib/pandemic.rb", "README.markdown"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arya-pandemic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arya Asemanfar
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-08-
|
12
|
+
date: 2009-08-09 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|