Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce IBasicPublishBatch #368

Merged
merged 12 commits into from
Dec 4, 2017
Merged

Conversation

kjnilsson
Copy link
Contributor

@kjnilsson kjnilsson commented Nov 7, 2017

I've changed @YulerB 's BatchMessage for an IBasicPublishBatch approach with an Add method with a similar signature to IModel.BasicPublish. This allows messages for arbitrary exchanges and routing keys to be grouped together in a single write(2) to the socket.

Writing in larger batches ensures tcp segments are better utilised as well as reduces the number of tcp acks that need to be issued. This yields fairly dramatic throughput improvements outlined here

@bording @YulerB @michaelklishin - WDYT?

Example:

        let mb = model.CreateBasicPublishBatch()
        mb.Add("exch", "key", false, basicProps, data)
        mb.Publish()

Batch publish allows sending multiple messages in one stream on the socket.
Sending in batches improves performance by reducing the number of TCP/IP messages sent and TCP/IP acknowledgments received.
This change compiles the commands for all publish messages into a memory buffer that is posted to the socket as a single stream.
Closing the socket/model/connection before the buffer has completed sending does not guarantee message delivery.
Addition of API items for BatchPublish
@kjnilsson kjnilsson changed the title DO NOT MERGE: Introduce IMessageBatch Introduce IMessageBatch Nov 7, 2017
{
public interface IMessageBatch
{
void Add(string exchange, string routingKey, bool mandatory, IBasicProperties properties, byte[] body);
Copy link
Contributor

@YulerB YulerB Nov 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love an overload called AddRange that took an IEnumerable of Message that contained these elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's something that could be very easily added as an application specific extension method as required. From a core api point of view I don't see the need to introduce a Message type to hold the fields in question (If I understood your request correctly).

that said maybe the name IMessageBatch isn't that great - IBasicPublishBatch feels more appropriate perhaps.

@kjnilsson kjnilsson changed the title Introduce IMessageBatch Introduce IBasicPublishBatch Nov 7, 2017
public void Publish()
{
model.SendCommands(commands);
}
Copy link
Contributor

@YulerB YulerB Nov 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we clear commands to allow reuse of the batch , or do you see this as a way of issuing a batch of commands daily, like an actor could?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the intention is that you'll be creating a new instance via CreateBasicPublishBatch() and not reusing these because the constructor is internal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've deliberately not put any particular intent on the API but just kept it as a simple buffer abstraction that users can use as they see fit.

@bording
Copy link
Collaborator

bording commented Nov 7, 2017

@kjnilsson 👍 For the API changes here.

I'm still not sure I can use it with NServiceBus because of how I have to correlate publisher confirm acks back to tasks awaiting completion, but I'll have to give that some more thought.

@kjnilsson
Copy link
Contributor Author

@bording Thanks for reviewing.
I would have hoped that these changes wouldn't change the semantics of publishing in any way and that confirms would flow back in a similar manner as if the messages were published one after the other (allowing for some timing related differences) but maybe the there are some further subtleties in how you implemented batching in NSB.

@YulerB
Copy link
Contributor

YulerB commented Nov 8, 2017

@kjnilsson 👍 For the API changes here also.

@bording
Copy link
Collaborator

bording commented Nov 8, 2017

I would have hoped that these changes wouldn't change the semantics of publishing in any way and that confirms would flow back in a similar manner as if the messages were published one after the other (allowing for some timing related differences) but maybe the there are some further subtleties in how you implemented batching in NSB.

@kjnilsson Currently there is a 1-to-1 mapping between each message sent and the API call to send it, so I can create a TaskCompletionSource for each message that is published, storing them in a dictionary with the sequence number as the key. That lets me associate each publisher confirm ack back to each entry in the dictionary (with some special handling when a multiple ack comes back).If I get a batch of messages, I'm able to return use Task.WhenAll to create a single task that completes when all of the messages in the batch receive acks.

If I use this new API, I no longer have an API call per message, so being able to wait for all of the individual acks before completing the operation becomes trickier to manage.

This PR probably isn't the correct place to go into more detail, but if you want to know more, I'd be happy to walk you through it.

@kjnilsson
Copy link
Contributor Author

Maybe you could grab the NextPublishSeqNo before and after batch Publish and build up the list of TCSs from that, not sure if that fits.

@bording
Copy link
Collaborator

bording commented Nov 8, 2017

Maybe you could grab the NextPublishSeqNo before and after batch Publish and build up the list of TCSs from that, not sure if that fits.

That was something I was thinking about, but then I'd have to add another Task.WhenAll in there to combine them back into a single task that I can return. That adds more overhead/allocations, so I'll have to test to see how it all balances out.

@kjnilsson
Copy link
Contributor Author

@michaelklishin @lukebakken I think we're there with this PR unless either of you would like any changes?

@michaelklishin
Copy link
Member

Not sure when I'd get a chance to take a look this week but I remember that this needs reviewing. Thank you for the reminder, though 👍

}

public void WriteFrameSet(IList<OutboundFrame> f)
public void WriteFrameSet(List<OutboundFrame> f)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is better it is also a breaking change, was this intentional?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was but given how often IFrameHandler appears in (unnecessarily) public APIs we should probably revert it. :/

@@ -77,6 +77,6 @@ public interface IFrameHandler

void WriteFrame(OutboundFrame frame);

void WriteFrameSet(IList<OutboundFrame> frames);
void WriteFrameSet(List<OutboundFrame> frames);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is better it is also a breaking change, was this intentional?

BasicPublishBatch can be used for more efficient batch publishing that
will better utilise TCP and provides more throughput.

[#152041636]
@michaelklishin michaelklishin merged commit 2746891 into master Dec 4, 2017
CornedBee pushed a commit to CornedBee/rabbitmq-dotnet-client that referenced this pull request Dec 6, 2017
@kjnilsson
Copy link
Contributor Author

There is a pre-release available now that contain this feature: https://www.nuget.org/packages/RabbitMQ.Client/5.1.0-pre2

@cjlotz
Copy link

cjlotz commented Oct 9, 2018

With the new BatchPublish behaviour, what is the expected behaviour w.r.t to publisher confirmations? Is the expectation to get a single confirmation if the complete batch of messages were all sent successfully or do we get an individual confirmation for each message in the batch? From testing it seems that you only receive a single confirmation with the Multiple flag set to true? Any more documentation available on the topic?

@michaelklishin
Copy link
Member

Please direct your questions to RabbitMQ users. Publishing a batch of messages is not semantically different from publishing them one by one since there is no batch publish operation in the protocol. This PR batches socket writes.

@lukebakken lukebakken deleted the rabbitmq-dotnet-client-364 branch January 24, 2020 17:43
@salixzs
Copy link

salixzs commented Apr 9, 2020

Documentation part of message batching still does not say anything about this approach. Is it on purpose?
https://www.rabbitmq.com/tutorials/tutorial-seven-dotnet.html

@michaelklishin
Copy link
Member

michaelklishin commented Apr 9, 2020

@salixzs you are welcome to contribute what you find missing. The purpose of tutorial 7 is to demonstrate publisher confirms, not be an extensive guide on publishing, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants