Пример #1
0
            public virtual Task MockableRetryAsync(IOperation operation, CancellationTokenPair cancellationToken)
            {
                // BucketBase.RetryAsync forwards to our mock IRetryOrchestrator, which forwards back to this method
                // This allows us to mock retry behavior, while BucketBase.RetryAsync remains non-virtual for performance.

                return(Task.CompletedTask);
            }
Пример #2
0
 protected sealed override TaskResult ExecuteTask(CancellationTokenPair token)
 {
     Dry(token);
     using var state = TaskResult.SpinLock(TimeSpan.FromMilliseconds(500));
     state.Value     = state.Value.WithTerminationTaskResultType(TaskResultCode.SuccessResult);
     return(state.Value);
 }
        private async Task AssertRetryThenSuccessAsync(OperationBase op, Exception exp)
        {
            var retryOrchestrator = CreateRetryOrchestrator();

            var bucketMock = new Mock <BucketBase>("fake", new ClusterContext(), new Mock <IScopeFactory>().Object,
                                                   retryOrchestrator, new Mock <ILogger>().Object, new Mock <IRedactor>().Object,
                                                   new Mock <IBootstrapperFactory>().Object, NoopRequestTracer.Instance, new Mock <IOperationConfigurator>().Object,
                                                   new BestEffortRetryStrategy());

            bucketMock.Setup(x => x.SendAsync(op, It.IsAny <CancellationTokenPair>())).Callback((IOperation op, CancellationTokenPair ct) =>
            {
                if (op.Completed.IsCompleted)
                {
                    Assert.True(false, "operation result should be reset before retry");
                }
                // complete the operation (ResponseStatus does not matter for this test)
                op.HandleOperationCompleted(AsyncState.BuildErrorResponse(op.Opaque, ResponseStatus.TemporaryFailure));
                if (op.Attempts == 1)
                {
                    throw exp;
                }
            }).Returns(op.Completed.AsTask());

            var tokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(2500));

            try
            {
                await retryOrchestrator.RetryAsync(bucketMock.Object, op, CancellationTokenPair.FromInternalToken(tokenSource.Token)).ConfigureAwait(false);
            }
            catch (Exception)
            {
                Assert.True(false, "Expected operation to succeed after retry");
            }
            Assert.True(op.Attempts > 1);
        }
        private async Task AssertRetryAsync <TExpected>(IOperation op, Exception exp, int minAttempts = 2,
                                                        CancellationToken externalCancellationToken   = default)
            where TExpected : Exception
        {
            var retryOrchestrator = CreateRetryOrchestrator();

            var bucketMock = new Mock <BucketBase>("fake", new ClusterContext(), new Mock <IScopeFactory>().Object,
                                                   retryOrchestrator, new Mock <ILogger>().Object, new Mock <IRedactor>().Object,
                                                   new Mock <IBootstrapperFactory>().Object, NoopRequestTracer.Instance,
                                                   new Mock <IOperationConfigurator>().Object,
                                                   new BestEffortRetryStrategy());

            bucketMock.Setup(x => x.SendAsync(op, It.IsAny <CancellationTokenPair>())).Throws(exp);

            using var tokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(25000));
            try
            {
                var tokenPair = new CancellationTokenPair(externalCancellationToken, tokenSource.Token);

                await retryOrchestrator.RetryAsync(bucketMock.Object, op, tokenPair).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                Assert.IsAssignableFrom <TExpected>(e);

                Assert.True(op.Attempts >= minAttempts);
            }
        }
