/// <summary> /// Sends a set of events to the associated Service Bus entity using a batched approach. If the size of events exceed the /// maximum size of a single batch, an exception will be triggered and the send will fail. /// </summary> /// /// <param name="messages">The set of event data to send.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// public override async Task SendAsync(IEnumerable <ServiceBusMessage> messages, CancellationToken cancellationToken) { Argument.AssertNotNull(messages, nameof(messages)); Argument.AssertNotClosed(_closed, nameof(AmqpSender)); AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messages); await SendAsync(messageFactory, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Initializes a new instance of the <see cref="AmqpMessageBatch"/> class. /// </summary> /// /// <param name="options">The set of options to apply to the batch.</param> /// public AmqpMessageBatch(CreateBatchOptions options) { Argument.AssertNotNull(options, nameof(options)); Argument.AssertNotNull(options.MaximumSizeInBytes, nameof(options.MaximumSizeInBytes)); Options = options; MaximumSizeInBytes = options.MaximumSizeInBytes.Value; // Initialize the size by reserving space for the batch envelope. using AmqpMessage envelope = AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(Enumerable.Empty <ServiceBusMessage>()); _sizeBytes = envelope.SerializedMessageSize; }
/// <summary> /// Sends a set of messages to the associated Queue/Topic using a batched approach. /// </summary> /// /// <param name="messageBatch">The set of messages to send.</param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// /// <returns>A task to be resolved on when the operation has completed.</returns> /// public override async Task SendBatchAsync( ServiceBusMessageBatch messageBatch, CancellationToken cancellationToken) { AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messageBatch.AsEnumerable <ServiceBusMessage>()); await _retryPolicy.RunOperation(async (timeout) => await SendBatchInternalAsync( messageFactory, timeout, cancellationToken).ConfigureAwait(false), _connectionScope, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Attempts to add a message to the batch, ensuring that the size /// of the batch does not exceed its maximum. /// </summary> /// /// <param name="message">The message to attempt to add to the batch.</param> /// /// <returns><c>true</c> if the message was added; otherwise, <c>false</c>.</returns> /// public override bool TryAddMessage(ServiceBusMessage message) { Argument.AssertNotNull(message, nameof(message)); Argument.AssertNotDisposed(_disposed, nameof(ServiceBusMessageBatch)); AmqpMessage amqpMessage = null; try { if (BatchMessages.Count == 0) { // Initialize the size by reserving space for the batch envelope taking into account the properties from the first // message which will be used to populate properties on the batch envelope. amqpMessage = AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(message, forceBatch: true); } else { amqpMessage = AmqpMessageConverter.SBMessageToAmqpMessage(message); } // Calculate the size for the message, based on the AMQP message size and accounting for a // bit of reserved overhead size. var size = _sizeBytes + amqpMessage.SerializedMessageSize + (amqpMessage.SerializedMessageSize <= MaximumBytesSmallMessage ? OverheadBytesSmallMessage : OverheadBytesLargeMessage); if (size > MaxSizeInBytes) { return(false); } _sizeBytes = size; BatchMessages.Add(message); return(true); } finally { amqpMessage?.Dispose(); } }
/// <summary> /// Sends a set of messages to the associated Queue/Topic using a batched approach. /// </summary> /// /// <param name="messageBatch"></param> /// <param name="timeout"></param> /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param> /// internal virtual async Task SendBatchInternalAsync( ServiceBusMessageBatch messageBatch, TimeSpan timeout, CancellationToken cancellationToken) { var stopWatch = Stopwatch.StartNew(); AmqpMessage messageFactory() => AmqpMessageConverter.BatchSBMessagesAsAmqpMessage(messageBatch.AsEnumerable <ServiceBusMessage>()); using (AmqpMessage batchMessage = messageFactory()) { //ServiceBusEventSource.Log.SendStart(Entityname, messageHash); string messageHash = batchMessage.GetHashCode().ToString(); SendingAmqpLink link = await _sendLink.GetOrCreateAsync(UseMinimum(_connectionScope.SessionTimeout, timeout)).ConfigureAwait(false); // Validate that the message is not too large to send. This is done after the link is created to ensure // that the maximum message size is known, as it is dictated by the service using the link. if (batchMessage.SerializedMessageSize > MaximumMessageSize) { throw new ServiceBusException(string.Format(Resources1.MessageSizeExceeded, messageHash, batchMessage.SerializedMessageSize, MaximumMessageSize, _entityName), ServiceBusException.FailureReason.MessageSizeExceeded); } // Attempt to send the message batch. var deliveryTag = new ArraySegment <byte>(BitConverter.GetBytes(Interlocked.Increment(ref _deliveryCount))); var outcome = await link.SendMessageAsync(batchMessage, deliveryTag, AmqpConstants.NullBinary, timeout.CalculateRemaining(stopWatch.Elapsed)).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); if (outcome.DescriptorCode != Accepted.Code) { throw AmqpError.CreateExceptionForError((outcome as Rejected)?.Error, _entityName); } //ServiceBusEventSource.Log.SendStop(Entityname, messageHash); cancellationToken.ThrowIfCancellationRequested <TaskCanceledException>(); stopWatch.Stop(); } }