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.
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