Пример #5
0
            internal override async Task SendAsync(IOperation op, CancellationTokenPair token = default)
            {
                var mockConnectionPool = new Mock <IConnectionPool>();

                var mockConnectionPoolFactory = new Mock <IConnectionPoolFactory>();

                mockConnectionPoolFactory
                .Setup(m => m.Create(It.IsAny <ClusterNode>()))
                .Returns(mockConnectionPool.Object);

                var clusterNode = new ClusterNode(new ClusterContext(), mockConnectionPoolFactory.Object,
                                                  new Mock <ILogger <ClusterNode> >().Object, new DefaultObjectPool <OperationBuilder>(new OperationBuilderPoolPolicy()),
                                                  new Mock <ICircuitBreaker>().Object,
                                                  new Mock <ISaslMechanismFactory>().Object,
                                                  new Mock <IRedactor>().Object,
                                                  new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11210),
                                                  BucketType.Couchbase,
                                                  new NodeAdapter(),
                                                  NoopRequestTracer.Instance);

                await clusterNode.ExecuteOp(op, token).ConfigureAwait(false);

                if (_statuses.TryDequeue(out ResponseStatus status))
                {
                    (op as OperationBase)?.HandleOperationCompleted(AsyncState.BuildErrorResponse(0, status));
                }
                else
                {
                    throw new InvalidOperationException();
                }
            }
        private CancellationTokenSource CreateRetryTimeoutCancellationTokenSource(
            FunctionOptionsBase options, out CancellationTokenPair tokenPair)
        {
            var cts = new CancellationTokenSource(options.Timeout);

            tokenPair = new CancellationTokenPair(options.Token, cts.Token);
            return(cts);
        }
Пример #7
0
        private CancellationTokenSource CreateRetryTimeoutCancellationTokenSource(
            ITimeoutOptions options, IOperation op, out CancellationTokenPair tokenPair)
        {
            var cts = new CancellationTokenSource(GetTimeout(options.Timeout, op));

            tokenPair = new CancellationTokenPair(options.Token, cts.Token);
            return(cts);
        }
Пример #8
0
        protected virtual void Dry(CancellationTokenPair pair)
        {
            byte unitsToRemove;
            {
                using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2));
                unitsToRemove = lls.LoadedLaundryItem != LaundryItems.InvalidItem
                    ? lls.LoadedLaundryItem.Dampness
                    : throw new StateLogicErrorException(
                                          "It should not be possible to lack laundry during the wash cycle.");
            }
            byte     unitsToDeduct = unitsToRemove;
            TimeSpan timeRequired  = TimeToDecrementDampnessPerUnit * unitsToRemove;
            double   randomFactor  = RandomNumberSource.Next(1, 11) / 100.0; //introduce randomness factor of 10%, either direction

            Debug.Assert(randomFactor >= 0.0 && randomFactor <= 0.11);
            TimeSpan randomTimeToAddOrSub = timeRequired * randomFactor;

            Debug.Assert(randomTimeToAddOrSub <= timeRequired);
            bool negate = RandomNumberSource.Next(1, 3) == 1;

            randomTimeToAddOrSub = negate ? -randomTimeToAddOrSub : +randomTimeToAddOrSub;
            timeRequired        += randomTimeToAddOrSub;

            Stopwatch sw = null;

            try
            {
                sw = HighPrecisionTimer;
                sw.Restart();
                SimulateWait(pair, timeRequired);
            }
            catch (OperationCanceledException)
            {
                if (sw == null)
                {
                    string log = "For some reason the stopwatch is null.";
                    TerminationHelper.TerminateApplication(log);
                    return;
                }

                TimeSpan elapsed    = sw.Elapsed;
                double   percentage = elapsed >= timeRequired ? 1.0 : elapsed / timeRequired;
                unitsToDeduct = Convert.ToByte(Math.Floor(unitsToRemove * percentage));
                throw;
            }
            finally
            {
                sw?.Reset();
                byte newSetting = (byte)(unitsToRemove - unitsToDeduct);
                Debug.Assert(newSetting <= unitsToRemove);
                using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2));
                lls.SetDampFactor(newSetting);
                Debug.Assert(lls.LoadedLaundryItem.Dampness <= unitsToRemove);
            }
        }
        public void CanBeCanceled_SameToken_True()
        {
            // Arrange

            using var cts = new CancellationTokenSource();
            var tokenPair = new CancellationTokenPair(cts.Token, cts.Token);

            // Assert

            Assert.True(tokenPair.CanBeCanceled);
        }
        public void CanBeCanceled_InternalToken_True()
        {
            // Arrange

            using var cts = new CancellationTokenSource();
            var tokenPair = CancellationTokenPair.FromInternalToken(cts.Token);

            // Assert

            Assert.True(tokenPair.CanBeCanceled);
        }
        public void IsCancellationRequested_TokensNotCanceled_False()
        {
            // Arrange

            using var cts  = new CancellationTokenSource();
            using var cts2 = new CancellationTokenSource();

            var tokenPair = new CancellationTokenPair(cts.Token, cts2.Token);

            // Assert

            Assert.False(tokenPair.IsCancellationRequested);
        }
