elecnix-workling 0.4.2 → 0.4.9

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 (60) hide show
  1. data/CHANGES.markdown +10 -0
  2. data/README.markdown +161 -4
  3. data/bin/workling_client +28 -0
  4. metadata +3 -75
  5. data/lib/rude_q/client.rb +0 -11
  6. data/lib/workling.rb +0 -150
  7. data/lib/workling/base.rb +0 -71
  8. data/lib/workling/clients/amqp_client.rb +0 -56
  9. data/lib/workling/clients/base.rb +0 -57
  10. data/lib/workling/clients/memcache_queue_client.rb +0 -83
  11. data/lib/workling/discovery.rb +0 -14
  12. data/lib/workling/remote.rb +0 -42
  13. data/lib/workling/remote/invokers/base.rb +0 -124
  14. data/lib/workling/remote/invokers/basic_poller.rb +0 -41
  15. data/lib/workling/remote/invokers/eventmachine_subscriber.rb +0 -41
  16. data/lib/workling/remote/invokers/threaded_poller.rb +0 -140
  17. data/lib/workling/remote/runners/backgroundjob_runner.rb +0 -35
  18. data/lib/workling/remote/runners/base.rb +0 -42
  19. data/lib/workling/remote/runners/client_runner.rb +0 -45
  20. data/lib/workling/remote/runners/not_remote_runner.rb +0 -23
  21. data/lib/workling/remote/runners/rudeq_runner.rb +0 -23
  22. data/lib/workling/remote/runners/spawn_runner.rb +0 -38
  23. data/lib/workling/remote/runners/starling_runner.rb +0 -13
  24. data/lib/workling/return/store/base.rb +0 -42
  25. data/lib/workling/return/store/iterator.rb +0 -24
  26. data/lib/workling/return/store/memory_return_store.rb +0 -26
  27. data/lib/workling/return/store/rudeq_return_store.rb +0 -24
  28. data/lib/workling/return/store/starling_return_store.rb +0 -31
  29. data/lib/workling/routing/base.rb +0 -13
  30. data/lib/workling/routing/class_and_method_routing.rb +0 -55
  31. data/lib/workling/rudeq.rb +0 -7
  32. data/lib/workling/rudeq/client.rb +0 -17
  33. data/lib/workling/rudeq/poller.rb +0 -116
  34. data/test/class_and_method_routing_test.rb +0 -18
  35. data/test/clients/memory_queue_client.rb +0 -36
  36. data/test/discovery_test.rb +0 -13
  37. data/test/invoker_basic_poller_test.rb +0 -29
  38. data/test/invoker_eventmachine_subscription_test.rb +0 -26
  39. data/test/invoker_threaded_poller_test.rb +0 -34
  40. data/test/memcachequeue_client_test.rb +0 -36
  41. data/test/memory_return_store_test.rb +0 -32
  42. data/test/mocks/client.rb +0 -9
  43. data/test/mocks/logger.rb +0 -5
  44. data/test/mocks/rude_queue.rb +0 -9
  45. data/test/mocks/spawn.rb +0 -5
  46. data/test/not_remote_runner_test.rb +0 -11
  47. data/test/remote_runner_test.rb +0 -58
  48. data/test/rescue_test.rb +0 -24
  49. data/test/return_store_test.rb +0 -24
  50. data/test/rudeq_client_test.rb +0 -30
  51. data/test/rudeq_poller_test.rb +0 -14
  52. data/test/rudeq_return_store_test.rb +0 -20
  53. data/test/rudeq_runner_test.rb +0 -22
  54. data/test/runners/thread_runner.rb +0 -22
  55. data/test/spawn_runner_test.rb +0 -10
  56. data/test/starling_return_store_test.rb +0 -29
  57. data/test/starling_runner_test.rb +0 -8
  58. data/test/test_helper.rb +0 -50
  59. data/test/workers/analytics/invites.rb +0 -10
  60. data/test/workers/util.rb +0 -25
