public async Task CreateBatchAsyncRespectsTheMaximumSizeWhenProvided()
        {
            var expectedMaximumSize = 512;
            var options             = new CreateBatchOptions {
                MaximumSizeInBytes = expectedMaximumSize
            };
            var retryPolicy = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, expectedMaximumSize + 27))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())))
            .Verifiable();

            using TransportEventBatch batch = await producer.Object.CreateBatchAsync(options, default);

            Assert.That(options.MaximumSizeInBytes, Is.EqualTo(expectedMaximumSize));
        }
        public async Task SendBatchRespectsTheCancellationTokenIfSetWhenCalled()
        {
            var expectedMaximumSize = 512;
            var options             = new CreateBatchOptions();
            var retryPolicy         = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, expectedMaximumSize))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())));

            using TransportEventBatch batch = await producer.Object.CreateBatchAsync(options, default);

            using CancellationTokenSource cancellationSource = new CancellationTokenSource();

            cancellationSource.Cancel();
            Assert.That(async() => await producer.Object.SendAsync(new EventDataBatch(batch, options), cancellationSource.Token), Throws.InstanceOf <TaskCanceledException>());
        }
        public async Task CreateBatchAsyncBuildsAnAmqpEventBatchWithTheOptions()
        {
            var options = new CreateBatchOptions {
                MaximumSizeInBytes = 512
            };
            var retryPolicy = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, options.MaximumSizeInBytes.Value + 982))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())))
            .Verifiable();

            using TransportEventBatch batch = await producer.Object.CreateBatchAsync(options, default);

            Assert.That(batch, Is.Not.Null, "The created batch should be populated.");
            Assert.That(batch, Is.InstanceOf <AmqpEventBatch>(), $"The created batch should be an { nameof(AmqpEventBatch) }.");
            Assert.That(GetEventBatchOptions((AmqpEventBatch)batch), Is.SameAs(options), "The provided options should have been used.");
        }
        public async Task SendBatchEnsuresNotClosed()
        {
            var expectedMaximumSize = 512;
            var options             = new CreateBatchOptions {
                MaximumSizeInBytes = null
            };
            var retryPolicy = new BasicRetryPolicy(new EventHubsRetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, expectedMaximumSize))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())));

            using TransportEventBatch batch = await producer.Object.CreateBatchAsync(options, default);

            await producer.Object.CloseAsync(CancellationToken.None);

            Assert.That(async() => await producer.Object.SendAsync(new EventDataBatch(batch, new SendEventOptions()), CancellationToken.None), Throws.InstanceOf <EventHubsException>().And.Property(nameof(EventHubsException.Reason)).EqualTo(EventHubsException.FailureReason.ClientClosed));
        }
