Fix an issue where specifying both max client and server outgoing UDP throttles would cause client throttles to be lower than expected when total requests exceeded the scene limit.
This was because specifying a max client throttle would always request the max from the parent server throttle, no matter the actual total requests on the client throttle.
This would lead to a lower server multiplier than expected.
This change also adds a 'target' column to the "show throttles" output that shows the target rate (as set by client) if adaptive throttles is active.
This commit also re-adds the functionality lost in recent 5c1a1458
to set a max client throttle when adaptive is active.
This commit also adds TestClientThrottlePerClientAndRegionLimited and TestClientThrottleAdaptiveNoLimit regression tests
ghosts
parent
458540400a
commit
00e31de872
|
@ -54,6 +54,10 @@ namespace OpenSim.Framework
|
||||||
public int assetThrottle;
|
public int assetThrottle;
|
||||||
public int textureThrottle;
|
public int textureThrottle;
|
||||||
public int totalThrottle;
|
public int totalThrottle;
|
||||||
|
|
||||||
|
// Used by adaptive only
|
||||||
|
public int targetThrottle;
|
||||||
|
|
||||||
public int maxThrottle;
|
public int maxThrottle;
|
||||||
|
|
||||||
public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();
|
public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();
|
||||||
|
|
|
@ -229,7 +229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
m_throttleClient
|
m_throttleClient
|
||||||
= new AdaptiveTokenBucket(
|
= new AdaptiveTokenBucket(
|
||||||
string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name),
|
string.Format("adaptive throttle for {0} in {1}", AgentID, server.Scene.Name),
|
||||||
parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
|
parentThrottle, 0, rates.Total, rates.AdaptiveThrottlesEnabled);
|
||||||
|
|
||||||
// Create an array of token buckets for this clients different throttle categories
|
// Create an array of token buckets for this clients different throttle categories
|
||||||
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
|
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
|
||||||
|
@ -247,7 +247,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
m_throttleCategories[i]
|
m_throttleCategories[i]
|
||||||
= new TokenBucket(
|
= new TokenBucket(
|
||||||
string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name),
|
string.Format("{0} throttle for {1} in {2}", type, AgentID, server.Scene.Name),
|
||||||
m_throttleClient, rates.GetRate(type));
|
m_throttleClient, rates.GetRate(type), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default the retransmission timeout to one second
|
// Default the retransmission timeout to one second
|
||||||
|
@ -293,6 +293,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
|
m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
|
||||||
m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
|
m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
|
||||||
m_info.totalThrottle = (int)m_throttleClient.DripRate;
|
m_info.totalThrottle = (int)m_throttleClient.DripRate;
|
||||||
|
m_info.targetThrottle = (int)m_throttleClient.TargetDripRate;
|
||||||
m_info.maxThrottle = (int)m_throttleClient.MaxDripRate;
|
m_info.maxThrottle = (int)m_throttleClient.MaxDripRate;
|
||||||
|
|
||||||
return m_info;
|
return m_info;
|
||||||
|
@ -441,28 +442,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the token buckets with new throttle values
|
// Update the token buckets with new throttle values
|
||||||
TokenBucket bucket;
|
if (m_throttleClient.AdaptiveEnabled)
|
||||||
|
{
|
||||||
|
long total = resend + land + wind + cloud + task + texture + asset;
|
||||||
|
m_throttleClient.TargetDripRate = total;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TokenBucket bucket;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
|
||||||
bucket.RequestedDripRate = resend;
|
bucket.RequestedDripRate = resend;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
|
||||||
bucket.RequestedDripRate = land;
|
bucket.RequestedDripRate = land;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
|
||||||
bucket.RequestedDripRate = wind;
|
bucket.RequestedDripRate = wind;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
|
||||||
bucket.RequestedDripRate = cloud;
|
bucket.RequestedDripRate = cloud;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
|
||||||
bucket.RequestedDripRate = asset;
|
bucket.RequestedDripRate = asset;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
|
||||||
bucket.RequestedDripRate = task;
|
bucket.RequestedDripRate = task;
|
||||||
|
|
||||||
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
|
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
|
||||||
bucket.RequestedDripRate = texture;
|
bucket.RequestedDripRate = texture;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the packed throttles cached data
|
// Reset the packed throttles cached data
|
||||||
m_packedThrottles = null;
|
m_packedThrottles = null;
|
||||||
|
|
|
@ -246,11 +246,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <summary>Bandwidth throttle for this UDP server</summary>
|
/// <summary>Bandwidth throttle for this UDP server</summary>
|
||||||
public TokenBucket Throttle { get; private set; }
|
public TokenBucket Throttle { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the maximum total drip rate allowed to all clients.
|
|
||||||
/// </summary>
|
|
||||||
public long MaxTotalDripRate { get { return Throttle.RequestedDripRate; } }
|
|
||||||
|
|
||||||
/// <summary>Per client throttle rates enforced by this server</summary>
|
/// <summary>Per client throttle rates enforced by this server</summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have.
|
/// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have.
|
||||||
|
@ -452,7 +447,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
// = new TokenBucket(
|
// = new TokenBucket(
|
||||||
// string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps);
|
// string.Format("server throttle bucket for {0}", Scene.Name), null, sceneThrottleBps);
|
||||||
|
|
||||||
Throttle = new TokenBucket("server throttle bucket", null, sceneThrottleBps);
|
Throttle = new TokenBucket("server throttle bucket", null, sceneThrottleBps, sceneThrottleBps);
|
||||||
|
|
||||||
ThrottleRates = new ThrottleRates(configSource);
|
ThrottleRates = new ThrottleRates(configSource);
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||||
cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled);
|
cdl.AddRow("Adaptive throttles", m_udpServer.ThrottleRates.AdaptiveThrottlesEnabled);
|
||||||
|
|
||||||
long maxSceneDripRate = m_udpServer.MaxTotalDripRate;
|
long maxSceneDripRate = m_udpServer.Throttle.MaxDripRate;
|
||||||
cdl.AddRow(
|
cdl.AddRow(
|
||||||
"Max scene throttle",
|
"Max scene throttle",
|
||||||
maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
|
maxSceneDripRate != 0 ? string.Format("{0} kbps", maxSceneDripRate * 8 / 1000) : "unset");
|
||||||
|
@ -360,7 +360,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
|
param, newValue, sp.Name, sp.IsChildAgent ? "child" : "root", m_udpServer.Scene.Name);
|
||||||
|
|
||||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||||
udpClient.FlowThrottle.Enabled = newValue;
|
udpClient.FlowThrottle.AdaptiveEnabled = newValue;
|
||||||
// udpClient.FlowThrottle.MaxDripRate = 0;
|
// udpClient.FlowThrottle.MaxDripRate = 0;
|
||||||
// udpClient.FlowThrottle.AdjustedDripRate = 0;
|
// udpClient.FlowThrottle.AdjustedDripRate = 0;
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||||
|
|
||||||
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
ConsoleDisplayList cdl = new ConsoleDisplayList();
|
||||||
cdl.AddRow("Adaptive throttle", udpClient.FlowThrottle.Enabled);
|
cdl.AddRow("Adaptive throttle", udpClient.FlowThrottle.AdaptiveEnabled);
|
||||||
cdl.AddRow("Max throttle", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000));
|
cdl.AddRow("Max throttle", string.Format("{0} kbps", udpClient.FlowThrottle.RequestedDripRate * 8 / 1000));
|
||||||
|
|
||||||
m_console.Output(cdl.ToString());
|
m_console.Output(cdl.ToString());
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using Nini.Config;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using OpenMetaverse.Packets;
|
using OpenMetaverse.Packets;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
@ -67,7 +68,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||||
|
|
||||||
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||||
// udpClient.ThrottleDebugLevel = 1;
|
|
||||||
|
udpServer.Throttle.DebugLevel = 1;
|
||||||
|
udpClient.ThrottleDebugLevel = 1;
|
||||||
|
|
||||||
int resendBytes = 1000;
|
int resendBytes = 1000;
|
||||||
int landBytes = 2000;
|
int landBytes = 2000;
|
||||||
|
@ -83,7 +86,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
ClientInfo ci = udpClient.GetClientInfo();
|
ClientInfo ci = udpClient.GetClientInfo();
|
||||||
|
|
||||||
// We expect this to be lower because of the minimum bound set by MTU
|
// We expect this to be lower because of the minimum bound set by MTU
|
||||||
float totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
int totalBytes = LLUDPServer.MTU + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
||||||
Assert.AreEqual(LLUDPServer.MTU, ci.resendThrottle);
|
Assert.AreEqual(LLUDPServer.MTU, ci.resendThrottle);
|
||||||
Assert.AreEqual(landBytes, ci.landThrottle);
|
Assert.AreEqual(landBytes, ci.landThrottle);
|
||||||
Assert.AreEqual(windBytes, ci.windThrottle);
|
Assert.AreEqual(windBytes, ci.windThrottle);
|
||||||
|
@ -92,6 +95,66 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
Assert.AreEqual(textureBytes, ci.textureThrottle);
|
Assert.AreEqual(textureBytes, ci.textureThrottle);
|
||||||
Assert.AreEqual(assetBytes, ci.assetThrottle);
|
Assert.AreEqual(assetBytes, ci.assetThrottle);
|
||||||
Assert.AreEqual(totalBytes, ci.totalThrottle);
|
Assert.AreEqual(totalBytes, ci.totalThrottle);
|
||||||
|
|
||||||
|
Assert.AreEqual(0, ci.maxThrottle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClientThrottleAdaptiveNoLimit()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
// TestHelpers.EnableLogging();
|
||||||
|
|
||||||
|
Scene scene = new SceneHelpers().SetupScene();
|
||||||
|
|
||||||
|
IniConfigSource ics = new IniConfigSource();
|
||||||
|
IConfig config = ics.AddConfig("ClientStack.LindenUDP");
|
||||||
|
config.Set("enable_adaptive_throttles", true);
|
||||||
|
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene, ics);
|
||||||
|
|
||||||
|
ScenePresence sp
|
||||||
|
= ClientStackHelpers.AddChildClient(
|
||||||
|
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||||
|
|
||||||
|
LLUDPClient udpClient = ((LLClientView)sp.ControllingClient).UDPClient;
|
||||||
|
|
||||||
|
udpServer.Throttle.DebugLevel = 1;
|
||||||
|
udpClient.ThrottleDebugLevel = 1;
|
||||||
|
|
||||||
|
// Total is 28000
|
||||||
|
int resendBytes = 10000;
|
||||||
|
int landBytes = 20000;
|
||||||
|
int windBytes = 30000;
|
||||||
|
int cloudBytes = 40000;
|
||||||
|
int taskBytes = 50000;
|
||||||
|
int textureBytes = 60000;
|
||||||
|
int assetBytes = 70000;
|
||||||
|
|
||||||
|
SetThrottles(
|
||||||
|
udpClient, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||||
|
|
||||||
|
ClientInfo ci = udpClient.GetClientInfo();
|
||||||
|
|
||||||
|
// We expect individual throttle changes to currently have no effect under adaptive, since this is managed
|
||||||
|
// purely by that throttle. However, we expect the max to change.
|
||||||
|
// XXX: At the moment we check against defaults, but at some point there should be a better test to
|
||||||
|
// active see change over time.
|
||||||
|
ThrottleRates defaultRates = udpServer.ThrottleRates;
|
||||||
|
|
||||||
|
// Current total is 66750
|
||||||
|
int totalBytes = defaultRates.Resend + defaultRates.Land + defaultRates.Wind + defaultRates.Cloud + defaultRates.Task + defaultRates.Texture + defaultRates.Asset;
|
||||||
|
int totalMaxBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
||||||
|
|
||||||
|
Assert.AreEqual(0, ci.maxThrottle);
|
||||||
|
Assert.AreEqual(totalMaxBytes, ci.targetThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Resend, ci.resendThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Land, ci.landThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Wind, ci.windThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Cloud, ci.cloudThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Task, ci.taskThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Texture, ci.textureThrottle);
|
||||||
|
Assert.AreEqual(defaultRates.Asset, ci.assetThrottle);
|
||||||
|
Assert.AreEqual(totalBytes, ci.totalThrottle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -238,6 +301,101 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
|
||||||
Assert.AreEqual(totalBytes, ci.totalThrottle);
|
Assert.AreEqual(totalBytes, ci.totalThrottle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClientThrottlePerClientAndRegionLimited()
|
||||||
|
{
|
||||||
|
TestHelpers.InMethod();
|
||||||
|
//TestHelpers.EnableLogging();
|
||||||
|
|
||||||
|
int resendBytes = 4000;
|
||||||
|
int landBytes = 6000;
|
||||||
|
int windBytes = 8000;
|
||||||
|
int cloudBytes = 10000;
|
||||||
|
int taskBytes = 12000;
|
||||||
|
int textureBytes = 14000;
|
||||||
|
int assetBytes = 16000;
|
||||||
|
|
||||||
|
// current total 70000
|
||||||
|
int totalBytes = resendBytes + landBytes + windBytes + cloudBytes + taskBytes + textureBytes + assetBytes;
|
||||||
|
|
||||||
|
Scene scene = new SceneHelpers().SetupScene();
|
||||||
|
TestLLUDPServer udpServer = ClientStackHelpers.AddUdpServer(scene);
|
||||||
|
udpServer.ThrottleRates.Total = (int)(totalBytes * 1.1);
|
||||||
|
udpServer.Throttle.RequestedDripRate = (int)(totalBytes * 1.5);
|
||||||
|
|
||||||
|
ScenePresence sp1
|
||||||
|
= ClientStackHelpers.AddChildClient(
|
||||||
|
scene, udpServer, TestHelpers.ParseTail(0x1), TestHelpers.ParseTail(0x2), 123456);
|
||||||
|
|
||||||
|
LLUDPClient udpClient1 = ((LLClientView)sp1.ControllingClient).UDPClient;
|
||||||
|
udpClient1.ThrottleDebugLevel = 1;
|
||||||
|
|
||||||
|
SetThrottles(
|
||||||
|
udpClient1, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||||
|
|
||||||
|
{
|
||||||
|
ClientInfo ci = udpClient1.GetClientInfo();
|
||||||
|
|
||||||
|
// Console.WriteLine(
|
||||||
|
// "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}",
|
||||||
|
// ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle);
|
||||||
|
|
||||||
|
Assert.AreEqual(resendBytes, ci.resendThrottle);
|
||||||
|
Assert.AreEqual(landBytes, ci.landThrottle);
|
||||||
|
Assert.AreEqual(windBytes, ci.windThrottle);
|
||||||
|
Assert.AreEqual(cloudBytes, ci.cloudThrottle);
|
||||||
|
Assert.AreEqual(taskBytes, ci.taskThrottle);
|
||||||
|
Assert.AreEqual(textureBytes, ci.textureThrottle);
|
||||||
|
Assert.AreEqual(assetBytes, ci.assetThrottle);
|
||||||
|
Assert.AreEqual(totalBytes, ci.totalThrottle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now add another client
|
||||||
|
ScenePresence sp2
|
||||||
|
= ClientStackHelpers.AddChildClient(
|
||||||
|
scene, udpServer, TestHelpers.ParseTail(0x10), TestHelpers.ParseTail(0x20), 123457);
|
||||||
|
|
||||||
|
LLUDPClient udpClient2 = ((LLClientView)sp2.ControllingClient).UDPClient;
|
||||||
|
udpClient2.ThrottleDebugLevel = 1;
|
||||||
|
|
||||||
|
SetThrottles(
|
||||||
|
udpClient2, resendBytes, landBytes, windBytes, cloudBytes, taskBytes, textureBytes, assetBytes);
|
||||||
|
|
||||||
|
{
|
||||||
|
ClientInfo ci = udpClient1.GetClientInfo();
|
||||||
|
|
||||||
|
// Console.WriteLine(
|
||||||
|
// "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}",
|
||||||
|
// ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle);
|
||||||
|
|
||||||
|
Assert.AreEqual(resendBytes * 0.75, ci.resendThrottle);
|
||||||
|
Assert.AreEqual(landBytes * 0.75, ci.landThrottle);
|
||||||
|
Assert.AreEqual(windBytes * 0.75, ci.windThrottle);
|
||||||
|
Assert.AreEqual(cloudBytes * 0.75, ci.cloudThrottle);
|
||||||
|
Assert.AreEqual(taskBytes * 0.75, ci.taskThrottle);
|
||||||
|
Assert.AreEqual(textureBytes * 0.75, ci.textureThrottle);
|
||||||
|
Assert.AreEqual(assetBytes * 0.75, ci.assetThrottle);
|
||||||
|
Assert.AreEqual(totalBytes * 0.75, ci.totalThrottle);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ClientInfo ci = udpClient2.GetClientInfo();
|
||||||
|
|
||||||
|
// Console.WriteLine(
|
||||||
|
// "Resend={0}, Land={1}, Wind={2}, Cloud={3}, Task={4}, Texture={5}, Asset={6}, TOTAL = {7}",
|
||||||
|
// ci.resendThrottle, ci.landThrottle, ci.windThrottle, ci.cloudThrottle, ci.taskThrottle, ci.textureThrottle, ci.assetThrottle, ci.totalThrottle);
|
||||||
|
|
||||||
|
Assert.AreEqual(resendBytes * 0.75, ci.resendThrottle);
|
||||||
|
Assert.AreEqual(landBytes * 0.75, ci.landThrottle);
|
||||||
|
Assert.AreEqual(windBytes * 0.75, ci.windThrottle);
|
||||||
|
Assert.AreEqual(cloudBytes * 0.75, ci.cloudThrottle);
|
||||||
|
Assert.AreEqual(taskBytes * 0.75, ci.taskThrottle);
|
||||||
|
Assert.AreEqual(textureBytes * 0.75, ci.textureThrottle);
|
||||||
|
Assert.AreEqual(assetBytes * 0.75, ci.assetThrottle);
|
||||||
|
Assert.AreEqual(totalBytes * 0.75, ci.totalThrottle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetThrottles(
|
private void SetThrottles(
|
||||||
LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes)
|
LLUDPClient udpClient, int resendBytes, int landBytes, int windBytes, int cloudBytes, int taskBytes, int textureBytes, int assetBytes)
|
||||||
{
|
{
|
||||||
|
|
|
@ -72,6 +72,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
|
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
|
||||||
|
|
||||||
|
// Current default total is 66750
|
||||||
Resend = throttleConfig.GetInt("resend_default", 6625);
|
Resend = throttleConfig.GetInt("resend_default", 6625);
|
||||||
Land = throttleConfig.GetInt("land_default", 9125);
|
Land = throttleConfig.GetInt("land_default", 9125);
|
||||||
Wind = throttleConfig.GetInt("wind_default", 1750);
|
Wind = throttleConfig.GetInt("wind_default", 1750);
|
||||||
|
|
|
@ -113,36 +113,65 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// The speed limit of this bucket in bytes per second. This is the
|
/// The speed limit of this bucket in bytes per second. This is the
|
||||||
/// number of tokens that are added to the bucket per quantum
|
/// number of tokens that are added to the bucket per quantum
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Tokens are added to the bucket any time
|
/// <remarks>
|
||||||
|
/// RequestedDripRate can never be above MaxDripRate.
|
||||||
|
/// Tokens are added to the bucket any time
|
||||||
/// <seealso cref="RemoveTokens"/> is called, at the granularity of
|
/// <seealso cref="RemoveTokens"/> is called, at the granularity of
|
||||||
/// the system tick interval (typically around 15-22ms)</remarks>
|
/// the system tick interval (typically around 15-22ms)</remarks>
|
||||||
protected Int64 m_dripRate;
|
protected Int64 m_dripRate;
|
||||||
public virtual Int64 RequestedDripRate
|
public virtual Int64 RequestedDripRate
|
||||||
{
|
{
|
||||||
get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
|
get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
|
||||||
set {
|
set
|
||||||
m_dripRate = (value < 0 ? 0 : value);
|
{
|
||||||
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
if (value <= 0)
|
||||||
|
m_dripRate = 0;
|
||||||
|
else if (MaxDripRate > 0 && value > MaxDripRate)
|
||||||
|
m_dripRate = MaxDripRate;
|
||||||
|
else
|
||||||
|
m_dripRate = value;
|
||||||
|
|
||||||
TotalDripRequest = m_dripRate;
|
TotalDripRequest = m_dripRate;
|
||||||
|
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
||||||
|
|
||||||
if (Parent != null)
|
if (Parent != null)
|
||||||
Parent.RegisterRequest(this,m_dripRate);
|
Parent.RegisterRequest(this, m_dripRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the drip rate.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>DripRate can never be above max.</value>
|
||||||
public virtual Int64 DripRate
|
public virtual Int64 DripRate
|
||||||
{
|
{
|
||||||
get {
|
get
|
||||||
|
{
|
||||||
if (Parent == null)
|
if (Parent == null)
|
||||||
return Math.Min(RequestedDripRate, TotalDripRequest);
|
return Math.Min(RequestedDripRate, TotalDripRequest);
|
||||||
|
|
||||||
double rate = (double)RequestedDripRate * Parent.DripRateModifier();
|
double rate = (double)RequestedDripRate * Parent.DripRateModifier();
|
||||||
if (rate < m_minimumDripRate)
|
if (rate < m_minimumDripRate)
|
||||||
rate = m_minimumDripRate;
|
rate = m_minimumDripRate;
|
||||||
|
else if (MaxDripRate > 0 && rate > MaxDripRate)
|
||||||
|
rate = MaxDripRate;
|
||||||
|
|
||||||
return (Int64)rate;
|
return (Int64)rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
// The maximum rate for flow control. Drip rate can never be greater than this.
|
||||||
|
// </summary>
|
||||||
|
// protected Int64 m_maxDripRate;
|
||||||
|
// public Int64 MaxDripRate
|
||||||
|
// {
|
||||||
|
// get { return m_maxDripRate; }
|
||||||
|
// //get { return (m_maxDripRate == 0 ? TotalDripRequest : m_maxDripRate); }
|
||||||
|
// set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value, m_minimumFlow)); }
|
||||||
|
// }
|
||||||
|
public Int64 MaxDripRate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current total of the requested maximum burst rates of
|
/// The current total of the requested maximum burst rates of
|
||||||
/// this bucket's children buckets.
|
/// this bucket's children buckets.
|
||||||
|
@ -161,12 +190,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// null if this is a root bucket</param>
|
/// null if this is a root bucket</param>
|
||||||
/// <param name="dripRate">Rate that the bucket fills, in bytes per
|
/// <param name="dripRate">Rate that the bucket fills, in bytes per
|
||||||
/// second. If zero, the bucket always remains full</param>
|
/// second. If zero, the bucket always remains full</param>
|
||||||
public TokenBucket(string identifier, TokenBucket parent, Int64 dripRate)
|
public TokenBucket(string identifier, TokenBucket parent, Int64 dripRate, Int64 maxDripRate)
|
||||||
{
|
{
|
||||||
Identifier = identifier;
|
Identifier = identifier;
|
||||||
|
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
RequestedDripRate = dripRate;
|
RequestedDripRate = dripRate;
|
||||||
|
MaxDripRate = maxDripRate;
|
||||||
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
|
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
|
||||||
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
|
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
|
||||||
m_lastDrip = Util.EnvironmentTickCount();
|
m_lastDrip = Util.EnvironmentTickCount();
|
||||||
|
@ -184,7 +214,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
protected double DripRateModifier()
|
protected double DripRateModifier()
|
||||||
{
|
{
|
||||||
Int64 driprate = DripRate;
|
Int64 driprate = DripRate;
|
||||||
return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
|
double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
|
||||||
|
|
||||||
|
// if (DebugLevel > 0)
|
||||||
|
// m_log.DebugFormat(
|
||||||
|
// "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}",
|
||||||
|
// driprate, TotalDripRequest, modifier, Identifier);
|
||||||
|
|
||||||
|
return modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -215,7 +252,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
// Pass the new values up to the parent
|
// Pass the new values up to the parent
|
||||||
if (Parent != null)
|
if (Parent != null)
|
||||||
Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
|
{
|
||||||
|
Int64 effectiveDripRate;
|
||||||
|
|
||||||
|
if (MaxDripRate > 0)
|
||||||
|
effectiveDripRate = Math.Min(MaxDripRate, TotalDripRequest);
|
||||||
|
else
|
||||||
|
effectiveDripRate = TotalDripRequest;
|
||||||
|
|
||||||
|
//Parent.RegisterRequest(this, Math.Min(RequestedDripRate, TotalDripRequest));
|
||||||
|
Parent.RegisterRequest(this, effectiveDripRate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -311,6 +358,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
public bool AdaptiveEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Target drip rate for this bucket.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks>
|
||||||
|
public Int64 TargetDripRate
|
||||||
|
{
|
||||||
|
get { return m_targetDripRate; }
|
||||||
|
set { m_targetDripRate = Math.Max(0, value); }
|
||||||
|
}
|
||||||
|
protected Int64 m_targetDripRate;
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
// Adjust drip rate in response to network conditions.
|
||||||
|
// </summary>
|
||||||
|
public virtual Int64 AdjustedDripRate
|
||||||
|
{
|
||||||
|
get { return m_dripRate; }
|
||||||
|
set {
|
||||||
|
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate);
|
||||||
|
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
||||||
|
if (Parent != null)
|
||||||
|
Parent.RegisterRequest(this, m_dripRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum rate for flow control. Minimum drip rate is one
|
/// The minimum rate for flow control. Minimum drip rate is one
|
||||||
/// packet per second. Open the throttle to 15 packets per second
|
/// packet per second. Open the throttle to 15 packets per second
|
||||||
|
@ -318,52 +392,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
|
protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
|
||||||
|
|
||||||
// <summary>
|
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 dripRate, Int64 maxDripRate, bool enabled)
|
||||||
// The maximum rate for flow control. Drip rate can never be
|
: base(identifier, parent, dripRate, maxDripRate)
|
||||||
// greater than this.
|
|
||||||
// </summary>
|
|
||||||
protected Int64 m_maxDripRate = 0;
|
|
||||||
public Int64 MaxDripRate
|
|
||||||
{
|
{
|
||||||
get { return (m_maxDripRate == 0 ? TotalDripRequest : m_maxDripRate); }
|
AdaptiveEnabled = enabled;
|
||||||
set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Enabled { get; set; }
|
if (AdaptiveEnabled)
|
||||||
|
|
||||||
// <summary>
|
|
||||||
//
|
|
||||||
// </summary>
|
|
||||||
public virtual Int64 AdjustedDripRate
|
|
||||||
{
|
|
||||||
get { return m_dripRate; }
|
|
||||||
set {
|
|
||||||
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate);
|
|
||||||
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
|
|
||||||
if (Parent != null)
|
|
||||||
Parent.RegisterRequest(this, m_dripRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 maxDripRate, bool enabled)
|
|
||||||
: base(identifier, parent, maxDripRate)
|
|
||||||
{
|
|
||||||
Enabled = enabled;
|
|
||||||
|
|
||||||
if (Enabled)
|
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
|
// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
|
||||||
MaxDripRate = maxDripRate;
|
|
||||||
AdjustedDripRate = m_minimumFlow;
|
AdjustedDripRate = m_minimumFlow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <summary>
|
// <summary>
|
||||||
//
|
// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
|
||||||
// </summary>
|
// </summary>
|
||||||
public void ExpirePackets(Int32 count)
|
public void ExpirePackets(Int32 count)
|
||||||
{
|
{
|
||||||
if (Enabled)
|
if (AdaptiveEnabled)
|
||||||
{
|
{
|
||||||
if (DebugLevel > 0)
|
if (DebugLevel > 0)
|
||||||
m_log.WarnFormat(
|
m_log.WarnFormat(
|
||||||
|
@ -375,11 +421,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
|
|
||||||
// <summary>
|
// <summary>
|
||||||
//
|
// Reliable packets acked by the client adjust the drip rate up.
|
||||||
// </summary>
|
// </summary>
|
||||||
public void AcknowledgePackets(Int32 count)
|
public void AcknowledgePackets(Int32 count)
|
||||||
{
|
{
|
||||||
if (Enabled)
|
if (AdaptiveEnabled)
|
||||||
AdjustedDripRate = AdjustedDripRate + count;
|
AdjustedDripRate = AdjustedDripRate + count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -487,8 +487,9 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
|
||||||
report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding));
|
report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding));
|
||||||
|
|
||||||
report.AppendFormat(
|
report.AppendFormat(
|
||||||
"{0,8} {1,7} {2,8} {3,7} {4,7} {5,7} {6,7} {7,9} {8,7}\n",
|
"{0,8} {1,8} {2,7} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7}\n",
|
||||||
"Max",
|
"Max",
|
||||||
|
"Target",
|
||||||
"Total",
|
"Total",
|
||||||
"Resend",
|
"Resend",
|
||||||
"Land",
|
"Land",
|
||||||
|
@ -500,7 +501,8 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
|
||||||
|
|
||||||
report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", "");
|
report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", "");
|
||||||
report.AppendFormat(
|
report.AppendFormat(
|
||||||
"{0,8} {1,7} {2,8} {3,7} {4,7} {5,7} {6,7} {7,9} {8,7}\n",
|
"{0,8} {1,8} {2,7} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7}\n",
|
||||||
|
"kb/s",
|
||||||
"kb/s",
|
"kb/s",
|
||||||
"kb/s",
|
"kb/s",
|
||||||
"kb/s",
|
"kb/s",
|
||||||
|
@ -542,8 +544,9 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
|
||||||
report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding));
|
report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding));
|
||||||
|
|
||||||
report.AppendFormat(
|
report.AppendFormat(
|
||||||
"{0,8} {1,7} {2,8} {3,7} {4,7} {5,7} {6,7} {7,9} {8,7}",
|
"{0,8} {1,8} {2,7} {3,8} {4,7} {5,7} {6,7} {7,7} {8,9} {9,7}\n",
|
||||||
(ci.maxThrottle * 8) / 1000,
|
ci.maxThrottle > 0 ? ((ci.maxThrottle * 8) / 1000).ToString() : "-",
|
||||||
|
llUdpClient.FlowThrottle.AdaptiveEnabled ? ((ci.targetThrottle * 8) / 1000).ToString() : "-",
|
||||||
(ci.totalThrottle * 8) / 1000,
|
(ci.totalThrottle * 8) / 1000,
|
||||||
(ci.resendThrottle * 8) / 1000,
|
(ci.resendThrottle * 8) / 1000,
|
||||||
(ci.landThrottle * 8) / 1000,
|
(ci.landThrottle * 8) / 1000,
|
||||||
|
@ -552,8 +555,6 @@ namespace OpenSim.Region.OptionalModules.UDP.Linden
|
||||||
(ci.taskThrottle * 8) / 1000,
|
(ci.taskThrottle * 8) / 1000,
|
||||||
(ci.textureThrottle * 8) / 1000,
|
(ci.textureThrottle * 8) / 1000,
|
||||||
(ci.assetThrottle * 8) / 1000);
|
(ci.assetThrottle * 8) / 1000);
|
||||||
|
|
||||||
report.AppendLine();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue