diff options
8 files changed, 287 insertions, 73 deletions
diff --git a/OpenSim/Framework/ClientInfo.cs b/OpenSim/Framework/ClientInfo.cs index d68078e..98e4465 100644 --- a/OpenSim/Framework/ClientInfo.cs +++ b/OpenSim/Framework/ClientInfo.cs | |||
@@ -54,6 +54,10 @@ namespace OpenSim.Framework | |||
54 | public int assetThrottle; | 54 | public int assetThrottle; |
55 | public int textureThrottle; | 55 | public int textureThrottle; |
56 | public int totalThrottle; | 56 | public int totalThrottle; |
57 | |||
58 | // Used by adaptive only | ||
59 | public int targetThrottle; | ||
60 | |||
57 | public int maxThrottle; | 61 | public int maxThrottle; |
58 | 62 | ||
59 | public Dictionary<string, int> SyncRequests = new Dictionary<string,int>(); | 63 | public Dictionary<string, int> SyncRequests = new Dictionary<string,int>(); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index 6864d37..c768662 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs | |||
@@ -229,7 +229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
229 | m_throttleClient | 229 | m_throttleClient |
230 | = new AdaptiveTokenBucket( | 230 | = new AdaptiveTokenBucket( |
231 | string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name), | 231 | string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name), |
232 | parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); | 232 | parentThrottle, 0, rates.Total, rates.AdaptiveThrottlesEnabled); |
233 | 233 | ||
234 | // Create an array of token buckets for this clients different throttle categories | 234 | // Create an array of token buckets for this clients different throttle categories |
235 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; | 235 | m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; |
@@ -247,7 +247,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
247 | m_throttleCategories[i] | 247 | m_throttleCategories[i] |
248 | = new TokenBucket( | 248 | = new TokenBucket( |
249 | string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name), | 249 | string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name), |
250 | m_throttleClient, rates.GetRate(type)); | 250 | m_throttleClient, rates.GetRate(type), 0); |
251 | } | 251 | } |
252 | 252 | ||
253 | // Default the retransmission timeout to one second | 253 | // Default the retransmission timeout to one second |
@@ -293,6 +293,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
293 | m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; | 293 | m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; |
294 | m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; | 294 | m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; |
295 | m_info.totalThrottle = (int)m_throttleClient.DripRate; | 295 | m_info.totalThrottle = (int)m_throttleClient.DripRate; |
296 | m_info.targetThrottle = (int)m_throttleClient.TargetDripRate; | ||
296 | m_info.maxThrottle = (int)m_throttleClient.MaxDripRate; | 297 | m_info.maxThrottle = (int)m_throttleClient.MaxDripRate; |
297 | 298 | ||
298 | return m_info; | 299 | return m_info; |
@@ -441,28 +442,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
441 | } | 442 | } |
442 | 443 | ||
443 | // Update the token buckets with new throttle values | 444 | // Update the token buckets with new throttle values |
444 | TokenBucket bucket; | 445 | if (m_throttleClient.AdaptiveEnabled) |
446 | { | ||
447 | long total = resend + land + wind + cloud + task + texture + asset; | ||
448 | m_throttleClient.TargetDripRate = total; | ||
449 | } | ||
450 | else | ||
451 | { | ||
452 | TokenBucket bucket; | ||
445 | 453 | ||
446 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; | 454 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; |
447 | bucket.RequestedDripRate = resend; | 455 | bucket.RequestedDripRate = resend; |
448 | 456 | ||
449 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; | 457 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; |
450 | bucket.RequestedDripRate = land; | 458 | bucket.RequestedDripRate = land; |
451 | 459 | ||
452 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; | 460 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; |
453 | bucket.RequestedDripRate = wind; | 461 | bucket.RequestedDripRate = wind; |
454 | 462 | ||
455 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; | 463 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; |
456 | bucket.RequestedDripRate = cloud; | 464 | bucket.RequestedDripRate = cloud; |
457 | 465 | ||
458 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; | 466 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; |
459 | bucket.RequestedDripRate = asset; | 467 | bucket.RequestedDripRate = asset; |
460 | 468 | ||
461 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; | 469 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; |
462 | bucket.RequestedDripRate = task; | 470 | bucket.RequestedDripRate = task; |
463 | 471 | ||
464 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; | 472 | bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; |
465 | bucket.RequestedDripRate = texture; | 473 | bucket.RequestedDripRate = texture; |
474 | } | ||
466 | 475 | ||
467 | // Reset the packed throttles cached data | 476 | // Reset the packed throttles cached data |
468 | m_packedThrottles = null; | 477 | m_packedThrottles = null; |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 610067e..64548f2 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -245,11 +245,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
245 | 245 | ||
246 | /// <summary>Bandwidth throttle for this UDP server</summary> | 246 | /// <summary>Bandwidth throttle for this UDP server</summary> |
247 | public TokenBucket Throttle { get; private set; } | 247 | public TokenBucket Throttle { get; private set; } |
248 | |||
249 | /// <summary> | ||
250 | /// Gets the maximum total drip rate allowed to all clients. | ||
251 | /// </summary> | ||
252 | public long MaxTotalDripRate { get { return Throttle.RequestedDripRate; } } | ||
253 | 248 | ||
254 | /// <summary>Per client throttle rates enforced by this server</summary> | 249 | /// <summary>Per client throttle rates enforced by this server</summary> |
255 | /// <remarks> | 250 | /// <remarks> |
@@ -452,7 +447,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
452 | // = new TokenBucket( | 447 | // = new TokenBucket( |
453 | // string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps); | 448 | // string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps); |
454 | 449 | ||
455 | Throttle = new TokenBucket("server throttle bucket", null, sceneThrottleBps); | 450 | Throttle = new TokenBucket("server throttle bucket", null, sceneThrottleBps, sceneThrottleBps); |
456 | 451 | ||
457 | ThrottleRates = new ThrottleRates(configSource); | 452 | ThrottleRates = new ThrottleRates(configSource); |
458 | 453 | ||
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs index 52247ab..325b04a 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs | |||
@@ -182,7 +182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
182 | ConsoleDisplayList cdl = new ConsoleDisplayList(); | 182 | ConsoleDisplayList cdl = new ConsoleDisplayList(); |
183 | cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled); | 183 | cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled); |
184 | 184 | ||
185 | long maxSceneDripRate = m_udpServer.MaxTotalDripRate; | 185 | long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate; |
186 | cdl.AddRow( | 186 | cdl.AddRow( |
187 | "Max scene throttle", | 187 | "Max scene throttle", |
188 | maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset"); | 188 | maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset"); |
@@ -360,7 +360,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
360 | param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); | 360 | param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name); |
361 | 361 | ||
362 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | 362 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; |
363 | udpClient.FlowThrottle.Enabled = newValue; | 363 | udpClient.FlowThrottle.AdaptiveEnabled = newValue; |
364 | // udpClient.FlowThrottle.MaxDripRate = 0; | 364 | // udpClient.FlowThrottle.MaxDripRate = 0; |
365 | // udpClient.FlowThrottle.AdjustedDripRate = 0; | 365 | // udpClient.FlowThrottle.AdjustedDripRate = 0; |
366 | } | 366 | } |
@@ -426,7 +426,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
426 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | 426 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; |
427 | 427 | ||
428 | ConsoleDisplayList cdl = new ConsoleDisplayList(); | 428 | ConsoleDisplayList cdl = new ConsoleDisplayList(); |
429 | cdl.AddRow("Adaptive throttle", udpClient.FlowThrottle.Enabled); | 429 | cdl.AddRow("Adaptive throttle", udpClient.FlowThrottle.AdaptiveEnabled); |
430 | cdl.AddRow("Max throttle", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000)); | 430 | cdl.AddRow("Max throttle", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000)); |
431 | 431 | ||
432 | m_console.Output(cdl.ToString()); | 432 | m_console.Output(cdl.ToString()); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs index b80a485..7991996 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/ThrottleTests.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using Nini.Config; | ||
29 | using NUnit.Framework; | 30 | using NUnit.Framework; |
30 | using OpenMetaverse.Packets; | 31 | using OpenMetaverse.Packets; |
31 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
@@ -67,7 +68,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
67 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | 68 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); |
68 | 69 | ||
69 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | 70 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; |
70 | // udpClient.ThrottleDebugLevel = 1; | 71 | |
72 | udpServer.Throttle.DebugLevel = 1; | ||
73 | udpClient.ThrottleDebugLevel = 1; | ||
71 | 74 | ||
72 | int resendBytes = 1000; | 75 | int resendBytes = 1000; |
73 | int landBytes = 2000; | 76 | int landBytes = 2000; |
@@ -83,7 +86,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
83 | ClientInfo ci = udpClient.GetClientInfo(); | 86 | ClientInfo ci = udpClient.GetClientInfo(); |
84 | 87 | ||
85 | // We expect this to be lower because of the minimum bound set by MTU | 88 | // We expect this to be lower because of the minimum bound set by MTU |
86 | float totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; | 89 | int totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; |
87 | Assert.AreEqual(LLUDPServer.MTU, ci.resendThrottle); | 90 | Assert.AreEqual(LLUDPServer.MTU, ci.resendThrottle); |
88 | Assert.AreEqual(landBytes, ci.landThrottle); | 91 | Assert.AreEqual(landBytes, ci.landThrottle); |
89 | Assert.AreEqual(windBytes, ci.windThrottle); | 92 | Assert.AreEqual(windBytes, ci.windThrottle); |
@@ -92,6 +95,66 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
92 | Assert.AreEqual(textureBytes, ci.textureThrottle); | 95 | Assert.AreEqual(textureBytes, ci.textureThrottle); |
93 | Assert.AreEqual(assetBytes, ci.assetThrottle); | 96 | Assert.AreEqual(assetBytes, ci.assetThrottle); |
94 | Assert.AreEqual(totalBytes, ci.totalThrottle); | 97 | Assert.AreEqual(totalBytes, ci.totalThrottle); |
98 | |||
99 | Assert.AreEqual(0, ci.maxThrottle); | ||
100 | } | ||
101 | |||
102 | [Test] | ||
103 | public void TestClientThrottleAdaptiveNoLimit() | ||
104 | { | ||
105 | TestHelpers.InMethod(); | ||
106 | // TestHelpers.EnableLogging(); | ||
107 | |||
108 | Scene scene = new SceneHelpers().SetupScene(); | ||
109 | |||
110 | IniConfigSource ics = new IniConfigSource(); | ||
111 | IConfig config = ics.AddConfig("ClientStack.LindenUDP"); | ||
112 | config.Set("enable_adaptive_throttles", true); | ||
113 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics); | ||
114 | |||
115 | ScenePresence sp | ||
116 | = ClientStackHelpers.AddChildClient( | ||
117 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
118 | |||
119 | LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient; | ||
120 | |||
121 | udpServer.Throttle.DebugLevel = 1; | ||
122 | udpClient.ThrottleDebugLevel = 1; | ||
123 | |||
124 | // Total is 28000 | ||
125 | int resendBytes = 10000; | ||
126 | int landBytes = 20000; | ||
127 | int windBytes = 30000; | ||
128 | int cloudBytes = 40000; | ||
129 | int taskBytes = 50000; | ||
130 | int textureBytes = 60000; | ||
131 | int assetBytes = 70000; | ||
132 | |||
133 | SetThrottles( | ||
134 | udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
135 | |||
136 | ClientInfo ci = udpClient.GetClientInfo(); | ||
137 | |||
138 | // We expect individual throttle changes to currently have no effect under adaptive, since this is managed | ||
139 | // purely by that throttle. However, we expect the max to change. | ||
140 | // XXX: At the moment we check against defaults, but at some point there should be a better test to | ||
141 | // active see change over time. | ||
142 | ThrottleRates defaultRates = udpServer.ThrottleRates; | ||
143 | |||
144 | // Current total is 66750 | ||
145 | int totalBytes = defaultRates.Resend + defaultRates.Land + defaultRates.Wind + defaultRates.Cloud + defaultRates.Task + defaultRates.Texture + defaultRates.Asset; | ||
146 | int totalMaxBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; | ||
147 | |||
148 | Assert.AreEqual(0, ci.maxThrottle); | ||
149 | Assert.AreEqual(totalMaxBytes, ci.targetThrottle); | ||
150 | Assert.AreEqual(defaultRates.Resend, ci.resendThrottle); | ||
151 | Assert.AreEqual(defaultRates.Land, ci.landThrottle); | ||
152 | Assert.AreEqual(defaultRates.Wind, ci.windThrottle); | ||
153 | Assert.AreEqual(defaultRates.Cloud, ci.cloudThrottle); | ||
154 | Assert.AreEqual(defaultRates.Task, ci.taskThrottle); | ||
155 | Assert.AreEqual(defaultRates.Texture, ci.textureThrottle); | ||
156 | Assert.AreEqual(defaultRates.Asset, ci.assetThrottle); | ||
157 | Assert.AreEqual(totalBytes, ci.totalThrottle); | ||
95 | } | 158 | } |
96 | 159 | ||
97 | /// <summary> | 160 | /// <summary> |
@@ -238,6 +301,101 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests | |||
238 | Assert.AreEqual(totalBytes, ci.totalThrottle); | 301 | Assert.AreEqual(totalBytes, ci.totalThrottle); |
239 | } | 302 | } |
240 | 303 | ||
304 | [Test] | ||
305 | public void TestClientThrottlePerClientAndRegionLimited() | ||
306 | { | ||
307 | TestHelpers.InMethod(); | ||
308 | //TestHelpers.EnableLogging(); | ||
309 | |||
310 | int resendBytes = 4000; | ||
311 | int landBytes = 6000; | ||
312 | int windBytes = 8000; | ||
313 | int cloudBytes = 10000; | ||
314 | int taskBytes = 12000; | ||
315 | int textureBytes = 14000; | ||
316 | int assetBytes = 16000; | ||
317 | |||
318 | // current total 70000 | ||
319 | int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes; | ||
320 | |||
321 | Scene scene = new SceneHelpers().SetupScene(); | ||
322 | TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene); | ||
323 | udpServer.ThrottleRates.Total = (int)(totalBytes * 1.1); | ||
324 | udpServer.Throttle.RequestedDripRate = (int)(totalBytes * 1.5); | ||
325 | |||
326 | ScenePresence sp1 | ||
327 | = ClientStackHelpers.AddChildClient( | ||
328 | scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456); | ||
329 | |||
330 | LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient; | ||
331 | udpClient1.ThrottleDebugLevel = 1; | ||
332 | |||
333 | SetThrottles( | ||
334 | udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
335 | |||
336 | { | ||
337 | ClientInfo ci = udpClient1.GetClientInfo(); | ||
338 | |||
339 | // Console.WriteLine( | ||
340 | // "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}", | ||
341 | // ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle); | ||
342 | |||
343 | Assert.AreEqual(resendBytes, ci.resendThrottle); | ||
344 | Assert.AreEqual(landBytes, ci.landThrottle); | ||
345 | Assert.AreEqual(windBytes, ci.windThrottle); | ||
346 | Assert.AreEqual(cloudBytes, ci.cloudThrottle); | ||
347 | Assert.AreEqual(taskBytes, ci.taskThrottle); | ||
348 | Assert.AreEqual(textureBytes, ci.textureThrottle); | ||
349 | Assert.AreEqual(assetBytes, ci.assetThrottle); | ||
350 | Assert.AreEqual(totalBytes, ci.totalThrottle); | ||
351 | } | ||
352 | |||
353 | // Now add another client | ||
354 | ScenePresence sp2 | ||
355 | = ClientStackHelpers.AddChildClient( | ||
356 | scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457); | ||
357 | |||
358 | LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient; | ||
359 | udpClient2.ThrottleDebugLevel = 1; | ||
360 | |||
361 | SetThrottles( | ||
362 | udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes); | ||
363 | |||
364 | { | ||
365 | ClientInfo ci = udpClient1.GetClientInfo(); | ||
366 | |||
367 | // Console.WriteLine( | ||
368 | // "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}", | ||
369 | // ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle); | ||
370 | |||
371 | Assert.AreEqual(resendBytes * 0.75, ci.resendThrottle); | ||
372 | Assert.AreEqual(landBytes * 0.75, ci.landThrottle); | ||
373 | Assert.AreEqual(windBytes * 0.75, ci.windThrottle); | ||
374 | Assert.AreEqual(cloudBytes * 0.75, ci.cloudThrottle); | ||
375 | Assert.AreEqual(taskBytes * 0.75, ci.taskThrottle); | ||
376 | Assert.AreEqual(textureBytes * 0.75, ci.textureThrottle); | ||
377 | Assert.AreEqual(assetBytes * 0.75, ci.assetThrottle); | ||
378 | Assert.AreEqual(totalBytes * 0.75, ci.totalThrottle); | ||
379 | } | ||
380 | |||
381 | { | ||
382 | ClientInfo ci = udpClient2.GetClientInfo(); | ||
383 | |||
384 | // Console.WriteLine( | ||
385 | // "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}", | ||
386 | // ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle); | ||
387 | |||
388 | Assert.AreEqual(resendBytes * 0.75, ci.resendThrottle); | ||
389 | Assert.AreEqual(landBytes * 0.75, ci.landThrottle); | ||
390 | Assert.AreEqual(windBytes * 0.75, ci.windThrottle); | ||
391 | Assert.AreEqual(cloudBytes * 0.75, ci.cloudThrottle); | ||
392 | Assert.AreEqual(taskBytes * 0.75, ci.taskThrottle); | ||
393 | Assert.AreEqual(textureBytes * 0.75, ci.textureThrottle); | ||
394 | Assert.AreEqual(assetBytes * 0.75, ci.assetThrottle); | ||
395 | Assert.AreEqual(totalBytes * 0.75, ci.totalThrottle); | ||
396 | } | ||
397 | } | ||
398 | |||
241 | private void SetThrottles( | 399 | private void SetThrottles( |
242 | LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes) | 400 | LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes) |
243 | { | 401 | { |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs index e5bae6e..dd15cc7 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs | |||
@@ -72,6 +72,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
72 | { | 72 | { |
73 | IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; | 73 | IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; |
74 | 74 | ||
75 | // Current default total is 66750 | ||
75 | Resend = throttleConfig.GetInt("resend_default", 6625); | 76 | Resend = throttleConfig.GetInt("resend_default", 6625); |
76 | Land = throttleConfig.GetInt("land_default", 9125); | 77 | Land = throttleConfig.GetInt("land_default", 9125); |
77 | Wind = throttleConfig.GetInt("wind_default", 1750); | 78 | Wind = throttleConfig.GetInt("wind_default", 1750); |
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs index d696265..e0633d3 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | |||
@@ -113,36 +113,65 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
113 | /// The speed limit of this bucket in bytes per second. This is the | 113 | /// The speed limit of this bucket in bytes per second. This is the |
114 | /// number of tokens that are added to the bucket per quantum | 114 | /// number of tokens that are added to the bucket per quantum |
115 | /// </summary> | 115 | /// </summary> |
116 | /// <remarks>Tokens are added to the bucket any time | 116 | /// <remarks> |
117 | /// RequestedDripRate can never be above MaxDripRate. | ||
118 | /// Tokens are added to the bucket any time | ||
117 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of | 119 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of |
118 | /// the system tick interval (typically around 15-22ms)</remarks> | 120 | /// the system tick interval (typically around 15-22ms)</remarks> |
119 | protected Int64 m_dripRate; | 121 | protected Int64 m_dripRate; |
120 | public virtual Int64 RequestedDripRate | 122 | public virtual Int64 RequestedDripRate |
121 | { | 123 | { |
122 | get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); } | 124 | get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); } |
123 | set { | 125 | set |
124 | m_dripRate = (value < 0 ? 0 : value); | 126 | { |
125 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | 127 | if (value <= 0) |
128 | m_dripRate = 0; | ||
129 | else if (MaxDripRate > 0 && value > MaxDripRate) | ||
130 | m_dripRate = MaxDripRate; | ||
131 | else | ||
132 | m_dripRate = value; | ||
133 | |||
126 | TotalDripRequest = m_dripRate; | 134 | TotalDripRequest = m_dripRate; |
135 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | ||
136 | |||
127 | if (Parent != null) | 137 | if (Parent != null) |
128 | Parent.RegisterRequest(this,m_dripRate); | 138 | Parent.RegisterRequest(this, m_dripRate); |
129 | } | 139 | } |
130 | } | 140 | } |
131 | 141 | ||
142 | /// <summary> | ||
143 | /// Gets the drip rate. | ||
144 | /// </summary> | ||
145 | /// <value>DripRate can never be above max.</value> | ||
132 | public virtual Int64 DripRate | 146 | public virtual Int64 DripRate |
133 | { | 147 | { |
134 | get { | 148 | get |
149 | { | ||
135 | if (Parent == null) | 150 | if (Parent == null) |
136 | return Math.Min(RequestedDripRate, TotalDripRequest); | 151 | return Math.Min(RequestedDripRate, TotalDripRequest); |
137 | 152 | ||
138 | double rate = (double)RequestedDripRate * Parent.DripRateModifier(); | 153 | double rate = (double)RequestedDripRate * Parent.DripRateModifier(); |
139 | if (rate < m_minimumDripRate) | 154 | if (rate < m_minimumDripRate) |
140 | rate = m_minimumDripRate; | 155 | rate = m_minimumDripRate; |
156 | else if (MaxDripRate > 0 && rate > MaxDripRate) | ||
157 | rate = MaxDripRate; | ||
141 | 158 | ||
142 | return (Int64)rate; | 159 | return (Int64)rate; |
143 | } | 160 | } |
144 | } | 161 | } |
145 | 162 | ||
163 | // <summary> | ||
164 | // The maximum rate for flow control. Drip rate can never be greater than this. | ||
165 | // </summary> | ||
166 | // protected Int64 m_maxDripRate; | ||
167 | // public Int64 MaxDripRate | ||
168 | // { | ||
169 | // get { return m_maxDripRate; } | ||
170 | // //get { return (m_maxDripRate == 0 ? TotalDripRequest : m_maxDripRate); } | ||
171 | // set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value, m_minimumFlow)); } | ||
172 | // } | ||
173 | public Int64 MaxDripRate { get; set; } | ||
174 | |||
146 | /// <summary> | 175 | /// <summary> |
147 | /// The current total of the requested maximum burst rates of | 176 | /// The current total of the requested maximum burst rates of |
148 | /// this bucket's children buckets. | 177 | /// this bucket's children buckets. |
@@ -161,12 +190,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
161 | /// null if this is a root bucket</param> | 190 | /// null if this is a root bucket</param> |
162 | /// <param name="dripRate">Rate that the bucket fills, in bytes per | 191 | /// <param name="dripRate">Rate that the bucket fills, in bytes per |
163 | /// second. If zero, the bucket always remains full</param> | 192 | /// second. If zero, the bucket always remains full</param> |
164 | public TokenBucket(string identifier, TokenBucket parent, Int64 dripRate) | 193 | public TokenBucket(string identifier, TokenBucket parent, Int64 dripRate, Int64 maxDripRate) |
165 | { | 194 | { |
166 | Identifier = identifier; | 195 | Identifier = identifier; |
167 | 196 | ||
168 | Parent = parent; | 197 | Parent = parent; |
169 | RequestedDripRate = dripRate; | 198 | RequestedDripRate = dripRate; |
199 | MaxDripRate = maxDripRate; | ||
170 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers | 200 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers |
171 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); | 201 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); |
172 | m_lastDrip = Util.EnvironmentTickCount(); | 202 | m_lastDrip = Util.EnvironmentTickCount(); |
@@ -184,7 +214,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
184 | protected double DripRateModifier() | 214 | protected double DripRateModifier() |
185 | { | 215 | { |
186 | Int64 driprate = DripRate; | 216 | Int64 driprate = DripRate; |
187 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; | 217 | double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; |
218 | |||
219 | // if (DebugLevel > 0) | ||
220 | // m_log.DebugFormat( | ||
221 | // "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}", | ||
222 | // driprate, TotalDripRequest, modifier, Identifier); | ||
223 | |||
224 | return modifier; | ||
188 | } | 225 | } |
189 | 226 | ||
190 | /// <summary> | 227 | /// <summary> |
@@ -215,7 +252,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
215 | 252 | ||
216 | // Pass the new values up to the parent | 253 | // Pass the new values up to the parent |
217 | if (Parent != null) | 254 | if (Parent != null) |
218 | Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | 255 | { |
256 | Int64 effectiveDripRate; | ||
257 | |||
258 | if (MaxDripRate > 0) | ||
259 | effectiveDripRate = Math.Min(MaxDripRate, TotalDripRequest); | ||
260 | else | ||
261 | effectiveDripRate = TotalDripRequest; | ||
262 | |||
263 | //Parent.RegisterRequest(this, Math.Min(RequestedDripRate, TotalDripRequest)); | ||
264 | Parent.RegisterRequest(this, effectiveDripRate); | ||
265 | } | ||
219 | } | 266 | } |
220 | 267 | ||
221 | /// <summary> | 268 | /// <summary> |
@@ -309,61 +356,60 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
309 | 356 | ||
310 | public class AdaptiveTokenBucket : TokenBucket | 357 | public class AdaptiveTokenBucket : TokenBucket |
311 | { | 358 | { |
312 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 359 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
360 | |||
361 | public bool AdaptiveEnabled { get; set; } | ||
313 | 362 | ||
314 | /// <summary> | 363 | /// <summary> |
315 | /// The minimum rate for flow control. Minimum drip rate is one | 364 | /// Target drip rate for this bucket. |
316 | /// packet per second. Open the throttle to 15 packets per second | ||
317 | /// or about 160kbps. | ||
318 | /// </summary> | 365 | /// </summary> |
319 | protected const Int64 m_minimumFlow = m_minimumDripRate * 15; | 366 | /// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks> |
320 | 367 | public Int64 TargetDripRate | |
321 | // <summary> | 368 | { |
322 | // The maximum rate for flow control. Drip rate can never be | 369 | get { return m_targetDripRate; } |
323 | // greater than this. | 370 | set { m_targetDripRate = Math.Max(0, value); } |
324 | // </summary> | ||
325 | protected Int64 m_maxDripRate = 0; | ||
326 | public Int64 MaxDripRate | ||
327 | { | ||
328 | get { return (m_maxDripRate == 0 ? TotalDripRequest : m_maxDripRate); } | ||
329 | set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } | ||
330 | } | 371 | } |
372 | protected Int64 m_targetDripRate; | ||
331 | 373 | ||
332 | public bool Enabled { get; set; } | ||
333 | |||
334 | // <summary> | 374 | // <summary> |
335 | // | 375 | // Adjust drip rate in response to network conditions. |
336 | // </summary> | 376 | // </summary> |
337 | public virtual Int64 AdjustedDripRate | 377 | public virtual Int64 AdjustedDripRate |
338 | { | 378 | { |
339 | get { return m_dripRate; } | 379 | get { return m_dripRate; } |
340 | set { | 380 | set { |
341 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); | 381 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate); |
342 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | 382 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); |
343 | if (Parent != null) | 383 | if (Parent != null) |
344 | Parent.RegisterRequest(this, m_dripRate); | 384 | Parent.RegisterRequest(this, m_dripRate); |
345 | } | 385 | } |
346 | } | 386 | } |
387 | |||
388 | /// <summary> | ||
389 | /// The minimum rate for flow control. Minimum drip rate is one | ||
390 | /// packet per second. Open the throttle to 15 packets per second | ||
391 | /// or about 160kbps. | ||
392 | /// </summary> | ||
393 | protected const Int64 m_minimumFlow = m_minimumDripRate * 15; | ||
347 | 394 | ||
348 | public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 maxDripRate, bool enabled) | 395 | public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 dripRate, Int64 maxDripRate, bool enabled) |
349 | : base(identifier, parent, maxDripRate) | 396 | : base(identifier, parent, dripRate, maxDripRate) |
350 | { | 397 | { |
351 | Enabled = enabled; | 398 | AdaptiveEnabled = enabled; |
352 | 399 | ||
353 | if (Enabled) | 400 | if (AdaptiveEnabled) |
354 | { | 401 | { |
355 | // m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); | 402 | // m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); |
356 | MaxDripRate = maxDripRate; | ||
357 | AdjustedDripRate = m_minimumFlow; | 403 | AdjustedDripRate = m_minimumFlow; |
358 | } | 404 | } |
359 | } | 405 | } |
360 | 406 | ||
361 | // <summary> | 407 | // <summary> |
362 | // | 408 | // Reliable packets sent to the client for which we never received an ack adjust the drip rate down. |
363 | // </summary> | 409 | // </summary> |
364 | public void ExpirePackets(Int32 count) | 410 | public void ExpirePackets(Int32 count) |
365 | { | 411 | { |
366 | if (Enabled) | 412 | if (AdaptiveEnabled) |
367 | { | 413 | { |
368 | if (DebugLevel > 0) | 414 | if (DebugLevel > 0) |
369 | m_log.WarnFormat( | 415 | m_log.WarnFormat( |
@@ -375,12 +421,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
375 | } | 421 | } |
376 | 422 | ||
377 | // <summary> | 423 | // <summary> |
378 | // | 424 | // Reliable packets acked by the client adjust the drip rate up. |
379 | // </summary> | 425 | // </summary> |
380 | public void AcknowledgePackets(Int32 count) | 426 | public void AcknowledgePackets(Int32 count) |
381 | { | 427 | { |
382 | if (Enabled) | 428 | if (AdaptiveEnabled) |
383 | AdjustedDripRate = AdjustedDripRate + count; | 429 | AdjustedDripRate = AdjustedDripRate + count; |
384 | } | 430 | } |
385 | } | 431 | } |
386 | } | 432 | } \ No newline at end of file |
diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs index d471062..cc6a1e8 100644 --- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs | |||
@@ -487,8 +487,9 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
487 | report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding)); | 487 | report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding)); |
488 | 488 | ||
489 | report.AppendFormat( | 489 | report.AppendFormat( |
490 | "{0,8} {1,7} {2,8} {3,7} {4,7} {5,7} {6,7} {7,9} {8,7}\n", | 490 | "{0,8} {1,8} {2,7} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7}\n", |
491 | "Max", | 491 | "Max", |
492 | "Target", | ||
492 | "Total", | 493 | "Total", |
493 | "Resend", | 494 | "Resend", |
494 | "Land", | 495 | "Land", |
@@ -500,7 +501,8 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
500 | 501 | ||
501 | report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", ""); | 502 | report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", ""); |
502 | report.AppendFormat( | 503 | report.AppendFormat( |
503 | "{0,8} {1,7} {2,8} {3,7} {4,7} {5,7} {6,7} {7,9} {8,7}\n", | 504 | "{0,8} {1,8} {2,7} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7}\n", |
505 | "kb/s", | ||
504 | "kb/s", | 506 | "kb/s", |
505 | "kb/s", | 507 | "kb/s", |
506 | "kb/s", | 508 | "kb/s", |
@@ -542,8 +544,9 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
542 | report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding)); | 544 | report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding)); |
543 | 545 | ||
544 | report.AppendFormat( | 546 | report.AppendFormat( |
545 | "{0,8} {1,7} {2,8} {3,7} {4,7} {5,7} {6,7} {7,9} {8,7}", | 547 | "{0,8} {1,8} {2,7} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7}\n", |
546 | (ci.maxThrottle * 8) / 1000, | 548 | ci.maxThrottle > 0 ? ((ci.maxThrottle * 8) / 1000).ToString() : "-", |
549 | llUdpClient.FlowThrottle.AdaptiveEnabled ? ((ci.targetThrottle * 8) / 1000).ToString() : "-", | ||
547 | (ci.totalThrottle * 8) / 1000, | 550 | (ci.totalThrottle * 8) / 1000, |
548 | (ci.resendThrottle * 8) / 1000, | 551 | (ci.resendThrottle * 8) / 1000, |
549 | (ci.landThrottle * 8) / 1000, | 552 | (ci.landThrottle * 8) / 1000, |
@@ -551,9 +554,7 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden | |||
551 | (ci.cloudThrottle * 8) / 1000, | 554 | (ci.cloudThrottle * 8) / 1000, |
552 | (ci.taskThrottle * 8) / 1000, | 555 | (ci.taskThrottle * 8) / 1000, |
553 | (ci.textureThrottle * 8) / 1000, | 556 | (ci.textureThrottle * 8) / 1000, |
554 | (ci.assetThrottle * 8) / 1000); | 557 | (ci.assetThrottle * 8) / 1000); |
555 | |||
556 | report.AppendLine(); | ||
557 | } | 558 | } |
558 | }); | 559 | }); |
559 | } | 560 | } |