Пример #12
0
        protected virtual void Soak(CancellationTokenPair token)
        {
            Stopwatch sw = null;

            try
            {
                Debug.WriteLine("Beginning Cleanse Soak");
                byte oldDampness;
                byte newDampness;
                sw = HighPrecisionTimer;
                sw.Reset();
                sw.Start();
                {
                    using LockedLaundryStatus lls =
                              LaundryFlags.SpinLock(token.IndividualToken, TimeSpan.FromSeconds(2));
                    var res = lls.SoakLaundry() ??
                              throw new StateLogicErrorException(
                                        "It is supposed to be impossible to start the machine without laundry in it.");
                    oldDampness = res.OldDampness;
                    newDampness = res.NewDampness;
                }

                Debug.Assert(newDampness >= oldDampness);
                int      dampnessUnitsIncrease = newDampness - oldDampness;
                TimeSpan totalTimeRequired     = TimeToIncrementDampnessPerUnit * dampnessUnitsIncrease;
                TimeSpan timeRemaining         = totalTimeRequired - sw.Elapsed;
                if (timeRemaining > TimeSpan.Zero)
                {
                    SimulateWait(token, timeRemaining, "Beginning soak wait", "Ending soak wait");
                }
            }
            catch (StateLogicErrorException ex)
            {
                Console.Error.WriteLineAsync(ex.ToString());
                Environment.Exit(-1);
            }
            catch (TimeoutException)
            {
                Console.Error.WriteAsync(
                    $"Unable to obtain lock in {nameof(Soak)} method of {nameof(WashTask)} task.");
                throw;
            }
            finally
            {
                sw?.Reset();
                Debug.WriteLine("Ending soak.");
            }
        }
Пример #13
0
        protected virtual void Cleanse(CancellationTokenPair token)
        {
            byte unitsToRemove;
            {
                using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2));
                unitsToRemove = lls.LoadedLaundryItem != LaundryItems.InvalidItem
                    ? lls.LoadedLaundryItem.SoiledFactor
                    : throw new StateLogicErrorException(
                                          "It should not be possible to lack laundry during the wash cycle.");
            }
            byte      unitsToDeduct = unitsToRemove;
            TimeSpan  timeRequired  = TimeToRemoveOneUnitOfSoil * unitsToRemove;
            Stopwatch sw            = null;

            try
            {
                sw = HighPrecisionTimer;
                sw.Restart();
                if (timeRequired > TimeSpan.Zero)
                {
                    SimulateWait(token, timeRequired, "Beginning cleanse wait.");
                }
            }
            catch (IndividualOperationCancelledException)
            {
                if (sw == null)
                {
                    string log = "For some reason the stopwatch is null.";
                    TerminationHelper.TerminateApplication(log);
                    return;
                }
                TimeSpan elapsed    = sw.Elapsed;
                double   percentage = elapsed >= timeRequired ? 1.0 : elapsed / timeRequired;
                unitsToDeduct = Convert.ToByte(Math.Floor(unitsToRemove * percentage));
                throw;
            }
            finally
            {
                sw?.Reset();
                byte newSetting = (byte)(unitsToRemove - unitsToDeduct);
                Debug.Assert(newSetting <= unitsToRemove);
                using var lls = LaundryFlags.SpinLock(TimeSpan.FromSeconds(2));
                lls.SetSoilFactor(newSetting);
                Debug.Assert(lls.LoadedLaundryItem.SoiledFactor <= unitsToRemove);
            }
        }
        internal override async Task SendAsync(IOperation op, CancellationTokenPair tokenPair = default)
        {
            if (KeyMapper == null)
            {
                ThrowHelper.ThrowInvalidOperationException($"Bucket {Name} is not bootstrapped.");
            }

            if (op.RequiresVBucketId)
            {
                var vBucket = (VBucket)KeyMapper.MapKey(op.Key, op.WasNmvb());

                var endPoint = op.ReplicaIdx != null && op.ReplicaIdx > -1
                    ? vBucket.LocateReplica(op.ReplicaIdx.GetValueOrDefault())
                    : vBucket.LocatePrimary();

                op.VBucketId = vBucket.Index;

                try
                {
                    if (Nodes.TryGet(endPoint.GetValueOrDefault(), out var clusterNode))
                    {
                        await clusterNode.SendAsync(op, tokenPair).ConfigureAwait(false);

                        return;
                    }
                }
                catch (ArgumentNullException)
                {
                    //We could not find a candidate node to send so put into the retry queue
                    //its likely were between cluster map updates and we'll try again later
                    throw new NodeNotAvailableException(
                              $"Cannot find a Couchbase Server node for {endPoint}.");
                }
            }

            //Make sure we use a node with the data service
            var node = Nodes.GetRandom(x => x.HasKv);

            if (node == null)
            {
                throw new NodeNotAvailableException(
                          $"Cannot find a Couchbase Server node for executing {op.GetType()}.");
            }
            await node.SendAsync(op, tokenPair).ConfigureAwait(false);
        }
        internal override Task SendAsync(IOperation op, CancellationTokenPair tokenPair = default)
        {
            if (KeyMapper == null)
            {
                throw new InvalidOperationException("Bucket is not bootstrapped.");
            }

            var bucket   = KeyMapper.MapKey(op.Key);
            var endPoint = bucket.LocatePrimary();

            if (Nodes.TryGet(endPoint, out var clusterNode))
            {
                return(clusterNode.ExecuteOp(op, tokenPair));
            }

            //raise exception that node is not found
            return(Task.CompletedTask);
        }
