Skip to content
This repository has been archived by the owner on Oct 12, 2023. It is now read-only.

[WIP] Batching #539

Open
wants to merge 22 commits into
base: dev
Choose a base branch
from
Open

[WIP] Batching #539

wants to merge 22 commits into from

Conversation

SeanFeldman
Copy link
Collaborator

@SeanFeldman SeanFeldman commented Jul 29, 2018

Fixes #538

The client allows to send a collection of messages (SendAsync(IList<Message>)) which is problematic when there's a large number of messages that would not fit into the maximum message size. As a result of that, an exception is thrown when the send operation is invoked.

This PR introduces a safe batching option. A Batch is constructed before send operation is invoked and messages are added using bool TryAdd(Message) API. As long as batch total size doesn't exceed the maximum, messages are added to it. Otherwise, message is not added and can be carried over to the next batch. With this API messages sent will not exceed maximum message size.

What's left:

  • Add tests to ensure common scenarios are verified (BatchTests and SenderReceiverTests)
  • Ensure received messages are not attempted to be sent using Batch (logic from MessageSender.ValidateMessage())
  • Raise an issue for the next major to bring SendAsync(Batch) to ISenderClient --> Define SendAsync(Batch) and CreateBatch() on ISenderClient #543
  • Log similar to other SendAsync() overloads
  • Retrieve message max size from the broker
  • Pass batched messages through plugins
  • BLOCKED* Support Diagnostics similar to how SendAsync(IList<Message>) does it

* blocked until Diagnostics can support a different approach of registering messages. See an experimental PR comments here.

Opening this PR to discuss further implementation details

@SeanFeldman SeanFeldman requested a review from a team as a code owner July 29, 2018 07:19
@SeanFeldman SeanFeldman changed the title Batching [WIP] Batching Jul 29, 2018
@SeanFeldman
Copy link
Collaborator Author

@makam this is the approach I'd like to take. If you see no issues, I'll continue with the rest of TODOs.

@SeanFeldman
Copy link
Collaborator Author

@vinaysurya I cannot trigger rebuilds for https://ci.appveyor.com/project/vinaysurya/azure-service-bus-dotnet. Could you please modify my permissions to allow me to do so? Thank you.

@SeanFeldman SeanFeldman force-pushed the batching branch 3 times, most recently from 6d45105 to 78ed01d Compare July 31, 2018 21:44
@SeanFeldman
Copy link
Collaborator Author

This PR could use #505 @nemakam 🙂

@SeanFeldman
Copy link
Collaborator Author

@makam @vinaysurya I've got everything except diagnostics.
Diagnostics (except exceptions) cannot be integrated at the moment and would have to wait until an alternative option to implement it is introduced.

I'd like to start a review of this PR. The feature could be used with a caveat of not having full Diagnostics available if users require this feature until alternative diagnostics registration is available and added later.

@SeanFeldman
Copy link
Collaborator Author

@nemakam @vinaysurya ping

@SeanFeldman
Copy link
Collaborator Author

@nemakam @vinaysurya let me know when you're ready to start reviewing. Thanks.

/// </summary>
/// <param name="maximumBatchSize">Maximum batch size allowed for batch.</param>
/// <param name="pluginsCallback">Plugins callback to invoke on outgoing messages regisered with batch.</param>
public Batch(ulong maximumBatchSize, Func<Message, Task<Message>> pluginsCallback)
Copy link
Contributor

Choose a reason for hiding this comment

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

public Batch [](start = 8, length = 12)

I think having this constructor will cause more confusion. We already have a way to create a batch on the sender.

For testing as of now, we could make this internal.
For customers who need to test this, they could mock a Sender (in the future version) and then create a batch accordingly.

Having this constructor exposed like this will definitely create confusion to customers which is just explained in remarks (which is not the most accessible comment).

I would think especially the second param - callback is going to be even more confusing for a beginner user.
Lets make this internal for now.

// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Threading.Tasks;

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: inside

using Microsoft.Azure.ServiceBus.Diagnostics;

[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")]
public class Batch : IDisposable
Copy link
Contributor

Choose a reason for hiding this comment

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

Batch [](start = 17, length = 5)

Message - MessageBatch.
Sounds more consistent. Maybe rename?

/// Convert batch to AMQP message.
/// </summary>
/// <returns></returns>
public AmqpMessage ToAmqpMessage()
Copy link
Contributor

Choose a reason for hiding this comment

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

public [](start = 8, length = 6)

This doesn't need to be public

@@ -149,5 +150,14 @@ internal static bool TryExtractContext(this Message message, out IList<KeyValueP
}
return false;
}


internal static void VerifyMessageIsNotPreviouslyReceived(this Message message)
Copy link
Contributor

Choose a reason for hiding this comment

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

VerifyMessageIsNotPreviouslyReceived [](start = 29, length = 36)

This class is dedicated to diagnostics. Lets move this out of this class


if (originalMessageData.messageId != null)
{
result.Properties.MessageId = originalMessageData.messageId;
Copy link
Contributor

Choose a reason for hiding this comment

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

result.Properties.MessageId = originalMessageData.messageId; [](start = 16, length = 60)

What if TryAdd succeeded, and then you tried to copy over MessageId and other properties, and you crossed your size limit?

Copy link
Contributor

Choose a reason for hiding this comment

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

Alternatively, the moment you receive your second TryAdd, form the result along with its updated properties so that you can validate the size.


In reply to: 219366896 [](ancestors = 219366896)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Absolutely. It also removes the need to "remember" original message system properties and tuple that stores those. Nice!

{
if (result == null)
{
throw new Exception("Batch is has been disposed and cannot be re-used.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Exception [](start = 26, length = 9)

ObjectDisposedException

{
if (result == null)
{
throw new Exception("Batch is has been disposed and cannot be re-used.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Batch is [](start = 37, length = 8)

typo

}
}

private string DebuggerDisplay => $"Batch: size={Size} message count={datas.Count} maximum size={maximumBatchSize}";
Copy link
Contributor

Choose a reason for hiding this comment

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

size={Size} message count= [](start = 51, length = 26)

Add a . or ; or any identifier between two properties so that its easier to read

const string PartitionIdName = "x-opt-partition-id";
const string ViaPartitionKeyName = "x-opt-via-partition-key";
internal const string PartitionKeyName = "x-opt-partition-key";
internal const string ViaPartitionKeyName = "x-opt-via-partition-key";
Copy link
Contributor

Choose a reason for hiding this comment

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

Reorder them to the beginning so that internals would come before private

@SeanFeldman SeanFeldman force-pushed the batching branch 3 times, most recently from 5a67d61 to 323d7cd Compare October 4, 2018 08:53
@SeanFeldman
Copy link
Collaborator Author

@nemakam integrated your feedback

Einarsson pushed a commit to Einarsson/azure-service-bus-dotnet that referenced this pull request Jan 29, 2019
@SeanFeldman SeanFeldman self-assigned this Feb 6, 2019
@SeanFeldman SeanFeldman changed the title Batching [WIP] Batching Feb 14, 2019
@SeanFeldman
Copy link
Collaborator Author

Marked PR as [WIP] until it's not and ready to be merged.

@axisc
Copy link

axisc commented May 1, 2019

@SeanFeldman - we are moving the underlying issue to the [Azure SDK for .NET] (https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/servicebus/Microsoft.Azure.ServiceBus) repo.

Once we archive this repo, the PR will be read only and we can leverage this test case in the new repo.

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

Successfully merging this pull request may close these issues.

Support safe batching
3 participants