/// <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); }
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); }
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); }
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); }
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(); }
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)); }
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(); }
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); }
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; } }
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; } }
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; } }
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); }
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(); }
/// <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); }
/// <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); }
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)); }