private void ScheduleForkIfNeeded(List <Task> currentTasks, Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas, CancellationToken cancellationToken) { if (currentReplicaIndex == totalReplicas - 1) { return; } if (currentTasks.Count >= maximumParallelism) { return; } var forkingDelay = delaysProvider.GetForkingDelay(request, budget, currentReplicaIndex, totalReplicas); if (forkingDelay == null) { return; } if (forkingDelay.Value < TimeSpan.Zero) { return; } if (forkingDelay.Value >= budget.Remaining) { return; } currentTasks.Add(delaysPlanner.Plan(forkingDelay.Value, cancellationToken)); }
public RequestContext( Request request, RequestParameters parameters, IRequestTimeBudget budget, ILog log, IClusterProvider clusterProvider, IReplicaOrdering replicaOrdering, ITransport transport, int maximumReplicasToUse, int connectionAttempts, string clientApplicationName = null, CancellationToken cancellationToken = default) { Request = request; Budget = budget; Log = log; ClusterProvider = clusterProvider; ReplicaOrdering = replicaOrdering; Transport = transport; Parameters = parameters; CancellationToken = cancellationToken; MaximumReplicasToUse = maximumReplicasToUse; ConnectionAttempts = connectionAttempts; ClientApplicationName = clientApplicationName; ResetReplicaResults(); }
private void LaunchRequest(List <Task> currentTasks, Request request, IRequestTimeBudget budget, IRequestSender sender, IEnumerator <Uri> replicasEnumerator, CancellationToken cancellationToken) { if (!replicasEnumerator.MoveNext()) { throw new InvalidOperationException("Replicas enumerator ended prematurely. This is definitely a bug in code."); } currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, budget.Remaining, cancellationToken)); }
/// <inheritdoc /> public TimeSpan GetTimeout(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) { if (currentReplicaIndex >= timeouts.Length) { return(tailBehaviour == TailTimeoutBehaviour.UseRemainingBudget ? budget.Remaining : TimeSpanArithmetics.Min(timeouts.Last(), budget.Remaining)); } return(TimeSpanArithmetics.Min(timeouts[currentReplicaIndex], budget.Remaining)); }
private void LaunchRequest(List <Task> currentTasks, Request request, IRequestTimeBudget budget, IRequestSender sender, IEnumerator <Uri> replicasEnumerator, TimeSpan?connectionTimeout, CancellationToken cancellationToken) { if (!replicasEnumerator.MoveNext()) { throw new InvalidOperationException("Replicas enumerator ended prematurely. This is definitely a bug in code."); } request = request.WithHeader(HeaderNames.ConcurrencyLevel, currentTasks.Count(task => task is Task <ReplicaResult>) + 1); currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, connectionTimeout, budget.Remaining, cancellationToken)); }
public TimeSpan GetTimeout(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) { if (currentReplicaIndex >= providers.Length) { return(tailBehaviour == TailTimeoutBehaviour.UseRemainingBudget ? budget.Remaining : TimeSpanExtensions.Min(providers.Last()(), budget.Remaining)); } return(TimeSpanExtensions.Min(providers[currentReplicaIndex](), budget.Remaining)); }
/// <inheritdoc /> public TimeSpan GetTimeout(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) { if (currentReplicaIndex >= divisionFactor) { return(budget.Remaining); } var effectiveDivisionFactor = Math.Min(divisionFactor, totalReplicas) - currentReplicaIndex; return(TimeSpanArithmetics.Max(TimeSpan.Zero, budget.Remaining.Divide(effectiveDivisionFactor))); }
public ClusterResultStatus Select(IList <ReplicaResult> results, IRequestTimeBudget budget) { if (results.Any(result => result.Verdict == ResponseVerdict.Accept)) { return(ClusterResultStatus.Success); } if (budget.HasExpired) { return(ClusterResultStatus.TimeExpired); } return(ClusterResultStatus.ReplicasExhausted); }
public async Task SendAsync( Request request, RequestParameters parameters, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { var requestPath = GetRequestPath(request.Url); var requestIsIdempotent = await idempotencyIdentifier.IsIdempotentAsync(request.Method, requestPath).ConfigureAwait(false); var selectedStrategy = requestIsIdempotent ? forkingStrategy : sequential1Strategy; await selectedStrategy.SendAsync(request, parameters, sender, budget, replicas, replicasCount, cancellationToken).ConfigureAwait(false); }
public Task SendAsync( Request request, RequestParameters parameters, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { return(sender.SendToReplicaAsync( replicas.Single(), request, parameters.ConnectionTimeout, budget.Remaining, cancellationToken)); }
public async Task SendAsync(Request request, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { var initialRequestCount = Math.Min(parallelismLevel, replicasCount); var currentTasks = new List <Task <ReplicaResult> >(initialRequestCount); using (var localCancellationSource = new CancellationTokenSource()) using (var linkedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, localCancellationSource.Token)) { var linkedCancellationToken = linkedCancellationSource.Token; using (var replicasEnumerator = replicas.GetEnumerator()) { for (var i = 0; i < initialRequestCount; i++) { if (!replicasEnumerator.MoveNext()) { throw new InvalidOperationException("Replicas enumerator ended prematurely. This is definitely a bug in code."); } currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, budget.Remaining, linkedCancellationToken)); } while (currentTasks.Count > 0) { var completedTask = await Task.WhenAny(currentTasks).ConfigureAwait(false); currentTasks.Remove(completedTask); var completedResult = await completedTask.ConfigureAwait(false); if (completedResult.Verdict == ResponseVerdict.Accept) { localCancellationSource.Cancel(); return; } cancellationToken.ThrowIfCancellationRequested(); if (replicasEnumerator.MoveNext()) { currentTasks.Add(sender.SendToReplicaAsync(replicasEnumerator.Current, request, budget.Remaining, linkedCancellationToken)); } } } } }
public void TestSetup() { replicas = new[] { new Uri("http://host1/"), new Uri("http://host2/") }; request = Request.Get("foo/bar"); budget = Budget.WithRemaining(5.Minutes()); parameters = RequestParameters.Empty.WithConnectionTimeout(1.Seconds()); result = new ReplicaResult(replicas[0], new Response(ResponseCode.NotFound), ResponseVerdict.Accept, TimeSpan.Zero); sender = Substitute.For <IRequestSender>(); sender.SendToReplicaAsync(null, null, parameters.ConnectionTimeout, TimeSpan.Zero, Arg.Any <CancellationToken>()).ReturnsForAnyArgs(_ => result); strategy = new SingleReplicaRequestStrategy(); }
/// <inheritdoc /> public TimeSpan?GetForkingDelay(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) { if (currentReplicaIndex < delays.Length) { return(delays[currentReplicaIndex]); } switch (tailBehaviour) { case TailDelayBehaviour.RepeatLastValue: return(delays.Last()); case TailDelayBehaviour.RepeatAllValues: return(delays[currentReplicaIndex % delays.Length]); default: return(null); } }
public void SetUp() { replicas = new[] { new Uri("http://host1/"), new Uri("http://host2/") }; request = Request.Get("foo/bar"); budget = Budget.WithRemaining(5.Minutes()); result = new ReplicaResult(replicas[0], new Response(ResponseCode.NotFound), ResponseVerdict.Accept, TimeSpan.Zero); sender = Substitute.For <IRequestSender>(); // ReSharper disable AssignNullToNotNullAttribute sender.SendToReplicaAsync(null, null, TimeSpan.Zero, Arg.Any <CancellationToken>()).ReturnsForAnyArgs(_ => result); // ReSharper restore AssignNullToNotNullAttribute strategy = new SingleReplicaRequestStrategy(); }
public RequestContext( Request request, IRequestStrategy strategy, IRequestTimeBudget budget, ILog log, CancellationToken cancellationToken, RequestPriority?priority, int maximumReplicasToUse) { Request = request; Strategy = strategy; Budget = budget; Log = log; Priority = priority; CancellationToken = cancellationToken; MaximumReplicasToUse = maximumReplicasToUse; ResetReplicaResults(); }
public async Task SendAsync(Request request, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { var currentTasks = new List <Task>(Math.Min(maximumParallelism, replicasCount)); using (var localCancellationSource = new CancellationTokenSource()) using (var linkedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, localCancellationSource.Token)) { var linkedCancellationToken = linkedCancellationSource.Token; using (var replicasEnumerator = replicas.GetEnumerator()) { for (var i = 0; i < replicasCount; i++) { if (budget.HasExpired) { break; } LaunchRequest(currentTasks, request, budget, sender, replicasEnumerator, linkedCancellationToken); ScheduleForkIfNeeded(currentTasks, request, budget, i, replicasCount, linkedCancellationToken); if (await WaitForAcceptedResultAsync(currentTasks).ConfigureAwait(false)) { localCancellationSource.Cancel(); return; } cancellationToken.ThrowIfCancellationRequested(); } } while (currentTasks.Count > 0) { if (budget.HasExpired || await WaitForAcceptedResultAsync(currentTasks).ConfigureAwait(false)) { return; } } } }
public RequestContext( Request request, RequestParameters parameters, IRequestTimeBudget budget, ILog log, ITransport transport, int maximumReplicasToUse, string clientApplicationName = null, CancellationToken cancellationToken = default) { Request = request; Budget = budget; Log = log; Transport = transport; Parameters = parameters; CancellationToken = cancellationToken; MaximumReplicasToUse = maximumReplicasToUse; ClientApplicationName = clientApplicationName; ResetReplicaResults(); }
public async Task SendAsync(Request request, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { var currentReplicaIndex = 0; foreach (var replica in replicas) { if (budget.HasExpired) { break; } var timeout = TimeSpanExtensions.Min(timeoutsProvider.GetTimeout(request, budget, currentReplicaIndex++, replicasCount), budget.Remaining); var result = await sender.SendToReplicaAsync(replica, request, timeout, cancellationToken).ConfigureAwait(false); if (result.Verdict == ResponseVerdict.Accept) { break; } cancellationToken.ThrowIfCancellationRequested(); } }
/// <inheritdoc /> public async Task SendAsync(Request request, RequestParameters parameters, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { var currentReplicaIndex = 0; foreach (var replica in replicas) { if (budget.HasExpired) { break; } if (request.ContainsAlreadyUsedStream()) { break; } var timeout = TimeSpanArithmetics.Min(timeoutsProvider.GetTimeout(request, budget, currentReplicaIndex++, replicasCount), budget.Remaining); var connectionAttemptTimeout = currentReplicaIndex == replicasCount ? null : parameters.ConnectionTimeout; var result = await sender.SendToReplicaAsync(replica, request, connectionAttemptTimeout, timeout, cancellationToken).ConfigureAwait(false); if (result.Verdict == ResponseVerdict.Accept) { break; } cancellationToken.ThrowIfCancellationRequested(); } }
public Task SendAsync(Request request, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { throw new NotImplementedException(); }
/// <inheritdoc /> public TimeSpan?GetForkingDelay(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) => budget.Total.Divide(Math.Min(divisionFactor, totalReplicas));
public TimeSpan GetTimeout(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) { return(currentReplicaIndex < fixedTimeoutsCount ? adHocProvider.GetTimeout(request, budget, currentReplicaIndex, totalReplicas) : equalProvider.GetTimeout(request, budget, currentReplicaIndex - fixedTimeoutsCount, totalReplicas)); }
public Task SendAsync(Request request, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { return(sender.SendToReplicaAsync(replicas.First(), request, budget.Remaining, cancellationToken)); }
private void Send(IRequestTimeBudget budget) { strategy.SendAsync(request, parameters, sender, budget, replicas, replicas.Length, token).GetAwaiter().GetResult(); }
public Task SendAsync(Request request, RequestParameters parameters, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) { var senderWithLatency = new LatencyRequestSender(sender, latencyPerformer, rateManager, latencyProvider(), rateProvider()); return(baseStrategy.SendAsync(request, parameters, senderWithLatency, budget, replicas, replicasCount, cancellationToken)); }
/// <inheritdoc /> public Task SendAsync(Request request, RequestParameters parameters, IRequestSender sender, IRequestTimeBudget budget, IEnumerable <Uri> replicas, int replicasCount, CancellationToken cancellationToken) => sender.SendToReplicaAsync(replicas.First(), request, null, budget.Remaining, cancellationToken);
public TimeSpan?GetForkingDelay(Request request, IRequestTimeBudget budget, int currentReplicaIndex, int totalReplicas) { return(currentReplicaIndex < fixedDelaysCount ? adHocProvider.GetForkingDelay(request, budget, currentReplicaIndex, totalReplicas) : equalProvider.GetForkingDelay(request, budget, currentReplicaIndex, totalReplicas)); }
private static void TryLaunchNextRequest(Request request, IRequestSender sender, IRequestTimeBudget budget, IEnumerator <Uri> replicas, List <Task <ReplicaResult> > currentTasks, TimeSpan?connectionTimeout, CancellationToken cancellationToken) { if (budget.HasExpired) { return; } if (request.ContainsAlreadyUsedStream() || request.ContainsAlreadyUsedContent()) { return; } if (replicas.MoveNext()) { currentTasks.Add(sender.SendToReplicaAsync(replicas.Current, request, connectionTimeout, budget.Remaining, cancellationToken)); } }