Пример #16
0
        protected async Task <T> SendAsync <T>(IOperation <T> op, IConnection connection, CancellationToken cancellationToken)
        {
            await op.SendAsync(connection, cancellationToken).ConfigureAwait(false);

            ResponseStatus status;

            using (new OperationCancellationRegistration(op, CancellationTokenPair.FromInternalToken(cancellationToken)))
            {
                status = await op.Completed.ConfigureAwait(false);
            }

            if (status != ResponseStatus.Success && status != ResponseStatus.AuthenticationContinue)
            {
                throw new AuthenticationFailureException(
                          $"Cannot authenticate the user. Reason: {status}");
            }

            return(op.GetValue() !);
        }
        public async Task ExternalCancellation_ReadOnlyOperation_Cancels(bool isSent)
        {
            // Arrange

            var operation = new Get <dynamic>
            {
                IsSent = isSent
            };

            var cts       = new CancellationTokenSource(100);
            var tokenPair = CancellationTokenPair.FromExternalToken(cts.Token);

            // Act

            using var registration = new OperationCancellationRegistration(operation, tokenPair);

            // Assert

            await Assert.ThrowsAnyAsync <OperationCanceledException>(() => operation.Completed.AsTask());
        }
        public async Task ExternalCancellation_MutationOperationNotSent_Cancels()
        {
            // Arrange

            var operation = new Set <dynamic>("fake", "fakeKey")
            {
                IsSent = false
            };

            var cts       = new CancellationTokenSource(100);
            var tokenPair = CancellationTokenPair.FromExternalToken(cts.Token);

            // Act

            using var registration = new OperationCancellationRegistration(operation, tokenPair);

            // Assert

            await Assert.ThrowsAnyAsync <OperationCanceledException>(() => operation.Completed.AsTask());
        }
        public void Dispose_CancelsRegistration()
        {
            // Arrange

            var operation = new Get <dynamic>();

            var cts       = new CancellationTokenSource();
            var tokenPair = CancellationTokenPair.FromInternalToken(cts.Token);

            // Act

            var registration = new OperationCancellationRegistration(operation, tokenPair);

            registration.Dispose();

            // Assert

            cts.Cancel();

            Assert.False(operation.Completed.IsCompleted);
        }
        public async Task Operation_Succeeds_Without_Retry()
        {
            var retryOrchestrator = CreateRetryOrchestrator();

            var op = new Get <dynamic> {
                RetryStrategy = new BestEffortRetryStrategy()
            };
            var bucketMock = new Mock <BucketBase>("fake", new ClusterContext(), new Mock <IScopeFactory>().Object,
                                                   retryOrchestrator, new Mock <ILogger>().Object, new Mock <IRedactor>().Object,
                                                   new Mock <IBootstrapperFactory>().Object, NoopRequestTracer.Instance,
                                                   new Mock <IOperationConfigurator>().Object,
                                                   new BestEffortRetryStrategy());

            bucketMock.Setup(x => x.SendAsync(op, It.IsAny <CancellationTokenPair>()))
            .Returns(Task.CompletedTask);

            var tokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(2500));

            await retryOrchestrator.RetryAsync(bucketMock.Object, op, CancellationTokenPair.FromInternalToken(tokenSource.Token)).ConfigureAwait(false);

            Assert.Equal(1u, op.Attempts);
        }
        public void IsCancellationRequested_TokensCanceled_True(bool cancelInternal, bool cancelExternal)
        {
            // Arrange

            using var cts  = new CancellationTokenSource();
            using var cts2 = new CancellationTokenSource();

            var tokenPair = new CancellationTokenPair(cts.Token, cts2.Token);

            if (cancelExternal)
            {
                cts.Cancel();
            }

            if (cancelInternal)
            {
                cts2.Cancel();
            }

            // Assert

            Assert.True(tokenPair.IsCancellationRequested);
        }
        internal override async Task SendAsync(IOperation op, CancellationTokenPair tokenPair = default)
        {
            if (KeyMapper == null)
            {
                ThrowHelper.ThrowInvalidOperationException($"Bucket {Name} is not bootstrapped.");
            }

            if (op.RequiresVBucketId)
            {
                var vBucket = (VBucket)KeyMapper.MapKey(op.Key);

                var endPoint = op.ReplicaIdx != null
                    ? vBucket.LocateReplica(op.ReplicaIdx.GetValueOrDefault())
                    : vBucket.LocatePrimary();

                op.VBucketId = vBucket.Index;

                if (Nodes.TryGet(endPoint !, out var clusterNode))
                {
                    await clusterNode.SendAsync(op, tokenPair);

                    return;
                }

                throw new NodeNotAvailableException(
                          $"Cannot find a Couchbase Server node for {endPoint}.");
            }

            var node = Nodes.GetRandom();

            if (node == null)
            {
                throw new NodeNotAvailableException(
                          $"Cannot find a Couchbase Server node for executing {op.GetType()}.");
            }
            await node.SendAsync(op, tokenPair);
        }
        private async Task AssertDoesNotRetryAsync(IOperation op, Exception exp)
        {
            var retryOrchestrator = CreateRetryOrchestrator();

            var bucketMock = new Mock <BucketBase>("name", new ClusterContext(), new Mock <IScopeFactory>().Object,
                                                   retryOrchestrator, new Mock <ILogger>().Object, new Mock <IRedactor>().Object,
                                                   new Mock <IBootstrapperFactory>().Object,
                                                   NoopRequestTracer.Instance,
                                                   new Mock <IOperationConfigurator>().Object,
                                                   new BestEffortRetryStrategy())
            {
                CallBase = true
            };

            bucketMock.Setup(x => x.SendAsync(op, It.IsAny <CancellationTokenPair>())).Throws(exp);

            var tokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(2500));

            try
            {
                await bucketMock.Object.RetryAsync(op, CancellationTokenPair.FromInternalToken(tokenSource.Token)).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                if (e.GetType() == exp.GetType())
                {
                    //expected
                }
                else
                {
                    throw;
                }
            }

            Assert.True(op.Attempts == 1);
        }
        public async Task ExternalCancellation_MutationOperationSent_WaitsForInternalCancellation()
        {
            // Arrange

            var operation = new Set <dynamic>("fake", "fakeKey")
            {
                IsSent = true
            };

            var externalCts = new CancellationTokenSource(100);
            var internalCts = new CancellationTokenSource();
            var tokenPair   = new CancellationTokenPair(externalCts.Token, internalCts.Token);

            // Act

            using var registration = new OperationCancellationRegistration(operation, tokenPair);
            tokenPair.Register(() => internalCts.CancelAfter(100));

            // Assert

            await Assert.ThrowsAnyAsync <OperationCanceledException>(() => operation.Completed.AsTask());

            Assert.True(internalCts.IsCancellationRequested);
        }
