From b12f71b7b09f8239aefc5fb8c91d6898c3bc4814 Mon Sep 17 00:00:00 2001 From: MihailK Date: Wed, 25 Feb 2026 13:59:21 +0500 Subject: [PATCH 1/5] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20Para?= =?UTF-8?q?llel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/ParallelClusterClient.cs | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs index 5531800..572a054 100644 --- a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs +++ b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs @@ -1,23 +1,48 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using log4net; namespace ClusterClient.Clients { - public class ParallelClusterClient : ClusterClientBase + public class ParallelClusterClient(string[] replicaAddresses) : ClusterClientBase(replicaAddresses) { - public ParallelClusterClient(string[] replicaAddresses) : base(replicaAddresses) + public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - } + var requestTasks = ReplicaAddresses + .Select(baseUri => ProcessRequestAsync(CreateRequest(baseUri + "?query=" + query))) + .ToList(); - public override Task ProcessRequestAsync(string query, TimeSpan timeout) - { - throw new NotImplementedException(); + var timeoutTask = Task.Delay(timeout); + + while (requestTasks.Count > 0) + { + var completedTask = await Task.WhenAny(requestTasks.Concat([timeoutTask]).ToArray()); + + if (timeoutTask.IsCompleted) + { + Log.Error("Request timed out"); + throw new TimeoutException("Request timed out"); + } + + requestTasks.Remove((Task)completedTask); + + try + { + var result = await (Task)completedTask; + if (string.IsNullOrEmpty(result)) continue; + Log.Info("Request completed"); + return result; + } + catch (Exception e) + { + Log.Debug($"Request failed: {e.Message}"); + } + } + + throw new TimeoutException("All replicas were timed out"); } protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); } -} +} \ No newline at end of file From 34bb34aa30b6ae77373a4b1e6932c2e1f8cce131 Mon Sep 17 00:00:00 2001 From: MihailK Date: Wed, 25 Feb 2026 21:27:17 +0500 Subject: [PATCH 2/5] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20Roun?= =?UTF-8?q?dRobin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/RoundRobinClusterClient.cs | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 0293628..608f14c 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Diagnostics; using System.Threading.Tasks; using log4net; @@ -13,9 +11,33 @@ public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresse { } - public override Task ProcessRequestAsync(string query, TimeSpan timeout) + public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - throw new NotImplementedException(); + var timer = Stopwatch.StartNew(); + var replicasLeft = ReplicaAddresses.Length; + + foreach (var replicaAddress in ReplicaAddresses) + { + var timeLeft = timeout - timer.Elapsed; + + if (timeLeft <= TimeSpan.Zero) + throw new TimeoutException(); + + var request = CreateRequest($"{replicaAddress}?query={Uri.EscapeDataString(query)}"); + var requestTask = ProcessRequestAsync(request); + var timeoutTask = Task.Delay(TimeSpan.FromTicks(timeLeft.Ticks / replicasLeft)); + replicasLeft--; + var completedTask = await Task.WhenAny(requestTask, timeoutTask); + + if (completedTask == timeoutTask) + continue; + if (requestTask.Status != TaskStatus.RanToCompletion) + continue; + + return await requestTask; + } + + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); From 72e1bf1d61e32e68c097dcc9e739f3fc6ee4147c Mon Sep 17 00:00:00 2001 From: MihailK Date: Wed, 25 Feb 2026 21:49:50 +0500 Subject: [PATCH 3/5] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20Smar?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/SmartClusterClient.cs | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index eb06d8b..ef72ef5 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -13,9 +14,58 @@ public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) { } - public override Task ProcessRequestAsync(string query, TimeSpan timeout) + public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - throw new NotImplementedException(); + var timer = Stopwatch.StartNew(); + var replicasLeft = ReplicaAddresses.Length; + var pendingTasks = new List>(); + + foreach (var replicaAddress in ReplicaAddresses) + { + var timeLeft = timeout - timer.Elapsed; + if (timeLeft <= TimeSpan.Zero) + throw new TimeoutException(); + + var request = CreateRequest($"{replicaAddress}?query={Uri.EscapeDataString(query)}"); + pendingTasks.Add(ProcessRequestAsync(request)); + + var timeoutTask = Task.Delay(TimeSpan.FromTicks(timeLeft.Ticks / replicasLeft)); + replicasLeft--; + var completedTask = await Task.WhenAny(pendingTasks.Append(timeoutTask).ToArray()); + + if (completedTask == timeoutTask) + continue; + + var finishedTask = (Task)completedTask; + pendingTasks.Remove(finishedTask); + + if (finishedTask.Status == TaskStatus.RanToCompletion) + { + return finishedTask.Result; + } + } + + while (pendingTasks.Count != 0) + { + var timeLeft = timeout - timer.Elapsed; + if (timeLeft <= TimeSpan.Zero) + throw new TimeoutException(); + + var checkTask = Task.Delay(10); + var doneTask = await Task.WhenAny(pendingTasks.Append(checkTask).ToArray()); + + if (doneTask == checkTask) + continue; + + var finishedTask = (Task)doneTask; + pendingTasks.Remove(finishedTask); + + if (finishedTask.Status == TaskStatus.RanToCompletion) + { + return finishedTask.Result; + } + } + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); From d4a8e6a3367efab3aaf36f41fb3276927a328237 Mon Sep 17 00:00:00 2001 From: MihailK Date: Wed, 25 Feb 2026 22:21:48 +0500 Subject: [PATCH 4/5] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D1=83=20=D1=81=D0=BE=20=D0=B7?= =?UTF-8?q?=D0=B2=D1=91=D0=B7=D0=B4=D0=BE=D1=87=D0=BA=D0=BE=D0=B9=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20RoundRobin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/RoundRobinClusterClient.cs | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 608f14c..6481a47 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using log4net; @@ -7,8 +9,15 @@ namespace ClusterClient.Clients { public class RoundRobinClusterClient : ClusterClientBase { + private readonly Dictionary _replicaStatus = new(); + private const double PenaltyTimeInMs = 100; + public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) { + foreach (var address in replicaAddresses) + { + _replicaStatus[address] = double.MaxValue; + } } public override async Task ProcessRequestAsync(string query, TimeSpan timeout) @@ -16,30 +25,44 @@ public override async Task ProcessRequestAsync(string query, TimeSpan ti var timer = Stopwatch.StartNew(); var replicasLeft = ReplicaAddresses.Length; - foreach (var replicaAddress in ReplicaAddresses) + var sortedReplicas = ReplicaAddresses + .OrderBy(address => _replicaStatus.GetValueOrDefault(address, double.MaxValue)) + .ToList(); + + foreach (var replicaAddress in sortedReplicas) { var timeLeft = timeout - timer.Elapsed; - + if (timeLeft <= TimeSpan.Zero) throw new TimeoutException(); - + + var replicaTimeout = TimeSpan.FromTicks(timeLeft.Ticks / replicasLeft); + replicasLeft--; + var request = CreateRequest($"{replicaAddress}?query={Uri.EscapeDataString(query)}"); var requestTask = ProcessRequestAsync(request); - var timeoutTask = Task.Delay(TimeSpan.FromTicks(timeLeft.Ticks / replicasLeft)); - replicasLeft--; + var timeoutTask = Task.Delay(replicaTimeout); + var completedTask = await Task.WhenAny(requestTask, timeoutTask); - + if (completedTask == timeoutTask) + { + _replicaStatus[replicaAddress] = replicaTimeout.TotalMilliseconds; continue; - if (requestTask.Status != TaskStatus.RanToCompletion) - continue; - - return await requestTask; + } + + if (requestTask.Status == TaskStatus.RanToCompletion) + { + _replicaStatus[replicaAddress] = replicaTimeout.TotalMilliseconds; + return await requestTask; + } + + _replicaStatus[replicaAddress] = PenaltyTimeInMs; } - + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); } -} +} \ No newline at end of file From ae4a3e0f1e7dda08e85b927bea8d13e04063ce36 Mon Sep 17 00:00:00 2001 From: MihailK Date: Wed, 25 Feb 2026 22:56:53 +0500 Subject: [PATCH 5/5] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D1=83=20=D1=81=D0=BE=20=D0=B7?= =?UTF-8?q?=D0=B2=D1=91=D0=B7=D0=B4=D0=BE=D1=87=D0=BA=D0=BE=D0=B9=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20Smart?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/SmartClusterClient.cs | 86 +++++++++++++------ 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index ef72ef5..9ce2456 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Text; using System.Threading.Tasks; using log4net; @@ -10,39 +9,60 @@ namespace ClusterClient.Clients { public class SmartClusterClient : ClusterClientBase { + private readonly Dictionary _replicaStatus = new(); + private const double PenaltyTimeInMs = 100; + private const int TaskCheckIntervalInMs = 10; + public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) { + foreach (var address in replicaAddresses) + { + _replicaStatus[address] = double.MaxValue; + } } public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { var timer = Stopwatch.StartNew(); var replicasLeft = ReplicaAddresses.Length; - var pendingTasks = new List>(); + var pendingTasks = new List<(Task Task, string Address)>(); - foreach (var replicaAddress in ReplicaAddresses) + var sortedReplicas = ReplicaAddresses + .OrderBy(address => _replicaStatus.GetValueOrDefault(address, double.MaxValue)) + .ToList(); + + foreach (var replicaAddress in sortedReplicas) { var timeLeft = timeout - timer.Elapsed; if (timeLeft <= TimeSpan.Zero) throw new TimeoutException(); - - var request = CreateRequest($"{replicaAddress}?query={Uri.EscapeDataString(query)}"); - pendingTasks.Add(ProcessRequestAsync(request)); - - var timeoutTask = Task.Delay(TimeSpan.FromTicks(timeLeft.Ticks / replicasLeft)); + + var replicaTimeout = TimeSpan.FromTicks(timeLeft.Ticks / replicasLeft); replicasLeft--; - var completedTask = await Task.WhenAny(pendingTasks.Append(timeoutTask).ToArray()); - + + var request = CreateRequest($"{replicaAddress}?query={Uri.EscapeDataString(query)}"); + var requestTask = ProcessRequestAsync(request); + pendingTasks.Add((requestTask, replicaAddress)); + + var timeoutTask = Task.Delay(replicaTimeout); + var completedTask = await Task.WhenAny( + pendingTasks.Select(pt => pt.Task) + .Append(timeoutTask).ToArray()); + if (completedTask == timeoutTask) continue; - - var finishedTask = (Task)completedTask; - pendingTasks.Remove(finishedTask); - if (finishedTask.Status == TaskStatus.RanToCompletion) + + var finishedItem = ExtractCompletedTask(pendingTasks, completedTask); + + + if (finishedItem.Task.Status == TaskStatus.RanToCompletion) { - return finishedTask.Result; + _replicaStatus[finishedItem.Address] = timer.ElapsedMilliseconds; + return finishedItem.Task.Result; } + + _replicaStatus[finishedItem.Address] = PenaltyTimeInMs; } while (pendingTasks.Count != 0) @@ -51,23 +71,41 @@ public override async Task ProcessRequestAsync(string query, TimeSpan ti if (timeLeft <= TimeSpan.Zero) throw new TimeoutException(); - var checkTask = Task.Delay(10); - var doneTask = await Task.WhenAny(pendingTasks.Append(checkTask).ToArray()); - + var checkTask = Task.Delay(TaskCheckIntervalInMs); + var doneTask = await Task.WhenAny( + pendingTasks + .Select(pt => pt.Task) + .Append(checkTask) + .ToList()); + + if (doneTask == checkTask) continue; - - var finishedTask = (Task)doneTask; - pendingTasks.Remove(finishedTask); - if (finishedTask.Status == TaskStatus.RanToCompletion) + var finishedItem = ExtractCompletedTask(pendingTasks, doneTask); + + if (finishedItem.Task.Status == TaskStatus.RanToCompletion) { - return finishedTask.Result; + _replicaStatus[finishedItem.Address] = timer.ElapsedMilliseconds; + return finishedItem.Task.Result; } + + _replicaStatus[finishedItem.Address] = PenaltyTimeInMs; } + throw new TimeoutException(); } + private static (Task Task, string Address) ExtractCompletedTask( + List<(Task Task, string Address)> pendingTasks, Task completedTask) + { + var finishedIndex = pendingTasks.FindIndex(pt => pt.Task == completedTask); + if (finishedIndex == -1) throw new InvalidOperationException(); + var finishedItem = pendingTasks[finishedIndex]; + pendingTasks.RemoveAt(finishedIndex); + return finishedItem; + } + protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); } -} +} \ No newline at end of file