Patch from mcortez to add basic caching to the groups module. This prevents database/network explosions when you have a significant number of group-owned prims in a scene

slimupdates
John Hurliman 2010-04-14 19:48:40 -07:00
parent 985faf4151
commit 8fa13e3871
2 changed files with 161 additions and 53 deletions

View File

@ -167,6 +167,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private bool m_debugEnabled = false; private bool m_debugEnabled = false;
private ExpiringCache<string, OSDMap> m_memoryCache;
private int m_cacheTimeout = 30;
// private IUserAccountService m_accountService = null; // private IUserAccountService m_accountService = null;
@ -203,7 +206,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return; return;
} }
m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Initializing {0}", this.Name);
m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty);
if ((m_groupsServerURI == null) || if ((m_groupsServerURI == null) ||
@ -214,6 +217,22 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return; return;
} }
m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30);
if (m_cacheTimeout == 0)
{
m_log.WarnFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Disabled.");
}
else
{
m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR] Groups Cache Timeout set to {0}.", m_cacheTimeout);
}
m_memoryCache = new ExpiringCache<string,OSDMap>();
// If we got all the config options we need, lets start'er'up // If we got all the config options we need, lets start'er'up
m_connectorEnabled = true; m_connectorEnabled = true;
@ -224,7 +243,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public void Close() public void Close()
{ {
m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); m_log.InfoFormat("[SIMIAN-GROUPS-CONNECTOR]: Closing {0}", this.Name);
} }
public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene)
@ -657,7 +676,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
}; };
OSDMap response = WebUtil.PostToService(m_groupsServerURI, requestArgs); OSDMap response = CachedPostRequest(requestArgs);
if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
{ {
OSDArray entryArray = (OSDArray)response["Entries"]; OSDArray entryArray = (OSDArray)response["Entries"];
@ -1086,7 +1105,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
}; };
OSDMap Response = WebUtil.PostToService(m_groupsServerURI, RequestArgs); OSDMap Response = CachedPostRequest(RequestArgs);
if (Response["Success"].AsBoolean()) if (Response["Success"].AsBoolean())
{ {
return true; return true;
@ -1113,7 +1132,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
}; };
OSDMap Response = WebUtil.PostToService(m_groupsServerURI, RequestArgs); OSDMap Response = CachedPostRequest(RequestArgs);
if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray)
{ {
OSDArray entryArray = (OSDArray)Response["Entries"]; OSDArray entryArray = (OSDArray)Response["Entries"];
@ -1153,7 +1172,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
}; };
OSDMap Response = WebUtil.PostToService(m_groupsServerURI, RequestArgs); OSDMap Response = CachedPostRequest(RequestArgs);
if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray)
{ {
OSDArray entryArray = (OSDArray)Response["Entries"]; OSDArray entryArray = (OSDArray)Response["Entries"];
@ -1194,7 +1213,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
}; };
OSDMap Response = WebUtil.PostToService(m_groupsServerURI, RequestArgs); OSDMap Response = CachedPostRequest(RequestArgs);
if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray) if (Response["Success"].AsBoolean() && Response["Entries"] is OSDArray)
{ {
OSDArray entryArray = (OSDArray)Response["Entries"]; OSDArray entryArray = (OSDArray)Response["Entries"];
@ -1234,7 +1253,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
OSDMap response = WebUtil.PostToService(m_groupsServerURI, requestArgs); OSDMap response = CachedPostRequest(requestArgs);
if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
{ {
maps = new Dictionary<string, OSDMap>(); maps = new Dictionary<string, OSDMap>();
@ -1272,7 +1291,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
OSDMap response = WebUtil.PostToService(m_groupsServerURI, requestArgs); OSDMap response = CachedPostRequest(requestArgs);
if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) if (response["Success"].AsBoolean() && response["Entries"] is OSDArray)
{ {
maps = new Dictionary<UUID, OSDMap>(); maps = new Dictionary<UUID, OSDMap>();
@ -1310,7 +1329,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
}; };
OSDMap response = WebUtil.PostToService(m_groupsServerURI, requestArgs); OSDMap response = CachedPostRequest(requestArgs);
if (response["Success"].AsBoolean()) if (response["Success"].AsBoolean())
{ {
return true; return true;
@ -1323,6 +1342,48 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
} }
#endregion #endregion
#region CheesyCache
OSDMap CachedPostRequest(NameValueCollection requestArgs)
{
// Immediately forward the request if the cache is disabled.
if (m_cacheTimeout == 0)
{
return WebUtil.PostToService(m_groupsServerURI, requestArgs);
}
// Check if this is an update or a request
if ( requestArgs["RequestMethod"] == "RemoveGeneric"
|| requestArgs["RequestMethod"] == "AddGeneric"
)
{
// Any and all updates cause the cache to clear
m_memoryCache.Clear();
// Send update to server, return the response without caching it
return WebUtil.PostToService(m_groupsServerURI, requestArgs);
}
// If we're not doing an update, we must be requesting data
// Create the cache key for the request and see if we have it cached
string CacheKey = WebUtil.BuildQueryString(requestArgs);
OSDMap response = null;
if (!m_memoryCache.TryGetValue(CacheKey, out response))
{
// if it wasn't in the cache, pass the request to the Simian Grid Services
response = WebUtil.PostToService(m_groupsServerURI, requestArgs);
// and cache the response
m_memoryCache.AddOrUpdate(CacheKey, response, TimeSpan.FromSeconds(m_cacheTimeout));
}
// return cached response
return response;
}
#endregion
} }
} }

