derfred-workling 0.4.9.1 → 0.4.9.2

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 (44) hide show
  1. data/lib/cattr_accessor.rb +51 -0
  2. data/lib/mattr_accessor.rb +55 -0
  3. data/lib/rude_q/client.rb +11 -0
  4. data/lib/workling/base.rb +115 -0
  5. data/lib/workling/clients/amqp_client.rb +38 -0
  6. data/lib/workling/clients/amqp_exchange_client.rb +50 -0
  7. data/lib/workling/clients/base.rb +57 -0
  8. data/lib/workling/clients/memcache_queue_client.rb +91 -0
  9. data/lib/workling/clients/sqs_client.rb +162 -0
  10. data/lib/workling/clients/xmpp_client.rb +100 -0
  11. data/lib/workling/discovery.rb +16 -0
  12. data/lib/workling/remote/invokers/amqp_single_subscriber.rb +45 -0
  13. data/lib/workling/remote/invokers/base.rb +124 -0
  14. data/lib/workling/remote/invokers/basic_poller.rb +41 -0
  15. data/lib/workling/remote/invokers/eventmachine_subscriber.rb +41 -0
  16. data/lib/workling/remote/invokers/looped_subscriber.rb +38 -0
  17. data/lib/workling/remote/invokers/thread_pool_poller.rb +169 -0
  18. data/lib/workling/remote/invokers/threaded_poller.rb +153 -0
  19. data/lib/workling/remote/runners/amqp_exchange_runner.rb +45 -0
  20. data/lib/workling/remote/runners/backgroundjob_runner.rb +35 -0
  21. data/lib/workling/remote/runners/base.rb +42 -0
  22. data/lib/workling/remote/runners/client_runner.rb +46 -0
  23. data/lib/workling/remote/runners/not_remote_runner.rb +23 -0
  24. data/lib/workling/remote/runners/not_runner.rb +17 -0
  25. data/lib/workling/remote/runners/rudeq_runner.rb +23 -0
  26. data/lib/workling/remote/runners/spawn_runner.rb +38 -0
  27. data/lib/workling/remote/runners/starling_runner.rb +13 -0
  28. data/lib/workling/remote.rb +69 -0
  29. data/lib/workling/return/store/base.rb +42 -0
  30. data/lib/workling/return/store/iterator.rb +24 -0
  31. data/lib/workling/return/store/memory_return_store.rb +26 -0
  32. data/lib/workling/return/store/rudeq_return_store.rb +24 -0
  33. data/lib/workling/return/store/starling_return_store.rb +31 -0
  34. data/lib/workling/routing/base.rb +16 -0
  35. data/lib/workling/routing/class_and_method_routing.rb +57 -0
  36. data/lib/workling/routing/static_routing.rb +47 -0
  37. data/lib/workling/rudeq/client.rb +17 -0
  38. data/lib/workling/rudeq/poller.rb +116 -0
  39. data/lib/workling/rudeq.rb +7 -0
  40. data/lib/workling.rb +195 -0
  41. data/lib/workling_server.rb +108 -0
  42. data/script/bj_invoker.rb +11 -0
  43. data/script/starling_status.rb +37 -0
  44. metadata +44 -1
