From 6f7b16d771e43653adddea474a5f66e3ad32c36a Mon Sep 17 00:00:00 2001 From: Artem Zubkov <17artem12@gmail.com> Date: Sun, 22 Feb 2026 22:16:45 +0500 Subject: [PATCH 1/5] ParallelClusterClient.cs done --- .../Clients/ParallelClusterClient.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs b/homework 2/ClusterClient/Clients/ParallelClusterClient.cs index 5531800..0d9fc41 100644 --- a/homework 2/ClusterClient/Clients/ParallelClusterClient.cs +++ b/homework 2/ClusterClient/Clients/ParallelClusterClient.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,38 @@ 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 timeoutTask = Task.Delay(timeout).ContinueWith(_ => throw new TimeoutException()); + + var uris = ReplicaAddresses.Select(async uri => + { + var webRequest = CreateRequest(uri + "?query=" + query); + Log.InfoFormat($"Processing {webRequest.RequestUri}"); + var resultTask = await ProcessRequestAsync(webRequest); + return resultTask; + + }).ToList(); + uris.Add(timeoutTask); + + while (uris.Any()) + { + try + { + var resultTask = await Task.WhenAny(uris); + if (resultTask == timeoutTask) + await timeoutTask; + return await resultTask; + } + catch (Exception e) + { + if (uris.Count == 2) + throw; + else + uris.Remove(uris.First()); + } + } + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(ParallelClusterClient)); From 2c3cb332aaf7b311e5a073893439d55514459143 Mon Sep 17 00:00:00 2001 From: Artem Zubkov <17artem12@gmail.com> Date: Wed, 25 Feb 2026 01:07:21 +0500 Subject: [PATCH 2/5] RoundRobinClusterClient.cs done --- .../Clients/RoundRobinClusterClient.cs | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 0293628..0a27d1c 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Text; using System.Threading.Tasks; using log4net; @@ -13,9 +14,38 @@ 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 partTimeout = timeout / ReplicaAddresses.Length; + + foreach (var replicaAddress in ReplicaAddresses) + { + try + { + var timeoutTask = Task.Delay(partTimeout); + var webRequest = CreateRequest(replicaAddress + "?query=" + query); + Log.InfoFormat($"Processing {webRequest.RequestUri}"); + var replicaTask = ProcessRequestAsync(webRequest); + var resultTask = await Task.WhenAny(replicaTask, timeoutTask); + + if (resultTask == timeoutTask) + continue; + else + { + if (ReplicaAddresses.Last() == replicaAddress) + return await replicaTask; + await resultTask; + continue; + } + } + catch (Exception e) + { + continue; + } + } + + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); From f7140db95821087a1a2dba07d8aec63c84355eca Mon Sep 17 00:00:00 2001 From: Artem Zubkov <17artem12@gmail.com> Date: Wed, 25 Feb 2026 17:17:31 +0500 Subject: [PATCH 3/5] SmartClusterClient.cs done --- .../Clients/SmartClusterClient.cs | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index eb06d8b..2e84260 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using log4net; @@ -13,9 +15,47 @@ 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 partOfTimeout = timeout / ReplicaAddresses.Length; + var tasksAtWork = new List>(); + var timeoutGlobal = Task.Delay(timeout).ContinueWith(_ => throw new TimeoutException()); + tasksAtWork.Add(timeoutGlobal); + + foreach (var replicaAddress in ReplicaAddresses) + { + var webRequest = CreateRequest(replicaAddress + "?query=" + query); + Log.InfoFormat($"Processing {webRequest.RequestUri}"); + var replicaTask = ProcessRequestAsync(webRequest); + var timeoutDelay = Task.Delay(partOfTimeout).ContinueWith(_ => throw new TimeoutException()); + + tasksAtWork.Add(timeoutDelay); + tasksAtWork.Add(replicaTask); + + var taskInCycle = await Task.WhenAny(tasksAtWork); + tasksAtWork.Remove(timeoutDelay); + + if (taskInCycle == timeoutGlobal) + throw new TimeoutException(); + + if (taskInCycle == timeoutDelay) + continue; + + try + { + return await taskInCycle; + } + catch (Exception e) + { + tasksAtWork.Remove(taskInCycle); + continue; + } + + } + var resultTask = await Task.WhenAny(tasksAtWork); + return await resultTask; + } } protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); From f33a4eec3f837a231253f0b0ab89075885ede93e Mon Sep 17 00:00:00 2001 From: Artem Zubkov <17artem12@gmail.com> Date: Wed, 25 Feb 2026 20:48:54 +0500 Subject: [PATCH 4/5] RoundRobinClusterClient.cs fix --- .../Clients/RoundRobinClusterClient.cs | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 0a27d1c..4d4eaa6 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -18,34 +18,41 @@ public override async Task ProcessRequestAsync(string query, TimeSpan ti { var partTimeout = timeout / ReplicaAddresses.Length; + var globalTimeout = Task.Delay(timeout); foreach (var replicaAddress in ReplicaAddresses) { - try - { - var timeoutTask = Task.Delay(partTimeout); - var webRequest = CreateRequest(replicaAddress + "?query=" + query); - Log.InfoFormat($"Processing {webRequest.RequestUri}"); - var replicaTask = ProcessRequestAsync(webRequest); - var resultTask = await Task.WhenAny(replicaTask, timeoutTask); + var timeoutTask = Task.Delay(partTimeout); + var webRequest = CreateRequest(replicaAddress + "?query=" + query); + Log.InfoFormat($"Processing {webRequest.RequestUri}"); + var replicaTask = ProcessRequestAsync(webRequest); + var resultTask = await Task.WhenAny(replicaTask, timeoutTask); - if (resultTask == timeoutTask) - continue; - else - { - if (ReplicaAddresses.Last() == replicaAddress) - return await replicaTask; - await resultTask; - continue; - } - } - catch (Exception e) - { + if (resultTask == timeoutTask) continue; - } + + if(resultTask.IsFaulted) + continue; + + if (ReplicaAddresses.Last() == replicaAddress) + return await replicaTask; + + await resultTask; } - throw new TimeoutException(); + var webRequestLast = CreateRequest(ReplicaAddresses.Last() + "?query=" + query); + Log.InfoFormat($"Processing {webRequestLast.RequestUri}"); + var replicaLastTask = ProcessRequestAsync(webRequestLast); + var resultLastTask = await Task.WhenAny(replicaLastTask, globalTimeout); + + if (resultLastTask == globalTimeout) + { + throw new TimeoutException(); + } + else + { + return await replicaLastTask; + } } protected override ILog Log => LogManager.GetLogger(typeof(RoundRobinClusterClient)); From 591557eee6c7ab1e4148f418a48fc8a09ef68ed2 Mon Sep 17 00:00:00 2001 From: Artem Zubkov <17artem12@gmail.com> Date: Fri, 27 Feb 2026 00:37:30 +0500 Subject: [PATCH 5/5] =?UTF-8?q?=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=20=D0=B7=D0=B2=D0=B5=D0=B7=D0=B4=D0=BE=D1=87=D0=BA?= =?UTF-8?q?=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Clients/RoundRobinClusterClient.cs | 54 ++++++++--- .../Clients/SmartClusterClient.cs | 93 +++++++++++++++---- 2 files changed, 115 insertions(+), 32 deletions(-) diff --git a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs index 4d4eaa6..b90ba48 100644 --- a/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs +++ b/homework 2/ClusterClient/Clients/RoundRobinClusterClient.cs @@ -10,18 +10,27 @@ namespace ClusterClient.Clients { public class RoundRobinClusterClient : ClusterClientBase { + private readonly Dictionary> stats = new (); + private readonly object lockObj = new (); + public RoundRobinClusterClient(string[] replicaAddresses) : base(replicaAddresses) { + foreach (var address in replicaAddresses) + stats[address] = new Queue(); } public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { - - var partTimeout = timeout / ReplicaAddresses.Length; + var orderedReplicas = ReplicaAddresses + .OrderBy(GetAverage) + .ToArray(); + + var partTimeout = timeout / orderedReplicas.Length; var globalTimeout = Task.Delay(timeout); - foreach (var replicaAddress in ReplicaAddresses) + foreach (var replicaAddress in orderedReplicas) { + var startTime = DateTime.UtcNow; var timeoutTask = Task.Delay(partTimeout); var webRequest = CreateRequest(replicaAddress + "?query=" + query); Log.InfoFormat($"Processing {webRequest.RequestUri}"); @@ -33,25 +42,44 @@ public override async Task ProcessRequestAsync(string query, TimeSpan ti if(resultTask.IsFaulted) continue; - - if (ReplicaAddresses.Last() == replicaAddress) - return await replicaTask; - - await resultTask; + + var result = await replicaTask; + UpdateStatistics(replicaAddress, DateTime.UtcNow - startTime); + return result; } - var webRequestLast = CreateRequest(ReplicaAddresses.Last() + "?query=" + query); + var lastReplica = orderedReplicas.Last(); + var webRequestLast = CreateRequest(lastReplica + "?query=" + query); Log.InfoFormat($"Processing {webRequestLast.RequestUri}"); + + var startLast = DateTime.UtcNow; var replicaLastTask = ProcessRequestAsync(webRequestLast); + var resultLastTask = await Task.WhenAny(replicaLastTask, globalTimeout); - + if (resultLastTask == globalTimeout) - { throw new TimeoutException(); + + var resultFinal = await replicaLastTask; + UpdateStatistics(lastReplica, DateTime.UtcNow - startLast); + return resultFinal; + } + + private double GetAverage(string replica) + { + lock (lockObj) + { + var queue = stats[replica]; + return queue.Count == 0 ? double.MaxValue : queue.Average(); } - else + } + + private void UpdateStatistics(string replica, TimeSpan time) + { + lock (lockObj) { - return await replicaLastTask; + var queue = stats[replica]; + queue.Enqueue(time.TotalMilliseconds); } } diff --git a/homework 2/ClusterClient/Clients/SmartClusterClient.cs b/homework 2/ClusterClient/Clients/SmartClusterClient.cs index 2e84260..5427cfe 100644 --- a/homework 2/ClusterClient/Clients/SmartClusterClient.cs +++ b/homework 2/ClusterClient/Clients/SmartClusterClient.cs @@ -11,53 +11,108 @@ namespace ClusterClient.Clients { public class SmartClusterClient : ClusterClientBase { + private readonly Dictionary> stats = new Dictionary>(); + private readonly object lockObj = new object(); + public SmartClusterClient(string[] replicaAddresses) : base(replicaAddresses) { + foreach (var address in replicaAddresses) + stats[address] = new Queue(); } public override async Task ProcessRequestAsync(string query, TimeSpan timeout) { { - var partOfTimeout = timeout / ReplicaAddresses.Length; - var tasksAtWork = new List>(); - var timeoutGlobal = Task.Delay(timeout).ContinueWith(_ => throw new TimeoutException()); - tasksAtWork.Add(timeoutGlobal); + var orderedReplicas = ReplicaAddresses + .OrderBy(GetAverage) + .ToArray(); + var partOfTimeout = timeout / orderedReplicas.Length; + var tasksAtWork = new List<(Task task, string replica, DateTime startTime)>(); + var timeoutGlobal = Task.Delay(timeout) + .ContinueWith(_ => throw new TimeoutException()); + var tasks = new List> { timeoutGlobal }; - foreach (var replicaAddress in ReplicaAddresses) + foreach (var replicaAddress in orderedReplicas) { var webRequest = CreateRequest(replicaAddress + "?query=" + query); Log.InfoFormat($"Processing {webRequest.RequestUri}"); + var startTime = DateTime.UtcNow; var replicaTask = ProcessRequestAsync(webRequest); + + tasksAtWork.Add((replicaTask, replicaAddress, startTime)); + tasks.Add(replicaTask); + var timeoutDelay = Task.Delay(partOfTimeout).ContinueWith(_ => throw new TimeoutException()); - - tasksAtWork.Add(timeoutDelay); - tasksAtWork.Add(replicaTask); - - var taskInCycle = await Task.WhenAny(tasksAtWork); - tasksAtWork.Remove(timeoutDelay); + + tasks.Add(timeoutDelay); + + var taskInCycle = await Task.WhenAny(tasks); + + tasks.Remove(timeoutDelay); if (taskInCycle == timeoutGlobal) - throw new TimeoutException(); + throw new TimeoutException(); if (taskInCycle == timeoutDelay) continue; try { - return await taskInCycle; + var result = await taskInCycle; + var info = tasksAtWork.First(t => t.task == taskInCycle); + UpdateStatistics(info.replica, DateTime.UtcNow - info.startTime); + + return result; + } catch + { + tasks.Remove(taskInCycle); + tasksAtWork.RemoveAll(t => t.task == taskInCycle); + } + } + while (tasksAtWork.Count > 1) + { + var completedTask = await Task.WhenAny(tasks); + + if (completedTask == timeoutGlobal) + throw new TimeoutException(); + + try + { + var result = await completedTask; + + var info = tasksAtWork.First(t => t.task == completedTask); + UpdateStatistics(info.replica, DateTime.UtcNow - info.startTime); + + return result; } - catch (Exception e) + catch { - tasksAtWork.Remove(taskInCycle); - continue; + tasks.Remove(completedTask); + tasks.RemoveAll(t => t == completedTask); } - } - var resultTask = await Task.WhenAny(tasksAtWork); - return await resultTask; } + throw new TimeoutException(); } protected override ILog Log => LogManager.GetLogger(typeof(SmartClusterClient)); + + private double GetAverage(string replica) + { + lock (lockObj) + { + var queue = stats[replica]; + return queue.Count == 0 ? double.MaxValue : queue.Average(); + } + } + + private void UpdateStatistics(string replica, TimeSpan time) + { + lock (lockObj) + { + var queue = stats[replica]; + queue.Enqueue(time.TotalMilliseconds); + } + } } }