@@ -1,41 +0,0 @@
1
- require 'workling/remote/invokers/base'
2
-
3
- #
4
- # A basic polling invoker.
5
- #
6
- module Workling
7
- module Remote
8
- module Invokers
9
- class BasicPoller < Workling::Remote::Invokers::Base
10
-
11
- #
12
- # set up client, sleep time
13
- #
14
- def initialize(routing, client_class)
15
- super
16
- end
17
-
18
- #
19
- # Starts main Invoker Loop. The invoker runs until stop() is called.
20
- #
21
- def listen
22
- connect do
23
- loop_routes do |route|
24
- if args = @client.retrieve(route)
25
- run(route, args)
26
- end
27
- end
28
- end
29
- end
30
-
31
- #
32
- # Gracefully stops the Invoker. The currently executing Jobs should be allowed
33
- # to finish.
34
- #
35
- def stop
36
- @shutdown = true
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,41 +0,0 @@
1
- require 'eventmachine'
2
- require 'workling/remote/invokers/base'
3
-
4
- #
5
- # Subscribes the workers to the correct queues.
6
- #
7
- module Workling
8
- module Remote
9
- module Invokers
10
- class EventmachineSubscriber < Workling::Remote::Invokers::Base
11
-
12
- def initialize(routing, client_class)
13
- super
14
- end
15
-
16
- #
17
- # Starts EM loop and sets up subscription callbacks for workers.
18
- #
19
- def listen
20
- EM.run do
21
- connect do
22
- routes.each do |route|
23
- @client.subscribe(route) do |args|
24
- begin
25
- run(route, args)
26
- rescue
27
- logger.error("EventmachineSubscriber listen error on #{route}: #{$!}")
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
34
-
35
- def stop
36
- EM.stop if EM.reactor_running?
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,140 +0,0 @@
1
- require 'workling/remote/invokers/base'
2
-
3
- #
4
- # A threaded polling Invoker.
5
- #
6
- # TODO: refactor this to make use of the base class.
7
- #
8
- module Workling
9
- module Remote
10
- module Invokers
11
- class ThreadedPoller < Workling::Remote::Invokers::Base
12
-
13
- cattr_accessor :sleep_time, :reset_time
14
-
15
- def initialize(routing, client_class)
16
- super
17
-
18
- ThreadedPoller.sleep_time = Workling.config[:sleep_time] || 2
19
- ThreadedPoller.reset_time = Workling.config[:reset_time] || 30
20
-
21
- @workers = ThreadGroup.new
22
- @mutex = Mutex.new
23
- end
24
-
25
- def listen
26
- # Create a thread for each worker.
27
- Workling::Discovery.discovered.each do |clazz|
28
- logger.debug("Discovered listener #{clazz}")
29
- @workers.add(Thread.new(clazz) { |c| clazz_listen(c) })
30
- end
31
-
32
- # Wait for all workers to complete
33
- @workers.list.each { |t| t.join }
34
-
35
- logger.debug("Reaped listener threads. ")
36
-
37
- # Clean up all the connections.
38
- ActiveRecord::Base.verify_active_connections!
39
- logger.debug("Cleaned up connection: out!")
40
- end
41
-
42
- # Check if all Worker threads have been started.
43
- def started?
44
- logger.debug("checking if started... list size is #{ worker_threads }")
45
- Workling::Discovery.discovered.size == worker_threads
46
- end
47
-
48
- # number of worker threads running
49
- def worker_threads
50
- @workers.list.size
51
- end
52
-
53
- # Gracefully stop processing
54
- def stop
55
- logger.info("stopping threaded poller...")
56
- sleep 1 until started? # give it a chance to start up before shutting down.
57
- logger.info("Giving Listener Threads a chance to shut down. This may take a while... ")
58
- @workers.list.each { |w| w[:shutdown] = true }
59
- logger.info("Listener threads were shut down. ")
60
- end
61
-
62
- # Listen for one worker class
63
- def clazz_listen(clazz)
64
- logger.debug("Listener thread #{clazz.name} started")
65
-
66
- # Read thread configuration if available
67
- if Workling.config.has_key?(:listeners)
68
- if Workling.config[:listeners].has_key?(clazz.to_s)
69
- config = Workling.config[:listeners][clazz.to_s].symbolize_keys
70
- thread_sleep_time = config[:sleep_time] if config.has_key?(:sleep_time)
71
- Thread.current.priority = config[:priority] if config.has_key?(:priority)
72
- end
73
- end
74
-
75
- thread_sleep_time ||= self.class.sleep_time
76
-
77
- # Setup connection to client (one per thread)
78
- connection = @client_class.new
79
- connection.connect
80
- logger.info("** Starting client #{ connection.class } for #{clazz.name} queue")
81
-
82
- # Start dispatching those messages
83
- while (!Thread.current[:shutdown]) do
84
- begin
85
-
86
- # Wrap code calling ActiveRecord::Base.connection_active? in a
87
- # mutex to avoid problem with spawning threads on multi-core
88
- # machines running MySQL.
89
- @mutex.synchronize do
90
- unless ActiveRecord::Base.connection.active? # Keep MySQL connection alive
91
- unless ActiveRecord::Base.connection.reconnect!
92
- logger.fatal("Failed - Database not available!")
93
- break
94
- end
95
- end
96
- end
97
-
98
- # Dispatch and process the messages
99
- n = dispatch!(connection, clazz)
100
- logger.debug("Listener thread #{clazz.name} processed #{n.to_s} queue items") if n > 0
101
- sleep(thread_sleep_time) unless n > 0
102
-
103
- # If there is a memcache error, hang for a bit to give it a chance to fire up again
104
- # and reset the connection.
105
- rescue Workling::WorklingConnectionError
106
- logger.warn("Listener thread #{clazz.name} failed to connect. Resetting connection.")
107
- sleep(self.class.reset_time)
108
- connection.reset
109
- end
110
- end
111
-
112
- logger.debug("Listener thread #{clazz.name} ended")
113
- end
114
-
115
- # Dispatcher for one worker class. Will throw MemCacheError if unable to connect.
116
- # Returns the number of worker methods called
117
- def dispatch!(connection, clazz)
118
- n = 0
119
- for queue in @routing.queue_names_routing_class(clazz)
120
- begin
121
- result = connection.retrieve(queue)
122
- if result
123
- n += 1
124
- handler = @routing[queue]
125
- method_name = @routing.method_name(queue)
126
- logger.debug("Calling #{handler.class.to_s}\##{method_name}(#{result.inspect})")
127
- handler.dispatch_to_worker_method(method_name, result)
128
- end
129
- rescue MemCache::MemCacheError => e
130
- logger.error("FAILED to connect with queue #{ queue }: #{ e } }")
131
- raise e
132
- end
133
- end
134
-
135
- return n
136
- end
137
- end
138
- end
139
- end
140
- end
@@ -1,35 +0,0 @@
1
- require 'workling/remote/runners/base'
2
-
3
- #
4
- # Use Ara Howards BackgroundJob to run the work. BackgroundJob loads Rails once per requested Job.
5
- # It persists over the database, and there is no requirement for separate processes to be started.
6
- # Since rails has to load before each request, it takes a moment for the job to run.
7
- #
8
- module Workling
9
- module Remote
10
- module Runners
11
- class BackgroundjobRunner < Workling::Remote::Runners::Base
12
- cattr_accessor :routing
13
-
14
- def initialize
15
- BackgroundjobRunner.routing =
16
- Workling::Routing::ClassAndMethodRouting.new
17
- end
18
-
19
- # passes the job to bj by serializing the options to xml and passing them to
20
- # ./script/bj_invoker.rb, which in turn routes the deserialized args to the
21
- # appropriate worker.
22
- def run(clazz, method, options = {})
23
- stdin = @@routing.queue_for(clazz, method) +
24
- " " +
25
- options.to_xml(:indent => 0, :skip_instruct => true)
26
-
27
- Bj.submit "./script/runner ./script/bj_invoker.rb",
28
- :stdin => stdin
29
-
30
- return nil # that means nothing!
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,42 +0,0 @@
1
- #
2
- # Base class for Workling Runners.
3
- #
4
- # Runners must subclass this and implement the method
5
- #
6
- # Workling::Remote::Runners::Base#run(clazz, method, options = {})
7
- #
8
- # which is responsible for pushing the requested job into the background. Depending
9
- # on the Runner, this may require other code to dequeue the job. The actual
10
- # invocation of the runner should be done like this:
11
- #
12
- # Workling.find(clazz, method).dispatch_to_worker_method(method, options)
13
- #
14
- # This ensures for consistent logging and handling of propagated exceptions. You can
15
- # also call the convenience method
16
- #
17
- # Workling::Remote::Runners::Base#dispatch!(clazz, method, options)
18
- #
19
- # which invokes this for you.
20
- #
21
- module Workling
22
- module Remote
23
- module Runners
24
- class Base
25
-
26
- # runner uses this to connect to a job broker
27
- cattr_accessor :client
28
-
29
- # default logger defined in Workling::Base.logger
30
- def logger
31
- Workling::Base.logger
32
- end
33
-
34
- # find the worker instance and invoke it. Invoking the worker method like this ensures for
35
- # consistent logging and handling of propagated exceptions.
36
- def dispatch!(clazz, method, options)
37
- Workling.find(clazz, method).dispatch_to_worker_method(method, options)
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,45 +0,0 @@
1
- require 'workling/remote/runners/base'
2
- require 'workling/clients/memcache_queue_client'
3
-
4
- #
5
- # Runs Jobs over a Client. The client should be a subclass of Workling::Client::Base.
6
- # Set the client like this:
7
- #
8
- # Workling::Remote::Runners::ClientRunner.client = Workling::Clients::AmqpClient.new
9
- #
10
- # Jobs are dispatched by requesting them on the Client. The Runner takes care of mapping of queue names to worker code.
11
- # this is done with Workling::ClassAndMethodRouting, but you can use your own by sublassing Workling::Routing.
12
- # Don’t worry about any of this if you’re not dealing directly with the queues.
13
- #
14
- # There’s a workling-client daemon that uses the configured invoker to retrieve work and dispatching these to the
15
- # responsible workers. If you intend to run this on a remote machine, then just check out your rails project
16
- # there and start up the workling client like this: ruby script/workling_client run.
17
- #
18
- module Workling
19
- module Remote
20
- module Runners
21
- class ClientRunner < Workling::Remote::Runners::Base
22
-
23
- # Routing class. Workling::Routing::ClassAndMethodRouting.new by default.
24
- cattr_accessor :routing
25
- @@routing ||= Workling::Routing::ClassAndMethodRouting.new
26
-
27
- # The workling Client class. Workling::Clients::MemcacheQueueClient.new by default.
28
- cattr_accessor :client
29
- @@client ||= Workling::Clients::MemcacheQueueClient.new
30
-
31
- # enqueues the job onto the client
32
- def run(clazz, method, options = {})
33
-
34
- # neet to connect in here as opposed to the constructor, since the EM loop is
35
- # not available there.
36
- @connected ||= self.class.client.connect
37
-
38
- self.class.client.request(@@routing.queue_for(clazz, method), options)
39
-
40
- return nil
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,23 +0,0 @@
1
- require 'workling/remote/runners/base'
2
-
3
- #
4
- # directly dispatches to the worker method, in-process. options are first marshalled then dumped
5
- # in order to simulate the sideeffects of a remote call.
6
- #
7
- module Workling
8
- module Remote
9
- module Runners
10
- class NotRemoteRunner < Workling::Remote::Runners::Base
11
-
12
- # directly dispatches to the worker method, in-process. options are first marshalled then dumped
13
- # in order to simulate the sideeffects of a remote call.
14
- def run(clazz, method, options = {})
15
- options = Marshal.load(Marshal.dump(options)) # get this to behave more like the remote runners
16
- dispatch!(clazz, method, options)
17
-
18
- return nil # nada. niente.
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,23 +0,0 @@
1
- require 'workling/remote/runners/base'
2
-
3
- module Workling
4
- module Remote
5
- module Runners
6
- class RudeqRunner < Workling::Remote::Runners::Base
7
- cattr_accessor :routing
8
- cattr_accessor :client
9
-
10
- def initialize
11
- RudeqRunner.client = Workling::Rudeq::Client.new
12
- RudeqRunner.routing = Workling::Starling::Routing::ClassAndMethodRouting.new
13
- end
14
-
15
- def run(clazz, method, options = {})
16
- RudeqRunner.client.set(@@routing.queue_for(clazz, method), options)
17
-
18
- return nil # empty.
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,38 +0,0 @@
1
- require 'workling/remote/runners/base'
2
-
3
- #
4
- # Run the job over the spawn plugin. Refer to the README for instructions on
5
- # installing Spawn.
6
- #
7
- # Spawn forks the entire process once for each job. This means that the job starts
8
- # with a very low latency, but takes up more memory for each job.
9
- #
10
- # It's also possible to configure Spawn to start a Thread for each job. Do this
11
- # by setting
12
- #
13
- # Workling::Remote::Runners::SpawnRunner.options = { :method => :thread }
14
- #
15
- # Have a look at the Spawn README to find out more about the characteristics of this.
16
- #
17
- module Workling
18
- module Remote
19
- module Runners
20
- class SpawnRunner < Workling::Remote::Runners::Base
21
- cattr_accessor :options
22
-
23
- # use thread for development and test modes. easier to hunt down exceptions that way.
24
- @@options = { :method => (RAILS_ENV == "test" || RAILS_ENV == "development" ? :thread : :fork) }
25
- include Spawn if Workling.spawn_installed?
26
-
27
- # dispatches to Spawn, using the :fork option.
28
- def run(clazz, method, options = {})
29
- spawn(SpawnRunner.options) do # exceptions are trapped in here.
30
- dispatch!(clazz, method, options)
31
- end
32
-
33
- return nil # that means nothing!
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,13 +0,0 @@
1
- require 'workling/remote/runners/client_runner'
2
-
3
- #
4
- # DEPRECATED. Should use ClientRunner instead.
5
- #
6
- module Workling
7
- module Remote
8
- module Runners
9
- class StarlingRunner < Workling::Remote::Runners::ClientRunner
10
- end
11
- end
12
- end
13
- end