Class: AceMQ::Rails::Consumer
- Inherits:
-
Object
- Object
- AceMQ::Rails::Consumer
- Defined in:
- lib/acemq/rails/consumer.rb
Overview
A handler for one queue, written as a class.
class OrdersConsumer < AceMQ::Rails::Consumer
queue "orders.new"
concurrency 4
retries max_attempts: 5, initial_delay: 1, max_delay: 60
def call()
Order.place!(.payload)
accept
end
end
A class rather than a block in an initializer, for three reasons that come
from Rails rather than from taste. It can be unit-tested by calling
new.call(message) with no broker anywhere. It lives in app/consumers,
where the autoloader finds it and a reload replaces it. And it has a name,
which is what a metric label, a log line and a stack trace want.
Subclassing registers it. Nothing runs until the consumer process starts it: the registry holds classes, and the code that turns a class into a subscription lives in Runner, which the web server never loads.
Constant Summary collapse
- Ack =
What #call must return. Re-exported so a consumer never has to reach into the library's namespace for the one constant it uses every time.
AceMQ::AMQP::Ack
Class Method Summary collapse
-
.arguments(table = nil) ⇒ Object
Arguments passed to
basic.consume, for a stream'sx-stream-offsetand the like. -
.broker_wait_threshold(seconds = nil) ⇒ Object
Where a retry of this consumer's messages waits.
-
.codec(instance = nil) ⇒ Object
The codec for this queue, when it is not the application's.
-
.concurrency(count = nil) ⇒ Object
Messages worked on at once by one instance of this consumer.
- .inherited(subclass) ⇒ Object private
-
.prefetch(count = nil) ⇒ Object
Unacknowledged messages this consumer holds at once.
-
.queue(name = nil) ⇒ Object
The queue this consumer reads.
-
.retries(policy = nil, **settings) ⇒ Object
The retry ladder for this queue.
-
.runnable? ⇒ Boolean
Whether this class is one the runner should subscribe.
-
.tag(value = nil) ⇒ Object
The consumer tag, which is what the broker's management interface shows beside the subscription.
Instance Method Summary collapse
-
#call(message) ⇒ AceMQ::AMQP::Ack
Handles one message.
Class Method Details
.arguments(table = nil) ⇒ Object
Arguments passed to basic.consume, for a stream's x-stream-offset
and the like.
110 111 112 113 114 |
# File 'lib/acemq/rails/consumer.rb', line 110 def arguments(table = nil) return setting(:arguments) || {} if table.nil? @arguments = table end |
.broker_wait_threshold(seconds = nil) ⇒ Object
Where a retry of this consumer's messages waits. See retries.
102 103 104 105 106 |
# File 'lib/acemq/rails/consumer.rb', line 102 def broker_wait_threshold(seconds = nil) return setting(:broker_wait_threshold) if seconds.nil? @broker_wait_threshold = seconds end |
.codec(instance = nil) ⇒ Object
The codec for this queue, when it is not the application's.
76 77 78 79 80 |
# File 'lib/acemq/rails/consumer.rb', line 76 def codec(instance = nil) return setting(:codec) if instance.nil? @codec = AceMQ::AMQP::Codec.check!(instance) end |
.concurrency(count = nil) ⇒ Object
Messages worked on at once by one instance of this consumer.
One, unless the application's consumer.concurrency says otherwise.
Raising it gives up the order the broker offers messages in — which is
the point of raising it, and worth saying out loud because
concurrency 8 reads like free throughput and is not.
62 63 64 65 66 |
# File 'lib/acemq/rails/consumer.rb', line 62 def concurrency(count = nil) return setting(:concurrency) if count.nil? @concurrency = Integer(count) end |
.inherited(subclass) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
133 134 135 136 |
# File 'lib/acemq/rails/consumer.rb', line 133 def inherited(subclass) super Registry.register(subclass) end |
.prefetch(count = nil) ⇒ Object
Unacknowledged messages this consumer holds at once.
69 70 71 72 73 |
# File 'lib/acemq/rails/consumer.rb', line 69 def prefetch(count = nil) return setting(:prefetch) if count.nil? @prefetch = Integer(count) end |
.queue(name = nil) ⇒ Object
The queue this consumer reads. Required, and not inherited.
50 51 52 53 54 |
# File 'lib/acemq/rails/consumer.rb', line 50 def queue(name = nil) return @queue if name.nil? @queue = name.to_s end |
.retries(policy = nil, **settings) ⇒ Object
The retry ladder for this queue.
retries max_attempts: 5, initial_delay: 1, max_delay: 60
retries AceMQ::AMQP::RetryPolicy.exponential(5, 1, 60)
broker_wait_threshold: decides where a wait is spent: shorter than
it, in this process holding a prefetch slot and holding up a drain;
longer, on a rung queue in the broker, where a restart does not have to
survive it. It is a shutdown setting as much as a reliability one — see
docs/lifecycle.md.
92 93 94 95 96 97 98 99 |
# File 'lib/acemq/rails/consumer.rb', line 92 def retries(policy = nil, **settings) return @retry_policy = policy if policy return setting(:retry_policy) if settings.empty? @broker_wait_threshold = settings.delete(:broker_wait_threshold) || @broker_wait_threshold @retry_policy = Configuration::ConsumerDefaults.new.assign(settings).retry_policy end |
.runnable? ⇒ Boolean
Whether this class is one the runner should subscribe.
A class with no queue is an abstract intermediate — the
ApplicationConsumer holding shared behaviour is the normal Rails
shape, and it must not produce a subscription to a queue called nil.
130 |
# File 'lib/acemq/rails/consumer.rb', line 130 def runnable? = !queue.nil? && !queue.empty? |
.tag(value = nil) ⇒ Object
The consumer tag, which is what the broker's management interface shows
beside the subscription. The class name by default, because
OrdersConsumer in a list of subscriptions is worth more than a UUID.
119 120 121 122 123 |
# File 'lib/acemq/rails/consumer.rb', line 119 def tag(value = nil) return @tag || name if value.nil? @tag = value.to_s end |
Instance Method Details
#call(message) ⇒ AceMQ::AMQP::Ack
Handles one message.
Must return an AMQP::Ack: #accept, #retry_later, #reject
or #park. Raising is the other way to reject — the retry ladder decides
what happens next, and an exception that reaches here is reported to
Rails.error before it is settled, so an error tracker sees it.
162 163 164 165 |
# File 'lib/acemq/rails/consumer.rb', line 162 def call() raise NotImplementedError, "#{self.class.name} must define #call(message) and return an Ack" end |