action_event 0.0.1 → 0.0.2
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/action_event.gemspec +2 -1
- data/generators/event/templates/poller +237 -2
- data/lib/action_event.rb +0 -1
- metadata +1 -3
- data/init.rb +0 -1
- data/lib/action_event/commands/poller.rb +0 -237
data/action_event.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "action_event"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.2"
|
4
4
|
s.date = "2009-10-06"
|
5
5
|
s.summary = "A framework for asynchronous message processing in a Rails application."
|
6
6
|
s.email = "wkonkel@gmail.com"
|
@@ -10,4 +10,5 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.authors = ["Warren Konkel"]
|
11
11
|
s.files = Dir.glob('**/*') - Dir.glob('test/*.rb')
|
12
12
|
s.test_files = Dir.glob('test/*.rb')
|
13
|
+
s.require_paths = ["lib"]
|
13
14
|
end
|
@@ -1,4 +1,239 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require File.dirname(__FILE__) + '/../config/boot'
|
3
|
-
|
4
|
-
require '
|
3
|
+
require 'fileutils'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
module ActionEvent
|
7
|
+
module Commands
|
8
|
+
class Poller
|
9
|
+
def initialize
|
10
|
+
@options = {
|
11
|
+
:id => 1,
|
12
|
+
:queues => %W(high medium low),
|
13
|
+
:command => 'start',
|
14
|
+
:environment => RAILS_ENV,
|
15
|
+
:daemon => false,
|
16
|
+
:max_load_average => 8,
|
17
|
+
:min_instances => 5,
|
18
|
+
:max_instances => 200,
|
19
|
+
:max_adjustment => 5,
|
20
|
+
:min_queue_size => 1000
|
21
|
+
}
|
22
|
+
|
23
|
+
OptionParser.new do |opts|
|
24
|
+
opts.banner = "Usage: #{$0} [options] <command>"
|
25
|
+
opts.on("-d", "--daemon", "Run as a daemon") { |v| @options[:daemon] = v }
|
26
|
+
opts.on("-i", "--id=N", Integer, "Specify ID used in PID file when running as daemon") { |v| @options[:id] = v }
|
27
|
+
opts.on("-q", "--queues='high medium low'", "Specify queue names in order") { |v| @options[:queues] = v.split(' ') }
|
28
|
+
opts.on("-e", "--environment=development", "Specify which rails environment to run in") { |v| @options[:environment] = v }
|
29
|
+
opts.separator ""
|
30
|
+
opts.separator "Cluster options:"
|
31
|
+
opts.on("-l", "--load-average=8", "Specify what load average to optimize to") { |v| @options[:max_load_average] = v }
|
32
|
+
opts.on("-m", "--min-instances=5", "Specify mimimum number of instances") { |v| @options[:min_instances] = v }
|
33
|
+
opts.on("-x", "--max-instances=200", "Specify maximum number of instances") { |v| @options[:max_instances] = v }
|
34
|
+
opts.on("-a", "--max-adjustment=5", "Specify how many the maximum amount of instances that will be adjusted") { |v| @options[:max_adjustment] = v }
|
35
|
+
opts.on("-s", "--min-queue-size=1000", "Specify how many must be in the queue to adjust instances") { |v| @options[:min_queue_size] = v }
|
36
|
+
opts.separator ""
|
37
|
+
opts.separator "Commands:"
|
38
|
+
opts.separator " start - starts up the poller"
|
39
|
+
opts.separator " stop - stops a poller currently running as a daemon"
|
40
|
+
opts.separator " status - prints the status of the queues"
|
41
|
+
opts.separator ""
|
42
|
+
opts.separator "Examples:"
|
43
|
+
opts.separator " #{$0} start (starts a poller running in the console)"
|
44
|
+
opts.separator " #{$0} -d -e production start (starts a poller running as a daemon with ID #1)"
|
45
|
+
opts.separator " #{$0} --daemon --id=5 start (starts poller with ID #5)"
|
46
|
+
opts.separator " #{$0} --daemon --id=5 stop (stops poller with ID #5)"
|
47
|
+
end.parse!
|
48
|
+
|
49
|
+
@options[:command] = ARGV.pop unless ARGV.empty?
|
50
|
+
|
51
|
+
case
|
52
|
+
when @options[:command] == 'start' && !@options[:daemon] then trap_ctrl_c and load_rails_environment and start_processing_loop
|
53
|
+
when @options[:command] == 'start' && @options[:daemon] then trap_term and start_daemon and load_rails_environment and start_processing_loop and remove_pid
|
54
|
+
when @options[:command] == 'stop' then stop_daemon
|
55
|
+
when @options[:command] == 'cluster' then load_rails_environment and refresh_cluster
|
56
|
+
when @options[:command] == 'status' then load_rails_environment and print_status
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def print_status
|
61
|
+
ActionEvent::Message.queue_status(@options[:queues]).to_a.sort { |a,b| a.first <=> b.first }.each do |table,messages_left|
|
62
|
+
log "#{table}:\t\t#{messages_left}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_rails_environment
|
67
|
+
ENV['ACTION_EVENT_USE_POLLER_DB'] = 'true'
|
68
|
+
ENV["RAILS_ENV"] = @options[:environment]
|
69
|
+
RAILS_ENV.replace(@options[:environment])
|
70
|
+
log "Loading #{RAILS_ENV} environment..."
|
71
|
+
require "#{RAILS_ROOT}/config/environment"
|
72
|
+
|
73
|
+
if defined?(NewRelic)
|
74
|
+
NewRelic::Control.instance.instance_eval do
|
75
|
+
@settings['app_name'] = @settings['app_name'] + ' (Poller)'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
# returns the name of the PID file to use for daemons
|
83
|
+
def pid_filename
|
84
|
+
@pid_filename ||= File.join(RAILS_ROOT, "/log/poller.#{@options[:id]}.pid")
|
85
|
+
end
|
86
|
+
|
87
|
+
# forks from the current process and closes out everything
|
88
|
+
def start_daemon
|
89
|
+
log "Starting daemon ##{@options[:id]}..."
|
90
|
+
|
91
|
+
# some process magic
|
92
|
+
exit if fork # Parent exits, child continues.
|
93
|
+
Process.setsid # Become session leader.
|
94
|
+
exit if fork # Zap session leader.
|
95
|
+
Dir.chdir "/" # Release old working directory.
|
96
|
+
File.umask 0000 # Ensure sensible umask. Adjust as needed.
|
97
|
+
|
98
|
+
# Free file descriptors and point them somewhere sensible.
|
99
|
+
STDIN.reopen "/dev/null"
|
100
|
+
STDOUT.reopen File.join(RAILS_ROOT, "log/poller.log"), "a"
|
101
|
+
STDERR.reopen STDOUT
|
102
|
+
|
103
|
+
# don't start up until the previous poller is dead
|
104
|
+
while (previous_pid = File.read(pid_filename).to_i rescue nil) do
|
105
|
+
break unless File.exists?("/proc/#{previous_pid}")
|
106
|
+
log "Waiting for previous poller to finish..."
|
107
|
+
Process.kill('TERM', previous_pid)
|
108
|
+
sleep(5)
|
109
|
+
end
|
110
|
+
|
111
|
+
# record pid
|
112
|
+
File.open(pid_filename, 'w') { |f| f << Process.pid }
|
113
|
+
end
|
114
|
+
|
115
|
+
def trap_ctrl_c
|
116
|
+
trap("SIGINT") do
|
117
|
+
@stop_processing = true
|
118
|
+
log "Sending stop signal..."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def trap_term
|
123
|
+
trap("SIGTERM") do
|
124
|
+
@stop_processing = true
|
125
|
+
log "Received stop signal..."
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def refresh_cluster
|
130
|
+
# gather some current stats
|
131
|
+
current_load = `uptime`.split(' ')[-3..-3][0].to_f
|
132
|
+
current_queue = ActionEvent::Message.queue_status(*@options[:queues]).to_a.map(&:last).sum
|
133
|
+
|
134
|
+
# remove stale pid files
|
135
|
+
current_pids = Dir[File.join(RAILS_ROOT, "log/poller.*.pid")]
|
136
|
+
active_pids, stale_pids = current_pids.partition { |f| (File.read("/proc/#{File.read(f).to_i}/cmdline").include?('poller') rescue false) }
|
137
|
+
stale_pids.each { |f| File.delete(f) }
|
138
|
+
|
139
|
+
# compute adjustment based on current load average and queue size
|
140
|
+
if active_pids.length > 0
|
141
|
+
current_instances = active_pids.length
|
142
|
+
needed_instances = ((current_instances*@options[:max_load_average])/current_load).floor
|
143
|
+
|
144
|
+
if needed_instances > current_instances
|
145
|
+
needed_instances = [needed_instances, current_instances + @options[:max_adjustment]].min
|
146
|
+
elsif needed_instances < current_instances && current_queue > @options[:min_queue_size]
|
147
|
+
needed_instances = [needed_instances, current_instances - @options[:max_adjustment]].max
|
148
|
+
end
|
149
|
+
else
|
150
|
+
current_instances = 0
|
151
|
+
needed_instances = @options[:min_instances]
|
152
|
+
end
|
153
|
+
|
154
|
+
needed_instances = @options[:max_instances] if needed_instances > @options[:max_instances]
|
155
|
+
needed_instances = @options[:min_instances] if needed_instances < @options[:min_instances]
|
156
|
+
|
157
|
+
|
158
|
+
# remove pids if there's too many or spawn new ones if there's not enough
|
159
|
+
if needed_instances < current_instances
|
160
|
+
active_pids.last(current_instances - needed_instances).each { |pid_file| puts "delete #{pid_file}" } #File.delete(pid_file) }
|
161
|
+
elsif needed_instances > current_instances
|
162
|
+
(needed_instances - current_instances).times do
|
163
|
+
next_id = (1..needed_instances).to_a.find { |i| !File.exists?(File.join(RAILS_ROOT, "log/poller.#{i}.pid")) }
|
164
|
+
puts "start at id #{next_id}"
|
165
|
+
# if fork
|
166
|
+
#
|
167
|
+
# end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def should_stop_processing?
|
173
|
+
@stop_processing || (@options[:daemon] && (File.read(pid_filename).to_i rescue 0) != Process.pid)
|
174
|
+
end
|
175
|
+
|
176
|
+
# finds the already running daemon and stops it...
|
177
|
+
def stop_daemon
|
178
|
+
if previous_pid = File.read(pid_filename).to_i rescue nil
|
179
|
+
log "Sending stop signal to daemon ##{@options[:id]}..."
|
180
|
+
Process.kill('TERM', previous_pid)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def remove_pid
|
185
|
+
if Process.pid == (File.read(pid_filename).to_i rescue nil)
|
186
|
+
log "Cleaning up PID file..."
|
187
|
+
FileUtils.rm(pid_filename)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# loops until should_stop_processing? set to true... in local mode, this is never set so it will loop forever
|
192
|
+
def start_processing_loop
|
193
|
+
log "Processing queues: #{@options[:queues].join(',')}"
|
194
|
+
next_iteration or sleep(0.5) until should_stop_processing?
|
195
|
+
log "Got signal to stop... exiting."
|
196
|
+
end
|
197
|
+
|
198
|
+
# if we can get a message, process it
|
199
|
+
def next_iteration
|
200
|
+
reload_application if RAILS_ENV == 'development'
|
201
|
+
if message = ActionEvent::Message.try_to_get_next_message(@options[:queues])
|
202
|
+
begin
|
203
|
+
log_text = "#{message[:queue_name]}:#{message[:event]} (#{message[:params].inspect})"
|
204
|
+
log "Processing #{log_text}"
|
205
|
+
"#{message[:event]}_event".camelize.constantize.process(message[:params])
|
206
|
+
log "Finished processing #{log_text}"
|
207
|
+
rescue Exception => e
|
208
|
+
log "Error processing #{log_text}: #{e} #{e.backtrace.join("\n")}"
|
209
|
+
end
|
210
|
+
return true
|
211
|
+
else
|
212
|
+
# return false if we didn't get a message... makes start_processing_loop sleep(1)
|
213
|
+
return false
|
214
|
+
end
|
215
|
+
rescue Exception => e
|
216
|
+
log "Error getting next message (#{e})"
|
217
|
+
ActionEvent::Message.connection.verify! rescue log("Error verifying DB connection... sleeping 5 seconds. (#{$!})") and sleep(5)
|
218
|
+
return true
|
219
|
+
end
|
220
|
+
|
221
|
+
def reload_application
|
222
|
+
ActionController::Routing::Routes.reload
|
223
|
+
ActionController::Base.view_paths.reload! rescue nil
|
224
|
+
ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear rescue nil
|
225
|
+
|
226
|
+
ActiveRecord::Base.reset_subclasses
|
227
|
+
ActiveSupport::Dependencies.clear
|
228
|
+
ActiveRecord::Base.clear_reloadable_connections!
|
229
|
+
end
|
230
|
+
|
231
|
+
def log(message)
|
232
|
+
$stdout.puts "[#{"#{@options[:id]}:#{Process.pid} " if @options[:daemon]}#{Time.now}] #{message}"
|
233
|
+
$stdout.flush
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
ActionEvent::Commands::Poller.new
|
data/lib/action_event.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: action_event
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Warren Konkel
|
@@ -29,9 +29,7 @@ files:
|
|
29
29
|
- generators/event/templates/event.rb
|
30
30
|
- generators/event/templates/migration.rb
|
31
31
|
- generators/event/templates/poller
|
32
|
-
- init.rb
|
33
32
|
- lib/action_event/base.rb
|
34
|
-
- lib/action_event/commands/poller.rb
|
35
33
|
- lib/action_event/message.rb
|
36
34
|
- lib/action_event/object_extensions.rb
|
37
35
|
- lib/action_event.rb
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'action_event'
|
@@ -1,237 +0,0 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'optparse'
|
3
|
-
|
4
|
-
module ActionEvent
|
5
|
-
module Commands
|
6
|
-
class Poller
|
7
|
-
def initialize
|
8
|
-
@options = {
|
9
|
-
:id => 1,
|
10
|
-
:queues => %W(high medium low),
|
11
|
-
:command => 'start',
|
12
|
-
:environment => RAILS_ENV,
|
13
|
-
:daemon => false,
|
14
|
-
:max_load_average => 8,
|
15
|
-
:min_instances => 5,
|
16
|
-
:max_instances => 200,
|
17
|
-
:max_adjustment => 5,
|
18
|
-
:min_queue_size => 1000
|
19
|
-
}
|
20
|
-
|
21
|
-
OptionParser.new do |opts|
|
22
|
-
opts.banner = "Usage: #{$0} [options] <command>"
|
23
|
-
opts.on("-d", "--daemon", "Run as a daemon") { |v| @options[:daemon] = v }
|
24
|
-
opts.on("-i", "--id=N", Integer, "Specify ID used in PID file when running as daemon") { |v| @options[:id] = v }
|
25
|
-
opts.on("-q", "--queues='high medium low'", "Specify queue names in order") { |v| @options[:queues] = v.split(' ') }
|
26
|
-
opts.on("-e", "--environment=development", "Specify which rails environment to run in") { |v| @options[:environment] = v }
|
27
|
-
opts.separator ""
|
28
|
-
opts.separator "Cluster options:"
|
29
|
-
opts.on("-l", "--load-average=8", "Specify what load average to optimize to") { |v| @options[:max_load_average] = v }
|
30
|
-
opts.on("-m", "--min-instances=5", "Specify mimimum number of instances") { |v| @options[:min_instances] = v }
|
31
|
-
opts.on("-x", "--max-instances=200", "Specify maximum number of instances") { |v| @options[:max_instances] = v }
|
32
|
-
opts.on("-a", "--max-adjustment=5", "Specify how many the maximum amount of instances that will be adjusted") { |v| @options[:max_adjustment] = v }
|
33
|
-
opts.on("-s", "--min-queue-size=1000", "Specify how many must be in the queue to adjust instances") { |v| @options[:min_queue_size] = v }
|
34
|
-
opts.separator ""
|
35
|
-
opts.separator "Commands:"
|
36
|
-
opts.separator " start - starts up the poller"
|
37
|
-
opts.separator " stop - stops a poller currently running as a daemon"
|
38
|
-
opts.separator " status - prints the status of the queues"
|
39
|
-
opts.separator ""
|
40
|
-
opts.separator "Examples:"
|
41
|
-
opts.separator " #{$0} start (starts a poller running in the console)"
|
42
|
-
opts.separator " #{$0} -d -e production start (starts a poller running as a daemon with ID #1)"
|
43
|
-
opts.separator " #{$0} --daemon --id=5 start (starts poller with ID #5)"
|
44
|
-
opts.separator " #{$0} --daemon --id=5 stop (stops poller with ID #5)"
|
45
|
-
end.parse!
|
46
|
-
|
47
|
-
@options[:command] = ARGV.pop unless ARGV.empty?
|
48
|
-
|
49
|
-
case
|
50
|
-
when @options[:command] == 'start' && !@options[:daemon] then trap_ctrl_c and load_rails_environment and start_processing_loop
|
51
|
-
when @options[:command] == 'start' && @options[:daemon] then trap_term and start_daemon and load_rails_environment and start_processing_loop and remove_pid
|
52
|
-
when @options[:command] == 'stop' then stop_daemon
|
53
|
-
when @options[:command] == 'cluster' then load_rails_environment and refresh_cluster
|
54
|
-
when @options[:command] == 'status' then load_rails_environment and print_status
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def print_status
|
59
|
-
ActionEvent::Message.queue_status(@options[:queues]).to_a.sort { |a,b| a.first <=> b.first }.each do |table,messages_left|
|
60
|
-
log "#{table}:\t\t#{messages_left}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def load_rails_environment
|
65
|
-
ENV['ACTION_EVENT_USE_POLLER_DB'] = 'true'
|
66
|
-
ENV["RAILS_ENV"] = @options[:environment]
|
67
|
-
RAILS_ENV.replace(@options[:environment])
|
68
|
-
log "Loading #{RAILS_ENV} environment..."
|
69
|
-
require "#{RAILS_ROOT}/config/environment"
|
70
|
-
|
71
|
-
if defined?(NewRelic)
|
72
|
-
NewRelic::Control.instance.instance_eval do
|
73
|
-
@settings['app_name'] = @settings['app_name'] + ' (Poller)'
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
true
|
78
|
-
end
|
79
|
-
|
80
|
-
# returns the name of the PID file to use for daemons
|
81
|
-
def pid_filename
|
82
|
-
@pid_filename ||= File.join(RAILS_ROOT, "/log/poller.#{@options[:id]}.pid")
|
83
|
-
end
|
84
|
-
|
85
|
-
# forks from the current process and closes out everything
|
86
|
-
def start_daemon
|
87
|
-
log "Starting daemon ##{@options[:id]}..."
|
88
|
-
|
89
|
-
# some process magic
|
90
|
-
exit if fork # Parent exits, child continues.
|
91
|
-
Process.setsid # Become session leader.
|
92
|
-
exit if fork # Zap session leader.
|
93
|
-
Dir.chdir "/" # Release old working directory.
|
94
|
-
File.umask 0000 # Ensure sensible umask. Adjust as needed.
|
95
|
-
|
96
|
-
# Free file descriptors and point them somewhere sensible.
|
97
|
-
STDIN.reopen "/dev/null"
|
98
|
-
STDOUT.reopen File.join(RAILS_ROOT, "log/poller.log"), "a"
|
99
|
-
STDERR.reopen STDOUT
|
100
|
-
|
101
|
-
# don't start up until the previous poller is dead
|
102
|
-
while (previous_pid = File.read(pid_filename).to_i rescue nil) do
|
103
|
-
break unless File.exists?("/proc/#{previous_pid}")
|
104
|
-
log "Waiting for previous poller to finish..."
|
105
|
-
Process.kill('TERM', previous_pid)
|
106
|
-
sleep(5)
|
107
|
-
end
|
108
|
-
|
109
|
-
# record pid
|
110
|
-
File.open(pid_filename, 'w') { |f| f << Process.pid }
|
111
|
-
end
|
112
|
-
|
113
|
-
def trap_ctrl_c
|
114
|
-
trap("SIGINT") do
|
115
|
-
@stop_processing = true
|
116
|
-
log "Sending stop signal..."
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def trap_term
|
121
|
-
trap("SIGTERM") do
|
122
|
-
@stop_processing = true
|
123
|
-
log "Received stop signal..."
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def refresh_cluster
|
128
|
-
# gather some current stats
|
129
|
-
current_load = `uptime`.split(' ')[-3..-3][0].to_f
|
130
|
-
current_queue = ActionEvent::Message.queue_status(*@options[:queues]).to_a.map(&:last).sum
|
131
|
-
|
132
|
-
# remove stale pid files
|
133
|
-
current_pids = Dir[File.join(RAILS_ROOT, "log/poller.*.pid")]
|
134
|
-
active_pids, stale_pids = current_pids.partition { |f| (File.read("/proc/#{File.read(f).to_i}/cmdline").include?('poller') rescue false) }
|
135
|
-
stale_pids.each { |f| File.delete(f) }
|
136
|
-
|
137
|
-
# compute adjustment based on current load average and queue size
|
138
|
-
if active_pids.length > 0
|
139
|
-
current_instances = active_pids.length
|
140
|
-
needed_instances = ((current_instances*@options[:max_load_average])/current_load).floor
|
141
|
-
|
142
|
-
if needed_instances > current_instances
|
143
|
-
needed_instances = [needed_instances, current_instances + @options[:max_adjustment]].min
|
144
|
-
elsif needed_instances < current_instances && current_queue > @options[:min_queue_size]
|
145
|
-
needed_instances = [needed_instances, current_instances - @options[:max_adjustment]].max
|
146
|
-
end
|
147
|
-
else
|
148
|
-
current_instances = 0
|
149
|
-
needed_instances = @options[:min_instances]
|
150
|
-
end
|
151
|
-
|
152
|
-
needed_instances = @options[:max_instances] if needed_instances > @options[:max_instances]
|
153
|
-
needed_instances = @options[:min_instances] if needed_instances < @options[:min_instances]
|
154
|
-
|
155
|
-
|
156
|
-
# remove pids if there's too many or spawn new ones if there's not enough
|
157
|
-
if needed_instances < current_instances
|
158
|
-
active_pids.last(current_instances - needed_instances).each { |pid_file| puts "delete #{pid_file}" } #File.delete(pid_file) }
|
159
|
-
elsif needed_instances > current_instances
|
160
|
-
(needed_instances - current_instances).times do
|
161
|
-
next_id = (1..needed_instances).to_a.find { |i| !File.exists?(File.join(RAILS_ROOT, "log/poller.#{i}.pid")) }
|
162
|
-
puts "start at id #{next_id}"
|
163
|
-
# if fork
|
164
|
-
#
|
165
|
-
# end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def should_stop_processing?
|
171
|
-
@stop_processing || (@options[:daemon] && (File.read(pid_filename).to_i rescue 0) != Process.pid)
|
172
|
-
end
|
173
|
-
|
174
|
-
# finds the already running daemon and stops it...
|
175
|
-
def stop_daemon
|
176
|
-
if previous_pid = File.read(pid_filename).to_i rescue nil
|
177
|
-
log "Sending stop signal to daemon ##{@options[:id]}..."
|
178
|
-
Process.kill('TERM', previous_pid)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def remove_pid
|
183
|
-
if Process.pid == (File.read(pid_filename).to_i rescue nil)
|
184
|
-
log "Cleaning up PID file..."
|
185
|
-
FileUtils.rm(pid_filename)
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
# loops until should_stop_processing? set to true... in local mode, this is never set so it will loop forever
|
190
|
-
def start_processing_loop
|
191
|
-
log "Processing queues: #{@options[:queues].join(',')}"
|
192
|
-
next_iteration or sleep(0.5) until should_stop_processing?
|
193
|
-
log "Got signal to stop... exiting."
|
194
|
-
end
|
195
|
-
|
196
|
-
# if we can get a message, process it
|
197
|
-
def next_iteration
|
198
|
-
reload_application if RAILS_ENV == 'development'
|
199
|
-
if message = ActionEvent::Message.try_to_get_next_message(@options[:queues])
|
200
|
-
begin
|
201
|
-
log_text = "#{message[:queue_name]}:#{message[:event]} (#{message[:params].inspect})"
|
202
|
-
log "Processing #{log_text}"
|
203
|
-
"#{message[:event]}_event".camelize.constantize.process(message[:params])
|
204
|
-
log "Finished processing #{log_text}"
|
205
|
-
rescue Exception => e
|
206
|
-
log "Error processing #{log_text}: #{e} #{e.backtrace.join("\n")}"
|
207
|
-
end
|
208
|
-
return true
|
209
|
-
else
|
210
|
-
# return false if we didn't get a message... makes start_processing_loop sleep(1)
|
211
|
-
return false
|
212
|
-
end
|
213
|
-
rescue Exception => e
|
214
|
-
log "Error getting next message (#{e})"
|
215
|
-
ActionEvent::Message.connection.verify! rescue log("Error verifying DB connection... sleeping 5 seconds. (#{$!})") and sleep(5)
|
216
|
-
return true
|
217
|
-
end
|
218
|
-
|
219
|
-
def reload_application
|
220
|
-
ActionController::Routing::Routes.reload
|
221
|
-
ActionController::Base.view_paths.reload! rescue nil
|
222
|
-
ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear rescue nil
|
223
|
-
|
224
|
-
ActiveRecord::Base.reset_subclasses
|
225
|
-
ActiveSupport::Dependencies.clear
|
226
|
-
ActiveRecord::Base.clear_reloadable_connections!
|
227
|
-
end
|
228
|
-
|
229
|
-
def log(message)
|
230
|
-
$stdout.puts "[#{"#{@options[:id]}:#{Process.pid} " if @options[:daemon]}#{Time.now}] #{message}"
|
231
|
-
$stdout.flush
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
ActionEvent::Commands::Poller.new
|