-
Notifications
You must be signed in to change notification settings - Fork 54
Азанов Андрей #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Азанов Андрей #61
Changes from all commits
dd03138
3a3f2ee
d94f244
21a51d3
4d57de6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,38 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using log4net; | ||
|
|
||
| namespace ClusterClient.Clients | ||
| namespace ClusterClient.Clients; | ||
|
|
||
| public class ParallelClusterClient : ClusterClientBase | ||
| { | ||
| public class ParallelClusterClient : ClusterClientBase | ||
| public ParallelClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| public ParallelClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| } | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| var requestTasks = ReplicaAddresses | ||
| .Select(t => CreateAndRunRequestTask(t, query)) | ||
| .ToHashSet(); | ||
| var timeoutTask = Task.Delay(timeout); | ||
|
|
||
| while (requestTasks.Count > 0 && !timeoutTask.IsCompleted) | ||
| { | ||
| throw new NotImplementedException(); | ||
| var completedTask = await Task.WhenAny(requestTasks.Append(timeoutTask)); | ||
|
|
||
| if (completedTask == timeoutTask) | ||
| throw new TimeoutException(); | ||
|
|
||
| if (completedTask.IsCompletedSuccessfully) | ||
| return await (Task<string>)completedTask; | ||
|
|
||
| requestTasks.Remove((Task<string>)completedTask); | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); | ||
| throw new TimeoutException(); | ||
| } | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| using System; | ||
|
|
||
| namespace ClusterClient.Clients; | ||
|
|
||
| public record ResponseStatistics(bool IsSuccessful, TimeSpan ResponseTime); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,45 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Diagnostics; | ||
| using System.Threading.Tasks; | ||
| using log4net; | ||
|
|
||
| namespace ClusterClient.Clients | ||
| namespace ClusterClient.Clients; | ||
|
|
||
| public class RoundRobinClusterClient : ClusterClientBase | ||
| { | ||
| public class RoundRobinClusterClient : ClusterClientBase | ||
| public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| } | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| var preparedAddresses = GetFilteredAndSortedAddresses(); | ||
| var goodAddressesCount = preparedAddresses.Length; | ||
| var sw = new Stopwatch(); | ||
| foreach (var replicaAddress in preparedAddresses) | ||
| { | ||
| throw new NotImplementedException(); | ||
| if (ResponseStatisticsHistory.TryGetValue(replicaAddress, out var statistics) && !statistics.IsSuccessful) | ||
| { | ||
| goodAddressesCount--; | ||
| continue; | ||
| } | ||
|
|
||
| sw.Restart(); | ||
| var requestTask = CreateAndRunRequestTask(replicaAddress, query); | ||
| var completedTask = await Task.WhenAny(requestTask, Task.Delay(timeout / goodAddressesCount)); | ||
|
|
||
| if (!completedTask.IsCompletedSuccessfully) | ||
| goodAddressesCount--; | ||
|
Comment on lines
+31
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Если запрос не успел выполнится до истечения таймаута, в В итоге не всегда будет уменьшаться |
||
|
|
||
| ResponseStatisticsHistory[replicaAddress] = | ||
| new ResponseStatistics(completedTask.IsCompletedSuccessfully, sw.Elapsed); | ||
|
Comment on lines
+34
to
+35
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут по причине выше мы записываем неверную статистику, мы для реплики записываем статистику "удачности" и "времени выполнения", вот только в случае таймаута мы в статистику будет записывать В итоге у нас почти никогда не будет неработающих реплик, только если ошибка запроса вывалится раньше, чем отработает таймаут |
||
|
|
||
| if (requestTask.IsCompletedSuccessfully) | ||
| return requestTask.Result; | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); | ||
| throw new TimeoutException(); | ||
| } | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,54 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
| using log4net; | ||
|
|
||
| namespace ClusterClient.Clients | ||
| namespace ClusterClient.Clients; | ||
|
|
||
| public class SmartClusterClient : ClusterClientBase | ||
| { | ||
| public class SmartClusterClient : ClusterClientBase | ||
| public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| } | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| var preparedAddresses = GetFilteredAndSortedAddresses(); | ||
| var goodAddressesCount = preparedAddresses.Length; | ||
| var requestTasks = new HashSet<Task<string>>(); | ||
| foreach (var replicaAddress in preparedAddresses) | ||
| { | ||
| throw new NotImplementedException(); | ||
| if (ResponseStatisticsHistory.TryGetValue(replicaAddress, out var statistics) && !statistics.IsSuccessful) | ||
| { | ||
| goodAddressesCount--; | ||
| continue; | ||
| } | ||
|
|
||
| var sw = Stopwatch.StartNew(); | ||
| var requestTask = CreateAndRunRequestTask(replicaAddress, query); | ||
| _ = requestTask.ContinueWith(t => ResponseStatisticsHistory[replicaAddress] | ||
| = new ResponseStatistics(t.IsCompletedSuccessfully, sw.Elapsed)); | ||
|
Comment on lines
+31
to
+32
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Тут происходит замыкание и переменная Здесь надо аккуратнее работать с замыканиями. Под капотом |
||
| requestTasks.Add(requestTask); | ||
| var timeoutTask = Task.Delay(timeout / goodAddressesCount); | ||
| var completedTask = await Task.WhenAny(requestTasks.Append(timeoutTask)); | ||
|
|
||
| if (completedTask == timeoutTask) | ||
| { | ||
| ResponseStatisticsHistory[replicaAddress] = new ResponseStatistics(true, sw.Elapsed); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Таумаут тоже считаем "успешным" завершением? Немного странновато тогда выглядит. Плюс у нас |
||
| continue; | ||
| } | ||
|
|
||
| if (completedTask.IsCompletedSuccessfully) | ||
| return await (Task<string>)completedTask; | ||
|
|
||
| goodAddressesCount--; | ||
| requestTasks.Remove((Task<string>)completedTask); | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); | ||
| throw new TimeoutException(); | ||
| } | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В доп задании требовалось сортировать их по времени, а не совсем исключать в случае ошибок)
Вне лабораторных условий может произойти ситуация, когда такой подход вычеркнет все реплики из-за к примеру кратковременной сетевой недоступности самого приложения. В итоге отправка запросов встанет до перезапуска.
Лучше либо делать скользящее окно — сохранять и использовать время последней ошибки и исключать из списка реплик только когда ошибка "свежая", либо просто делать их ResponseTime максимально плохим, чтобы они становились последними в списке.
Совсем исключать их навсегда — плохой подход