From 9eb9fd1f31d463569c22f30784e8b31443b7d82c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=91=D0=B0=D0=B1=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Wed, 25 Feb 2026 23:34:18 +0500 Subject: [PATCH 1/4] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=20par?= =?UTF-8?q?allel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/ParallelClusterClient.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs index 5531800..1ceebfe 100644 --- a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs +++ b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs @@ -13,9 +13,33 @@ public ParallelClusterClient(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 tasks = ReplicaAddresses + .Select(address => + { + var request = CreateRequest(address + "?query=" + query); + return ProcessRequestAsync(request); + }) + .ToList(); + + var timeoutTask = Task.Delay(timeout); + + while (tasks.Count != 0) + { + var completed = await Task.WhenAny(tasks.Append(timeoutTask)); + + if (completed == timeoutTask) + throw new TimeoutException(); + + var finishedTask = (Task)completed; + tasks.Remove(finishedTask); + + if (finishedTask.IsCompletedSuccessfully) + return finishedTask.Result; + } + + throw new Exception("All replicas failed"); } protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); From cfcda85800da8592cf5af2fcd68777690df99e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=91=D0=B0=D0=B1=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Thu, 26 Feb 2026 00:08:04 +0500 Subject: [PATCH 2/4] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=20Rou?= =?UTF-8?q?ndRobin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/ParallelClusterClient.cs | 2 +- .../Clients/RoundRobinClusterClient.cs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs index 1ceebfe..670af01 100644 --- a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs +++ b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs @@ -18,7 +18,7 @@ public override async Task ProcessRequestAsync(string query, TimeSpan ti var tasks = ReplicaAddresses .Select(address => { - var request = CreateRequest(address + "?query=" + query); + var request = CreateRequest($"{address}?query={query}"); return ProcessRequestAsync(request); }) .ToList(); diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 0293628..daaedf8 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -13,9 +13,20 @@ 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(); + foreach (var address in ReplicaAddresses) + { + var request = CreateRequest($"{address}?query={query}"); + var requestTask = ProcessRequestAsync(request); + + await Task.WhenAny(requestTask, Task.Delay(timeout / ReplicaAddresses.Length)); + + if (requestTask.IsCompletedSuccessfully) + return requestTask.Result; + } + + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); From e2d4323172e1cbf959f1d1e2bbc2bde82749b4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=91=D0=B0=D0=B1=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Thu, 26 Feb 2026 00:24:49 +0500 Subject: [PATCH 3/4] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D0=BA=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=20Sma?= =?UTF-8?q?rt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/SmartClusterClient.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index eb06d8b..d175157 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -13,9 +13,42 @@ 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 allTasks = new List>(); + var timeoutTask = Task.Delay(timeout); + + foreach (var address in ReplicaAddresses) + { + var request = CreateRequest($"{address}?query={query}"); + + var task = ProcessRequestAsync(request); + allTasks.Add(task); + + var completed = await Task.WhenAny(task, Task.Delay(timeout / ReplicaAddresses.Length), timeoutTask); + + if (completed == timeoutTask) + throw new TimeoutException(); + + if (completed == task && task.IsCompletedSuccessfully) + return task.Result; + } + + while (allTasks.Count != 0) + { + var completed = await Task.WhenAny(allTasks.Append(timeoutTask)); + + if (completed == timeoutTask) + throw new TimeoutException(); + + var finishedTask = (Task)completed; + allTasks.Remove(finishedTask); + + if (finishedTask.IsCompletedSuccessfully) + return finishedTask.Result; + } + + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); From c64d7fde530f8edc59722972cad3c97ebca964b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D1=83=D1=80=20=D0=91=D0=B0=D0=B1=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Fri, 27 Feb 2026 00:17:13 +0500 Subject: [PATCH 4/4] =?UTF-8?q?=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=D0=B3=20=D0=B8=20=D0=BF=D1=80=D0=B8=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=82=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=80?= =?UTF-8?q?=D0=B5=D0=BF=D0=BB=D0=B8=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/ClusterClientBase.cs | 50 ++++++++++++++++++- .../Clients/RoundRobinClusterClient.cs | 12 ++--- .../Clients/SmartClusterClient.cs | 37 +++++--------- 3 files changed, 67 insertions(+), 32 deletions(-) diff --git a/homework 2/ClusterClient/Clients/ClusterClientBase.cs b/homework 2/ClusterClient/Clients/ClusterClientBase.cs index 23a2ffd..b978679 100644 --- a/homework 2/ClusterClient/Clients/ClusterClientBase.cs +++ b/homework 2/ClusterClient/Clients/ClusterClientBase.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; @@ -10,6 +13,12 @@ namespace ClusterClient.Clients { public abstract class ClusterClientBase { + private class ReplicaStats + { + public double AverageMs; + public int Count; + } + protected string[] ReplicaAddresses { get; set; } protected ClusterClientBase(string[] replicaAddresses) @@ -19,7 +28,9 @@ protected ClusterClientBase(string[] replicaAddresses) public abstract Task ProcessRequestAsync(string query, TimeSpan timeout); protected abstract ILog Log { get; } - + + private readonly ConcurrentDictionary _stats = []; + protected static HttpWebRequest CreateRequest(string uriStr) { var request = WebRequest.CreateHttp(Uri.EscapeUriString(uriStr)); @@ -40,5 +51,42 @@ protected async Task ProcessRequestAsync(WebRequest request) return result; } } + + protected List OrderReplicas() + { + return ReplicaAddresses + .OrderBy(r => _stats.TryGetValue(r, out var s) ? s.AverageMs : double.MaxValue).ToList(); + } + + protected void UpdateStats(string replica, long elapsedMs) + { + _stats.AddOrUpdate( + replica, + _ => new ReplicaStats { AverageMs = elapsedMs, Count = 1 }, + (_, old) => + { + var newCount = old.Count + 1; + var newAvg = (old.AverageMs * old.Count + elapsedMs) / newCount; + + return new ReplicaStats + { + AverageMs = newAvg, + Count = newCount + }; + }); + } + + protected async Task ProcessWithStatsAsync(string replica, string query) + { + var request = CreateRequest($"{replica}?query={query}"); + var stopwatch = Stopwatch.StartNew(); + + var result = await ProcessRequestAsync(request); + + stopwatch.Stop(); + UpdateStats(replica, stopwatch.ElapsedMilliseconds); + + return result; + } } } \ No newline at end of file diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index daaedf8..c592d00 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -15,15 +15,15 @@ public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresse public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - foreach (var address in ReplicaAddresses) + var replicas = OrderReplicas().ToList(); + foreach (var replica in replicas) { - var request = CreateRequest($"{address}?query={query}"); - var requestTask = ProcessRequestAsync(request); + var task = ProcessWithStatsAsync(replica, query); - await Task.WhenAny(requestTask, Task.Delay(timeout / ReplicaAddresses.Length)); + await Task.WhenAny(task, Task.Delay(timeout / ReplicaAddresses.Length)); - if (requestTask.IsCompletedSuccessfully) - return requestTask.Result; + if (task.IsCompletedSuccessfully) + return task.Result; } throw new TimeoutException(); diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index d175157..279c558 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -15,40 +15,27 @@ public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - var allTasks = new List>(); + var replicas = OrderReplicas().ToArray(); + var tasks = new List>(); var timeoutTask = Task.Delay(timeout); - foreach (var address in ReplicaAddresses) + foreach (var replica in replicas) { - var request = CreateRequest($"{address}?query={query}"); + var task = ProcessWithStatsAsync(replica, query); + tasks.Add(task); - var task = ProcessRequestAsync(request); - allTasks.Add(task); + await Task.WhenAny(task, Task.Delay(timeout / replicas.Length), timeoutTask); - var completed = await Task.WhenAny(task, Task.Delay(timeout / ReplicaAddresses.Length), timeoutTask); - - if (completed == timeoutTask) - throw new TimeoutException(); - - if (completed == task && task.IsCompletedSuccessfully) + if (task.IsCompletedSuccessfully) return task.Result; } + + var final = await Task.WhenAny(tasks.Append(timeoutTask)); - while (allTasks.Count != 0) - { - var completed = await Task.WhenAny(allTasks.Append(timeoutTask)); - - if (completed == timeoutTask) - throw new TimeoutException(); - - var finishedTask = (Task)completed; - allTasks.Remove(finishedTask); - - if (finishedTask.IsCompletedSuccessfully) - return finishedTask.Result; - } + if (final == timeoutTask) + throw new TimeoutException(); - throw new TimeoutException(); + return await (Task)final; } protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient));