-
Notifications
You must be signed in to change notification settings - Fork 54
Медведев Фома, 2ДЗ #51
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?
Changes from all commits
46edc8f
f0f6c74
0c1ec8f
3d0eb61
5daa9fc
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,5 +1,6 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
@@ -9,13 +10,50 @@ namespace ClusterClient.Clients | |
| { | ||
| public class RoundRobinClusterClient : ClusterClientBase | ||
| { | ||
| private readonly ReplicasStatistics _replicasStatistics; | ||
| public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| _replicasStatistics = new ReplicasStatistics(replicaAddresses); | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| throw new NotImplementedException(); | ||
| var timer = Stopwatch.StartNew(); | ||
| var sortedReplicas = ReplicaAddresses | ||
| .OrderBy(x => _replicasStatistics.GetStats(x)) | ||
| .ToArray(); | ||
| var sentTasks = new Dictionary<Task<string>, (string, DateTime)>(); | ||
| for (int i = 0; i < sortedReplicas.Length; i++) | ||
| { | ||
| var remainingTime = timeout - timer.Elapsed; | ||
| if (remainingTime <= TimeSpan.Zero) | ||
| throw new TimeoutException(); | ||
| var timeoutForReplica = TimeSpan.FromTicks(remainingTime.Ticks / (sortedReplicas.Length - i)); | ||
| if (timeoutForReplica <= TimeSpan.Zero) | ||
| throw new TimeoutException(); | ||
| var timeoutTask = Task.Delay(timeoutForReplica); | ||
| var request = CreateRequest(sortedReplicas[i] + "?query=" + query); | ||
| var task = ProcessRequestAsync(request); | ||
| sentTasks.Add(task, (address: sortedReplicas[i], startTime: DateTime.Now)); | ||
| var requestResult = await Task.WhenAny(task, timeoutTask); | ||
| if (requestResult != timeoutTask) | ||
|
Comment on lines
+38
to
+39
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. если |
||
| { | ||
| var replicaData = sentTasks[task]; | ||
| var time = DateTime.Now - replicaData.Item2; | ||
| var result = (Task<string>)requestResult; | ||
| if (result.Status == TaskStatus.RanToCompletion) | ||
| { | ||
| _replicasStatistics.UpdateStats(replicaData.Item1, time.TotalMilliseconds); | ||
| sentTasks.Remove(task); | ||
| return result.Result; | ||
| } | ||
| _replicasStatistics.UpdateStats(replicaData.Item1, timeout.TotalMilliseconds); | ||
| sentTasks.Remove(task); | ||
| } | ||
| else | ||
| _replicasStatistics.UpdateStats(sortedReplicas[i], timeoutForReplica.TotalMilliseconds); | ||
| } | ||
| throw new TimeoutException(); | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Threading.Tasks; | ||
|
|
@@ -9,15 +10,56 @@ namespace ClusterClient.Clients | |
| { | ||
| public class SmartClusterClient : ClusterClientBase | ||
| { | ||
| private readonly ReplicasStatistics _replicasStatistics; | ||
|
|
||
| public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) | ||
| { | ||
| _replicasStatistics = new ReplicasStatistics(replicaAddresses); | ||
| } | ||
|
|
||
| public override Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| public override async Task<string> ProcessRequestAsync(string query, TimeSpan timeout) | ||
| { | ||
| throw new NotImplementedException(); | ||
| var timer = Stopwatch.StartNew(); | ||
| var sortedReplicas = ReplicaAddresses | ||
| .OrderBy(x => _replicasStatistics.GetStats(x)) | ||
| .ToArray(); | ||
| var requests = new Dictionary<Task<string>, (string, DateTime)>(); | ||
| for (int i = 0; i < sortedReplicas.Length; i++) | ||
| { | ||
| var remainingTime = timeout - timer.Elapsed; | ||
| if (remainingTime <= TimeSpan.Zero) | ||
| throw new TimeoutException(); | ||
| var timeoutForReplica = TimeSpan.FromTicks(remainingTime.Ticks / (sortedReplicas.Length - i)); | ||
| if (timeoutForReplica <= TimeSpan.Zero) | ||
| throw new TimeoutException(); | ||
| var timeoutTask = Task.Delay(timeoutForReplica); | ||
| var request = CreateRequest(sortedReplicas[i] + "?query=" + query); | ||
| requests.Add(ProcessRequestAsync(request), (sortedReplicas[i], DateTime.Now)); | ||
| var requestResult = await Task.WhenAny(requests.Keys.Concat(new[] { timeoutTask })); | ||
| if (requestResult != timeoutTask) | ||
| { | ||
| var result = (Task<string>)requestResult; | ||
| var replicaData = requests[result]; | ||
| var time = DateTime.Now - replicaData.Item2; | ||
| if (result.Status == TaskStatus.RanToCompletion) | ||
| { | ||
| _replicasStatistics.UpdateStats(replicaData.Item1, time.TotalMilliseconds); | ||
| requests.Remove(result); | ||
| foreach (var badRequest in requests) | ||
| _replicasStatistics.UpdateStats(badRequest.Value.Item1, timeoutForReplica.TotalMilliseconds); | ||
| requests.Clear(); | ||
| return result.Result; | ||
|
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. Если все реплики будут тормозить, то соответствующие таски так и останутся в requests, и статистика для них не обновится |
||
| } | ||
| _replicasStatistics.UpdateStats(replicaData.Item1, time.TotalMilliseconds); | ||
| requests.Remove(result); | ||
| } | ||
| else | ||
| _replicasStatistics.UpdateStats(sortedReplicas[i], timeoutForReplica.TotalMilliseconds); | ||
| } | ||
|
|
||
| throw new TimeoutException(); | ||
| } | ||
|
|
||
| protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| using System.Collections.Concurrent; | ||
| using System.Linq; | ||
|
|
||
| namespace ClusterClient; | ||
|
|
||
| public class ReplicasStatistics | ||
| { | ||
| private const double ConfidenceFactor = 0.8; | ||
| private readonly ConcurrentDictionary<string, double> _replicaStatistics; | ||
|
|
||
| public ReplicasStatistics(string[] replicaAddresses) | ||
| { | ||
| _replicaStatistics = new ConcurrentDictionary<string, double>(); | ||
| foreach (var replicaAddress in replicaAddresses) | ||
| _replicaStatistics.TryAdd(replicaAddress, 0); | ||
| } | ||
|
|
||
| public void UpdateStats(string replicaAddress, double workTime) | ||
| { | ||
| _replicaStatistics.AddOrUpdate( | ||
| replicaAddress, | ||
| workTime, | ||
| (_, previousValue) => previousValue * ConfidenceFactor + workTime * (1 - ConfidenceFactor) | ||
| ); | ||
| } | ||
|
|
||
| public double GetStats(string replicaAddress) | ||
| { | ||
| return _replicaStatistics.TryGetValue(replicaAddress, out double value) ? value : 0; | ||
| } | ||
| } |
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.
remainingTime и timeoutForReplica могут оказаться отрицательными. Task.Delay может или выбросить исключение, или, с маленьким шансом, превратиться в никогда не заканчивающуюся таску