Пример #25
0
        protected override TaskResult ExecuteTask(CancellationTokenPair pair)
        {
            bool needToClearError;

            using (var lsfLck = LaundryFlags.Lock(TimeSpan.FromSeconds(2)))
            {
                pair.ThrowIfCancellationRequested();
                needToClearError = lsfLck.ExecuteQuery((in LaundryStatusFlags lsf) =>
                                                       lsf.ErrorRegistrationStatus != ErrorRegistrationStatus.NilStatus);
            }

            if (needToClearError)
            {
                using (var lsfLck = LaundryFlags.Lock(TimeSpan.FromSeconds(2)))
                {
                    lsfLck.ExecuteAction((ref LaundryStatusFlags lsf) =>
                    {
                        bool processing = lsf.ProcessError();
                        if (!processing)
                        {
                            throw new StateLogicErrorException(
                                "Bad state ... error not being handled in correct sequence.");
                        }
                    });
                }

                ClearError(pair);
                pair.ThrowIfCancellationRequested();
                using (var lsfLck = LaundryFlags.Lock(TimeSpan.FromSeconds(2)))
                {
                    lsfLck.ExecuteAction((ref LaundryStatusFlags lsf) =>
                    {
                        bool cleared = lsf.ClearError();
                        if (cleared)
                        {
                            lsf.ResetError();
                        }
                        else
                        {
                            throw new StateLogicErrorException("Ut oh, we couln't fix the error for some reason.");
                        }
                    });
                }
                pair.ThrowIfCancellationRequested();
            }

            TimeSpan simulatedTurnOnCycleTime;

            try
            {
                using var rgen           = RandomNumberSource.RGenVault.SpinLock();
                simulatedTurnOnCycleTime = TimeSpan.FromSeconds(rgen.Value.Next(1, 4));
            }
            catch (TimeoutException ex)
            {
                Console.Error.WriteLineAsync($"Error getting lock on the rgen vault ... exception: [{ex}]");
                simulatedTurnOnCycleTime = TimeSpan.FromSeconds(2);
            }

            SimulateWait(in pair, simulatedTurnOnCycleTime);

            TaskResult ret;

            using var lck = TaskResult.SpinLock(TimeSpan.FromSeconds(2));
            ret           = lck.Value = lck.Value.WithTerminationTaskResultType(TaskResultCode.SuccessResult);
            return(ret);
        }
