active_worker 0.50.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/.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
|