View File

@ -29,6 +29,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Text;
using Nwc.XmlRpc; using Nwc.XmlRpc;
@ -70,6 +71,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private IUserAccountService m_accountService = null; private IUserAccountService m_accountService = null;
private ExpiringCache<string, XmlRpcResponse> m_memoryCache;
private int m_cacheTimeout = 30;
// Used to track which agents are have dropped from a group chat session // Used to track which agents are have dropped from a group chat session
// Should be reset per agent, on logon // Should be reset per agent, on logon
// TODO: move this to Flotsam XmlRpc Service // TODO: move this to Flotsam XmlRpc Service
@ -111,7 +115,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return; return;
} }
m_log.InfoFormat("[GROUPS-CONNECTOR]: Initializing {0}", this.Name); m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Initializing {0}", this.Name);
m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty); m_groupsServerURI = groupsConfig.GetString("GroupsServerURI", string.Empty);
if ((m_groupsServerURI == null) || if ((m_groupsServerURI == null) ||
@ -127,7 +131,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
m_groupReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty); m_groupReadKey = groupsConfig.GetString("XmlRpcServiceReadKey", string.Empty);
m_groupWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty); m_groupWriteKey = groupsConfig.GetString("XmlRpcServiceWriteKey", string.Empty);
m_cacheTimeout = groupsConfig.GetInt("GroupsCacheTimeout", 30);
if (m_cacheTimeout == 0)
{
m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Disabled.");
}
else
{
m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Groups Cache Timeout set to {0}.", m_cacheTimeout);
}
// If we got all the config options we need, lets start'er'up // If we got all the config options we need, lets start'er'up
@ -137,7 +150,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
public void Close() public void Close()
{ {
m_log.InfoFormat("[GROUPS-CONNECTOR]: Closing {0}", this.Name); m_log.InfoFormat("[XMLRPC-GROUPS-CONNECTOR]: Closing {0}", this.Name);
} }
public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene) public void AddRegion(OpenSim.Region.Framework.Scenes.Scene scene)
@ -919,50 +932,84 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
/// </summary> /// </summary>
private Hashtable XmlRpcCall(UUID requestingAgentID, string function, Hashtable param) private Hashtable XmlRpcCall(UUID requestingAgentID, string function, Hashtable param)
{ {
string UserService;
UUID SessionID;
GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID);
param.Add("requestingAgentID", requestingAgentID.ToString());
param.Add("RequestingAgentUserService", UserService);
param.Add("RequestingSessionID", SessionID.ToString());
param.Add("ReadKey", m_groupReadKey);
param.Add("WriteKey", m_groupWriteKey);
IList parameters = new ArrayList();
parameters.Add(param);
ConfigurableKeepAliveXmlRpcRequest req;
req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive);
XmlRpcResponse resp = null; XmlRpcResponse resp = null;
string CacheKey = null;
try // Only bother with the cache if it isn't disabled.
if (m_cacheTimeout > 0)
{ {
resp = req.Send(m_groupsServerURI, 10000); if (!function.StartsWith("groups.get"))
{
// Any and all updates cause the cache to clear
m_memoryCache.Clear();
}
else
{
StringBuilder sb = new StringBuilder(requestingAgentID + function);
foreach (object key in param.Keys)
{
if (param[key] != null)
{
sb.AppendFormat(",{0}:{1}", key.ToString(), param[key].ToString());
}
}
CacheKey = sb.ToString();
m_memoryCache.TryGetValue(CacheKey, out resp);
}
} }
catch (Exception e)
if( resp == null )
{ {
string UserService;
UUID SessionID;
GetClientGroupRequestID(requestingAgentID, out UserService, out SessionID);
param.Add("requestingAgentID", requestingAgentID.ToString());
param.Add("RequestingAgentUserService", UserService);
param.Add("RequestingSessionID", SessionID.ToString());
m_log.ErrorFormat("[XMLRPCGROUPDATA]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function);
m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", e.ToString());
foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine },StringSplitOptions.None)) param.Add("ReadKey", m_groupReadKey);
param.Add("WriteKey", m_groupWriteKey);
IList parameters = new ArrayList();
parameters.Add(param);
ConfigurableKeepAliveXmlRpcRequest req;
req = new ConfigurableKeepAliveXmlRpcRequest(function, parameters, m_disableKeepAlive);
try
{ {
m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} ", ResponseLine); resp = req.Send(m_groupsServerURI, 10000);
}
foreach (string key in param.Keys) if ((m_cacheTimeout > 0) && (CacheKey != null))
{
m_memoryCache.AddOrUpdate(CacheKey, resp, TimeSpan.FromSeconds(m_cacheTimeout));
}
}
catch (Exception e)
{ {
m_log.WarnFormat("[XMLRPCGROUPDATA]: {0} :: {1}", key, param[key].ToString()); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: An error has occured while attempting to access the XmlRpcGroups server method: {0}", function);
} m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", e.ToString());
Hashtable respData = new Hashtable(); foreach (string ResponseLine in req.RequestResponse.Split(new string[] { Environment.NewLine }, StringSplitOptions.None))
respData.Add("error", e.ToString()); {
return respData; m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} ", ResponseLine);
}
foreach (string key in param.Keys)
{
m_log.WarnFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", key, param[key].ToString());
}
Hashtable respData = new Hashtable();
respData.Add("error", e.ToString());
return respData;
}
} }
if (resp.Value is Hashtable) if (resp.Value is Hashtable)
@ -976,21 +1023,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
return respData; return respData;
} }
m_log.ErrorFormat("[XMLRPCGROUPDATA]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString()); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: The XmlRpc server returned a {1} instead of a hashtable for {0}", function, resp.Value.GetType().ToString());
if (resp.Value is ArrayList) if (resp.Value is ArrayList)
{ {
ArrayList al = (ArrayList)resp.Value; ArrayList al = (ArrayList)resp.Value;
m_log.ErrorFormat("[XMLRPCGROUPDATA]: Contains {0} elements", al.Count); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Contains {0} elements", al.Count);
foreach (object o in al) foreach (object o in al)
{ {
m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0} :: {1}", o.GetType().ToString(), o.ToString()); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0} :: {1}", o.GetType().ToString(), o.ToString());
} }
} }
else else
{ {
m_log.ErrorFormat("[XMLRPCGROUPDATA]: Function returned: {0}", resp.Value.ToString()); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Function returned: {0}", resp.Value.ToString());
} }
Hashtable error = new Hashtable(); Hashtable error = new Hashtable();
@ -1000,16 +1047,16 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
private void LogRespDataToConsoleError(Hashtable respData) private void LogRespDataToConsoleError(Hashtable respData)
{ {
m_log.Error("[XMLRPCGROUPDATA]: Error:"); m_log.Error("[XMLRPC-GROUPS-CONNECTOR]: Error:");
foreach (string key in respData.Keys) foreach (string key in respData.Keys)
{ {
m_log.ErrorFormat("[XMLRPCGROUPDATA]: Key: {0}", key); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: Key: {0}", key);
string[] lines = respData[key].ToString().Split(new char[] { '\n' }); string[] lines = respData[key].ToString().Split(new char[] { '\n' });
foreach (string line in lines) foreach (string line in lines)
{ {
m_log.ErrorFormat("[XMLRPCGROUPDATA]: {0}", line); m_log.ErrorFormat("[XMLRPC-GROUPS-CONNECTOR]: {0}", line);
} }
} }