Пример #26
0
        public async Task RetryAsync(BucketBase bucket, IOperation operation, CancellationTokenPair tokenPair = default)
        {
            try
            {
                var backoff = ControlledBackoff.Create();
                operation.Token = tokenPair;

                do
                {
                    tokenPair.ThrowIfCancellationRequested();

                    try
                    {
                        operation.Attempts++;

                        try
                        {
                            await bucket.SendAsync(operation, tokenPair).ConfigureAwait(false);

                            break;
                        }
                        catch (CouchbaseException e) when(e is ScopeNotFoundException || e is CollectionNotFoundException)
                        {
                            // We catch CollectionOutdatedException separately from the CouchbaseException catch block
                            // in case RefreshCollectionId fails. This causes that failure to trigger normal retry logic.

                            _logger.LogInformation("Updating stale manifest for collection and retrying.", e);
                            if (!await RefreshCollectionId(bucket, operation)
                                .ConfigureAwait(false))
                            {
                                // rethrow if we fail to refresh he collection ID so we hit retry logic
                                // otherwise we'll loop and retry immediately
                                throw;
                            }
                        }
                    }
                    catch (CouchbaseException e) when(e is IRetryable && !tokenPair.IsCancellationRequested)
                    {
                        var reason = e.ResolveRetryReason();

                        if (reason.AlwaysRetry())
                        {
                            _logger.LogDebug("Retrying op {opaque}/{key} because {reason} and always retry.",
                                             operation.Opaque,
                                             operation.Key, reason);

                            await backoff.Delay(operation).ConfigureAwait(false);

                            // no need to reset op in this case as it was not actually sent
                            if (reason != RetryReason.CircuitBreakerOpen)
                            {
                                operation.Reset();
                            }

                            continue;
                        }

                        var strategy = operation.RetryStrategy;
                        var action   = strategy.RetryAfter(operation, reason);

                        if (action.Retry)
                        {
                            _logger.LogDebug("Retrying op {opaque}/{key} because {reason} and action duration.",
                                             operation.Opaque,
                                             operation.Key, reason);

                            // Reset first so operation is not marked as sent if canceled during the delay
                            operation.Reset();

                            await Task.Delay(action.DurationValue.GetValueOrDefault(), tokenPair)
                            .ConfigureAwait(false);
                        }
                        else
                        {
                            throw; //don't retry
                        }
                    }
                } while (true);
            }
            catch (OperationCanceledException) when(!tokenPair.IsExternalCancellation)
            {
                ThrowHelper.ThrowTimeoutException(operation, new KeyValueErrorContext
                {
                    BucketName      = operation.BucketName,
                    ClientContextId = operation.Opaque.ToString(),
                    DocumentKey     = operation.Key,
                    Cas             = operation.Cas,
                    CollectionName  = operation.CName,
                    ScopeName       = operation.SName,
                    OpCode          = operation.OpCode
                });
            }
        }
 internal override Task SendAsync(IOperation op, CancellationTokenPair token = default)
 {
     throw new NotImplementedException();
 }
        private static async Task <ConcurrentDictionary <string, IEnumerable <IEndpointDiagnostics> > > GetEndpointDiagnosticsAsync(ClusterContext context,
                                                                                                                                    IEnumerable <IClusterNode> clusterNodes, bool ping, ICollection <ServiceType> serviceTypes, CancellationToken token)
        {
            var endpoints = new ConcurrentDictionary <string, IEnumerable <IEndpointDiagnostics> >();

            IOperationConfigurator operationConfigurator = ping
               ? context.ServiceProvider.GetRequiredService <IOperationConfigurator>()
               : null;

            foreach (var clusterNode in clusterNodes)
            {
                if (serviceTypes.Contains(ServiceType.KeyValue) && clusterNode.HasKv)
                {
                    var kvEndpoints = (List <IEndpointDiagnostics>)endpoints.GetOrAdd("kv", new List <IEndpointDiagnostics>());

                    foreach (var connection in clusterNode.ConnectionPool.GetConnections())
                    {
                        var endPointDiagnostics =
                            CreateEndpointHealth(clusterNode.Owner?.Name, DateTime.UtcNow, connection);

                        if (ping)
                        {
                            await RecordLatencyAsync(endPointDiagnostics, async() =>
                            {
                                var op = new Noop();
                                operationConfigurator.Configure(op);
                                await clusterNode.ExecuteOp(connection, op, CancellationTokenPair.FromExternalToken(token)).ConfigureAwait(false);
                            }).ConfigureAwait(false);
                        }

                        kvEndpoints.Add(endPointDiagnostics);
                    }
                }

                if (serviceTypes.Contains(ServiceType.Views) && clusterNode.HasViews)
                {
                    if (clusterNode.Owner is CouchbaseBucket bucket)
                    {
                        var kvEndpoints         = (List <IEndpointDiagnostics>)endpoints.GetOrAdd("view", new List <IEndpointDiagnostics>());
                        var endPointDiagnostics = CreateEndpointHealth(bucket.Name, ServiceType.Views, DateTime.UtcNow, clusterNode.LastViewActivity, clusterNode.EndPoint);

                        if (ping)
                        {
                            await RecordLatencyAsync(endPointDiagnostics,
                                                     async() => await bucket.ViewQueryAsync <object, object>("p", "p").ConfigureAwait(false))
                            .ConfigureAwait(false);
                        }

                        kvEndpoints.Add(endPointDiagnostics);
                    }
                }

                if (serviceTypes.Contains(ServiceType.Query) && clusterNode.HasQuery)
                {
                    var kvEndpoints         = (List <IEndpointDiagnostics>)endpoints.GetOrAdd("n1ql", new List <IEndpointDiagnostics>());
                    var endPointDiagnostics = CreateEndpointHealth("Cluster", ServiceType.Query, DateTime.UtcNow, clusterNode.LastQueryActivity, clusterNode.EndPoint);

                    if (ping)
                    {
                        await RecordLatencyAsync(endPointDiagnostics,
                                                 () => context.Cluster.QueryAsync <dynamic>("SELECT 1;"))
                        .ConfigureAwait(false);
                    }

                    kvEndpoints.Add(endPointDiagnostics);
                }

                if (serviceTypes.Contains(ServiceType.Analytics) && clusterNode.HasAnalytics)
                {
                    var kvEndpoints         = (List <IEndpointDiagnostics>)endpoints.GetOrAdd("cbas", new List <IEndpointDiagnostics>());
                    var endPointDiagnostics = CreateEndpointHealth("Cluster", ServiceType.Analytics, DateTime.UtcNow, clusterNode.LastQueryActivity, clusterNode.EndPoint);

                    if (ping)
                    {
                        await RecordLatencyAsync(endPointDiagnostics,
                                                 () => context.Cluster.AnalyticsQueryAsync <dynamic>("SELECT 1;"))
                        .ConfigureAwait(false);
                    }

                    kvEndpoints.Add(endPointDiagnostics);
                }

                if (serviceTypes.Contains(ServiceType.Search) && clusterNode.HasSearch)
                {
                    var kvEndpoints         = (List <IEndpointDiagnostics>)endpoints.GetOrAdd("fts", new List <IEndpointDiagnostics>());
                    var endPointDiagnostics = CreateEndpointHealth("Cluster", ServiceType.Search, DateTime.UtcNow, clusterNode.LastQueryActivity, clusterNode.EndPoint);

                    if (ping)
                    {
                        var index = "ping";
                        await RecordLatencyAsync(endPointDiagnostics,
                                                 () => context.Cluster.SearchQueryAsync(index, new NoOpQuery())).ConfigureAwait(false);
                    }

                    kvEndpoints.Add(endPointDiagnostics);
                }
            }

            return(endpoints);
        }
