/// <inheritdoc/>
        public async Task <bool> MoveNextAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            bool hasMore;

            try
            {
                hasMore = await _cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                if (RetryabilityHelper.IsResumableChangeStreamException(ex))
                {
                    _cursor = await _changeStreamOperation.ResumeAsync(_binding, cancellationToken).ConfigureAwait(false);

                    hasMore = await _cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    throw;
                }
            }

            ProcessBatch(hasMore);
            return(hasMore);
        }
Example #2
0
        public void IsCommandRetryable_should_return_expected_result(string command, bool isRetryable)
        {
            var commandDocument = BsonDocument.Parse(command);

            var result = RetryabilityHelper.IsCommandRetryable(commandDocument);

            result.Should().Be(isRetryable);
        }
Example #3
0
        public void IsRetryableReadException_should_return_expected_result_using_exception_type(Type exceptionType, bool expectedResult)
        {
            var exception = CoreExceptionHelper.CreateException(exceptionType);

            var result = RetryabilityHelper.IsRetryableReadException(exception);

            result.Should().Be(expectedResult);
        }
        public void IsResumableChangeStreamException_should_return_expected_result_using_error_label(string label, bool expectedResult)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(label: label);

            var result = RetryabilityHelper.IsResumableChangeStreamException(exception);

            result.Should().Be(expectedResult);
        }
Example #5
0
        public void IsRetryableReadException_should_return_expected_result_using_code(int code, bool expectedResult)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(code);

            var result = RetryabilityHelper.IsRetryableReadException(exception);

            result.Should().Be(expectedResult);
        }
Example #6
0
        public void IsResumableChangeStreamException_should_return_expected_result_for_servers_with_new_behavior_and_errors(Type exceptionType, bool isResumable)
        {
            var exception = (MongoException)CoreExceptionHelper.CreateException(exceptionType);

            var result = RetryabilityHelper.IsResumableChangeStreamException(exception, Feature.ServerReturnsResumableChangeStreamErrorLabel.FirstSupportedVersion);

            result.Should().Be(isResumable);
        }
        public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_for_network_errors()
        {
            var exception = (MongoException)CoreExceptionHelper.CreateException(typeof(MongoConnectionException));

            RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception);

            var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError");

            hasRetryableWriteErrorLabel.Should().BeTrue();
        }
Example #8
0
        public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_for_MongoWriteConcernException_when_required(int errorCode, bool shouldAddErrorLabel)
        {
            var exception = CoreExceptionHelper.CreateMongoWriteConcernException(BsonDocument.Parse($"{{ writeConcernError : {{ code : {errorCode} }} }}"));

            RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, Feature.ServerReturnsRetryableWriteErrorLabel.LastNotSupportedVersion);

            var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError");

            hasRetryableWriteErrorLabel.Should().Be(shouldAddErrorLabel);
        }
        public static bool ShouldConnectionAcquireBeRetried(RetryableWriteContext context, ServerDescription serverDescription, Exception exception)
        {
            var innerException = exception is MongoAuthenticationException mongoAuthenticationException ? mongoAuthenticationException.InnerException : exception;

            // According the spec error during handshake should be handle according to RetryableReads logic
            return(context.RetryRequested &&
                   AreRetryableWritesSupported(serverDescription) &&
                   context.Binding.Session.Id != null &&
                   !context.Binding.Session.IsInTransaction &&
                   RetryabilityHelper.IsRetryableReadException(innerException));
        }
Example #10
0
        public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_for_network_errors([Values(false, true)] bool serverReturnsRetryableWriteErrorLabel)
        {
            var exception     = (MongoException)CoreExceptionHelper.CreateException(typeof(MongoConnectionException));
            var feature       = Feature.ServerReturnsRetryableWriteErrorLabel;
            var serverVersion = serverReturnsRetryableWriteErrorLabel ? feature.FirstSupportedVersion : feature.LastNotSupportedVersion;

            RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, serverVersion);

            var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError");

            hasRetryableWriteErrorLabel.Should().BeTrue();
        }
Example #11
0
        public void IsResumableChangeStreamException_should_return_expected_result_for_servers_with_new_behavior([Values(false, true)] bool hasResumableChangeStreamErrorLabel)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(-1);

            if (hasResumableChangeStreamErrorLabel)
            {
                exception.AddErrorLabel("ResumableChangeStreamError");
            }

            var result = RetryabilityHelper.IsResumableChangeStreamException(exception, Feature.ServerReturnsResumableChangeStreamErrorLabel.FirstSupportedVersion);

            result.Should().Be(hasResumableChangeStreamErrorLabel);
        }
