nats.java
nats.java copied to clipboard
No blocking/back-pressure for fast producers
#237 introduced fast producer back-pressure by blocking the client from publishing new messages until a sufficient number of outstanding outgoing messages were written to the NATS server. An internal LinkedBlockingQueue is used and outgoing messages are added to the queue by way of LinkedBlockingQueue#put, which is correctly blocking when the queue is at capacity. However, #301 changed the way outgoing messages were added to the queue by using LinkedBlockingQueue#add (later changed to LinkedBlockingQueue#offer), and neither of these methods are blocking.
In such a case where the fast producer is trying to publish more messages than the client's executor can write to the server so that the outgoing message queue is filled to capacity, the current behaviour might throw an error by default, or be handled with an error-listener if one is configured in the options (and discardMessagesWhenOutgoingQueueFull property is set). However I don't believe it will ever block and perform the limited back-pressure on the publishing client as the documentation insists. Can a maintainer confirm that this is the case, and if this is intended behaviour?
I don't think this was designed as a backpressure thing. The default maxMessagesInOutgoingQueue is 5000 (see Options), and this should only be reached if a server/cluster goes down or the network is disconnected. I believe the intention is to be able to hold an amount of messages so when the client does reconnect, it can send them, for instance on a remote client that has intermittent connectivity.
Thanks for the reply! I think the behaviour you mentioned is reasonable. But in the constructor documentation for MessageQueue it seems to clearly state:
If publishHighwaterMark is set to 0 the underlying queue can grow forever (or until the max size of a linked blocking queue that is). * A value of 0 is used by readers to prevent the read thread from blocking. * If set to a number of messages, the publish command will block, which provides * backpressure on a publisher if the writer is slow to push things onto the network.
As you said, the default is 5000, but if I've read the code correctly, instead of blocking, it will throw or else be handled asynchronously by a configured error-listener. The original implementation and intention seems to have been for jnats to block even async-publishing requests if the client couldn't push outgoing messages to the network quickly enough (hence the use of a BlockingQueue in the first place).
This original intent is also mentioned in documentation under the default 5000 setting as well:
This value is used internally to limit the number of messages allowed in the outgoing queue. When this limit is reached, publish requests will be blocked until the queue can clear. ...
Is this change in behaviour a regression or should the documentation simply be amended to reflect that the client will raise an exception rather than block if there are too many outgoing messages which have not yet been pushed to the network?