Ejemplo n.º 5
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="EventDataBatch"/> class.
        /// </summary>
        ///
        /// <param name="transportBatch">The  transport-specific batch responsible for performing the batch operations.</param>
        /// <param name="sendOptions">The set of options that should be used when publishing the batch.</param>
        ///
        /// <remarks>
        ///   As an internal type, this class performs only basic sanity checks against its arguments.  It
        ///   is assumed that callers are trusted and have performed deep validation.
        ///
        ///   Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose;
        ///   creation of clones or otherwise protecting the parameters is assumed to be the purview of the
        ///   caller.
        /// </remarks>
        ///
        internal EventDataBatch(TransportEventBatch transportBatch,
                                SendOptions sendOptions)
        {
            Guard.ArgumentNotNull(nameof(transportBatch), transportBatch);
            Guard.ArgumentNotNull(nameof(sendOptions), sendOptions);

            InnerBatch  = transportBatch;
            SendOptions = sendOptions;
        }
        /// <summary>
        ///   Initializes a new instance of the <see cref="EventDataBatch"/> class.
        /// </summary>
        ///
        /// <param name="transportBatch">The  transport-specific batch responsible for performing the batch operations.</param>
        /// <param name="sendOptions">The set of options that should be used when publishing the batch.</param>
        ///
        /// <remarks>
        ///   As an internal type, this class performs only basic sanity checks against its arguments.  It
        ///   is assumed that callers are trusted and have performed deep validation.
        ///
        ///   Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose;
        ///   creation of clones or otherwise protecting the parameters is assumed to be the purview of the
        ///   caller.
        /// </remarks>
        ///
        internal EventDataBatch(TransportEventBatch transportBatch,
                                SendEventOptions sendOptions)
        {
            Argument.AssertNotNull(transportBatch, nameof(transportBatch));
            Argument.AssertNotNull(sendOptions, nameof(sendOptions));

            InnerBatch  = transportBatch;
            SendOptions = sendOptions;
        }
        /// <summary>
        ///   Creates a size-constraint batch to which <see cref="EventData" /> may be added using a try-based pattern.  If an event would
        ///   exceed the maximum allowable size of the batch, the batch will not allow adding the event and signal that scenario using its
        ///   return value.
        ///
        ///   Because events that would violate the size constraint cannot be added, publishing a batch will not trigger an exception when
        ///   attempting to send the events to the Event Hubs service.
        /// </summary>
        ///
        /// <param name="options">The set of options to consider when creating this batch.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>An <see cref="EventDataBatch" /> with the requested <paramref name="options"/>.</returns>
        ///
        /// <seealso cref="CreateBatchAsync(CreateBatchOptions, CancellationToken)" />
        ///
        public virtual async ValueTask <EventDataBatch> CreateBatchAsync(CreateBatchOptions options,
                                                                         CancellationToken cancellationToken = default)
        {
            options = options?.Clone() ?? new CreateBatchOptions();
            AssertSinglePartitionReference(options.PartitionId, options.PartitionKey);

            TransportEventBatch transportBatch = await GatewayProducer.CreateBatchAsync(options, cancellationToken).ConfigureAwait(false);

            return(new EventDataBatch(transportBatch, options));
        }
        /// <summary>
        ///   Creates a size-constraint batch to which <see cref="EventData" /> may be added using a try-based pattern.  If an event would
        ///   exceed the maximum allowable size of the batch, the batch will not allow adding the event and signal that scenario using its
        ///   return value.
        ///
        ///   Because events that would violate the size constraint cannot be added, publishing a batch will not trigger an exception when
        ///   attempting to send the events to the Event Hubs service.
        /// </summary>
        ///
        /// <param name="options">The set of options to consider when creating this batch.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken" /> instance to signal the request to cancel the operation.</param>
        ///
        /// <returns>An <see cref="EventDataBatch" /> with the requested <paramref name="options"/>.</returns>
        ///
        /// <seealso cref="CreateBatchAsync(CreateBatchOptions, CancellationToken)" />
        ///
        public virtual async ValueTask <EventDataBatch> CreateBatchAsync(CreateBatchOptions options,
                                                                         CancellationToken cancellationToken = default)
        {
            options = options?.Clone() ?? new CreateBatchOptions();
            AssertSinglePartitionReference(options.PartitionId, options.PartitionKey);

            TransportEventBatch transportBatch = await PartitionProducerPool.EventHubProducer.CreateBatchAsync(options, cancellationToken).ConfigureAwait(false);

            return(new EventDataBatch(transportBatch, FullyQualifiedNamespace, EventHubName, options.ToSendOptions()));
        }
Ejemplo n.º 9
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="EventDataBatch"/> class.
        /// </summary>
        ///
        /// <param name="transportBatch">The  transport-specific batch responsible for performing the batch operations.</param>
        /// <param name="sendOptions">The set of options that should be used when publishing the batch.</param>
        ///
        /// <remarks>
        ///   As an internal type, this class performs only basic sanity checks against its arguments.  It
        ///   is assumed that callers are trusted and have performed deep validation.
        ///
        ///   Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose;
        ///   creation of clones or otherwise protecting the parameters is assumed to be the purview of the
        ///   caller.
        /// </remarks>
        ///
        internal EventDataBatch(TransportEventBatch transportBatch,
                                SendOptions sendOptions)
        {
            Guard.ArgumentNotNull(nameof(transportBatch), transportBatch);
            Guard.ArgumentNotNull(nameof(sendOptions), sendOptions);

            InnerBatch  = transportBatch;
            SendOptions = sendOptions;

            _clientDiagnostics = new ClientDiagnostics(isActivityEnabled: true);
        }
Ejemplo n.º 10
0
        /// <summary>
        ///   Initializes a new instance of the <see cref="EventDataBatch"/> class.
        /// </summary>
        ///
        /// <param name="transportBatch">The  transport-specific batch responsible for performing the batch operations.</param>
        /// <param name="fullyQualifiedNamespace">The fully qualified Event Hubs namespace to use for instrumentation.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to associate the events with during instrumentation.</param>
        /// <param name="sendOptions">The set of options that should be used when publishing the batch.</param>
        ///
        /// <remarks>
        ///   As an internal type, this class performs only basic sanity checks against its arguments.  It
        ///   is assumed that callers are trusted and have performed deep validation.
        ///
        ///   Any parameters passed are assumed to be owned by this instance and safe to mutate or dispose;
        ///   creation of clones or otherwise protecting the parameters is assumed to be the purview of the
        ///   caller.
        /// </remarks>
        ///
        internal EventDataBatch(TransportEventBatch transportBatch,
                                string fullyQualifiedNamespace,
                                string eventHubName,
                                SendEventOptions sendOptions)
        {
            Argument.AssertNotNull(transportBatch, nameof(transportBatch));
            Argument.AssertNotNullOrEmpty(fullyQualifiedNamespace, nameof(fullyQualifiedNamespace));
            Argument.AssertNotNullOrEmpty(eventHubName, nameof(eventHubName));
            Argument.AssertNotNull(sendOptions, nameof(sendOptions));

            InnerBatch = transportBatch;
            FullyQualifiedNamespace = fullyQualifiedNamespace;
            EventHubName            = eventHubName;
            SendOptions             = sendOptions;
        }
