|
| 1 | +using CodexPlugin; |
| 2 | +using FileUtils; |
| 3 | +using Logging; |
| 4 | +using NUnit.Framework; |
| 5 | +using Utils; |
| 6 | + |
| 7 | +namespace CodexTests.DownloadConnectivityTests |
| 8 | +{ |
| 9 | + [TestFixture] |
| 10 | + public class MultiswarmTests : AutoBootstrapDistTest |
| 11 | + { |
| 12 | + [Test] |
| 13 | + [Combinatorial] |
| 14 | + public void Multiswarm( |
| 15 | + [Values(3, 5)] int numFiles, |
| 16 | + [Values(5, 20)] int fileSizeMb, |
| 17 | + [Values(1)] int uploadersPerFile, |
| 18 | + [Values(3)] int downloadersPerFile, |
| 19 | + [Values(1)] int maxUploadsPerNode, |
| 20 | + [Values(2, 3)] int maxDownloadsPerNode |
| 21 | + ) |
| 22 | + { |
| 23 | + var plan = CreateThePlan(numFiles, uploadersPerFile, downloadersPerFile, maxUploadsPerNode, maxDownloadsPerNode); |
| 24 | + Assert.That(plan.NodePlans.Count, Is.LessThan(30)); |
| 25 | + |
| 26 | + RunThePlan(plan, fileSizeMb); |
| 27 | + } |
| 28 | + |
| 29 | + private void RunThePlan(Plan plan, int fileSizeMb) |
| 30 | + { |
| 31 | + foreach (var filePlan in plan.FilePlans) filePlan.File = GenerateTestFile(fileSizeMb.MB()); |
| 32 | + var nodes = StartCodex(plan.NodePlans.Count); |
| 33 | + for (int i = 0; i < plan.NodePlans.Count; i++) plan.NodePlans[i].Node = nodes[i]; |
| 34 | + |
| 35 | + // Upload all files to their nodes. |
| 36 | + foreach (var filePlan in plan.FilePlans) |
| 37 | + { |
| 38 | + foreach (var uploader in filePlan.Uploaders) |
| 39 | + { |
| 40 | + filePlan.Cid = uploader.Node!.UploadFile(filePlan.File!); |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + Thread.Sleep(5000); // Everything is processed and announced. |
| 45 | + |
| 46 | + // Start all downloads (almost) simultaneously. |
| 47 | + var tasks = new List<Task>(); |
| 48 | + foreach (var filePlan in plan.FilePlans) |
| 49 | + { |
| 50 | + foreach (var downloader in filePlan.Downloaders) |
| 51 | + { |
| 52 | + tasks.Add(Task.Run(() => |
| 53 | + { |
| 54 | + var downloadedFile = downloader.Node!.DownloadContent(filePlan.Cid!); |
| 55 | + lock (filePlan.DownloadedFiles) |
| 56 | + { |
| 57 | + filePlan.DownloadedFiles.Add(downloadedFile); |
| 58 | + } |
| 59 | + })); |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + Task.WaitAll(tasks.ToArray()); |
| 64 | + |
| 65 | + // Assert all files are correct. |
| 66 | + foreach (var filePlan in plan.FilePlans) |
| 67 | + { |
| 68 | + foreach (var downloadedFile in filePlan.DownloadedFiles) |
| 69 | + { |
| 70 | + filePlan.File!.AssertIsEqual(downloadedFile); |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + private Plan CreateThePlan(int numFiles, int uploadersPerFile, int downloadersPerFile, int maxUploadsPerNode, int maxDownloadsPerNode) |
| 76 | + { |
| 77 | + var plan = new Plan(numFiles, uploadersPerFile, downloadersPerFile, maxUploadsPerNode, maxDownloadsPerNode); |
| 78 | + plan.Initialize(); |
| 79 | + plan.LogPlan(GetTestLog()); |
| 80 | + return plan; |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + public class FilePlan |
| 85 | + { |
| 86 | + public FilePlan(int number) |
| 87 | + { |
| 88 | + Number = number; |
| 89 | + } |
| 90 | + |
| 91 | + public int Number { get; } |
| 92 | + public TrackedFile? File { get; set; } |
| 93 | + public ContentId? Cid { get; set; } |
| 94 | + public List<TrackedFile?> DownloadedFiles { get; } = new List<TrackedFile?>(); |
| 95 | + public List<NodePlan> Uploaders { get; } = new List<NodePlan>(); |
| 96 | + public List<NodePlan> Downloaders { get; } = new List<NodePlan>(); |
| 97 | + |
| 98 | + public override string ToString() |
| 99 | + { |
| 100 | + return $"FilePlan[{Number}] " + |
| 101 | + $"Uploaders:[{string.Join(",", Uploaders.Select(u => u.Number.ToString()))}] " + |
| 102 | + $"Downloaders:[{string.Join(",", Downloaders.Select(u => u.Number.ToString()))}]"; |
| 103 | + } |
| 104 | + } |
| 105 | + |
| 106 | + public class NodePlan |
| 107 | + { |
| 108 | + public NodePlan(int number) |
| 109 | + { |
| 110 | + Number = number; |
| 111 | + } |
| 112 | + |
| 113 | + public int Number { get; } |
| 114 | + public ICodexNode? Node { get; set; } |
| 115 | + public List<FilePlan> Uploads { get; } = new List<FilePlan>(); |
| 116 | + public List<FilePlan> Downloads { get; } = new List<FilePlan>(); |
| 117 | + |
| 118 | + public bool Contains(FilePlan plan) |
| 119 | + { |
| 120 | + return Uploads.Contains(plan) || Downloads.Contains(plan); |
| 121 | + } |
| 122 | + |
| 123 | + public override string ToString() |
| 124 | + { |
| 125 | + return $"NodePlan[{Number}] " + |
| 126 | + $"Uploads:[{string.Join(",", Uploads.Select(u => u.Number.ToString()))}] " + |
| 127 | + $"Downloads:[{string.Join(",", Downloads.Select(u => u.Number.ToString()))}]"; |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + public class Plan |
| 132 | + { |
| 133 | + private readonly int numFiles; |
| 134 | + private readonly int uploadersPerFile; |
| 135 | + private readonly int downloadersPerFile; |
| 136 | + private readonly int maxUploadsPerNode; |
| 137 | + private readonly int maxDownloadsPerNode; |
| 138 | + |
| 139 | + public Plan(int numFiles, int uploadersPerFile, int downloadersPerFile, int maxUploadsPerNode, int maxDownloadsPerNode) |
| 140 | + { |
| 141 | + this.numFiles = numFiles; |
| 142 | + this.uploadersPerFile = uploadersPerFile; |
| 143 | + this.downloadersPerFile = downloadersPerFile; |
| 144 | + this.maxUploadsPerNode = maxUploadsPerNode; |
| 145 | + this.maxDownloadsPerNode = maxDownloadsPerNode; |
| 146 | + } |
| 147 | + |
| 148 | + public List<FilePlan> FilePlans { get; } = new List<FilePlan>(); |
| 149 | + public List<NodePlan> NodePlans { get; } = new List<NodePlan>(); |
| 150 | + |
| 151 | + public void Initialize() |
| 152 | + { |
| 153 | + for (int i = 0; i < numFiles; i++) FilePlans.Add(new FilePlan(i)); |
| 154 | + foreach (var filePlan in FilePlans) |
| 155 | + { |
| 156 | + while (filePlan.Uploaders.Count < uploadersPerFile) AddUploader(filePlan); |
| 157 | + while (filePlan.Downloaders.Count < downloadersPerFile) AddDownloader(filePlan); |
| 158 | + } |
| 159 | + |
| 160 | + CollectionAssert.AllItemsAreUnique(FilePlans.Select(f => f.Number)); |
| 161 | + CollectionAssert.AllItemsAreUnique(NodePlans.Select(f => f.Number)); |
| 162 | + |
| 163 | + foreach (var filePlan in FilePlans) |
| 164 | + { |
| 165 | + Assert.That(filePlan.Uploaders.Count, Is.EqualTo(uploadersPerFile)); |
| 166 | + Assert.That(filePlan.Downloaders.Count, Is.EqualTo(downloadersPerFile)); |
| 167 | + } |
| 168 | + foreach (var nodePlan in NodePlans) |
| 169 | + { |
| 170 | + Assert.That(nodePlan.Uploads.Count, Is.LessThanOrEqualTo(maxUploadsPerNode)); |
| 171 | + Assert.That(nodePlan.Downloads.Count, Is.LessThanOrEqualTo(maxDownloadsPerNode)); |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + public void LogPlan(ILog log) |
| 176 | + { |
| 177 | + log.Log("The plan:"); |
| 178 | + log.Log("Input:"); |
| 179 | + log.Log($"numFiles: {numFiles}"); |
| 180 | + log.Log($"uploadersPerFile: {uploadersPerFile}"); |
| 181 | + log.Log($"downloadersPerFile: {downloadersPerFile}"); |
| 182 | + log.Log($"maxUploadsPerNode: {maxUploadsPerNode}"); |
| 183 | + log.Log($"maxDownloadsPerNode: {maxDownloadsPerNode}"); |
| 184 | + log.Log("Setup:"); |
| 185 | + log.Log($"number of nodes: {NodePlans.Count}"); |
| 186 | + foreach (var filePlan in FilePlans) log.Log(filePlan.ToString()); |
| 187 | + foreach (var nodePlan in NodePlans) log.Log(nodePlan.ToString()); |
| 188 | + } |
| 189 | + |
| 190 | + private void AddDownloader(FilePlan filePlan) |
| 191 | + { |
| 192 | + var nodePlan = GetOrCreateDownloaderNode(filePlan); |
| 193 | + filePlan.Downloaders.Add(nodePlan); |
| 194 | + nodePlan.Downloads.Add(filePlan); |
| 195 | + } |
| 196 | + |
| 197 | + private void AddUploader(FilePlan filePlan) |
| 198 | + { |
| 199 | + var nodePlan = GetOrCreateUploaderNode(filePlan); |
| 200 | + filePlan.Uploaders.Add(nodePlan); |
| 201 | + nodePlan.Uploads.Add(filePlan); |
| 202 | + } |
| 203 | + |
| 204 | + private NodePlan GetOrCreateDownloaderNode(FilePlan notIn) |
| 205 | + { |
| 206 | + var available = NodePlans.Where(n => |
| 207 | + n.Downloads.Count < maxDownloadsPerNode && !n.Contains(notIn) |
| 208 | + ).ToArray(); |
| 209 | + if (available.Any()) return RandomUtils.GetOneRandom(available); |
| 210 | + |
| 211 | + var newNodePlan = new NodePlan(NodePlans.Count); |
| 212 | + NodePlans.Add(newNodePlan); |
| 213 | + return newNodePlan; |
| 214 | + } |
| 215 | + |
| 216 | + private NodePlan GetOrCreateUploaderNode(FilePlan notIn) |
| 217 | + { |
| 218 | + var available = NodePlans.Where(n => |
| 219 | + n.Uploads.Count < maxUploadsPerNode && !n.Contains(notIn) |
| 220 | + ).ToArray(); |
| 221 | + if (available.Any()) return RandomUtils.GetOneRandom(available); |
| 222 | + |
| 223 | + var newNodePlan = new NodePlan(NodePlans.Count); |
| 224 | + NodePlans.Add(newNodePlan); |
| 225 | + return newNodePlan; |
| 226 | + } |
| 227 | + } |
| 228 | +} |
0 commit comments