refinery 0.12.2 → 1.0
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/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
|