Example #1
0
        public virtual async Task <bool> InitiateCleanClose(bool initiatedByServer, AmqpError error)
        {
            if (_isClosed)
            {
                return(false);
            }
            Thread.MemoryBarrier();
            _isClosed = true;

            if (LogAdapter.IsDebugEnabled)
            {
                LogAdapter.LogDebug(LogSource, "Clean close initiated. By server? " + initiatedByServer);
            }

            if (initiatedByServer)
            {
                await SendCloseConfirmation().ConfigureAwait(false);
            }
            else
            {
                await SendStartClose().ConfigureAwait(false);
            }

            DrainPending(error);

            if (error != null)
            {
                await FireErrorEvent(error);
            }

            return(true);
        }
Example #2
0
 internal void DrainPendingIfNeeded(AmqpError error)
 {
     if (this._confirmationKeeper != null)
     {
         this._confirmationKeeper.DrainDueToFailure(error);
     }
 }
Example #3
0
        public override async Task <bool> InitiateCleanClose(bool initiatedByServer, AmqpError error)
        {
            if (!initiatedByServer)
            {
                _conn.NotifyClosedByUser();

                while (!_commandOutbox.IsEmpty && !_socketHolder.IsClosed)
                {
                    // give it some time to finish writing to the socket (if it's open)
                    Thread.Sleep(250);
                }
            }
            else
            {
                if (_conn.NotifyClosedByServer() == RecoveryAction.WillReconnect)
                {
                    CancelPendingCommands(error);

                    return(false);
                }
            }

            _conn.CloseAllChannels(initiatedByServer, error);

            await base.InitiateCleanClose(initiatedByServer, error).ConfigureAwait(false);

            CancelPendingCommands(error);

            _socketHolder.Close();

            OnSocketClosed();

            return(true);
        }
        public void GetPartitionPropertiesAsyncAppliesTheRetryPolicyForAmqpErrors(EventHubsRetryOptions retryOptions)
        {
            var eventHubName       = "myName";
            var partitionId        = "Barney";
            var tokenValue         = "123ABC";
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var retriableException = AmqpError.CreateExceptionForError(new Error {
                Condition = AmqpError.ServerBusyError
            }, "dummy");
            var mockConverter  = new Mock <AmqpMessageConverter>();
            var mockCredential = new Mock <EventHubTokenCredential>(Mock.Of <TokenCredential>());
            var mockScope      = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            mockCredential
            .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.Is <CancellationToken>(value => value == cancellationSource.Token)))
            .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, DateTimeOffset.MaxValue)));

            mockConverter
            .Setup(converter => converter.CreatePartitionPropertiesRequest(It.Is <string>(value => value == eventHubName), It.Is <string>(value => value == partitionId), It.Is <string>(value => value == tokenValue)))
            .Returns(default(AmqpMessage));

            mockScope
            .Setup(scope => scope.OpenManagementLinkAsync(It.IsAny <TimeSpan>(), It.IsAny <CancellationToken>()))
            .Throws(retriableException);

            var client = new InjectableMockClient("my.eventhub.com", eventHubName, mockCredential.Object, new EventHubConnectionOptions(), mockScope.Object, mockConverter.Object);

            Assert.That(async() => await client.GetPartitionPropertiesAsync(partitionId, retryPolicy, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            mockScope.Verify(scope => scope.OpenManagementLinkAsync(It.IsAny <TimeSpan>(), It.IsAny <CancellationToken>()), Times.Exactly(1 + retryOptions.MaximumRetries));
        }
Example #5
0
        public void CreateExceptionForErrorWithCondition(AmqpSymbol condition,
                                                         Type exceptionType,
                                                         EventHubsException.FailureReason?reason)
        {
            var description  = "This is a test description.";
            var resourceName = "TestHub";

            var error = new Error
            {
                Condition   = condition,
                Description = description
            };

            Exception exception = AmqpError.CreateExceptionForError(error, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf(exceptionType), "The exception should be the proper type");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");

            if (exception is EventHubsException)
            {
                Assert.That(((EventHubsException)exception).Reason, Is.EqualTo(reason), "The proper failure reason should be specified");
                Assert.That(((EventHubsException)exception).EventHubName, Is.EqualTo(resourceName), "The exception should report the proper resource");
            }
        }
        public void SendEnumerableAppliesTheRetryPolicyForAmqpErrors(EventHubsRetryOptions retryOptions)
        {
            var partitionId        = "testMe";
            var retriableException = AmqpError.CreateExceptionForError(new Error {
                Value = AmqpError.ServerBusyError
            }, "dummy");
            var retryPolicy = new BasicRetryPolicy(retryOptions);

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

            producer
            .Protected()
            .Setup <Task <SendingAmqpLink> >("CreateLinkAndEnsureProducerStateAsync",
                                             ItExpr.IsAny <string>(),
                                             ItExpr.IsAny <TimeSpan>(),
                                             ItExpr.IsAny <CancellationToken>())
            .Throws(retriableException);

            using CancellationTokenSource cancellationSource = new CancellationTokenSource();
            Assert.That(async() => await producer.Object.SendAsync(new[] { new EventData(new byte[] { 0x65 }) }, new SendEventOptions(), cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            producer
            .Protected()
            .Verify("CreateLinkAndEnsureProducerStateAsync", Times.Exactly(1 + retryOptions.MaximumRetries),
                    ItExpr.Is <string>(value => value == partitionId),
                    ItExpr.IsAny <TimeSpan>(),
                    ItExpr.IsAny <CancellationToken>());
        }
        public void SendBatchAppliesTheRetryPolicyForAmqpErrors(EventHubsRetryOptions retryOptions)
        {
            var partitionKey = "testMe";
            var options      = new CreateBatchOptions {
                PartitionKey = partitionKey
            };
            var retriableException = AmqpError.CreateExceptionForError(new Error {
                Value = AmqpError.ServerBusyError
            }, "dummy");
            var retryPolicy = new BasicRetryPolicy(retryOptions);
            var batch       = new EventDataBatch(Mock.Of <TransportEventBatch>(), "ns", "eh", options.ToSendOptions());

            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>())
            .Throws(retriableException);

            using CancellationTokenSource cancellationSource = new CancellationTokenSource();
            Assert.That(async() => await producer.Object.SendAsync(batch, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            producer
            .Protected()
            .Verify("CreateLinkAndEnsureProducerStateAsync", Times.Exactly(1 + retryOptions.MaximumRetries),
                    ItExpr.Is <string>(value => value == null),
                    ItExpr.IsAny <TimeSpan>(),
                    ItExpr.IsAny <CancellationToken>());
        }
        public void ReceiveAsyncAppliesTheRetryPolicyForAmqpErrors(EventHubsRetryOptions retryOptions)
        {
            var eventHub           = "eventHubName";
            var consumerGroup      = "$DEFAULT";
            var partition          = "3";
            var identifier         = "cusTOM-1D";
            var eventPosition      = EventPosition.FromOffset(123);
            var trackLastEnqueued  = false;
            var invalidateOnSteal  = true;
            var ownerLevel         = 123L;
            var tokenValue         = "123ABC";
            var retryPolicy        = new BasicRetryPolicy(retryOptions);
            var retriableException = AmqpError.CreateExceptionForError(new Error {
                Condition = AmqpError.ServerBusyError
            }, "dummy");
            var mockConverter  = new Mock <AmqpMessageConverter>();
            var mockCredential = new Mock <TokenCredential>();
            var mockScope      = new Mock <AmqpConnectionScope>();

            using var cancellationSource = new CancellationTokenSource();

            mockCredential
            .Setup(credential => credential.GetTokenAsync(It.IsAny <TokenRequestContext>(), It.Is <CancellationToken>(value => value == cancellationSource.Token)))
            .Returns(new ValueTask <AccessToken>(new AccessToken(tokenValue, DateTimeOffset.MaxValue)));

            mockScope
            .Setup(scope => scope.OpenConsumerLinkAsync(
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <EventPosition>(),
                       It.IsAny <TimeSpan>(),
                       It.IsAny <TimeSpan>(),
                       It.IsAny <uint>(),
                       It.IsAny <long?>(),
                       It.IsAny <long?>(),
                       It.IsAny <bool>(),
                       It.IsAny <string>(),
                       It.IsAny <CancellationToken>()))
            .Throws(retriableException);

            var consumer = new AmqpConsumer(eventHub, consumerGroup, partition, identifier, eventPosition, trackLastEnqueued, invalidateOnSteal, ownerLevel, null, null, mockScope.Object, Mock.Of <AmqpMessageConverter>(), retryPolicy);

            Assert.That(async() => await consumer.ReceiveAsync(100, null, cancellationSource.Token), Throws.InstanceOf(retriableException.GetType()));

            mockScope
            .Verify(scope => scope.OpenConsumerLinkAsync(
                        It.Is <string>(value => value == consumerGroup),
                        It.Is <string>(value => value == partition),
                        It.Is <EventPosition>(value => value == eventPosition),
                        It.IsAny <TimeSpan>(),
                        It.IsAny <TimeSpan>(),
                        It.IsAny <uint>(),
                        It.IsAny <long?>(),
                        It.Is <long?>(value => value == ownerLevel),
                        It.Is <bool>(value => value == trackLastEnqueued),
                        It.Is <string>(value => value == identifier),
                        It.IsAny <CancellationToken>()),
                    Times.Exactly(1 + retryOptions.MaximumRetries));
        }
Example #9
0
        public void CreateExceptionForResponseWithNoResponse()
        {
            var exception = AmqpError.CreateExceptionForResponse(null, null);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsException>(), "The exception should be a generic Event Hubs exception");
            Assert.That(exception.Message, Is.EqualTo(Resources.UnknownCommunicationException), "The exception message should indicate an unknown failure");
            Assert.That(((EventHubsException)exception).IsTransient, Is.True, "The exception should be considered transient");
        }
Example #10
0
        private void FireErrorEvent(AmqpError error)
        {
            var ev = this.OnError;

            if (ev != null && error != null)
            {
                ev(error);
            }
        }
Example #11
0
        private void CancelPendingCommands(AmqpError error)
        {
            CommandToSend cmdToSend;

            while (_commandOutbox.TryDequeue(out cmdToSend))
            {
                cmdToSend.RunReplyAction(0, 0, error).IntentionallyNotAwaited();
            }
        }
Example #12
0
        /// <summary>
        ///   Determines if a given AMQP message response is an error and, if so, throws the
        ///   appropriate corresponding exception type.
        /// </summary>
        ///
        /// <param name="response">The AMQP response message to consider.</param>
        /// <param name="eventHubName">The name of the Event Hub associated with the request.</param>
        ///
        private static void ThrowIfErrorResponse(AmqpMessage response,
                                                 string eventHubName)
        {
            var statusCode = default(int);

            if ((response?.ApplicationProperties?.Map.TryGetValue(AmqpResponse.StatusCode, out statusCode) != true) ||
                (!AmqpResponse.IsSuccessStatus((AmqpResponseStatusCode)statusCode)))
            {
                throw AmqpError.CreateExceptionForResponse(response, eventHubName);
            }
        }
        private void CancelPendingCommands(AmqpError error)
        {
            CommandToSend cmdToSend;

            while (_commandOutbox.TryDequeue(out cmdToSend))
            {
#pragma warning disable 4014
                cmdToSend.RunReplyAction(0, 0, error);
#pragma warning restore 4014
            }
        }
Example #14
0
        public void ThrowIfErrorResponseThrowsOnFailure(AmqpResponseStatusCode statusCode)
        {
            var description  = "This is a test description.";
            var resourceName = "TestHub";

            using var response             = AmqpMessage.Create();
            response.ApplicationProperties = new ApplicationProperties();
            response.ApplicationProperties.Map[AmqpResponse.StatusCode]        = (int)statusCode;
            response.ApplicationProperties.Map[AmqpResponse.StatusDescription] = description;

            Assert.That(() => AmqpError.ThrowIfErrorResponse(response, resourceName), Throws.Exception);
        }
Example #15
0
        // A disconnect may be expected coz we send a connection close, etc..
        // or it may be something abruptal
        internal void HandleDisconnect()
        {
            if (_isClosed)
            {
                return;                        // we have initiated the close
            }
            // otherwise

            _lastError = new AmqpError {
                ClassId = 0, MethodId = 0, ReplyCode = 0, ReplyText = "disconnected"
            };

            DrainPending(_lastError);
        }
Example #16
0
        internal void CloseAllChannels(bool initiatedByServer, AmqpError error)
        {
            foreach (var channel in _channels)
            {
                if (channel == null)
                {
                    continue;
                }

#pragma warning disable 4014
                channel._io.InitiateCleanClose(initiatedByServer, error);
#pragma warning restore 4014
            }
        }
Example #17
0
        public void CreateExceptionForResponseWithUnknownCondition()
        {
            var description  = "NOT_KNOWN";
            var resourceName = "TestHub";

            using var response             = AmqpMessage.Create();
            response.ApplicationProperties = new ApplicationProperties();
            response.ApplicationProperties.Map[AmqpResponse.ErrorCondition]    = new AmqpSymbol("Invalid");
            response.ApplicationProperties.Map[AmqpResponse.StatusDescription] = description;

            var exception = AmqpError.CreateExceptionForResponse(response, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
        public void TranslateServiceExceptionTranslatesOperationCanceledWithEmbeddedAmqpException()
        {
            var eventHub  = "someHub";
            var exception = new OperationCanceledException("oops", AmqpError.CreateExceptionForError(new Error {
                Condition = AmqpError.ServerBusyError
            }, eventHub));
            var translated = exception.TranslateServiceException(eventHub);

            Assert.That(translated, Is.Not.Null, "An exception should have been returned.");

            var eventHubsException = translated as EventHubsException;

            Assert.That(eventHubsException, Is.Not.Null, "The exception type should be appropriate for the `Server Busy` scenario.");
            Assert.That(eventHubsException.Reason, Is.EqualTo(EventHubsException.FailureReason.ServiceBusy), "The exception reason should indicate `Server Busy`.");
            Assert.That(eventHubsException.EventHubName, Is.EqualTo(eventHub), "The Event Hub name should match.");
        }
Example #19
0
        public void CreateExceptionForResponseWithTimeoutConditionAndPattern()
        {
            var description  = GetNotFoundExpression().ToString().Replace("*.", "some value");
            var resourceName = "TestHub";

            using var response             = AmqpMessage.Create();
            response.ApplicationProperties = new ApplicationProperties();
            response.ApplicationProperties.Map[AmqpResponse.ErrorCondition]    = AmqpErrorCode.NotFound;
            response.ApplicationProperties.Map[AmqpResponse.StatusDescription] = description;

            var exception = AmqpError.CreateExceptionForResponse(response, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsResourceNotFoundException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #20
0
        internal void CloseAllChannels(bool initiatedByServer, AmqpError error)
        {
            LogAdapter.LogDebug("Connection", "Closing all channels");

            foreach (var channel in _channels)
            {
                if (channel == null)
                {
                    continue;
                }

#pragma warning disable 4014
                channel._io.InitiateCleanClose(initiatedByServer, error);
#pragma warning restore 4014
            }
        }
Example #21
0
        public void CreateExceptionForResponseWithUnmappedStatus()
        {
            var description  = "Some description";
            var resourceName = "TestHub";

            using var response             = AmqpMessage.Create();
            response.ApplicationProperties = new ApplicationProperties();
            response.ApplicationProperties.Map[AmqpResponse.StatusCode]        = Int32.MaxValue;
            response.ApplicationProperties.Map[AmqpResponse.StatusDescription] = description;

            var exception = AmqpError.CreateExceptionForResponse(response, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #22
0
        public void CreateExceptionForResponseWithTimeoutConditionAndStatusDescription()
        {
            var description  = $"This has { GetNotFoundStatusText() } embedded in it";
            var resourceName = "TestHub";

            using var response             = AmqpMessage.Create();
            response.ApplicationProperties = new ApplicationProperties();
            response.ApplicationProperties.Map[AmqpResponse.ErrorCondition]    = AmqpErrorCode.NotFound;
            response.ApplicationProperties.Map[AmqpResponse.StatusDescription] = description;

            var exception = AmqpError.CreateExceptionForResponse(response, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsResourceNotFoundException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
        public void CreateExceptionForErrorWithUnknownTimeout()
        {
            var description  = "NOT_KNOWN";
            var resourceName = "TestHub";

            var error = new Error
            {
                Condition   = AmqpErrorCode.NotFound,
                Description = description
            };

            Exception exception = AmqpError.CreateExceptionForError(error, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsCommunicationException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #24
0
        public void CreateExceptionForErrorWithTimeoutStatusDescription()
        {
            var description  = $"This has { GetNotFoundStatusText() } embedded in it";
            var resourceName = "TestHub";

            var error = new Error
            {
                Condition   = AmqpErrorCode.NotFound,
                Description = description
            };

            var exception = AmqpError.CreateExceptionForError(error, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsResourceNotFoundException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #25
0
        public void CreateExceptionForResponseWithUnknownTimeout()
        {
            var description  = "NOT_KNOWN";
            var resourceName = "TestHub";

            using var response             = AmqpMessage.Create();
            response.ApplicationProperties = new ApplicationProperties();
            response.ApplicationProperties.Map[AmqpResponse.ErrorCondition]    = AmqpErrorCode.NotFound;
            response.ApplicationProperties.Map[AmqpResponse.StatusDescription] = description;

            Exception exception = AmqpError.CreateExceptionForResponse(response, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsException>(), "The exception should match");
            Assert.That(((EventHubsException)exception).Reason, Is.EqualTo(EventHubsException.FailureReason.ServiceCommunicationProblem), "The exception reason should match.");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #26
0
        public void CreateExceptionForErrorWithTimeoutPattern()
        {
            var description  = GetNotFoundExpression().ToString().Replace("*.", "some value");
            var resourceName = "TestHub";

            var error = new Error
            {
                Condition   = AmqpErrorCode.NotFound,
                Description = description
            };

            var exception = AmqpError.CreateExceptionForError(error, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsResourceNotFoundException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #27
0
        public void CreateExceptionForErrorWithUnknownCondition()
        {
            var description  = "NOT_KNOWN";
            var resourceName = "TestHub";

            var error = new Error
            {
                Condition   = new AmqpSymbol("Invalid"),
                Description = description
            };

            var exception = AmqpError.CreateExceptionForError(error, resourceName);

            Assert.That(exception, Is.Not.Null, "An exception should have been created");
            Assert.That(exception, Is.TypeOf <EventHubsException>(), "The exception should match");
            Assert.That(exception.Message, Is.SupersetOf(description), "The exception message should contain the description");
        }
Example #28
0
        private async Task FireErrorEvent(AmqpError error)
        {
            Func <AmqpError, Task>[] copy = null;
            lock (ErrorCallbacks)
            {
                if (ErrorCallbacks == null || ErrorCallbacks.Count == 0)
                {
                    return;
                }

                copy = ErrorCallbacks.ToArray();
            }

            foreach (var errorCallback in copy)
            {
                await errorCallback(error);
            }
        }
Example #29
0
        // To be use in case of exceptions on our end. Close everything asap
        internal virtual async Task InitiateAbruptClose(Exception reason)
        {
            if (_isClosed)
            {
                return;
            }
            Thread.MemoryBarrier();
            _isClosed = true;

            var syntheticError = new AmqpError {
                ReplyText = reason.Message
            };

            DrainPending(syntheticError);

            await FireErrorEvent(syntheticError);

            this.Dispose();
        }
Example #30
0
        protected virtual void DrainPending(AmqpError error)
        {
            // releases every task awaiting
            CommandToSend sent;

            while (_awaitingReplyQueue.TryDequeue(out sent))
            {
                if (error != null && sent.ClassId == error.ClassId && sent.MethodId == error.MethodId)
                {
                    // if we find the "offending" command, then it gets a better error message
                    sent.RunReplyAction(0, 0, error);
                }
                else
                {
                    // any other task dies with a generic error.
                    sent.RunReplyAction(0, 0, null);
                }
            }
        }