hot_potato 0.12.1 → 0.12.2
Sign up to get free protection for your applications and to get access to all the features.
- data/hot_potato.gemspec +6 -6
- data/lib/hot_potato/app_task.rb +7 -5
- data/lib/hot_potato/supervisor_server.rb +7 -6
- data/lib/hot_potato/version.rb +1 -1
- data/readme.md +113 -87
- data/test/helper.rb +6 -2
- data/test/utils_test.rb +23 -0
- metadata +15 -13
data/hot_potato.gemspec
CHANGED
@@ -12,12 +12,12 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{A Real-time Processing Framework}
|
13
13
|
s.description = %q{Hot Potato is an open source real-time processing framework written in Ruby. Originally designed to process the Twitter firehose at 3,000+ tweets per second, it has been extended to support any type of streaming data as input or output to the framework. The framework excels with applications such as, social media analysis, log processing, fraud prevention, spam detection, instant messaging, and many others that include the processing of streaming data.}
|
14
14
|
|
15
|
-
s.add_dependency 'json'
|
16
|
-
s.add_dependency 'redis'
|
17
|
-
s.add_dependency 'bunny'
|
18
|
-
s.add_dependency 'sinatra'
|
19
|
-
s.add_dependency "vegas"
|
20
|
-
s.add_dependency "snappy"
|
15
|
+
s.add_dependency 'json', "~> 1.5.3"
|
16
|
+
s.add_dependency 'redis', "~> 2.2.1"
|
17
|
+
s.add_dependency 'bunny', "~> 0.6.0"
|
18
|
+
s.add_dependency 'sinatra', "~> 1.2.6"
|
19
|
+
s.add_dependency "vegas", "~> 0.1.8"
|
20
|
+
s.add_dependency "snappy", "~> 0.0.2"
|
21
21
|
|
22
22
|
s.rubyforge_project = "hot_potato"
|
23
23
|
|
data/lib/hot_potato/app_task.rb
CHANGED
@@ -6,32 +6,34 @@ module HotPotato
|
|
6
6
|
# starting AppTasks. There are three types: Faucets, Workers, and Sinks.
|
7
7
|
module AppTask
|
8
8
|
|
9
|
-
HEARTBEAT_INTERVAL
|
9
|
+
HEARTBEAT_INTERVAL = 20
|
10
|
+
HEARTBEAT_EXPIRE = 30
|
11
|
+
MESSAGE_COUNT_EXPIRE = 600
|
10
12
|
|
11
13
|
include HotPotato::Core
|
12
14
|
|
13
15
|
# Used to keep AppTask statistics when a message is received.
|
14
16
|
def count_message_in
|
15
17
|
stat.incr "hotpotato.counter.apptask.#{Socket.gethostname}.#{self.class.name}.#{Process.pid}.messages_in"
|
16
|
-
stat.expire "hotpotato.counter.apptask.#{Socket.gethostname}.#{self.class.name}.#{Process.pid}.messages_in",
|
18
|
+
stat.expire "hotpotato.counter.apptask.#{Socket.gethostname}.#{self.class.name}.#{Process.pid}.messages_in", MESSAGE_COUNT_EXPIRE
|
17
19
|
end
|
18
20
|
|
19
21
|
# Used to keep AppTask statistics when a message is sent.
|
20
22
|
def count_message_out
|
21
23
|
stat.incr "hotpotato.counter.apptask.#{Socket.gethostname}.#{self.class.name}.#{Process.pid}.messages_out"
|
22
|
-
stat.expire "hotpotato.counter.apptask.#{Socket.gethostname}.#{self.class.name}.#{Process.pid}.messages_out",
|
24
|
+
stat.expire "hotpotato.counter.apptask.#{Socket.gethostname}.#{self.class.name}.#{Process.pid}.messages_out", MESSAGE_COUNT_EXPIRE
|
23
25
|
end
|
24
26
|
|
25
27
|
# Starts the HeartBeat service to maintain the process id table.
|
26
28
|
def start_heartbeat_service
|
27
29
|
ati = AppTaskInfo.new(:classname => self.class.name)
|
28
|
-
stat.set ati.key, ati.to_json,
|
30
|
+
stat.set ati.key, ati.to_json, HEARTBEAT_EXPIRE
|
29
31
|
|
30
32
|
Thread.new do
|
31
33
|
log.info "Thread created for AppTask [Heartbeat]"
|
32
34
|
loop do
|
33
35
|
ati.touch
|
34
|
-
stat.set ati.key, ati.to_json,
|
36
|
+
stat.set ati.key, ati.to_json, HEARTBEAT_EXPIRE
|
35
37
|
sleep HEARTBEAT_INTERVAL
|
36
38
|
end
|
37
39
|
end
|
@@ -209,14 +209,15 @@ module HotPotato
|
|
209
209
|
options.hostname = Socket.gethostname
|
210
210
|
options.running = true
|
211
211
|
|
212
|
-
config["servers"]
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
212
|
+
if config["servers"]
|
213
|
+
config["servers"].each do |server|
|
214
|
+
if server["hostname"] == options.hostname
|
215
|
+
options.max_app_tasks = server["max_app_tasks"] || MAX_APP_TASKS
|
216
|
+
options.group = server["group"] || ""
|
217
|
+
break
|
218
|
+
end
|
217
219
|
end
|
218
220
|
end
|
219
|
-
|
220
221
|
return options
|
221
222
|
end
|
222
223
|
|
data/lib/hot_potato/version.rb
CHANGED
data/readme.md
CHANGED
@@ -17,35 +17,41 @@ Hot Potato is an open source real-time processing framework written in Ruby. Ori
|
|
17
17
|
|
18
18
|
Start by downloading the gem (this requires Ruby 1.9):
|
19
19
|
|
20
|
-
|
20
|
+
```bash
|
21
|
+
$ gem install hotpotato
|
22
|
+
```
|
21
23
|
|
22
24
|
Next create a project:
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
26
|
+
```bash
|
27
|
+
$ hotpotato sample
|
28
|
+
Hot Potato (v0.12.1)
|
29
|
+
Generating application sample...
|
30
|
+
create sample
|
31
|
+
add sample/Gemfile
|
32
|
+
add sample/Rakefile
|
33
|
+
create sample/app
|
34
|
+
create sample/bin
|
35
|
+
add sample/bin/admin
|
36
|
+
add sample/bin/app_task
|
37
|
+
add sample/bin/supervisor
|
38
|
+
create sample/config
|
39
|
+
add sample/config/boot.rb
|
40
|
+
add sample/config/config.yml
|
41
|
+
add sample/config/routes.rb
|
42
|
+
create sample/docs
|
43
|
+
create sample/logs
|
44
|
+
create sample/test
|
45
|
+
create sample/tmp
|
46
|
+
```
|
43
47
|
|
44
48
|
## Generating AppTasks
|
45
49
|
|
46
50
|
To help with creating AppTasks, there is a generator available:
|
47
51
|
|
48
|
-
|
52
|
+
```bash
|
53
|
+
$ bin/generate [faucet|worker|sink] name
|
54
|
+
```
|
49
55
|
|
50
56
|
# The Details
|
51
57
|
|
@@ -66,18 +72,20 @@ Each faucet is a ruby file in the app directory that extends HotPotato::Faucet a
|
|
66
72
|
the perform method. For each message received, the method should call the send_message to send
|
67
73
|
it to the next AppTask.
|
68
74
|
|
69
|
-
|
75
|
+
```ruby
|
76
|
+
class TwitterFaucet < HotPotato::Faucet
|
70
77
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
def perform
|
79
|
+
TweetStream::Client.new("user", "secret").sample do |s|
|
80
|
+
message = {}
|
81
|
+
message["username"] = s.user.screen_name
|
82
|
+
message["text"] = s.text
|
83
|
+
send_message message
|
84
|
+
end
|
85
|
+
end
|
79
86
|
|
80
|
-
|
87
|
+
end
|
88
|
+
```
|
81
89
|
|
82
90
|
### Workers
|
83
91
|
|
@@ -86,14 +94,16 @@ Data, and Filter Data. Each worker is a ruby file in the app directory that ext
|
|
86
94
|
and implements the perform(message) method. For each message the worker wants to send to the next AppTask,
|
87
95
|
the send_message method should be called.
|
88
96
|
|
89
|
-
|
90
|
-
|
91
|
-
def perform(message)
|
92
|
-
message["influence"] = rand(100)
|
93
|
-
send_message message
|
94
|
-
end
|
97
|
+
```ruby
|
98
|
+
class Influencer < HotPotato::Worker
|
95
99
|
|
96
|
-
|
100
|
+
def perform(message)
|
101
|
+
message["influence"] = rand(100)
|
102
|
+
send_message message
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
```
|
97
107
|
|
98
108
|
### Sinks
|
99
109
|
|
@@ -102,13 +112,15 @@ File Writer. Each sink is a ruby file in the app directory that extends HotPota
|
|
102
112
|
the perform(message) method. There is no send_message for the sink to call since it is a final destination
|
103
113
|
for the message.
|
104
114
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
115
|
+
```ruby
|
116
|
+
class LogWriter < HotPotato::Sink
|
117
|
+
|
118
|
+
def perform(message)
|
119
|
+
log.debug "#{message["username"]}:#{message["influence"]}"
|
120
|
+
end
|
110
121
|
|
111
|
-
|
122
|
+
end
|
123
|
+
```
|
112
124
|
|
113
125
|
## Supervisor
|
114
126
|
|
@@ -126,7 +138,9 @@ The supervisor also starts the Heartbeat service and logging service as backgrou
|
|
126
138
|
|
127
139
|
The supervisor can be managed from the command line:
|
128
140
|
|
129
|
-
|
141
|
+
```bash
|
142
|
+
$ bin/supervisor [run|start|stop|restart]
|
143
|
+
```
|
130
144
|
|
131
145
|
If started without any settings, it will default to run.
|
132
146
|
|
@@ -136,30 +150,32 @@ The admin server is a Sinatra-based application to display statistical and diagn
|
|
136
150
|
|
137
151
|
The admin server can be managed from the command line:
|
138
152
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
153
|
+
```
|
154
|
+
$ bin/admin --help
|
155
|
+
|
156
|
+
Usage: ./admin [options]
|
157
|
+
|
158
|
+
Vegas options:
|
159
|
+
-K, --kill kill the running process and exit
|
160
|
+
-S, --status display the current running PID and URL then quit
|
161
|
+
-s, --server SERVER serve using SERVER (thin/mongrel/webrick)
|
162
|
+
-o, --host HOST listen on HOST (default: 0.0.0.0)
|
163
|
+
-p, --port PORT use PORT (default: 5678)
|
164
|
+
-x, --no-proxy ignore env proxy settings (e.g. http_proxy)
|
165
|
+
-e, --env ENVIRONMENT use ENVIRONMENT for defaults (default: development)
|
166
|
+
-F, --foreground don't daemonize, run in the foreground
|
167
|
+
-L, --no-launch don't launch the browser
|
168
|
+
-d, --debug raise the log level to :debug (default: :info)
|
169
|
+
--app-dir APP_DIR set the app dir where files are stored (default: ~/.vegas/Hot_Potato_Admin_Server)/)
|
170
|
+
-P, --pid-file PID_FILE set the path to the pid file (default: app_dir/Hot_Potato_Admin_Server.pid)
|
171
|
+
--log-file LOG_FILE set the path to the log file (default: app_dir/Hot_Potato_Admin_Server.log)
|
172
|
+
--url-file URL_FILE set the path to the URL file (default: app_dir/Hot_Potato_Admin_Server.url)
|
173
|
+
|
174
|
+
Common options:
|
175
|
+
-h, --help Show this message
|
176
|
+
--version Show version
|
177
|
+
```
|
178
|
+
|
163
179
|
The page can be accessed at http://localhost:5678
|
164
180
|
|
165
181
|
## Routes
|
@@ -173,39 +189,49 @@ The routes file (config/routes.rb) is a Ruby DSL that does the following:
|
|
173
189
|
|
174
190
|
Example:
|
175
191
|
|
176
|
-
|
192
|
+
```ruby
|
193
|
+
HotPotato::Route.build do
|
177
194
|
|
178
|
-
|
179
|
-
|
180
|
-
|
195
|
+
faucet :twitter_faucet
|
196
|
+
worker :influencer, :source => :twitter_faucet
|
197
|
+
sink :log_writer, :source => :influencer
|
181
198
|
|
182
|
-
|
199
|
+
end
|
200
|
+
```
|
183
201
|
|
184
202
|
Multiple sources can be attached to a worker or sink:
|
185
203
|
|
186
|
-
|
204
|
+
```ruby
|
205
|
+
worker :influencer, :source => [:twitter_faucet. :other_source]
|
206
|
+
```
|
187
207
|
|
188
208
|
The number of instances is set to 1. This can be changed by setting the number of instances:
|
189
209
|
|
190
|
-
|
210
|
+
```ruby
|
211
|
+
worker :influencer, :source => :twitter_faucet, :instances => 2
|
212
|
+
```
|
191
213
|
|
192
214
|
AppTasks can be limited to a specific server (or set of servers) by creating a group in the
|
193
215
|
config/config.yml file:
|
194
216
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
217
|
+
```yaml
|
218
|
+
development:
|
219
|
+
redis_hostname: localhost
|
220
|
+
redis_port: 6379
|
221
|
+
servers:
|
222
|
+
- hostname: worker01
|
223
|
+
group: incoming
|
224
|
+
max_app_tasks: 15
|
225
|
+
- hostname: worker02
|
226
|
+
group: worker
|
227
|
+
max_app_tasks: 15
|
228
|
+
```
|
205
229
|
|
206
230
|
and specifying the group in the routes files:
|
207
231
|
|
208
|
-
|
232
|
+
```ruby
|
233
|
+
faucet :twitter_faucet, :group => :incoming
|
234
|
+
```
|
209
235
|
|
210
236
|
# Support
|
211
237
|
|
data/test/helper.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup(:default, :test)
|
4
|
+
Bundler.require(:default, :test)
|
5
|
+
|
1
6
|
require 'test/unit'
|
2
|
-
$LOAD_PATH << File.dirname(__FILE__) + '/../lib/'
|
3
7
|
|
8
|
+
$LOAD_PATH << File.dirname(__FILE__) + '/../lib/'
|
4
9
|
APP_PATH ||= File.expand_path('..', __FILE__)
|
5
10
|
RACK_ENV ||= 'test'
|
6
11
|
|
7
|
-
require 'hot_potato'
|
data/test/utils_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class UtilsTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_process_alive
|
6
|
+
pid = Process.pid
|
7
|
+
assert Process.alive?(pid)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_clasify
|
11
|
+
assert_equal "MyTask", classify("my_task")
|
12
|
+
assert_equal "Mytask", classify("mytask")
|
13
|
+
assert_equal "MyTask", classify("MyTask")
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_underscore
|
18
|
+
assert_equal "my_task", underscore("MyTask")
|
19
|
+
assert_equal "mytask", underscore("Mytask")
|
20
|
+
assert_equal "my_task", underscore("my_task")
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: hot_potato
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.12.
|
5
|
+
version: 0.12.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Darian Shimy
|
@@ -19,9 +19,9 @@ dependencies:
|
|
19
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
|
-
- -
|
22
|
+
- - ~>
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version:
|
24
|
+
version: 1.5.3
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -30,9 +30,9 @@ dependencies:
|
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ~>
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
35
|
+
version: 2.2.1
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
38
|
- !ruby/object:Gem::Dependency
|
@@ -41,9 +41,9 @@ dependencies:
|
|
41
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
43
43
|
requirements:
|
44
|
-
- -
|
44
|
+
- - ~>
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
46
|
+
version: 0.6.0
|
47
47
|
type: :runtime
|
48
48
|
version_requirements: *id003
|
49
49
|
- !ruby/object:Gem::Dependency
|
@@ -52,9 +52,9 @@ dependencies:
|
|
52
52
|
requirement: &id004 !ruby/object:Gem::Requirement
|
53
53
|
none: false
|
54
54
|
requirements:
|
55
|
-
- -
|
55
|
+
- - ~>
|
56
56
|
- !ruby/object:Gem::Version
|
57
|
-
version:
|
57
|
+
version: 1.2.6
|
58
58
|
type: :runtime
|
59
59
|
version_requirements: *id004
|
60
60
|
- !ruby/object:Gem::Dependency
|
@@ -63,9 +63,9 @@ dependencies:
|
|
63
63
|
requirement: &id005 !ruby/object:Gem::Requirement
|
64
64
|
none: false
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.1.8
|
69
69
|
type: :runtime
|
70
70
|
version_requirements: *id005
|
71
71
|
- !ruby/object:Gem::Dependency
|
@@ -74,9 +74,9 @@ dependencies:
|
|
74
74
|
requirement: &id006 !ruby/object:Gem::Requirement
|
75
75
|
none: false
|
76
76
|
requirements:
|
77
|
-
- -
|
77
|
+
- - ~>
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version:
|
79
|
+
version: 0.0.2
|
80
80
|
type: :runtime
|
81
81
|
version_requirements: *id006
|
82
82
|
description: Hot Potato is an open source real-time processing framework written in Ruby. Originally designed to process the Twitter firehose at 3,000+ tweets per second, it has been extended to support any type of streaming data as input or output to the framework. The framework excels with applications such as, social media analysis, log processing, fraud prevention, spam detection, instant messaging, and many others that include the processing of streaming data.
|
@@ -132,6 +132,7 @@ files:
|
|
132
132
|
- lib/hot_potato/worker.rb
|
133
133
|
- readme.md
|
134
134
|
- test/helper.rb
|
135
|
+
- test/utils_test.rb
|
135
136
|
- test/version_test.rb
|
136
137
|
has_rdoc: true
|
137
138
|
homepage: http://github.com/dshimy/hotpotato
|
@@ -163,4 +164,5 @@ specification_version: 3
|
|
163
164
|
summary: A Real-time Processing Framework
|
164
165
|
test_files:
|
165
166
|
- test/helper.rb
|
167
|
+
- test/utils_test.rb
|
166
168
|
- test/version_test.rb
|