derfred-workling 0.4.9.2 → 0.4.9.3

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 (58) hide show
  1. data/bin/workling_client +9 -7
  2. data/{script → contrib}/bj_invoker.rb +0 -0
  3. data/{script → contrib}/starling_status.rb +0 -0
  4. data/lib/{cattr_accessor.rb → extensions/cattr_accessor.rb} +0 -0
  5. data/lib/{mattr_accessor.rb → extensions/mattr_accessor.rb} +0 -0
  6. data/lib/workling.rb +98 -94
  7. data/lib/workling/base.rb +6 -11
  8. data/lib/workling/clients/amqp_client.rb +13 -5
  9. data/lib/workling/clients/amqp_exchange_client.rb +19 -11
  10. data/lib/workling/clients/backgroundjob_client.rb +25 -0
  11. data/lib/workling/clients/base.rb +40 -8
  12. data/lib/workling/clients/broker_base.rb +63 -0
  13. data/lib/workling/clients/memcache_queue_client.rb +24 -12
  14. data/lib/workling/clients/memory_queue_client.rb +34 -0
  15. data/lib/workling/clients/not_client.rb +14 -0
  16. data/lib/workling/clients/not_remote_client.rb +17 -0
  17. data/lib/workling/clients/spawn_client.rb +47 -0
  18. data/lib/workling/clients/sqs_client.rb +9 -8
  19. data/lib/workling/clients/thread_client.rb +18 -0
  20. data/lib/workling/clients/xmpp_client.rb +14 -4
  21. data/lib/workling/discovery.rb +1 -1
  22. data/lib/workling/invokers/amqp_single_subscriber.rb +42 -0
  23. data/lib/workling/invokers/base.rb +124 -0
  24. data/lib/workling/invokers/basic_poller.rb +38 -0
  25. data/lib/workling/invokers/eventmachine_subscriber.rb +38 -0
  26. data/lib/workling/invokers/looped_subscriber.rb +34 -0
  27. data/lib/workling/invokers/thread_pool_poller.rb +165 -0
  28. data/lib/workling/invokers/threaded_poller.rb +149 -0
  29. data/lib/workling/remote.rb +10 -41
  30. data/lib/workling/return/store/base.rb +7 -7
  31. data/lib/workling/return/store/memory_return_store.rb +0 -2
  32. data/lib/workling/return/store/starling_return_store.rb +7 -8
  33. data/lib/workling/routing/base.rb +1 -4
  34. data/lib/workling/routing/class_and_method_routing.rb +0 -2
  35. data/lib/workling/routing/static_routing.rb +0 -4
  36. data/lib/{workling_server.rb → workling_daemon.rb} +27 -25
  37. metadata +21 -28
  38. data/lib/rude_q/client.rb +0 -11
  39. data/lib/workling/remote/invokers/amqp_single_subscriber.rb +0 -45
  40. data/lib/workling/remote/invokers/base.rb +0 -124
  41. data/lib/workling/remote/invokers/basic_poller.rb +0 -41
  42. data/lib/workling/remote/invokers/eventmachine_subscriber.rb +0 -41
  43. data/lib/workling/remote/invokers/looped_subscriber.rb +0 -38
  44. data/lib/workling/remote/invokers/thread_pool_poller.rb +0 -169
  45. data/lib/workling/remote/invokers/threaded_poller.rb +0 -153
  46. data/lib/workling/remote/runners/amqp_exchange_runner.rb +0 -45
  47. data/lib/workling/remote/runners/backgroundjob_runner.rb +0 -35
  48. data/lib/workling/remote/runners/base.rb +0 -42
  49. data/lib/workling/remote/runners/client_runner.rb +0 -46
  50. data/lib/workling/remote/runners/not_remote_runner.rb +0 -23
  51. data/lib/workling/remote/runners/not_runner.rb +0 -17
  52. data/lib/workling/remote/runners/rudeq_runner.rb +0 -23
  53. data/lib/workling/remote/runners/spawn_runner.rb +0 -38
  54. data/lib/workling/remote/runners/starling_runner.rb +0 -13
  55. data/lib/workling/return/store/rudeq_return_store.rb +0 -24
  56. data/lib/workling/rudeq.rb +0 -7
  57. data/lib/workling/rudeq/client.rb +0 -17
  58. data/lib/workling/rudeq/poller.rb +0 -116