Example #12
0
        public void IsRetryableWriteException_should_return_expected_result([Values(false, true)] bool hasRetryableWriteLabel)
        {
            var exception = CoreExceptionHelper.CreateMongoCommandException(-1);

            if (hasRetryableWriteLabel)
            {
                exception.AddErrorLabel("RetryableWriteError");
            }

            var result = RetryabilityHelper.IsRetryableWriteException(exception);

            result.Should().Be(hasRetryableWriteLabel);
        }
        public static async Task <TResult> ExecuteAsync <TResult>(IRetryableReadOperation <TResult> operation, RetryableReadContext context, CancellationToken cancellationToken)
        {
            if (!ShouldReadBeRetried(context))
            {
                return(await operation.ExecuteAttemptAsync(context, attempt : 1, transactionNumber : null, cancellationToken).ConfigureAwait(false));
            }

            var       initialServerVersion = context.Channel.ConnectionDescription.ServerVersion;
            Exception originalException;

            try
            {
                return(await operation.ExecuteAttemptAsync(context, attempt : 1, transactionNumber : null, cancellationToken).ConfigureAwait(false));
            }
            catch (Exception ex) when(RetryabilityHelper.IsRetryableReadException(ex))
            {
                originalException = ex;
            }

            try
            {
                context.ReplaceChannelSource(context.Binding.GetReadChannelSource(cancellationToken));
                context.ReplaceChannel(context.ChannelSource.GetChannel(cancellationToken));
            }
            catch
            {
                throw originalException;
            }

            if (context.Channel.ConnectionDescription.ServerVersion < initialServerVersion)
            {
                throw originalException;
            }

            if (!AreRetryableReadsSupported(context))
            {
                throw originalException;
            }

            try
            {
                return(await operation.ExecuteAttemptAsync(context, attempt : 2, transactionNumber : null, cancellationToken).ConfigureAwait(false));
            }
            catch (Exception ex) when(ShouldThrowOriginalException(ex))
            {
                throw originalException;
            }
        }
Example #14
0
        public void IsResumableChangeStreamException_should_return_expected_result_for_servers_with_old_behavior(object exceptionDescription, bool isResumable)
        {
            MongoException exception;

            if (exceptionDescription is Type exceptionType)
            {
                exception = (MongoException)CoreExceptionHelper.CreateException(exceptionType);
            }
            else
            {
                exception = CoreExceptionHelper.CreateMongoCommandException((int)exceptionDescription);
            }

            var result = RetryabilityHelper.IsResumableChangeStreamException(exception, Feature.ServerReturnsResumableChangeStreamErrorLabel.LastNotSupportedVersion);

            result.Should().Be(isResumable);
        }
        public static async Task <TResult> ExecuteAsync <TResult>(IRetryableWriteOperation <TResult> operation, RetryableWriteContext context, CancellationToken cancellationToken)
        {
            if (!AreRetriesAllowed(operation, context))
            {
                return(await operation.ExecuteAttemptAsync(context, 1, null, cancellationToken).ConfigureAwait(false));
            }

            var       transactionNumber = context.Binding.Session.AdvanceTransactionNumber();
            Exception originalException;

            try
            {
                return(await operation.ExecuteAttemptAsync(context, 1, transactionNumber, cancellationToken).ConfigureAwait(false));
            }
            catch (Exception ex) when(RetryabilityHelper.IsRetryableWriteException(ex))
            {
                originalException = ex;
            }

            try
            {
                context.ReplaceChannelSource(await context.Binding.GetWriteChannelSourceAsync(cancellationToken).ConfigureAwait(false));
                context.ReplaceChannel(await context.ChannelSource.GetChannelAsync(cancellationToken).ConfigureAwait(false));
            }
            catch
            {
                throw originalException;
            }

            if (!AreRetryableWritesSupported(context.Channel.ConnectionDescription))
            {
                throw originalException;
            }

            try
            {
                return(await operation.ExecuteAttemptAsync(context, 2, transactionNumber, cancellationToken).ConfigureAwait(false));
            }
            catch (Exception ex) when(ShouldThrowOriginalException(ex))
            {
                throw originalException;
            }
        }
Example #16
0
        public static TResult Execute <TResult>(IRetryableWriteOperation <TResult> operation, RetryableWriteContext context, CancellationToken cancellationToken)
        {
            if (!context.RetryRequested || !AreRetryableWritesSupported(context.Channel.ConnectionDescription))
            {
                return(operation.ExecuteAttempt(context, 1, null, cancellationToken));
            }

            var       transactionNumber = context.Binding.Session.AdvanceTransactionNumber();
            Exception originalException;

            try
            {
                return(operation.ExecuteAttempt(context, 1, transactionNumber, cancellationToken));
            }
            catch (Exception ex) when(RetryabilityHelper.IsRetryableWriteException(ex))
            {
                originalException = ex;
            }

            try
            {
                context.ReplaceChannelSource(context.Binding.GetWriteChannelSource(cancellationToken));
                context.ReplaceChannel(context.ChannelSource.GetChannel(cancellationToken));
            }
            catch
            {
                throw originalException;
            }

            if (!AreRetryableWritesSupported(context.Channel.ConnectionDescription))
            {
                throw originalException;
            }

            try
            {
                return(operation.ExecuteAttempt(context, 2, transactionNumber, cancellationToken));
            }
            catch (Exception ex) when(ShouldThrowOriginalException(ex))
            {
                throw originalException;
            }
        }
