refinery 0.12.2 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/refinery.rb +1 -99
- data/refinery.gemspec +16 -117
- metadata +39 -118
- data/.gitignore +0 -6
- data/CHANGELOG +0 -2
- data/LICENSE +0 -21
- data/README.rdoc +0 -58
- data/README.textile +0 -58
- data/Rakefile +0 -43
- data/VERSION +0 -1
- data/bin/epub +0 -64
- data/bin/monitor +0 -47
- data/bin/pubnow +0 -61
- data/bin/refinery +0 -64
- data/config/config.example.yml +0 -21
- data/lib/refinery/beanstalk_queue.rb +0 -36
- data/lib/refinery/beanstalk_queue_provider.rb +0 -18
- data/lib/refinery/config.rb +0 -48
- data/lib/refinery/configurable.rb +0 -15
- data/lib/refinery/daemon.rb +0 -148
- data/lib/refinery/event_publisher.rb +0 -131
- data/lib/refinery/heartbeat.rb +0 -33
- data/lib/refinery/loggable.rb +0 -9
- data/lib/refinery/monitor.rb +0 -113
- data/lib/refinery/processor.rb +0 -55
- data/lib/refinery/publisher.rb +0 -42
- data/lib/refinery/queueable.rb +0 -48
- data/lib/refinery/server.rb +0 -88
- data/lib/refinery/statistics.rb +0 -61
- data/lib/refinery/stats_server.rb +0 -135
- data/lib/refinery/utilities.rb +0 -33
- data/lib/refinery/validations.rb +0 -48
- data/lib/refinery/worker.rb +0 -65
- data/logs/README +0 -1
- data/publishers/error.rb +0 -6
- data/publishers/sample.rb +0 -6
- data/publishers/sleep.rb +0 -5
- data/test/config.yml +0 -10
- data/test/test_helper.rb +0 -21
- data/test/unit/config_test.rb +0 -42
- data/test/unit/configurable_test.rb +0 -13
- data/test/unit/daemon_test.rb +0 -63
- data/test/unit/event_publisher_test.rb +0 -12
- data/test/unit/heartbeat_test.rb +0 -25
- data/test/unit/loggable_test.rb +0 -12
- data/test/unit/processor_test.rb +0 -34
- data/test/unit/publisher_test.rb +0 -13
- data/test/unit/queueable_test.rb +0 -26
- data/test/unit/server_test.rb +0 -34
- data/test/unit/statistics_test.rb +0 -44
- data/test/unit/utilities_test.rb +0 -25
- data/test/unit/validations_test.rb +0 -37
- data/test/unit/worker_test.rb +0 -44
- data/workers/error.rb +0 -8
- data/workers/sample.rb +0 -8
- data/workers/sleep.rb +0 -7
data/lib/refinery/processor.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# This class is used to monitor all of the threads for a single
|
3
|
-
# processor.
|
4
|
-
class Processor < Thread
|
5
|
-
include Refinery::Configurable
|
6
|
-
include Refinery::Loggable
|
7
|
-
|
8
|
-
attr_reader :server
|
9
|
-
attr_reader :key
|
10
|
-
attr_reader :settings
|
11
|
-
attr_reader :daemons
|
12
|
-
|
13
|
-
# Initialize the processor.
|
14
|
-
def initialize(server, key, settings={})
|
15
|
-
@server = server
|
16
|
-
@key = key
|
17
|
-
@settings = settings
|
18
|
-
@daemons = []
|
19
|
-
super do
|
20
|
-
execute
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
# Execute the processor
|
26
|
-
def execute
|
27
|
-
queue_prefix = config['prefix'] || ''
|
28
|
-
|
29
|
-
if defined?(java.lang.Thread)
|
30
|
-
java.lang.Thread.current_thread.name = "#{key} Processor"
|
31
|
-
end
|
32
|
-
|
33
|
-
logger.debug "Creating daemons for #{key}"
|
34
|
-
1.upto(settings['workers']['initial']) do
|
35
|
-
daemons << Daemon.new(self, key, queue_prefix, settings)
|
36
|
-
end
|
37
|
-
|
38
|
-
logger.debug "Running #{daemons.length} daemons"
|
39
|
-
|
40
|
-
wait = ThreadsWait.new(*daemons)
|
41
|
-
wait.all_waits do |daemon|
|
42
|
-
logger.debug "a #{daemon.name} just died"
|
43
|
-
daemons.delete(daemon)
|
44
|
-
logger.debug "waiting for 60 seconds before starting a new #{key} daemon"
|
45
|
-
sleep(60)
|
46
|
-
logger.debug "starting a new #{key} daemon"
|
47
|
-
daemon = Daemon.new(self, key, queue_prefix, settings)
|
48
|
-
daemons << daemon
|
49
|
-
wait.join(daemon)
|
50
|
-
end
|
51
|
-
|
52
|
-
logger.debug "Processor #{key} is exiting"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
data/lib/refinery/publisher.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# Base class for publishers to be implemented by subclasses.
|
3
|
-
class Publisher
|
4
|
-
include Refinery::Loggable
|
5
|
-
include Refinery::Queueable
|
6
|
-
|
7
|
-
# Initialize the publisher with the queue to publish messages to.
|
8
|
-
def initialize(waiting_queue_name)
|
9
|
-
@waiting_queue_name = waiting_queue_name
|
10
|
-
end
|
11
|
-
|
12
|
-
protected
|
13
|
-
# Get the publish queue name
|
14
|
-
def waiting_queue_name
|
15
|
-
@waiting_queue_name
|
16
|
-
end
|
17
|
-
|
18
|
-
# Publish the message. The message will be converted to JSON and pushed
|
19
|
-
# into the queue associated with the publisher.
|
20
|
-
def publish(message)
|
21
|
-
with_queue(waiting_queue_name) do |waiting_queue|
|
22
|
-
publish_to_queue(waiting_queue, message)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# Publish the given message if the waiting queue is empty. The message will
|
27
|
-
# be converted to JSON and pushed into the queue associated with the
|
28
|
-
# publisher.
|
29
|
-
def publish_if_empty(message)
|
30
|
-
with_queue(waiting_queue_name) do |waiting_queue|
|
31
|
-
publish_to_queue(waiting_queue, message) if waiting_queue.size == 0
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Publish the given message to the queue. The message will be converted
|
36
|
-
# to JSON and pushed into the queue associated with the publisher.
|
37
|
-
def publish_to_queue(queue, message)
|
38
|
-
logger.debug "Publisher #{self.class.name} sending message: #{message.to_json}"
|
39
|
-
queue.send_message(Base64.encode64(message.to_json))
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
data/lib/refinery/queueable.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# Mix this module in to classes that want to access a queue.
|
3
|
-
module Queueable
|
4
|
-
include Loggable
|
5
|
-
include Configurable
|
6
|
-
# Get a named queue
|
7
|
-
def queue(name)
|
8
|
-
queue_provider.queue(name)
|
9
|
-
end
|
10
|
-
|
11
|
-
# Given the queue name and a block, yield the named queue into
|
12
|
-
# the block.
|
13
|
-
def with_queue(name, &block)
|
14
|
-
begin
|
15
|
-
yield queue(name)
|
16
|
-
rescue Exception => e
|
17
|
-
logger.error "An error occurred when communicating with queue #{name}: #{e.message}"
|
18
|
-
sleep(30)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
protected
|
23
|
-
# Get the queue provider. Defaults to RightAws::SqsGen2 running
|
24
|
-
# in multi-thread mode.
|
25
|
-
def queue_provider
|
26
|
-
if queue_engine = config['queue_engine']
|
27
|
-
if queue_engine['provider'] == 'beanstalk' && defined?(Beanstalk)
|
28
|
-
@queue_provider ||= Refinery::BeanstalkQueueProvider.new
|
29
|
-
else
|
30
|
-
raise RuntimeError, "Unknown queue provider: #{queue_engine['provider']}"
|
31
|
-
end
|
32
|
-
else
|
33
|
-
if defined?(Typica)
|
34
|
-
@queue_provider ||= Typica::Sqs::QueueService.new(
|
35
|
-
config['aws']['credentials']["access_key_id"],
|
36
|
-
config['aws']['credentials']["secret_access_key"]
|
37
|
-
)
|
38
|
-
else
|
39
|
-
@queue_provider ||= RightAws::SqsGen2.new(
|
40
|
-
config['aws']['credentials']["access_key_id"],
|
41
|
-
config['aws']['credentials']["secret_access_key"],
|
42
|
-
{:multi_thread => true}
|
43
|
-
)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/refinery/server.rb
DELETED
@@ -1,88 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# The server instance provides a runtime environment for daemons.
|
3
|
-
# To start the server create an Refinery::Server instance and invoke run.
|
4
|
-
class Server
|
5
|
-
include Refinery::Loggable
|
6
|
-
include Refinery::Configurable
|
7
|
-
include Refinery::Queueable
|
8
|
-
include Refinery::Utilities
|
9
|
-
|
10
|
-
# The directory where worker source files are stored. Defaults to
|
11
|
-
# ./workers
|
12
|
-
attr_accessor :workers_directory
|
13
|
-
|
14
|
-
# Get a server-wide logger
|
15
|
-
def self.logger
|
16
|
-
@logger ||= begin
|
17
|
-
logger = Logger.new(STDOUT)
|
18
|
-
logger.level = Logger::WARN
|
19
|
-
logger.formatter = CustomFormatter.new
|
20
|
-
logger
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# Initialize the server.
|
25
|
-
#
|
26
|
-
# Options:
|
27
|
-
# * <tt>:config</tt>: Provide a file path to load that config
|
28
|
-
# * <tt>:debug</tt>: Set to true to enable debug logging
|
29
|
-
# * <tt>:verbose</tt>: Set to true to enable info logging
|
30
|
-
# * <tt>:workers</tt>: The workers directory
|
31
|
-
def initialize(options={})
|
32
|
-
logger.level = Logger::INFO if options[:verbose]
|
33
|
-
logger.level = Logger::DEBUG if options[:debug]
|
34
|
-
config.load_file(options[:config]) if options[:config]
|
35
|
-
self.workers_directory = options[:workers] if options[:workers]
|
36
|
-
end
|
37
|
-
|
38
|
-
# The directory where workers are found. Defaults to ./workers
|
39
|
-
def workers_directory
|
40
|
-
@workers_directory ||= "./workers"
|
41
|
-
end
|
42
|
-
|
43
|
-
# Stop the server
|
44
|
-
def stop
|
45
|
-
logger.info "Stopping Refinery Server"
|
46
|
-
daemons.each { |daemon| daemon.stop }
|
47
|
-
end
|
48
|
-
|
49
|
-
# An array of all daemons
|
50
|
-
def daemons
|
51
|
-
@daemons ||= []
|
52
|
-
end
|
53
|
-
|
54
|
-
# Run the server
|
55
|
-
def run
|
56
|
-
logger.info "Starting Refinery server"
|
57
|
-
execute_processors
|
58
|
-
logger.info "Server is exiting"
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
def execute_processors
|
63
|
-
|
64
|
-
@processors = config['processors'].map do |key, settings|
|
65
|
-
Processor.new(self, key, settings)
|
66
|
-
end
|
67
|
-
|
68
|
-
Heartbeat.new(self)
|
69
|
-
|
70
|
-
begin
|
71
|
-
@processors.each { |p| p.join }
|
72
|
-
rescue Interrupt => e
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
class CustomFormatter
|
78
|
-
def format
|
79
|
-
@format ||= "%s, [%s#%d][%s] %5s -- %s: %s\n"
|
80
|
-
end
|
81
|
-
def call(severity, time, progname, msg)
|
82
|
-
format % [severity[0..0], format_datetime(time.utc), $$, Thread.current.object_id.to_s, severity, progname, msg.to_s]
|
83
|
-
end
|
84
|
-
def format_datetime(time)
|
85
|
-
time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
data/lib/refinery/statistics.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# The statistics class provides a means to record runtime stats
|
3
|
-
# about completed jobs and errors. The stats are stored in a SQL
|
4
|
-
# database (using SQLite3 by default).
|
5
|
-
class Statistics
|
6
|
-
include Refinery::Loggable
|
7
|
-
|
8
|
-
# Record the done record into the
|
9
|
-
def record_done(message)
|
10
|
-
db[:completed_jobs] << {
|
11
|
-
:host => message['host_info']['hostname'],
|
12
|
-
:pid => message['host_info']['pid'],
|
13
|
-
:run_time => message['run_time'],
|
14
|
-
:original_message => message['original'],
|
15
|
-
:when => Time.now
|
16
|
-
}
|
17
|
-
end
|
18
|
-
|
19
|
-
# Record the error message into the statistics database.
|
20
|
-
def record_error(message)
|
21
|
-
db[:errors] << {
|
22
|
-
:host => message['host_info']['hostname'],
|
23
|
-
:pid => message['host_info']['pid'],
|
24
|
-
:error_class => message['error']['class'],
|
25
|
-
:error_message => message['error']['message'],
|
26
|
-
:original_message => message['original'],
|
27
|
-
:when => Time.now
|
28
|
-
}
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
# Get a Sequel connection to the stats database
|
33
|
-
def db
|
34
|
-
@db ||= begin
|
35
|
-
db = Sequel.connect('sqlite://stats.db')
|
36
|
-
unless db.table_exists?(:completed_jobs)
|
37
|
-
db.create_table :completed_jobs do
|
38
|
-
primary_key :id
|
39
|
-
column :host, :text
|
40
|
-
column :pid, :integer
|
41
|
-
column :run_time, :float
|
42
|
-
column :original_message, :text
|
43
|
-
column :when, :time
|
44
|
-
end
|
45
|
-
end
|
46
|
-
unless db.table_exists?(:errors)
|
47
|
-
db.create_table :errors do
|
48
|
-
primary_key :id
|
49
|
-
column :host, :text
|
50
|
-
column :pid, :integer
|
51
|
-
column :error_class, :text
|
52
|
-
column :error_message, :text
|
53
|
-
column :original_message, :text
|
54
|
-
column :when, :time
|
55
|
-
end
|
56
|
-
end
|
57
|
-
db
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# The StatsServer class provides a build in web server that provides
|
3
|
-
# a view into the refinery statistics. This functionality is very
|
4
|
-
# experimental.
|
5
|
-
class StatsServer
|
6
|
-
include Refinery::Loggable
|
7
|
-
|
8
|
-
# Run the stats server.
|
9
|
-
def run
|
10
|
-
begin
|
11
|
-
Ramaze::Log.loggers.clear # supress all Ramaze logging
|
12
|
-
Ramaze.start # start the Ramaze server on port 7000
|
13
|
-
rescue NameError
|
14
|
-
self.logger.warn "Install Ramaze to enable the stats server"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
if const_defined?(:Ramaze)
|
19
|
-
class MainController < ::Ramaze::Controller #:nodoc:
|
20
|
-
map '/'
|
21
|
-
|
22
|
-
def index
|
23
|
-
%(
|
24
|
-
<html>
|
25
|
-
<head>
|
26
|
-
<title>Refinery Stats</title>
|
27
|
-
<style>
|
28
|
-
.widget { border: 1px solid #777; margin-bottom: 10px; padding: 4px; }
|
29
|
-
.widget h2 { font-size: 14pt; margin-top: 2px; margin-bottom: 2px; }
|
30
|
-
#left-column { float: left; width: 600px; }
|
31
|
-
#right-column { margin-left: 610px; width: 300px; }
|
32
|
-
table { background-color: #ddd; width: 100%; }
|
33
|
-
table td { background-color: #eee; }
|
34
|
-
table th { background-color: #ccc; }
|
35
|
-
</style>
|
36
|
-
</head>
|
37
|
-
<body>
|
38
|
-
<h1>Refinery Stats</h1>
|
39
|
-
<div id="left-column">
|
40
|
-
<div class="run_time widget">
|
41
|
-
<h2>Runtime Averages</h2>
|
42
|
-
#{avg_run_time}
|
43
|
-
</div>
|
44
|
-
<div class="errors widget">
|
45
|
-
<h2>Last 5 Errors</h2>
|
46
|
-
#{errors_table}
|
47
|
-
</div>
|
48
|
-
<div class="completed widget">
|
49
|
-
<h2>Last 5 Completed Jobs</h2>
|
50
|
-
#{completed_jobs_table}
|
51
|
-
</div>
|
52
|
-
</div>
|
53
|
-
<div id="right-column">
|
54
|
-
<div class="overview widget">
|
55
|
-
<h2>Overview</h2>
|
56
|
-
<div>#{db[:completed_jobs].count} jobs completed</div>
|
57
|
-
<div>#{db[:errors].count} errors</div>
|
58
|
-
</div>
|
59
|
-
|
60
|
-
</div>
|
61
|
-
</body>
|
62
|
-
</html>
|
63
|
-
)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
def db
|
68
|
-
Sequel.connect("sqlite://stats.db")
|
69
|
-
end
|
70
|
-
|
71
|
-
def avg_run_time
|
72
|
-
rows = db[:completed_jobs].group(:host, :pid).select(:host, :pid, :AVG.sql_function(:run_time)).map do |record|
|
73
|
-
%(<tr>
|
74
|
-
<td>#{record[:host]}</td>
|
75
|
-
<td>#{record[:pid]}</td>
|
76
|
-
<td>#{sprintf("%.6f", record[:"AVG(`run_time`)"])}</td>
|
77
|
-
</tr>)
|
78
|
-
end.join
|
79
|
-
%(
|
80
|
-
<table>
|
81
|
-
<tr>
|
82
|
-
<th>Host</th>
|
83
|
-
<th>PID</th>
|
84
|
-
<th>Avg Run Time</th>
|
85
|
-
</tr>
|
86
|
-
#{rows}
|
87
|
-
</table>
|
88
|
-
)
|
89
|
-
end
|
90
|
-
|
91
|
-
def completed_jobs_table
|
92
|
-
jobs_list = db[:completed_jobs].limit(5).map do |record|
|
93
|
-
%Q( <tr>
|
94
|
-
<td>#{record[:host]}</td>
|
95
|
-
<td>#{record[:pid]}</td>
|
96
|
-
<td>#{record[:run_time]}</td>
|
97
|
-
</tr>
|
98
|
-
)
|
99
|
-
end
|
100
|
-
%Q( <table>
|
101
|
-
<tr>
|
102
|
-
<th>Host</th>
|
103
|
-
<th>PID</th>
|
104
|
-
<th>Run Time</th>
|
105
|
-
</tr>
|
106
|
-
#{jobs_list.join}
|
107
|
-
</table>
|
108
|
-
)
|
109
|
-
end
|
110
|
-
|
111
|
-
def errors_table
|
112
|
-
errors = db[:errors].limit(5).map do |record|
|
113
|
-
%(<tr>
|
114
|
-
<td>#{record[:host]}</td>
|
115
|
-
<td>#{record[:pid]}</td>
|
116
|
-
<td>#{record[:error_class]}</td>
|
117
|
-
<td>#{record[:error_message]}</td>
|
118
|
-
</tr>
|
119
|
-
)
|
120
|
-
end
|
121
|
-
%(<table>
|
122
|
-
<tr>
|
123
|
-
<th>Host</th>
|
124
|
-
<th>PID</th>
|
125
|
-
<th>Error Class</th>
|
126
|
-
<th>Error Message</th>
|
127
|
-
</tr>
|
128
|
-
#{errors.join}
|
129
|
-
</table>
|
130
|
-
)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
data/lib/refinery/utilities.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
module Refinery #:nodoc:
|
2
|
-
# Utilities that can be mixed into a class
|
3
|
-
module Utilities
|
4
|
-
# Camelize the given word.
|
5
|
-
def camelize(word, first_letter_in_uppercase = true)
|
6
|
-
if first_letter_in_uppercase
|
7
|
-
word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
8
|
-
else
|
9
|
-
word.first.downcase + camelize(word)[1..-1]
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# Decode the message_body from Base 64 and then parse from JSON.
|
14
|
-
def decode_message(message_body)
|
15
|
-
JSON.parse(Base64.decode64(message_body))
|
16
|
-
end
|
17
|
-
|
18
|
-
# Convert the given message_data object to JSON and then Base 64
|
19
|
-
# encode it
|
20
|
-
def encode_message(message_data)
|
21
|
-
Base64.encode64(message_data.to_json)
|
22
|
-
end
|
23
|
-
|
24
|
-
# Get a Hash of useful host information that can be sent with
|
25
|
-
# messages to the monitoring system.
|
26
|
-
def host_info
|
27
|
-
{
|
28
|
-
'hostname' => Socket.gethostname,
|
29
|
-
'pid' => $$
|
30
|
-
}
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|