@@ -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,38 +0,0 @@
1
- require 'workling/remote/invokers/base'
2
-
3
- #
4
- # Subscribes the workers to the correct queues.
5
- #
6
- module Workling
7
- module Remote
8
- module Invokers
9
- class LoopedSubscriber < Workling::Remote::Invokers::Base
10
-
11
- def initialize(routing, client_class)
12
- super
13
- end
14
-
15
- #
16
- # Starts EM loop and sets up subscription callbacks for workers.
17
- #
18
- def listen
19
- connect do
20
- routes.each do |route|
21
- @client.subscribe(route) do |args|
22
- run(route, args)
23
- end
24
- end
25
-
26
- loop do
27
- sleep 1
28
- end
29
- end
30
- end
31
-
32
- def stop
33
-
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,169 +0,0 @@
1
- require 'workling/remote/invokers/base'
2
-
3
- require 'thread'
4
- require 'mutex_m'
5
-
6
- #
7
- # A polling invoker that executes the jobs using a thread pool.
8
- #
9
- # This invoker was designed for long running tasks and Rails 2.2. It is expected
10
- # that each worker will manage it's own database connections using ActiveRecord::Base.connection_pool.
11
- #
12
- # This implementation isn't using most of the features provided by Invokers::Base
13
- # because of it's assumptions about database connections
14
- #
15
- # Example:
16
- # user = nil
17
- # ActiveRecord::Base.connection_pool.with_connection do
18
- # user = User.find(options[:id])
19
- # end
20
- #
21
- # # Do long running stuff here
22
- # user.wait_until_birthday
23
- # user.buy_gift 'BRAWNDO!'
24
- #
25
- # ActiveRecord::Base.connection_pool.with_connection do
26
- # user.save
27
- # end
28
- #
29
- module Workling
30
- module Remote
31
- module Invokers
32
- class ThreadPoolPoller < Workling::Remote::Invokers::Base
33
- attr_reader :sleep_time, :reset_time, :pool_capacity
34
-
35
- def initialize(routing, client_class)
36
- @routing = routing
37
- @client_class = client_class
38
-
39
- # Grab settings out of the config file
40
- @sleep_time = (Workling.config[:sleep_time] || 2).to_f
41
- @reset_time = (Workling.config[:reset_time] || 30).to_f
42
-
43
- # Pool of polling threads
44
- @pollers = []
45
- @pollers.extend(Mutex_m)
46
-
47
- # Pool of worker threads
48
- @workers = []
49
- @workers.extend(Mutex_m)
50
-
51
- # Connection to the job queue
52
- @pool_capacity = (Workling.config[:pool_size] || 25).to_i
53
- end
54
-
55
- # Start up the checking for items on the queue. Will block until stop is called and all pollers
56
- # and workers have finished execution.
57
- def listen
58
- logger.info("Starting ThreadPoolPoller...")
59
-
60
- # Determine which queues need checking
61
- Workling::Discovery.discovered.map do |klass|
62
- @pollers.synchronize do
63
- # Polls the backing queue for jobs to be done
64
- @pollers << Thread.new do
65
- poller_thread(@routing.queue_names_routing_class(klass))
66
- end
67
- end
68
- end
69
-
70
- # Wait for the poller and all outstanding workers to finish.
71
- #
72
- # This is a little tricky because we're doing some synchronization on pollers... but
73
- # the list of pollers is never modified after being setup above.
74
- @pollers.synchronize { @pollers.dup }.each { |p| p.join }
75
- @pollers.synchronize { @pollers.clear }
76
- logger.info("Pollers have all finished")
77
-
78
- @workers.synchronize { @workers.dup }.each { |w| w.join }
79
- logger.info("Worker threads have all finished")
80
- end
81
-
82
- # Instructs the thread pool poller to stop checking for new jobs on the backing queue.
83
- def stop
84
- logger.info("Stopping thread pool invoker pollers and workers...")
85
- @pollers.synchronize { @pollers.each { |p| p[:shutdown] = true } }
86
- end
87
-
88
- # Set pool_size in workling config to adjust the maximum number of threads in the pool
89
- def workers_available?
90
- worker_threads < @pool_capacity
91
- end
92
-
93
- # Number of correctly active worker threads
94
- def worker_threads
95
- @workers.synchronize { @workers.size }
96
- end
97
-
98
- # Number of currently active polling threads
99
- def poller_threads
100
- @pollers.synchronize { @pollers.size }
101
- end
102
-
103
- private
104
-
105
- def poller_thread(queues)
106
- # Make sure queues is an array
107
- queues = [queues].flatten!
108
-
109
- # Connect our client to the backing queue
110
- client = @client_class.new
111
- client.connect
112
- logger.info("** Starting client #{ client.class } for #{ queues.inspect }") if logger.info?
113
-
114
- # Poll each queue for new items
115
- while(!Thread.current[:shutdown]) do
116
- # Check each queue for a job posting
117
- queues.each do |queue|
118
- break if Thread.current[:shutdown]
119
-
120
- begin
121
- # Take a job off the queue and execute it in a new worker thread.
122
- #
123
- # Don't pop any jobs off the backing queue if the thread pool
124
- # is full. This keeps them on the master queue so other instances
125
- # can process them.
126
- while(workers_available? && (options = client.retrieve(queue)))
127
- logger.debug("#{queue} received job #{ options.inspect }") if logger.debug?
128
-
129
- @workers.synchronize do
130
- @workers << Thread.new do
131
- begin
132
- # Execute the job
133
- run(queue, options)
134
- rescue Exception => e
135
- # Log the exception since there isn't much else we can do about it at this point
136
- logger.error(e) if logger.error?
137
- ensure
138
- # Make sure the current thread's connection gets released
139
- if(ActiveRecord::Base.connection_pool)
140
- ActiveRecord::Base.connection_pool.release_connection
141
- end
142
-
143
- # Remove this thread from the list of active workers
144
- @workers.synchronize do
145
- @workers.delete(Thread.current)
146
- end
147
- end
148
- end
149
- end
150
-
151
- # Break out of the checks early if shutdown was called
152
- break if Thread.current[:shutdown]
153
- end
154
- rescue Workling::WorklingError => e
155
- logger.error("FAILED to connect with queue #{ queue }: #{ e } }") if logger.error?
156
- sleep(@reset_time)
157
-
158
- # FIXME: This will _definitely_ blow up with AMQP since there is no reset call
159
- client.reset
160
- end
161
- end
162
-
163
- sleep(@sleep_time) unless Thread.current[:shutdown]
164
- end
165
- end
166
- end
167
- end
168
- end
169
- end
@@ -1,153 +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
- if defined?(ActiveRecord::Base)
39
- ActiveRecord::Base.verify_active_connections!
40
- end
41
-
42
- logger.debug("Cleaned up connection: out!")
43
- end
44
-
45
- # Check if all Worker threads have been started.
46
- def started?
47
- logger.debug("checking if started... list size is #{ worker_threads }")
48
- Workling::Discovery.discovered.size == worker_threads
49
- end
50
-
51
- # number of worker threads running
52
- def worker_threads
53
- @workers.list.size
54
- end
55
-
56
- # Gracefully stop processing
57
- def stop
58
- logger.info("stopping threaded poller...")
59
- sleep 1 until started? # give it a chance to start up before shutting down.
60
- logger.info("Giving Listener Threads a chance to shut down. This may take a while... ")
61
- @workers.list.each { |w| w[:shutdown] = true }
62
- logger.info("Listener threads were shut down. ")
63
- end
64
-
65
- # Listen for one worker class
66
- def clazz_listen(clazz)
67
- logger.debug("Listener thread #{clazz.name} started")
68
-
69
- # Read thread configuration if available
70
- if Workling.config.has_key?(:listeners)
71
- if Workling.config[:listeners].has_key?(clazz.to_s)
72
- config = Workling.config[:listeners][clazz.to_s].symbolize_keys
73
- thread_sleep_time = config[:sleep_time] if config.has_key?(:sleep_time)
74
- Thread.current.priority = config[:priority] if config.has_key?(:priority)
75
- end
76
- end
77
-
78
- thread_sleep_time ||= self.class.sleep_time
79
-
80
- # Setup connection to client (one per thread)
81
- connection = @client_class.new
82
- connection.connect
83
- logger.info("** Starting client #{ connection.class } for #{clazz.name} queue")
84
-
85
- # Start dispatching those messages
86
- while (!Thread.current[:shutdown]) do
87
- begin
88
-
89
- # Thanks for this Brent!
90
- #
91
- # ...Just a heads up, due to how rails’ MySQL adapter handles this
92
- # call ‘ActiveRecord::Base.connection.active?’, you’ll need
93
- # to wrap the code that checks for a connection in in a mutex.
94
- #
95
- # ....I noticed this while working with a multi-core machine that
96
- # was spawning multiple workling threads. Some of my workling
97
- # threads would hit serious issues at this block of code without
98
- # the mutex.
99
- #
100
- if defined?(ActiveRecord::Base)
101
- @mutex.synchronize do
102
- unless ActiveRecord::Base.connection.active? # Keep MySQL connection alive
103
- unless ActiveRecord::Base.connection.reconnect!
104
- logger.fatal("Failed - Database not available!")
105
- break
106
- end
107
- end
108
- end
109
- end
110
-
111
- # Dispatch and process the messages
112
- n = dispatch!(connection, clazz)
113
- logger.debug("Listener thread #{clazz.name} processed #{n.to_s} queue items") if n > 0
114
- sleep(thread_sleep_time) unless n > 0
115
-
116
- # If there is a memcache error, hang for a bit to give it a chance to fire up again
117
- # and reset the connection.
118
- rescue Workling::WorklingConnectionError
119
- logger.warn("Listener thread #{clazz.name} failed to connect. Resetting connection.")
120
- sleep(self.class.reset_time)
121
- connection.reset
122
- end
123
- end
124
-
125
- logger.debug("Listener thread #{clazz.name} ended")
126
- end
127
-
128
- # Dispatcher for one worker class. Will throw MemCacheError if unable to connect.
129
- # Returns the number of worker methods called
130
- def dispatch!(connection, clazz)
131
- n = 0
132
- for queue in @routing.queue_names_routing_class(clazz)
133
- begin
134
- result = connection.retrieve(queue)
135
- if result
136
- n += 1
137
- handler = @routing[queue]
138
- method_name = @routing.method_name(queue)
139
- logger.debug("Calling #{handler.class.to_s}\##{method_name}(#{result.inspect})")
140
- handler.dispatch_to_worker_method(method_name, result)
141
- end
142
- rescue Workling::WorklingError => e
143
- logger.error("FAILED to connect with queue #{ queue }: #{ e } }")
144
- raise e
145
- end
146
- end
147
-
148
- return n
149
- end
150
- end
151
- end
152
- end
153
- end
@@ -1,45 +0,0 @@
1
- require 'workling/remote/runners/base'
2
-
3
- #
4
- # Runs Jobs over a Client. The client should be a subclass of Workling::Client::Base.
5
- # Set the client like this:
6
- #
7
- # Workling::Remote::Runners::ClientRunner.client = Workling::Clients::AmqpClient.new
8
- #
9
- # Jobs are dispatched by requesting them on the Client. The Runner takes care of mapping of queue names to worker code.
10
- # this is done with Workling::ClassAndMethodRouting, but you can use your own by sublassing Workling::Routing.
11
- # Don’t worry about any of this if you’re not dealing directly with the queues.
12
- #
13
- # There’s a workling-client daemon that uses the configured invoker to retrieve work and dispatching these to the
14
- # responsible workers. If you intend to run this on a remote machine, then just check out your rails project
15
- # there and start up the workling client like this: ruby script/workling_client run.
16
- #
17
- module Workling
18
- module Remote
19
- module Runners
20
- class AmqpExchangeRunner < Workling::Remote::Runners::Base
21
-
22
- # Routing class. Workling::Routing::ClassAndMethodRouting.new by default.
23
- cattr_accessor :routing
24
- @@routing ||= Workling::Routing::ClassAndMethodRouting.new
25
-
26
- # The workling Client class. Workling::Clients::MemcacheQueueClient.new by default.
27
- cattr_accessor :client
28
- @@client ||= Workling::Clients::AmqpExchangeClient.new
29
-
30
- # enqueues the job onto the client
31
- def run(clazz, method, options = {})
32
-
33
- # neet to connect in here as opposed to the constructor, since the EM loop is
34
- # not available there.
35
- @connected ||= self.class.client.connect
36
-
37
- # NOTE - currently hardcoded to use the default exchange
38
- self.class.client.request("amq.topic", options)
39
-
40
- return nil
41
- end
42
- end
43
- end
44
- end
45
- end