Example #17
0
        public void AddRetryableWriteErrorLabelIfRequired_should_add_RetryableWriteError_when_required(object exceptionDescription, bool shouldAddErrorLabel)
        {
            MongoException exception;

            if (exceptionDescription is Type exceptionType)
            {
                exception = (MongoException)CoreExceptionHelper.CreateException(exceptionType);
            }
            else
            {
                exception = CoreExceptionHelper.CreateMongoCommandException((int)exceptionDescription);
            }

            RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, Feature.ServerReturnsRetryableWriteErrorLabel.LastNotSupportedVersion);

            var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError");

            hasRetryableWriteErrorLabel.Should().Be(shouldAddErrorLabel);
        }
Example #18
0
        public void AddRetryableWriteErrorLabelIfRequired_should_not_add_error_label_for_non_retryWrites_server(
            [Values(false, true)] bool isNetworkError)
        {
            MongoException exception = null;

            if (isNetworkError)
            {
                exception = (MongoException)CoreExceptionHelper.CreateException(typeof(MongoConnectionException));
            }
            else
            {
                exception = CoreExceptionHelper.CreateMongoCommandException((int)ServerErrorCode.HostNotFound);
            }

            RetryabilityHelper.AddRetryableWriteErrorLabelIfRequired(exception, Feature.RetryableWrites.LastNotSupportedVersion);

            var hasRetryableWriteErrorLabel = exception.HasErrorLabel("RetryableWriteError");

            hasRetryableWriteErrorLabel.Should().BeFalse();
        }
Example #19
0
        /// <inheritdoc/>
        public bool MoveNext(CancellationToken cancellationToken = default(CancellationToken))
        {
            bool hasMore;

            while (true)
            {
                try
                {
                    hasMore = _cursor.MoveNext(cancellationToken);
                    break;
                }
                catch (Exception ex) when(RetryabilityHelper.IsResumableChangeStreamException(ex))
                {
                    var newCursor = _changeStreamOperation.Resume(_binding, cancellationToken);

                    _cursor.Dispose();
                    _cursor = newCursor;
                }
            }

            ProcessBatch(hasMore);
            return(hasMore);
        }
        public static TResult Execute <TResult>(IRetryableReadOperation <TResult> operation, RetryableReadContext context, CancellationToken cancellationToken)
        {
            if (!ShouldReadBeRetried(context))
            {
                return(operation.ExecuteAttempt(context, attempt: 1, transactionNumber: null, cancellationToken));
            }

            Exception originalException;

            try
            {
                return(operation.ExecuteAttempt(context, attempt: 1, transactionNumber: null, cancellationToken));
            }
            catch (Exception ex) when(RetryabilityHelper.IsRetryableReadException(ex))
            {
                originalException = ex;
            }

            try
            {
                context.ReplaceChannelSource(context.Binding.GetReadChannelSource(cancellationToken));
                context.ReplaceChannel(context.ChannelSource.GetChannel(cancellationToken));
            }
            catch
            {
                throw originalException;
            }

            try
            {
                return(operation.ExecuteAttempt(context, attempt: 2, transactionNumber: null, cancellationToken));
            }
            catch (Exception ex) when(ShouldThrowOriginalException(ex))
            {
                throw originalException;
            }
        }
        /// <inheritdoc/>
        public bool MoveNext(CancellationToken cancellationToken = default(CancellationToken))
        {
            bool hasMore;

            try
            {
                hasMore = _cursor.MoveNext(cancellationToken);
            }
            catch (Exception ex)
            {
                if (RetryabilityHelper.IsResumableChangeStreamException(ex))
                {
                    _cursor = _changeStreamOperation.Resume(_binding, cancellationToken);
                    hasMore = _cursor.MoveNext(cancellationToken);
                }
                else
                {
                    throw;
                }
            }

            ProcessBatch(hasMore);
            return(hasMore);
        }
Example #22
0
        /// <inheritdoc/>
        public async Task <bool> MoveNextAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            bool hasMore;

            while (true)
            {
                try
                {
                    hasMore = await _cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false);

                    break;
                }
                catch (Exception ex) when(RetryabilityHelper.IsResumableChangeStreamException(ex))
                {
                    var newCursor = await _changeStreamOperation.ResumeAsync(_binding, cancellationToken).ConfigureAwait(false);

                    _cursor.Dispose();
                    _cursor = newCursor;
                }
            }

            ProcessBatch(hasMore);
            return(hasMore);
        }
Example #23
0
        public static bool ShouldConnectionAcquireBeRetried(RetryableReadContext context, Exception ex)
        {
            // According the spec error during handshake should be handle according to RetryableReads logic
            var innerException = ex is MongoAuthenticationException mongoAuthenticationException ? mongoAuthenticationException.InnerException : ex;

            return(context.RetryRequested && !context.Binding.Session.IsInTransaction && RetryabilityHelper.IsRetryableReadException(innerException));
        }