active_worker 0.50.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.document +5 -0
  2. data/.rvmrc +1 -0
  3. data/Gemfile +22 -0
  4. data/Gemfile.lock +77 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/Rakefile +46 -0
  8. data/VERSION +1 -0
  9. data/active_worker.gemspec +108 -0
  10. data/lib/active_worker.rb +29 -0
  11. data/lib/active_worker/behavior/acts_as_root_object.rb +80 -0
  12. data/lib/active_worker/behavior/can_be_notified.rb +23 -0
  13. data/lib/active_worker/behavior/create_from_error.rb +21 -0
  14. data/lib/active_worker/behavior/execute_concurrently.rb +142 -0
  15. data/lib/active_worker/behavior/has_modes.rb +44 -0
  16. data/lib/active_worker/behavior/has_root_object.rb +50 -0
  17. data/lib/active_worker/behavior/hashable.rb +79 -0
  18. data/lib/active_worker/configuration.rb +143 -0
  19. data/lib/active_worker/controller.rb +112 -0
  20. data/lib/active_worker/event.rb +68 -0
  21. data/lib/active_worker/expandable.rb +77 -0
  22. data/lib/active_worker/failure_event.rb +16 -0
  23. data/lib/active_worker/finished_event.rb +9 -0
  24. data/lib/active_worker/host_information.rb +13 -0
  25. data/lib/active_worker/job_queue/job_executer.rb +52 -0
  26. data/lib/active_worker/job_queue/queue_manager.rb +46 -0
  27. data/lib/active_worker/job_queue/run_remotely.rb +52 -0
  28. data/lib/active_worker/modes_map.rb +37 -0
  29. data/lib/active_worker/notification_event.rb +9 -0
  30. data/lib/active_worker/parent_event.rb +5 -0
  31. data/lib/active_worker/started_event.rb +10 -0
  32. data/lib/active_worker/templatable.rb +46 -0
  33. data/lib/active_worker/template.rb +41 -0
  34. data/lib/active_worker/termination_event.rb +21 -0
  35. data/test/mongoid.yml +28 -0
  36. data/test/test_acts_as_root_object.rb +123 -0
  37. data/test/test_can_be_notified.rb +44 -0
  38. data/test/test_configuration.rb +281 -0
  39. data/test/test_controller.rb +205 -0
  40. data/test/test_event.rb +75 -0
  41. data/test/test_execute_concurrently.rb +134 -0
  42. data/test/test_expandable.rb +113 -0
  43. data/test/test_failure_event.rb +69 -0
  44. data/test/test_finished_event.rb +35 -0
  45. data/test/test_has_modes.rb +56 -0
  46. data/test/test_helper.rb +120 -0
  47. data/test/test_integration.rb +56 -0
  48. data/test/test_job_executer.rb +65 -0
  49. data/test/test_queue_manager.rb +106 -0
  50. data/test/test_run_remotely.rb +63 -0
  51. data/test/test_started_event.rb +23 -0
  52. data/test/test_templatable.rb +45 -0
  53. data/test/test_template.rb +29 -0
  54. data/test/test_termination_event.rb +28 -0
  55. 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,9 @@
1
+ module ActiveWorker
2
+ class FinishedEvent < ActiveWorker::Event
3
+ def generate_message
4
+ "#{configuration.event_name} finished"
5
+ end
6
+
7
+ after_create :notify_root_of_finished
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module ActiveWorker
2
+ module HostInformation
3
+
4
+ def self.hostname
5
+ `hostname -f`.chomp
6
+ end
7
+
8
+ def self.short_hostname
9
+ `hostname`.chomp
10
+ end
11
+
12
+ end
13
+ 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