active_worker 0.50.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +22 -0
- data/Gemfile.lock +77 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/active_worker.gemspec +108 -0
- data/lib/active_worker.rb +29 -0
- data/lib/active_worker/behavior/acts_as_root_object.rb +80 -0
- data/lib/active_worker/behavior/can_be_notified.rb +23 -0
- data/lib/active_worker/behavior/create_from_error.rb +21 -0
- data/lib/active_worker/behavior/execute_concurrently.rb +142 -0
- data/lib/active_worker/behavior/has_modes.rb +44 -0
- data/lib/active_worker/behavior/has_root_object.rb +50 -0
- data/lib/active_worker/behavior/hashable.rb +79 -0
- data/lib/active_worker/configuration.rb +143 -0
- data/lib/active_worker/controller.rb +112 -0
- data/lib/active_worker/event.rb +68 -0
- data/lib/active_worker/expandable.rb +77 -0
- data/lib/active_worker/failure_event.rb +16 -0
- data/lib/active_worker/finished_event.rb +9 -0
- data/lib/active_worker/host_information.rb +13 -0
- data/lib/active_worker/job_queue/job_executer.rb +52 -0
- data/lib/active_worker/job_queue/queue_manager.rb +46 -0
- data/lib/active_worker/job_queue/run_remotely.rb +52 -0
- data/lib/active_worker/modes_map.rb +37 -0
- data/lib/active_worker/notification_event.rb +9 -0
- data/lib/active_worker/parent_event.rb +5 -0
- data/lib/active_worker/started_event.rb +10 -0
- data/lib/active_worker/templatable.rb +46 -0
- data/lib/active_worker/template.rb +41 -0
- data/lib/active_worker/termination_event.rb +21 -0
- data/test/mongoid.yml +28 -0
- data/test/test_acts_as_root_object.rb +123 -0
- data/test/test_can_be_notified.rb +44 -0
- data/test/test_configuration.rb +281 -0
- data/test/test_controller.rb +205 -0
- data/test/test_event.rb +75 -0
- data/test/test_execute_concurrently.rb +134 -0
- data/test/test_expandable.rb +113 -0
- data/test/test_failure_event.rb +69 -0
- data/test/test_finished_event.rb +35 -0
- data/test/test_has_modes.rb +56 -0
- data/test/test_helper.rb +120 -0
- data/test/test_integration.rb +56 -0
- data/test/test_job_executer.rb +65 -0
- data/test/test_queue_manager.rb +106 -0
- data/test/test_run_remotely.rb +63 -0
- data/test/test_started_event.rb +23 -0
- data/test/test_templatable.rb +45 -0
- data/test/test_template.rb +29 -0
- data/test/test_termination_event.rb +28 -0
- metadata +201 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
module ActiveWorker
|
2
|
+
class Controller
|
3
|
+
extend Behavior::ExecuteConcurrently
|
4
|
+
extend JobQueue::RunRemotely
|
5
|
+
|
6
|
+
attr_reader :configuration
|
7
|
+
|
8
|
+
def self.execute_expanded(configuration_id)
|
9
|
+
config = Configuration.find(configuration_id)
|
10
|
+
configurations = config.expand_for_threads
|
11
|
+
|
12
|
+
execute_concurrently(configurations)
|
13
|
+
|
14
|
+
after_thread_launch_methods.each { |method| send(method, config, configurations) }
|
15
|
+
|
16
|
+
wait_for_children
|
17
|
+
ensure
|
18
|
+
worker_cleanup_methods.each { |method| send(method, configurations) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.execute(configuration)
|
22
|
+
worker = new(configuration)
|
23
|
+
worker.started
|
24
|
+
worker.execute
|
25
|
+
worker.finished
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.handle_error(error, method, params)
|
29
|
+
configuration_id = params.shift
|
30
|
+
configuration = Configuration.find(configuration_id)
|
31
|
+
|
32
|
+
if threaded?
|
33
|
+
FailureEvent.from_error(configuration, error)
|
34
|
+
end
|
35
|
+
|
36
|
+
if forking? && parent?
|
37
|
+
ParentEvent.create_error_from_configuration(configuration, error)
|
38
|
+
kill_children
|
39
|
+
wait_for_children
|
40
|
+
end
|
41
|
+
|
42
|
+
if forked?
|
43
|
+
FailureEvent.create_error_from_configuration(configuration, error)
|
44
|
+
end
|
45
|
+
|
46
|
+
rescue Exception => e
|
47
|
+
puts "ERROR: While Handling #{error} Had : #{e}\n#{e.backtrace.join("\n")}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.handle_termination(params)
|
51
|
+
if forking? && parent?
|
52
|
+
kill_children
|
53
|
+
wait_for_children
|
54
|
+
end
|
55
|
+
|
56
|
+
configuration_id = params.shift
|
57
|
+
configuration = Configuration.find(configuration_id)
|
58
|
+
|
59
|
+
if threaded?
|
60
|
+
TerminationEvent.from_termination(configuration)
|
61
|
+
end
|
62
|
+
if forked?
|
63
|
+
TerminationEvent.create_termination_from_configuration(configuration)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.after_thread_launch(method)
|
69
|
+
after_thread_launch_methods << method
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.after_thread_launch_methods
|
73
|
+
@after_thread_launch_methods ||= []
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.worker_cleanup(method)
|
77
|
+
worker_cleanup_methods << method
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.worker_cleanup_methods
|
81
|
+
@worker_cleanup_methods ||= []
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize(configuration)
|
85
|
+
@configuration = configuration
|
86
|
+
setup
|
87
|
+
end
|
88
|
+
|
89
|
+
def setup
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
def started
|
94
|
+
configuration.started
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute
|
98
|
+
raise "Can't call execute on base controller #{configuration.inspect}'"
|
99
|
+
end
|
100
|
+
|
101
|
+
def finished
|
102
|
+
configuration.finished
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def finished?(configurations)
|
108
|
+
FinishedEvent.exists_for_configurations?(configurations)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative "behavior/has_root_object"
|
2
|
+
module ActiveWorker
|
3
|
+
class Event
|
4
|
+
include Mongoid::Document
|
5
|
+
include Mongoid::Timestamps
|
6
|
+
include Behavior::HasRootObject
|
7
|
+
|
8
|
+
belongs_to :configuration, :class_name => "ActiveWorker::Configuration"
|
9
|
+
alias_method :root_owner, :configuration
|
10
|
+
root_object_relation :events
|
11
|
+
|
12
|
+
index({:configuration_id => -1, :_type => 1}, {:background => true})
|
13
|
+
|
14
|
+
before_save :set_message, :set_process_information
|
15
|
+
|
16
|
+
validates_presence_of :configuration, :message => "Events must be owned by a Configuration"
|
17
|
+
|
18
|
+
field :message, :type => String
|
19
|
+
field :host, :type => String
|
20
|
+
field :process_id, :type => Integer
|
21
|
+
field :worker_pid, :type => Integer
|
22
|
+
|
23
|
+
scope :for_root_object_id, lambda {|root_object_id| where(:root_object_id => root_object_id).descending(:created_at)}
|
24
|
+
|
25
|
+
|
26
|
+
def self.exists_for_configurations?(configurations)
|
27
|
+
where(:configuration_id.in => configurations.map(&:id)).count == configurations.size
|
28
|
+
end
|
29
|
+
|
30
|
+
def fields_for_view
|
31
|
+
view_fields = {}
|
32
|
+
fields.keys.each do |field|
|
33
|
+
view_fields[field] = self.send(field)
|
34
|
+
end
|
35
|
+
view_fields
|
36
|
+
end
|
37
|
+
|
38
|
+
def event_type
|
39
|
+
self.class.name.split('::').last.underscore
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_process_information
|
43
|
+
self.host = HostInformation.hostname
|
44
|
+
self.process_id = get_pid
|
45
|
+
#self.worker_pid = get_worker_pid
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_pid
|
49
|
+
Process.pid.to_i
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_worker_pid
|
53
|
+
worker = JobQueue::QueueManager.new.active_jobs_for_configurations([configuration.to_param]).first
|
54
|
+
return worker["pid"] if worker
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_message
|
59
|
+
return if message
|
60
|
+
self.message = generate_message
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate_message
|
64
|
+
"#{configuration.event_name} base message"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module ActiveWorker
|
2
|
+
module Expandable
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.template_field :number_of_threads, type: Integer, default: 1
|
6
|
+
base.template_field :number_of_workers, type: Integer, default: 1
|
7
|
+
base.belongs_to :thread_root_configuration, :polymorphic => true
|
8
|
+
base.has_many :thread_expanded_configurations,
|
9
|
+
:as => :thread_root_configuration,
|
10
|
+
:class_name => 'ActiveWorker::Configuration',
|
11
|
+
:autosave => true
|
12
|
+
end
|
13
|
+
|
14
|
+
def expand_for_threads
|
15
|
+
maps = expansion_maps_for_threads
|
16
|
+
maps[1..-1].each do |map_hash|
|
17
|
+
map_hash[:thread_root_configuration_id] = id
|
18
|
+
map_hash[:thread_root_configuration_type] = _type
|
19
|
+
end
|
20
|
+
expand_from_maps(maps)
|
21
|
+
end
|
22
|
+
|
23
|
+
def expand_for_workers
|
24
|
+
maps = expansion_maps_for_workers
|
25
|
+
expand_from_maps(maps)
|
26
|
+
end
|
27
|
+
|
28
|
+
def configurations_for_events
|
29
|
+
[self] + thread_expanded_configurations
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def expand_from_maps(maps)
|
35
|
+
first_config = true
|
36
|
+
maps.map do |map_hash|
|
37
|
+
if first_config
|
38
|
+
first_config = false
|
39
|
+
modify_for_expansion(map_hash)
|
40
|
+
else
|
41
|
+
create_for_expansion(map_hash)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_for_expansion(options = {})
|
47
|
+
self.class.create(defined_fields.merge(parent_configuration: parent_configuration,
|
48
|
+
root_object_id: root_object_id,
|
49
|
+
root_object_type: root_object_type,
|
50
|
+
renderable: false).merge(options))
|
51
|
+
end
|
52
|
+
|
53
|
+
def modify_for_expansion(options = {})
|
54
|
+
self.update_attributes!(options)
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def expansion_maps_for_workers
|
60
|
+
expansion_maps_for(number_of_workers)
|
61
|
+
end
|
62
|
+
|
63
|
+
def expansion_maps_for_threads
|
64
|
+
expansion_maps_for(number_of_threads)
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def expansion_maps_for(number_of_configurations)
|
69
|
+
maps = []
|
70
|
+
number_of_configurations.times do
|
71
|
+
maps << {}
|
72
|
+
end
|
73
|
+
maps
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ActiveWorker
|
2
|
+
class FailureEvent < ActiveWorker::FinishedEvent
|
3
|
+
extend Behavior::CreateFromError
|
4
|
+
|
5
|
+
def self.from_error(thread_root_configuration, error)
|
6
|
+
events = []
|
7
|
+
thread_root_configuration.configurations_for_events.each do |configuration|
|
8
|
+
events << create_error_from_configuration(configuration,error) unless configuration.completed?
|
9
|
+
end
|
10
|
+
events
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'resque/errors'
|
2
|
+
|
3
|
+
module ActiveWorker
|
4
|
+
module JobQueue
|
5
|
+
class JobExecuter
|
6
|
+
|
7
|
+
Thread.abort_on_exception = false
|
8
|
+
|
9
|
+
@queue = :execute
|
10
|
+
|
11
|
+
def self.perform(args)
|
12
|
+
execute_task_from_args(args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.execute_task_from_args(args)
|
16
|
+
class_name = args["class_name"]
|
17
|
+
method = args["method"]
|
18
|
+
params = args["params"]
|
19
|
+
|
20
|
+
klass = class_name.constantize
|
21
|
+
klass.send(method,*params)
|
22
|
+
|
23
|
+
rescue Resque::TermException => e
|
24
|
+
handle_termination(klass, params, e)
|
25
|
+
rescue SignalException => e
|
26
|
+
handle_exception(e, method, params, klass)
|
27
|
+
raise e
|
28
|
+
rescue Exception => e
|
29
|
+
handle_exception(e, method, params, klass)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.handle_exception(e, method, params, klass)
|
33
|
+
log_error "Handling exception for #{klass} because #{e.message}"
|
34
|
+
klass.handle_error e, method, params
|
35
|
+
rescue Exception => handle_error_error
|
36
|
+
log_error "Handle error exception: #{handle_error_error.message}"
|
37
|
+
log_error handle_error_error.backtrace.join("\n")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.handle_termination(klass, params, exception = nil)
|
41
|
+
log_error "Handling #{exception || "termination" } for #{klass}"
|
42
|
+
klass.handle_termination params
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.log_error(message)
|
47
|
+
puts "JOB EXECUTOR: #{message}"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveWorker
|
2
|
+
module JobQueue
|
3
|
+
class QueueManager
|
4
|
+
|
5
|
+
def active_jobs
|
6
|
+
Resque.working.map {|w| create_job_hash_from_worker(w)}.compact
|
7
|
+
end
|
8
|
+
|
9
|
+
def active_jobs_for_configurations(configuration_ids)
|
10
|
+
workers = Resque.working.select do|w|
|
11
|
+
configuration_ids.include? configuration_id_from_worker(w)
|
12
|
+
end
|
13
|
+
workers.map do |w|
|
14
|
+
create_job_hash_from_worker(w)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def configuration_id_from_worker(worker)
|
19
|
+
params = params_from_worker(worker)
|
20
|
+
params.first if params
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_job_hash_from_worker(worker)
|
24
|
+
worker_id = worker.to_s.split(":")
|
25
|
+
host = worker_id[0]
|
26
|
+
pid = worker_id[1]
|
27
|
+
queues = worker_id[2].split(",")
|
28
|
+
args = args_from_worker(worker)
|
29
|
+
|
30
|
+
if worker_id && host && pid && queues && args
|
31
|
+
return {"host" => host, "queues" => queues, "pid" => pid, "args" => args }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def params_from_worker(worker)
|
36
|
+
args = args_from_worker(worker)
|
37
|
+
args["params"] if args
|
38
|
+
end
|
39
|
+
|
40
|
+
def args_from_worker(worker)
|
41
|
+
payload = worker.job["payload"]
|
42
|
+
payload["args"].first if payload
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ActiveWorker
|
2
|
+
module JobQueue
|
3
|
+
module RunRemotely
|
4
|
+
|
5
|
+
THREADED = "threaded"
|
6
|
+
RESQUE = "resque"
|
7
|
+
|
8
|
+
|
9
|
+
def self.worker_mode=(mode)
|
10
|
+
@@worker_mode = mode
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.worker_mode
|
14
|
+
@@worker_mode
|
15
|
+
end
|
16
|
+
|
17
|
+
self.worker_mode = RESQUE
|
18
|
+
|
19
|
+
class RemoteRunner
|
20
|
+
def initialize(klass)
|
21
|
+
@klass = klass
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method,*params)
|
25
|
+
args = construct_args(method,params)
|
26
|
+
thread = nil
|
27
|
+
case RunRemotely.worker_mode
|
28
|
+
when THREADED
|
29
|
+
thread = Thread.new do
|
30
|
+
ActiveWorker::JobQueue::JobExecuter.execute_task_from_args(args)
|
31
|
+
end
|
32
|
+
when RESQUE
|
33
|
+
Resque.enqueue(JobExecuter,args)
|
34
|
+
end
|
35
|
+
thread
|
36
|
+
end
|
37
|
+
|
38
|
+
def construct_args(method, params)
|
39
|
+
{
|
40
|
+
"class_name" => @klass.to_s,
|
41
|
+
"method" => method.to_s,
|
42
|
+
"params" => params
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_remotely
|
48
|
+
RemoteRunner.new(self.to_s)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|