@@ -0,0 +1,51 @@
1
+ # Extends the class object with class and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ #
4
+ # class Person
5
+ # cattr_accessor :hair_colors
6
+ # end
7
+ #
8
+ # Person.hair_colors = [:brown, :black, :blonde, :red]
9
+ class Class
10
+ def cattr_reader(*syms)
11
+ syms.flatten.each do |sym|
12
+ next if sym.is_a?(Hash)
13
+ class_eval(<<-EOS, __FILE__, __LINE__)
14
+ unless defined? @@#{sym} # unless defined? @@hair_colors
15
+ @@#{sym} = nil # @@hair_colors = nil
16
+ end # end
17
+ #
18
+ def self.#{sym} # def self.hair_colors
19
+ @@#{sym} # @@hair_colors
20
+ end # end
21
+ #
22
+ def #{sym} # def hair_colors
23
+ @@#{sym} # @@hair_colors
24
+ end # end
25
+ EOS
26
+ end
27
+ end
28
+
29
+ def cattr_writer(*syms)
30
+ syms.flatten.each do |sym|
31
+ class_eval(<<-EOS, __FILE__, __LINE__)
32
+ unless defined? @@#{sym} # unless defined? @@hair_colors
33
+ @@#{sym} = nil # @@hair_colors = nil
34
+ end # end
35
+ #
36
+ def self.#{sym}=(obj) # def self.hair_colors=(obj)
37
+ @@#{sym} = obj # @@hair_colors = obj
38
+ end # end
39
+ #
40
+ def #{sym}=(obj) # def hair_colors=(obj)
41
+ @@#{sym} = obj # @@hair_colors = obj
42
+ end # end
43
+ EOS
44
+ end
45
+ end
46
+
47
+ def cattr_accessor(*syms)
48
+ cattr_reader(*syms)
49
+ cattr_writer(*syms)
50
+ end
51
+ end
@@ -0,0 +1,55 @@
1
+ # Extends the module object with module and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ #
4
+ # module AppConfiguration
5
+ # mattr_accessor :google_api_key
6
+ # self.google_api_key = "123456789"
7
+ #
8
+ # mattr_accessor :paypal_url
9
+ # self.paypal_url = "www.sandbox.paypal.com"
10
+ # end
11
+ #
12
+ # AppConfiguration.google_api_key = "overriding the api key!"
13
+ class Module
14
+ def mattr_reader(*syms)
15
+ syms.each do |sym|
16
+ next if sym.is_a?(Hash)
17
+ class_eval(<<-EOS, __FILE__, __LINE__)
18
+ unless defined? @@#{sym} # unless defined? @@pagination_options
19
+ @@#{sym} = nil # @@pagination_options = nil
20
+ end # end
21
+ #
22
+ def self.#{sym} # def self.pagination_options
23
+ @@#{sym} # @@pagination_options
24
+ end # end
25
+ #
26
+ def #{sym} # def pagination_options
27
+ @@#{sym} # @@pagination_options
28
+ end # end
29
+ EOS
30
+ end
31
+ end
32
+
33
+ def mattr_writer(*syms)
34
+ syms.each do |sym|
35
+ class_eval(<<-EOS, __FILE__, __LINE__)
36
+ unless defined? @@#{sym} # unless defined? @@pagination_options
37
+ @@#{sym} = nil # @@pagination_options = nil
38
+ end # end
39
+ #
40
+ def self.#{sym}=(obj) # def self.pagination_options=(obj)
41
+ @@#{sym} = obj # @@pagination_options = obj
42
+ end # end
43
+ #
44
+ def #{sym}=(obj) # def pagination_options=(obj)
45
+ @@#{sym} = obj # @@pagination_options = obj
46
+ end # end
47
+ EOS
48
+ end
49
+ end
50
+
51
+ def mattr_accessor(*syms)
52
+ mattr_reader(*syms)
53
+ mattr_writer(*syms)
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ #
2
+ # A RudeQ client that behvaes somewhat like memcache-client
3
+ #
4
+ module RudeQ
5
+ class Client
6
+ def initialize(*args); super(); end
7
+ def set(key, value); RudeQueue.set(key, value); end;
8
+ def get(key); RudeQueue.get(key); end;
9
+ def stats; ActiveRecord::Base.connection; end
10
+ end
11
+ end
@@ -0,0 +1,115 @@
1
+ #require 'struct'
2
+
3
+ #
4
+ # All worker classes must inherit from this class, and be saved in app/workers.
5
+ #
6
+ # The Worker lifecycle:
7
+ # The Worker is loaded once, at which point the instance method 'create' is called.
8
+ #
9
+ # Invoking Workers:
10
+ # Calling async_my_method on the worker class will trigger background work.
11
+ # This means that the loaded Worker instance will receive a call to the method
12
+ # my_method(:uid => "thisjobsuid2348732947923").
13
+ #
14
+ # The Worker method must have a single hash argument. Note that the job :uid will
15
+ # be merged into the hash.
16
+ #
17
+ module Workling
18
+ class Base
19
+ cattr_writer :logger
20
+ def self.logger
21
+ @@logger ||= ::RAILS_DEFAULT_LOGGER
22
+ end
23
+
24
+ cattr_accessor :exposed_methods
25
+ @@exposed_methods ||= {}
26
+
27
+ def self.inherited(subclass)
28
+ Workling::Discovery.discovered << subclass
29
+ end
30
+
31
+ # expose a method using a custom queue name
32
+ def self.expose(method, options)
33
+ self.exposed_methods[method.to_s] = options[:as]
34
+ end
35
+
36
+ # identify the queue for a given method
37
+ def self.queue_for(method)
38
+ if self.exposed_methods.has_key?(method)
39
+ self.exposed_methods[method]
40
+ else
41
+ "#{ self.to_s.tableize }/#{ method }".split("/").join("__") # Don't split with : because it messes up memcache stats
42
+ end
43
+ end
44
+
45
+ # identify the method linked to the queue
46
+ def self.method_for(queue)
47
+ if self.exposed_methods.invert.has_key?(queue)
48
+ self.exposed_methods.invert[queue]
49
+ else
50
+ queue.split("__").last
51
+ end
52
+ end
53
+
54
+ def method_for(queue)
55
+ self.class.method_for(queue)
56
+ end
57
+
58
+
59
+ def initialize
60
+ super
61
+
62
+ create
63
+ end
64
+
65
+ # Put worker initialization code in here. This is good for restarting jobs that
66
+ # were interrupted.
67
+ def create
68
+ end
69
+
70
+ # override this if you want to set up exception notification
71
+ def notify_exception(exception, method, options)
72
+ logger.error "WORKLING ERROR: runner could not invoke #{ self.class }:#{ method } with #{ options.inspect }. error was: #{ exception.inspect }\n #{ exception.backtrace.join("\n") }"
73
+ end
74
+
75
+ # takes care of suppressing remote errors but raising Workling::WorklingNotFoundError
76
+ # where appropriate. swallow workling exceptions so that everything behaves like remote code.
77
+ # otherwise StarlingRunner and SpawnRunner would behave too differently to NotRemoteRunner.
78
+ def dispatch_to_worker_method(method, options = {})
79
+ begin
80
+ options = default_options.merge(options)
81
+ self.send(method, options)
82
+ rescue Workling::WorklingError => e
83
+ raise e
84
+ rescue Exception => e
85
+ notify_exception e, method, options
86
+ on_error(e) if respond_to?(:on_error)
87
+
88
+ # reraise after logging. the exception really can't go anywhere in many cases. (spawn traps the exception)
89
+ raise e if Workling.raise_exceptions?
90
+ end
91
+ end
92
+
93
+ # supply default_options as a hash in classes that inherit Workling::Base
94
+ def default_options
95
+ {}
96
+ end
97
+
98
+ # thanks to blaine cook for this suggestion.
99
+ def self.method_missing(method, *args, &block)
100
+ if method.to_s =~ /^asynch?_(.*)/
101
+ Workling::Remote.run(self.to_s.dasherize, $1, *args)
102
+ else
103
+ super
104
+ end
105
+ end
106
+
107
+ def self.logger
108
+ @logger ||= defined?(RAILS_DEFAULT_LOGGER) ? ::RAILS_DEFAULT_LOGGER : Logger.new($stdout)
109
+ end
110
+
111
+ def logger
112
+ self.class.logger
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,38 @@
1
+ require 'workling/clients/base'
2
+ Workling.try_load_an_amqp_client
3
+
4
+ #
5
+ # An Ampq client
6
+ #
7
+ module Workling
8
+ module Clients
9
+ class AmqpClient < Workling::Clients::Base
10
+
11
+ # starts the client.
12
+ def connect
13
+ begin
14
+ connection = AMQP.start((Workling.config[:amqp_options] ||{}).symbolize_keys)
15
+ @amq = MQ.new connection
16
+ rescue
17
+ raise WorklingError.new("couldn't start amq client. if you're running this in a server environment, then make sure the server is evented (ie use thin or evented mongrel, not normal mongrel.)")
18
+ end
19
+ end
20
+
21
+ # no need for explicit closing. when the event loop
22
+ # terminates, the connection is closed anyway.
23
+ def close; true; end
24
+
25
+ # subscribe to a queue
26
+ def subscribe(key)
27
+ @amq.queue(key).subscribe do |value|
28
+ data = Marshal.load(value) rescue value
29
+ yield data
30
+ end
31
+ end
32
+
33
+ # request and retrieve work
34
+ def retrieve(key); @amq.queue(key); end
35
+ def request(key, value); @amq.queue(key).publish(Marshal.dump(value)); end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ require 'workling/clients/base'
2
+ Workling.try_load_an_amqp_client
3
+
4
+ #
5
+ # An Ampq client
6
+ #
7
+ module Workling
8
+ module Clients
9
+ class AmqpExchangeClient < Workling::Clients::Base
10
+
11
+ # starts the client.
12
+ def connect
13
+ begin
14
+ @amq = MQ.new
15
+ rescue
16
+ raise WorklingError.new("couldn't start amq client. if you're running this in a server environment, then make sure the server is evented (ie use thin or evented mongrel, not normal mongrel.)")
17
+ end
18
+ end
19
+
20
+ # no need for explicit closing. when the event loop
21
+ # terminates, the connection is closed anyway.
22
+ def close; true; end
23
+
24
+ # subscribe to a queue
25
+ def subscribe(key)
26
+ @amq.queue(key).subscribe do |header, body|
27
+
28
+ puts "***** received msg with header - #{header.inspect}"
29
+
30
+ value = YAML.load(body)
31
+ yield value
32
+ end
33
+ end
34
+
35
+ # request and retrieve work
36
+ def retrieve(key)
37
+ @amq.queue(key)
38
+ end
39
+
40
+ # publish message to exchange
41
+ # using the specified routing key
42
+ def request(exchange_name, value)
43
+ key = value.delete(:routing_key)
44
+ msg = YAML.dump(value)
45
+ exchange = @amq.topic(exchange_name)
46
+ exchange.publish(msg, :routing_key => key)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,57 @@
1
+ #
2
+ # Clients are responsible for communicating with a job broker (ie connecting to starling or rabbitmq.)
3
+ #
4
+ # Clients are used to request jobs on a broker, get results for a job from a broker, and subscribe to results
5
+ # from a specific type of job.
6
+ #
7
+ module Workling
8
+ module Clients
9
+ class Base
10
+
11
+ # returns the Workling::Base.logger
12
+ def logger; Workling::Base.logger; end
13
+
14
+ #
15
+ # Requests a job on the broker.
16
+ #
17
+ # work_type:
18
+ # arguments: the argument to the worker method
19
+ #
20
+ def request(work_type, arguments)
21
+ raise NotImplementedError.new("Implement request(work_type, arguments) in your client. ")
22
+ end
23
+
24
+ #
25
+ # Gets job results off a job broker. Returns nil if there are no results.
26
+ #
27
+ # worker_uid: the uid returned by workling when the work was dispatched
28
+ #
29
+ def retrieve(work_uid)
30
+ raise NotImplementedError.new("Implement retrieve(work_uid) in your client. ")
31
+ end
32
+
33
+ #
34
+ # Subscribe to job results in a job broker.
35
+ #
36
+ # worker_type:
37
+ #
38
+ def subscribe(work_type)
39
+ raise NotImplementedError.new("Implement subscribe(work_type) in your client. ")
40
+ end
41
+
42
+ #
43
+ # Opens a connection to the job broker.
44
+ #
45
+ def connect
46
+ raise NotImplementedError.new("Implement connect() in your client. ")
47
+ end
48
+
49
+ #
50
+ # Closes the connection to the job broker.
51
+ #
52
+ def close
53
+ raise NotImplementedError.new("Implement close() in your client. ")
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,91 @@
1
+ require 'workling/clients/base'
2
+ require 'memcache'
3
+
4
+ #
5
+ # This client can be used for all Queue Servers that speak Memcached, such as Starling.
6
+ #
7
+ # Wrapper for the memcache connection. The connection is made using fiveruns-memcache-client,
8
+ # or memcache-client, if this is not available. See the README for a discussion of the memcache
9
+ # clients.
10
+ #
11
+ # method_missing delegates all messages through to the underlying memcache connection.
12
+ #
13
+ module Workling
14
+ module Clients
15
+ class MemcacheQueueClient < Workling::Clients::Base
16
+
17
+ # the class with which the connection is instantiated
18
+ cattr_accessor :memcache_client_class
19
+ @@memcache_client_class ||= ::MemCache
20
+
21
+ # the url with which the memcache client expects to reach starling
22
+ attr_accessor :queueserver_urls
23
+
24
+ # the memcache connection object
25
+ attr_accessor :connection
26
+
27
+ #
28
+ # the client attempts to connect to queueserver using the configuration options found in
29
+ #
30
+ # Workling.config. this can be configured in config/workling.yml.
31
+ #
32
+ # the initialization code will raise an exception if memcache-client cannot connect
33
+ # to queueserver.
34
+ #
35
+ def connect
36
+ @queueserver_urls = Workling.config[:listens_on].split(',').map { |url| url ? url.strip : url }
37
+ options = [@queueserver_urls, Workling.config[:memcache_options]].compact
38
+ self.connection = MemcacheQueueClient.memcache_client_class.new(*options)
39
+
40
+ raise_unless_connected!
41
+ end
42
+
43
+ # closes the memcache connection
44
+ def close
45
+ self.connection.flush_all
46
+ self.connection.reset
47
+ end
48
+
49
+ # implements the client job request and retrieval
50
+ def request(key, value)
51
+ set(key, value)
52
+ end
53
+
54
+ def retrieve(key)
55
+ begin
56
+ get(key)
57
+ rescue MemCache::MemCacheError => e
58
+ # failed to enqueue, raise a workling error so that it propagates upwards
59
+ raise Workling::WorklingConnectionError.new("#{e.class.to_s} - #{e.message}")
60
+ end
61
+ end
62
+
63
+ private
64
+ # make sure we can actually connect to queueserver on the given port
65
+ def raise_unless_connected!
66
+ begin
67
+ self.connection.stats
68
+ rescue
69
+ raise Workling::QueueserverNotFoundError.new
70
+ end
71
+ end
72
+
73
+ [:get, :set].each do |method|
74
+ class_eval <<-EOS
75
+ def #{method}(*args, &block)
76
+ self.connection.#{method}(*args, &block)
77
+ end
78
+ EOS
79
+ end
80
+
81
+ # delegates directly through to the memcache connection.
82
+ def method_missing(method, *args)
83
+ begin
84
+ self.connection.send(method, *args)
85
+ rescue MemCache::MemCacheError => e
86
+ raise Workling::WorklingConnectionError.new("#{e.class.to_s} - #{e.message}")
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,162 @@
1
+ require 'workling/clients/base'
2
+ require 'json'
3
+ require 'right_aws'
4
+
5
+ #
6
+ # An SQS client
7
+ #
8
+ # Requires the following configuration in workling.yml:
9
+ #
10
+ # production:
11
+ # sqs_options:
12
+ # aws_access_key_id: <your AWS access key id>
13
+ # aws_secret_access_key: <your AWS secret access key>
14
+ #
15
+ # You can also add the following optional parameters:
16
+ #
17
+ # # Queue names consist of an optional prefix, followed by the environment
18
+ # # and the name of the key.
19
+ # prefix: foo_
20
+ #
21
+ # # The number of SQS messages to retrieve at once. The maximum and default
22
+ # # value is 10.
23
+ # messages_per_req: 5
24
+ #
25
+ # # The SQS visibility timeout for retrieved messages. Defaults to 30 seconds.
26
+ # visibility_timeout: 15
27
+ #
28
+ module Workling
29
+ module Clients
30
+ class SqsClient < Workling::Clients::Base
31
+
32
+ AWS_MAX_QUEUE_NAME = 80
33
+
34
+ # Note that 10 is the maximum number of messages that can be retrieved
35
+ # in a single request.
36
+ DEFAULT_MESSAGES_PER_REQ = 10
37
+ DEFAULT_VISIBILITY_TIMEOUT = 30
38
+ DEFAULT_VISIBILITY_RESERVE = 10
39
+
40
+ # Mainly exposed for testing purposes
41
+ attr_reader :sqs_options
42
+ attr_reader :messages_per_req
43
+ attr_reader :visibility_timeout
44
+
45
+ # Starts the client.
46
+ def connect
47
+ @sqs_options = Workling.config[:sqs_options]
48
+
49
+ # Make sure that required options were specified
50
+ unless (@sqs_options.include?('aws_access_key_id') &&
51
+ @sqs_options.include?('aws_secret_access_key'))
52
+ raise WorklingError, 'Unable to start SqsClient due to missing SQS options'
53
+ end
54
+
55
+ # Optional settings
56
+ @messages_per_req = @sqs_options['messages_per_req'] || DEFAULT_MESSAGES_PER_REQ
57
+ @visibility_timeout = @sqs_options['visibility_timeout'] || DEFAULT_VISIBILITY_TIMEOUT
58
+ @visibility_reserve = @sqs_options['visibility_reserve'] || DEFAULT_VISIBILITY_RESERVE
59
+
60
+ begin
61
+ @sqs = RightAws::SqsGen2.new(
62
+ @sqs_options['aws_access_key_id'],
63
+ @sqs_options['aws_secret_access_key'],
64
+ :multi_thread => true)
65
+ rescue => e
66
+ raise WorklingError, "Unable to connect to SQS. Error: #{e}"
67
+ end
68
+ end
69
+
70
+ # No need for explicit closing, since there is no persistent
71
+ # connection to SQS.
72
+ def close
73
+ true
74
+ end
75
+
76
+ # Retrieve work.
77
+ def retrieve(key)
78
+ begin
79
+ # We're using a buffer per key to retrieve several messages at once,
80
+ # then return them one at a time until the buffer is empty.
81
+ # Workling seems to create one thread per worker class, each with its own
82
+ # client. But to be sure (and to be less dependent on workling internals),
83
+ # we store each buffer in a thread local variable.
84
+ buffer = Thread.current["buffer_#{key}"]
85
+ if buffer.nil? || buffer.empty?
86
+ Thread.current["buffer_#{key}"] = buffer = queue_for_key(key).receive_messages(
87
+ @messages_per_req, @visibility_timeout)
88
+ end
89
+
90
+ if buffer.empty?
91
+ nil
92
+ else
93
+ msg = buffer.shift
94
+
95
+ # We need to protect against the case that processing one of the
96
+ # messages in the buffer took so much time that the visibility
97
+ # timeout for the remaining messages has expired. To be on the
98
+ # safe side (since we need to leave enough time to delete the
99
+ # message), we drop it if more than half of the visibility timeout
100
+ # has elapsed.
101
+ if msg.received_at < (Time.now - (@visibility_timeout - @visibility_reserve))
102
+ nil
103
+ else
104
+ # Need to wrap in HashWithIndifferentAccess, as JSON serialization
105
+ # loses symbol keys.
106
+ parsed_msg = HashWithIndifferentAccess.new(JSON.parse(msg.body))
107
+
108
+ # Delete the msg from SQS, so we don't re-retrieve it after the
109
+ # visibility timeout. Ideally we would defer deleting a msg until
110
+ # after Workling has successfully processed it, but it currently
111
+ # doesn't provide the necessary hooks for this.
112
+ msg.delete
113
+
114
+ parsed_msg
115
+ end
116
+ end
117
+
118
+ rescue => e
119
+ logger.error "Error retrieving msg for key: #{key}; Error: #{e}\n#{e.backtrace.join("\n")}"
120
+ end
121
+
122
+ end
123
+
124
+ # Request work.
125
+ def request(key, value)
126
+ begin
127
+ queue_for_key(key).send_message(value.to_json)
128
+ rescue => e
129
+ logger.error "SQS Client: Error sending msg for key: #{key}, value: #{value.inspect}; Error: #{e}"
130
+ raise WorklingError, "Error sending msg for key: #{key}, value: #{value.inspect}; Error: #{e}"
131
+ end
132
+ end
133
+
134
+ # Returns the queue that corresponds to the specified key. Creates the
135
+ # queue if it doesn't exist yet.
136
+ def queue_for_key(key)
137
+ # Use thread local for storing queues, for the same reason as for buffers
138
+ Thread.current["queue_#{key}"] ||= @sqs.queue(queue_name(key), true, @visibility_timeout)
139
+ end
140
+
141
+ # Returns the queue name for the specified key. The name consists of an
142
+ # optional prefix, followed by the environment and the key itself. Note
143
+ # that with a long worker class / method name, the name could exceed the
144
+ # 80 character maximum for SQS queue names. We truncate the name until it
145
+ # fits, but there's still the danger of this not being unique any more.
146
+ # Might need to implement a more robust naming scheme...
147
+ def queue_name(key)
148
+ "#{@sqs_options['prefix'] || ''}#{env}_#{key}"[0, AWS_MAX_QUEUE_NAME]
149
+ end
150
+
151
+ private
152
+
153
+ def logger
154
+ Rails.logger
155
+ end
156
+
157
+ def env
158
+ Rails.env
159
+ end
160
+ end
161
+ end
162
+ end