Class: AceMQ::AMQP::PublishPermits Private
- Inherits:
-
Object
- Object
- AceMQ::AMQP::PublishPermits
- Defined in:
- lib/acemq/amqp/transport.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
How many messages may be on the wire with no confirm back yet.
The ceiling is the only backpressure a publisher that does not wait per message has. Without it a caller who publishes faster than the broker confirms accumulates unconfirmed messages until the process dies, which looks like throughput right up to the moment it does not — and the bigger the array handed to Transport#publish_all, the closer that moment.
A permit is taken before the message is written and given back when the
broker has answered for it, whichever way it answered: an ack, a nack and
a basic.return are all answers. A message nothing ever answered for
keeps its permit, because as far as the broker is concerned that message
is still outstanding — which is what makes the ceiling bite when a broker
stops keeping up rather than only when somebody hands in a huge array.
Java holds the permit in exactly the same place, in a Semaphore released
from its confirm listener, and .NET in a SemaphoreSlim.
Nothing waits here. Java can afford to block for its confirm timeout because its publishes are asynchronous and another thread's confirm can release a permit while this one waits; Ruby's transport publishes under the publishing channel's mutex, so the thread that would wait is the only thread that could ever release one, and a wait would be a deadlock dressed up as patience. Exhaustion is therefore reported straight away, in the sentence Java uses for it, rather than stalling silently.
Constant Summary collapse
- DEFAULT =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
What Java's
ConnectionConfig.maxOutstandingPublishesand .NET'sMaxOutstandingPublishesdefault to. Large enough that an ordinary batch never notices it, small enough that a runaway publisher is stopped while the process still has memory to report it with. 1_000
Instance Attribute Summary collapse
-
#limit ⇒ Integer
readonly
private
The ceiling this was built with.
Instance Method Summary collapse
-
#available ⇒ Object
private
How many more messages may go out before one has to be answered for.
-
#exhausted ⇒ Object
private
What to say when there is no room left.
-
#initialize(limit = DEFAULT) ⇒ PublishPermits
constructor
private
A new instance of PublishPermits.
-
#release(count) ⇒ Object
private
Gives back the permits of messages the broker has answered for.
-
#reopened ⇒ Object
private
Forgets everything outstanding, for a publishing channel that has been reopened.
-
#take ⇒ Object
private
Takes one permit, or answers false when there are none left.
Constructor Details
#initialize(limit = DEFAULT) ⇒ PublishPermits
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.
Returns a new instance of PublishPermits.
161 162 163 164 165 166 167 168 169 170 |
# File 'lib/acemq/amqp/transport.rb', line 161 def initialize(limit = DEFAULT) @limit = Integer(limit) if @limit < 1 raise ConfigurationError, "max_outstanding_publishes must be at least 1, was #{limit.inspect}" end @available = @limit @lock = Mutex.new end |
Instance Attribute Details
#limit ⇒ Integer (readonly)
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.
Returns the ceiling this was built with.
157 158 159 |
# File 'lib/acemq/amqp/transport.rb', line 157 def limit @limit end |
Instance Method Details
#available ⇒ 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.
How many more messages may go out before one has to be answered for.
173 |
# File 'lib/acemq/amqp/transport.rb', line 173 def available = @lock.synchronize { @available } |
#exhausted ⇒ 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.
What to say when there is no room left. The second sentence is Java's, word for word, so one runbook covers both.
205 206 207 208 209 |
# File 'lib/acemq/amqp/transport.rb', line 205 def exhausted "#{@limit} publishes are already waiting for a confirm and none of them " \ "completed. The broker is not keeping up; publish more slowly rather " \ "than buffering more." end |
#release(count) ⇒ 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.
Gives back the permits of messages the broker has answered for.
186 187 188 189 190 191 |
# File 'lib/acemq/amqp/transport.rb', line 186 def release(count) return if count.zero? @lock.synchronize { @available = [@available + count, @limit].min } nil end |
#reopened ⇒ 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.
Forgets everything outstanding, for a publishing channel that has been reopened. The messages those permits were held for went down with the old channel and can no longer be confirmed on the new one, which is the same conclusion Java's shutdown listener reaches when it fails every pending publish.
198 199 200 201 |
# File 'lib/acemq/amqp/transport.rb', line 198 def reopened @lock.synchronize { @available = @limit } nil end |
#take ⇒ 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.
Takes one permit, or answers false when there are none left.
176 177 178 179 180 181 182 183 |
# File 'lib/acemq/amqp/transport.rb', line 176 def take @lock.synchronize do next false if @available.zero? @available -= 1 true end end |