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,205 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveWorker
4
+ class ControllerTest < ActiveSupport::TestCase
5
+
6
+ class ThreadedConfig < Configuration
7
+ include Expandable
8
+ end
9
+
10
+ class SpawnsChildrenController < Controller
11
+ def execute
12
+ sleep 30
13
+ end
14
+ end
15
+
16
+ test "can create started event" do
17
+ configuration = Configuration.create
18
+ controller = Controller.new(configuration)
19
+ controller.started
20
+ assert_equal 1, StartedEvent.where(configuration_id: configuration.id).size
21
+ end
22
+
23
+ test "creates started event during execution" do
24
+
25
+ configuration = Configuration.create
26
+
27
+ Controller.any_instance.stubs(:execute)
28
+ Controller.execute(configuration)
29
+
30
+ assert_equal 1, StartedEvent.where(configuration_id: configuration.id).size
31
+ end
32
+
33
+ test "can run concurrently in threads" do
34
+ number_of_threads = 2
35
+ configuration = ThreadedConfig.create number_of_threads: number_of_threads
36
+
37
+ Controller::local_worker_mode = THREADED_MODE
38
+
39
+ Controller.any_instance.expects(:execute).twice
40
+ Controller.execute_expanded(configuration.id)
41
+
42
+ assert_equal 0, FailureEvent.count
43
+ assert_equal number_of_threads, StartedEvent.count
44
+ assert_equal number_of_threads, FinishedEvent.count
45
+
46
+ end
47
+
48
+ test "can run concurrently in forks" do
49
+
50
+ number_of_threads = 2
51
+ configuration = ThreadedConfig.create number_of_threads: number_of_threads
52
+
53
+ assert_equal FORKING_MODE, Controller::local_worker_mode
54
+
55
+ Controller.any_instance.stubs(:execute)
56
+ Controller.execute_expanded(configuration.id)
57
+
58
+ assert_equal 0, FailureEvent.count
59
+ assert_equal number_of_threads, StartedEvent.count
60
+ assert_equal number_of_threads, FinishedEvent.count
61
+ assert_equal 0, Controller.pids.size
62
+ end
63
+
64
+ test "creates finished event during execution" do
65
+
66
+ configuration = Configuration.create
67
+
68
+ Controller.any_instance.stubs(:execute)
69
+ Controller.execute(configuration)
70
+
71
+ assert_equal 1, FinishedEvent.where(configuration_id: configuration.id).size
72
+
73
+ end
74
+
75
+ test "calls setup during initialization" do
76
+ Controller.any_instance.expects(:setup)
77
+ configuration = Configuration.create
78
+ controller = Controller.new(configuration)
79
+ controller.started
80
+ assert_equal 1, StartedEvent.where(configuration_id: configuration.id).size
81
+ end
82
+
83
+ test "call worker cleanup methods during launch thread" do
84
+ class TestController < Controller
85
+ worker_cleanup :test_worker_cleanup_method
86
+
87
+ def execute
88
+
89
+ end
90
+ end
91
+ configuration = Configuration.create
92
+
93
+ TestController.expects(:test_worker_cleanup_method)
94
+ TestController.execute_expanded(configuration.id)
95
+ end
96
+
97
+
98
+ test "handle termination while forked" do
99
+ number_of_threads = 2
100
+ configuration = ThreadedConfig.create number_of_threads: number_of_threads
101
+
102
+
103
+ thread = Thread.new do
104
+ SpawnsChildrenController.execute_expanded(configuration.id)
105
+ end
106
+
107
+ sleep 0.1 until (StartedEvent.count == number_of_threads)
108
+ SpawnsChildrenController.handle_termination([configuration.id])
109
+ thread.join
110
+
111
+ assert_equal number_of_threads, TerminationEvent.count
112
+
113
+ FailureEvent.all.each do |event|
114
+ puts event.message
115
+ end
116
+ assert_equal 0, FailureEvent.count
117
+
118
+ assert_equal number_of_threads, StartedEvent.count
119
+ assert_equal 0, ParentEvent.count
120
+ end
121
+
122
+ test "can handle exception while forked" do
123
+ Controller.any_instance.stubs(:execute).raises
124
+
125
+ number_of_threads = 2
126
+ configuration = ThreadedConfig.create number_of_threads: number_of_threads
127
+
128
+ assert_equal FORKING_MODE, Controller::local_worker_mode
129
+
130
+ Controller.execute_expanded(configuration.id)
131
+
132
+ assert_equal number_of_threads, FailureEvent.count
133
+ assert_equal number_of_threads, StartedEvent.count
134
+ assert_equal number_of_threads, FinishedEvent.count
135
+ assert_equal 0, ParentEvent.count
136
+ assert_equal 0, Controller.pids.size
137
+ end
138
+
139
+ test "can handle parent exception while forked" do
140
+
141
+ number_of_threads = 2
142
+ configuration = ThreadedConfig.create number_of_threads: number_of_threads
143
+
144
+ thread = Thread.new do
145
+ SpawnsChildrenController.execute_expanded(configuration.id)
146
+ end
147
+
148
+ error = StandardError.new
149
+ error.expects(:backtrace).returns ["Line1", "Line2"]
150
+
151
+ sleep 0.1 until (StartedEvent.count == number_of_threads)
152
+ SpawnsChildrenController.handle_error(error, :test, [configuration.id])
153
+ thread.join
154
+
155
+ assert_equal number_of_threads, TerminationEvent.count
156
+
157
+ assert_equal 1, ParentEvent.count
158
+ assert_equal 0, FailureEvent.count
159
+ assert_equal number_of_threads, StartedEvent.count
160
+
161
+ end
162
+
163
+ test "puts stack trace on FailureEvent from error" do
164
+ config = Configuration.create
165
+
166
+ exception = create_exception
167
+ ActiveWorker::Controller.expects(:threaded?).returns(true)
168
+ ActiveWorker::Controller.expects(:forking?).returns(false)
169
+ ActiveWorker::Controller.handle_error(exception, :create, [config.id])
170
+
171
+ event = ActiveWorker::FailureEvent.where(:configuration_id => config.id).first
172
+
173
+ assert_match exception.message, event.message
174
+ assert_equal exception.backtrace.join("\n"), event.stack_trace
175
+ end
176
+
177
+ test "creates error for threads when threaded" do
178
+ config1 = ThreadedConfig.create
179
+ config1.thread_expanded_configurations << ThreadedConfig.create
180
+
181
+ exception = create_exception
182
+ Controller.expects(:threaded?).returns(true)
183
+ Controller.expects(:forking?).returns(false)
184
+ Controller.expects(:forked?).returns(false)
185
+ Controller.handle_error(exception, :create, [config1.id])
186
+
187
+ assert_equal 2, FailureEvent.count
188
+
189
+ end
190
+
191
+ test "creates terminaion events for threads when threaded" do
192
+ config1 = ThreadedConfig.create
193
+ config1.thread_expanded_configurations << ThreadedConfig.create
194
+
195
+ Controller.expects(:threaded?).returns(true)
196
+ Controller.expects(:forking?).returns(false)
197
+ Controller.expects(:forked?).returns(false)
198
+ Controller.handle_termination([config1.id])
199
+
200
+ assert_equal 2, TerminationEvent.count
201
+ end
202
+
203
+
204
+ end
205
+ end
@@ -0,0 +1,75 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveWorker
4
+ class EventTest < ActiveSupport::TestCase
5
+
6
+ test "can find event for root object" do
7
+ top_level_object = Configuration.create
8
+ config = Configuration.create root_object: top_level_object
9
+
10
+ event =Event.create configuration: config
11
+
12
+ assert_equal event, ActiveWorker::Event.for_root_object_id(top_level_object.id).first
13
+ end
14
+
15
+ test "field events for view" do
16
+ config = Configuration.create
17
+ event = Event.create configuration: config, message: "this is the message"
18
+
19
+ fields = event.fields_for_view
20
+
21
+ assert_equal "this is the message", fields["message"]
22
+ assert_equal config.id, fields["configuration_id"]
23
+ end
24
+
25
+ test "sets host information" do
26
+ config = Configuration.create
27
+ HostInformation.expects(:hostname).returns("test")
28
+ event = Event.create configuration: config
29
+ assert_equal "test", event.host
30
+ end
31
+
32
+ test "sets pid" do
33
+ config = Configuration.create
34
+ Event.any_instance.stubs(:get_pid).returns(12345)
35
+ event = Event.create configuration: config
36
+ assert_equal 12345, event.process_id
37
+ end
38
+
39
+ test "events must be owned by a configuration" do
40
+ assert_raise Mongoid::Errors::Validations do
41
+ Event.create!
42
+ end
43
+ end
44
+
45
+ test "exists with array of configurations" do
46
+ config1 = Configuration.create
47
+ config2 = Configuration.create
48
+ config3 = Configuration.create
49
+
50
+ Event.create configuration: config1
51
+ Event.create configuration: config2
52
+
53
+ configs = [config1, config2, config3]
54
+
55
+ assert_equal false, Event.exists_for_configurations?(configs)
56
+
57
+ Event.create configuration: config3
58
+
59
+ assert Event.exists_for_configurations?(configs)
60
+ end
61
+
62
+ test "sets worker pid" do
63
+ pending "Re-factoring Resque Worker PID lookup" do
64
+ JobQueue::QueueManager.any_instance.stubs(:active_jobs_for_configurations).returns([{"pid" => 12345}])
65
+ config = Configuration.create
66
+
67
+ event = Event.create configuration: config
68
+
69
+
70
+ assert_equal 12345, event.worker_pid
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,134 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveWorker
4
+ module Behavior
5
+ class ExecuteConcurrentlyTest < ActiveSupport::TestCase
6
+
7
+ class ThreadedConfig < Configuration
8
+ include Expandable
9
+ end
10
+
11
+ class FakeController
12
+ extend ExecuteConcurrently
13
+
14
+ class FakeControllerException < StandardError;
15
+ end
16
+
17
+ def self.execute(param)
18
+ end
19
+
20
+ def self.handle_error(error, method, params)
21
+ puts "#{error} #{method} #{params}"
22
+ end
23
+
24
+ def execute
25
+ end
26
+ end
27
+
28
+ teardown do
29
+ FakeController.role = nil
30
+ end
31
+
32
+ test "can set threaded mode" do
33
+ params = [1, 2]
34
+ FakeController::local_worker_mode = THREADED_MODE
35
+ params.each do |param|
36
+ FakeController.expects(:execute_thread).with(param)
37
+ end
38
+
39
+ FakeController.execute_concurrently(params)
40
+ end
41
+
42
+ test "can set forking mode" do
43
+ params = [1, 2]
44
+ FakeController::local_worker_mode = FORKING_MODE
45
+ params.each do |param|
46
+ FakeController.expects(:execute_fork).with(param)
47
+ end
48
+
49
+ FakeController.execute_concurrently(params)
50
+ end
51
+
52
+ test "in_fork calls after_fork" do
53
+ FakeController.expects(:after_fork)
54
+ FakeController.stubs(:execute)
55
+ FakeController.in_fork(nil)
56
+ end
57
+
58
+ test "after fork reset mongoid and resque" do
59
+ FakeController.expects(:reset_mongoid)
60
+ FakeController.expects(:reset_resque)
61
+ FakeController.after_fork(nil)
62
+ end
63
+
64
+ test "after_fork sets mode" do
65
+ FakeController.after_fork(nil)
66
+ assert_equal FORKED, FakeController.role
67
+ end
68
+
69
+ test "after fork sets process name" do
70
+ param = "foo"
71
+ FakeController.expects(:set_process_name).with(param)
72
+ FakeController.after_fork(param)
73
+
74
+ end
75
+
76
+ test "can set process name" do
77
+ param = "foo"
78
+ FakeController.after_fork(param)
79
+ assert_equal "ActiveWorker Forked from #{Process.ppid} for foo", $0
80
+
81
+ end
82
+
83
+ test "can reset mongoid" do
84
+ first_mongoid_session = Mongoid.default_session
85
+ FakeController.reset_mongoid
86
+ assert_not_equal first_mongoid_session, Mongoid.default_session
87
+ end
88
+
89
+ test "keeps track of spawned child pids" do
90
+ params = [1, 2]
91
+ assert_equal FORKING_MODE, FakeController::local_worker_mode
92
+
93
+ threads = FakeController.execute_concurrently(params)
94
+ assert_equal params.size, threads.size
95
+ assert_equal params.size, FakeController.pids.size
96
+ assert_equal threads.map(&:pid), FakeController.pids
97
+ end
98
+
99
+ test "cleans up child pids" do
100
+ params = [1, 2]
101
+ assert_equal FORKING_MODE, FakeController::local_worker_mode
102
+
103
+ FakeController.execute_concurrently(params)
104
+ assert_equal params.size, FakeController.pids.size
105
+
106
+ FakeController.wait_for_children
107
+ assert_empty FakeController.pids
108
+ end
109
+
110
+ test "can reset resque" do
111
+ Resque.redis.client.expects(:reconnect)
112
+ FakeController.expects(:trap).with("TERM", "DEFAULT")
113
+ FakeController.reset_resque
114
+ end
115
+
116
+
117
+ test "can handle exception in fork" do
118
+ FakeController.expects(:execute).raises(StandardError)
119
+ FakeController.expects(:handle_error)
120
+ FakeController.in_fork(:foo)
121
+ end
122
+
123
+ test "can handle signal exception in fork" do
124
+ FakeController.expects(:execute).raises(SignalException.new("TERM"))
125
+ FakeController.expects(:handle_termination)
126
+ FakeController.expects(:exit)
127
+
128
+ FakeController.in_fork(:foo)
129
+ end
130
+
131
+
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,113 @@
1
+ require_relative "test_helper"
2
+
3
+ module ActiveWorker
4
+ class ExpandableTest < ActiveSupport::TestCase
5
+
6
+
7
+
8
+ test "can expand configuration for workers" do
9
+ root_object = Rootable.create
10
+ parent_config = TopConfig.create(root_object_id: root_object.id)
11
+
12
+ config = ExpandableConfig.create(
13
+ name: "First",
14
+ size: 0,
15
+ parent_configuration: parent_config,
16
+ foo: "bar",
17
+ root_object_id: parent_config.root_object_id,
18
+ number_of_workers: 4,
19
+ number_of_threads: 5,
20
+ )
21
+
22
+ defined_fields = config.defined_fields.dup
23
+
24
+ expanded_configs = config.expand_for_workers
25
+
26
+ expected_number_of_configs = 4
27
+ assert_expanded_configs(config, defined_fields, expanded_configs, expected_number_of_configs, parent_config, root_object)
28
+ end
29
+
30
+ test "can expand configuration for threads" do
31
+ root_object = Rootable.create
32
+ parent_config = TopConfig.create(root_object_id: root_object.id)
33
+
34
+ config = ExpandableConfig.create(
35
+ name: "First",
36
+ size: 0,
37
+ parent_configuration: parent_config,
38
+ foo: "bar",
39
+ root_object_id: parent_config.root_object_id,
40
+ number_of_workers: 4,
41
+ number_of_threads: 5,
42
+ )
43
+
44
+ defined_fields = config.defined_fields.dup
45
+
46
+ expanded_configs = config.expand_for_threads
47
+
48
+ expected_number_of_configs = 5
49
+ assert_expanded_configs(config, defined_fields, expanded_configs, expected_number_of_configs, parent_config, root_object)
50
+ end
51
+
52
+ test "can expand threads using maps" do
53
+ root_object = Rootable.create
54
+ parent_config = TopConfig.create(root_object_id: root_object.id)
55
+
56
+ config = MappedExpandableConfig.create(
57
+ name: "First",
58
+ size: 0,
59
+ parent_configuration: parent_config,
60
+ foo: "bar",
61
+ root_object_id: parent_config.root_object_id,
62
+ number_of_workers: 4,
63
+ number_of_threads: 5,
64
+ )
65
+ expected_number_of_configs = 5
66
+ expanded_configs = config.expand_for_threads
67
+
68
+ assert_equal expected_number_of_configs, expanded_configs.size
69
+ assert_equal [0,1,2,3,4], expanded_configs.map(&:size)
70
+ assert_nil expanded_configs.first.thread_root_configuration_id
71
+ expanded_configs[1..-1].each do |threaded_config|
72
+ assert_equal expanded_configs.first, threaded_config.thread_root_configuration
73
+ end
74
+ end
75
+
76
+ test "can expand workers using maps" do
77
+ root_object = Rootable.create
78
+ parent_config = TopConfig.create(root_object_id: root_object.id)
79
+
80
+ config = MappedExpandableConfig.create(
81
+ name: "First",
82
+ size: 0,
83
+ parent_configuration: parent_config,
84
+ foo: "bar",
85
+ root_object_id: parent_config.root_object_id,
86
+ number_of_workers: 4,
87
+ number_of_threads: 5,
88
+ )
89
+ expected_number_of_configs = 4
90
+ expanded_configs = config.expand_for_workers
91
+
92
+ assert_equal expected_number_of_configs, expanded_configs.size
93
+ assert_equal [0,1,2,3], expanded_configs.map(&:size)
94
+ end
95
+
96
+ def assert_expanded_configs(config, defined_fields, expanded_configs, expected_number_of_configs, parent_config, root_object)
97
+ assert_equal expected_number_of_configs, expanded_configs.size
98
+
99
+ expanded_configs.each do |expanded_config|
100
+ assert_equal parent_config, expanded_config.parent_configuration
101
+ assert_equal root_object.id, expanded_config.root_object_id
102
+ assert_equal defined_fields, expanded_config.defined_fields
103
+ end
104
+
105
+ expanded_configs.each do |expanded_config|
106
+ next if expanded_config == config
107
+ assert_nil expanded_config.foo
108
+ assert_equal false, expanded_config.renderable
109
+ end
110
+ end
111
+
112
+ end
113
+ end