Ejemplo n.º 11
0
        public async Task SendBatchCreatesTheAmqpMessageFromTheBatch(string partitonKey)
        {
            var messageFactory      = default(Func <AmqpMessage>);
            var expectedMaximumSize = 512;
            var options             = new CreateBatchOptions {
                PartitionKey = partitonKey
            };
            var retryPolicy = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, expectedMaximumSize))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())));

            producer
            .Protected()
            .Setup <Task>("SendAsync",
                          ItExpr.IsAny <Func <AmqpMessage> >(),
                          ItExpr.IsAny <string>(),
                          ItExpr.IsAny <CancellationToken>())
            .Callback <Func <AmqpMessage>, string, CancellationToken>((factory, key, token) => messageFactory = factory)
            .Returns(Task.CompletedTask);

            using TransportEventBatch transportBatch = await producer.Object.CreateBatchAsync(options, default);

            using var batch = new EventDataBatch(transportBatch, options);
            batch.TryAdd(new EventData(new byte[] { 0x15 }));

            await producer.Object.SendAsync(batch, CancellationToken.None);

            Assert.That(messageFactory, Is.Not.Null, "The batch message factory should have been set.");

            using var batchMessage   = new AmqpMessageConverter().CreateBatchFromMessages(batch.AsEnumerable <AmqpMessage>(), partitonKey);
            using var factoryMessage = messageFactory();

            Assert.That(factoryMessage.SerializedMessageSize, Is.EqualTo(batchMessage.SerializedMessageSize), "The serialized size of the messages should match.");
        }
Ejemplo n.º 12
0
        public async Task SendBatchDoesNotDisposeTheEventsInTheSourceBatch()
        {
            var expectedMaximumSize = 512;
            var options             = new CreateBatchOptions {
                MaximumSizeInBytes = null
            };
            var retryPolicy = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, expectedMaximumSize))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())));

            producer
            .Protected()
            .Setup <Task>("SendAsync",
                          ItExpr.IsAny <Func <AmqpMessage> >(),
                          ItExpr.IsAny <string>(),
                          ItExpr.IsAny <CancellationToken>())
            .Returns(Task.CompletedTask);

            using TransportEventBatch transportBatch = await producer.Object.CreateBatchAsync(options, default);

            using var batch = new EventDataBatch(transportBatch, options);
            batch.TryAdd(new EventData(new byte[] { 0x15 }));

            await producer.Object.SendAsync(batch, CancellationToken.None);

            Assert.That(batch, Is.Not.Null, "The batch should not have been set to null.");
            Assert.That(() => batch.AsEnumerable <AmqpMessage>().Single().ThrowIfDisposed(), Throws.Nothing, "The events within the source batch should not have been disposed.");
        }
Ejemplo n.º 13
0
        public async Task SendBatchUsesThePartitionKey()
        {
            var expectedMaximumSize  = 512;
            var expectedPartitionKey = "some key";
            var options = new CreateBatchOptions {
                PartitionKey = expectedPartitionKey
            };
            var retryPolicy = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });

            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, expectedMaximumSize))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())));

            producer
            .Protected()
            .Setup <Task>("SendAsync",
                          ItExpr.IsAny <Func <AmqpMessage> >(),
                          ItExpr.Is <string>(value => value == expectedPartitionKey),
                          ItExpr.IsAny <CancellationToken>())
            .Returns(Task.CompletedTask)
            .Verifiable();

            using TransportEventBatch batch = await producer.Object.CreateBatchAsync(options, default);

            await producer.Object.SendAsync(new EventDataBatch(batch, options), CancellationToken.None);

            producer.VerifyAll();
        }
Ejemplo n.º 14
0
        public async Task CreateBatchAsyncEnsuresMaximumMessageSizeIsPopulated()
        {
            var retryPolicy = new BasicRetryPolicy(new RetryOptions {
                TryTimeout = TimeSpan.FromSeconds(17)
            });
            var producer = new Mock <AmqpProducer>("aHub", null, Mock.Of <AmqpConnectionScope>(), new AmqpMessageConverter(), retryPolicy)
            {
                CallBase = true
            };

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Callback(() => SetMaximumMessageSize(producer.Object, 512))
            .Returns(Task.FromResult(new SendingAmqpLink(new AmqpLinkSettings())))
            .Verifiable();

            using TransportEventBatch batch = await producer.Object.CreateBatchAsync(new CreateBatchOptions (), default);

            producer.VerifyAll();
        }