Пример #29
0
        public async Task RetryAsync(BucketBase bucket, IOperation operation, CancellationTokenPair tokenPair = default)
        {
            try
            {
                var backoff = ControlledBackoff.Create();

                do
                {
                    tokenPair.ThrowIfCancellationRequested();

                    try
                    {
                        if (operation.Attempts > 1)
                        {
                            MetricTracker.KeyValue.TrackRetry(operation.OpCode);
                        }

                        try
                        {
                            await bucket.SendAsync(operation, tokenPair).ConfigureAwait(false);

                            break;
                        }
                        catch (CouchbaseException e) when(operation is not GetCid &&
                                                          (e is ScopeNotFoundException ||
                                                           e is CollectionNotFoundException))
                        {
                            // We catch CollectionOutdatedException separately from the CouchbaseException catch block
                            // in case RefreshCollectionId fails. This causes that failure to trigger normal retry logic.

                            LogRefreshingCollectionId(e);
                            if (!await RefreshCollectionId(bucket, operation)
                                .ConfigureAwait(false))
                            {
                                // rethrow if we fail to refresh he collection ID so we hit retry logic
                                // otherwise we'll loop and retry immediately
                                throw;
                            }
                        }
                    }
                    catch (CouchbaseException e) when(e is IRetryable && !tokenPair.IsCancellationRequested)
                    {
                        var reason = e.ResolveRetryReason();

                        if (reason.AlwaysRetry())
                        {
                            LogRetryDueToAlwaysRetry(operation.Opaque, _redactor.UserData(operation.Key), reason);

                            await backoff.Delay(operation).ConfigureAwait(false);

                            // no need to reset op in this case as it was not actually sent
                            if (reason != RetryReason.CircuitBreakerOpen)
                            {
                                operation.Reset();
                            }
                            operation.IncrementAttempts(reason);

                            continue;
                        }

                        var strategy = operation.RetryStrategy;
                        var action   = strategy.RetryAfter(operation, reason);

                        if (action.Retry)
                        {
                            LogRetryDueToDuration(operation.Opaque, _redactor.UserData(operation.Key), reason);

                            // Reset first so operation is not marked as sent if canceled during the delay
                            operation.Reset();
                            operation.IncrementAttempts(reason);

                            await Task.Delay(action.DurationValue.GetValueOrDefault(), tokenPair)
                            .ConfigureAwait(false);
                        }
                        else
                        {
                            throw; //don't retry
                        }
                    }
                } while (true);
            }
            catch (OperationCanceledException) when(!tokenPair.IsExternalCancellation)
            {
                MetricTracker.KeyValue.TrackTimeout(operation.OpCode);

                ThrowHelper.ThrowTimeoutException(operation, new KeyValueErrorContext
                {
                    BucketName      = operation.BucketName,
                    ClientContextId = operation.Opaque.ToStringInvariant(),
                    DocumentKey     = operation.Key,
                    Cas             = operation.Cas,
                    CollectionName  = operation.CName,
                    ScopeName       = operation.SName,
                    OpCode          = operation.OpCode,
                    DispatchedFrom  = operation.LastDispatchedFrom,
                    DispatchedTo    = operation.LastDispatchedTo,
                    RetryReasons    = operation.RetryReasons
                });
            }
        }