in RegionInfoCache, replace lib omv ExpireCaches by a single dedicated one

LSLKeyTest
UbitUmarov 2016-07-30 01:44:33 +01:00
parent 3d58198c55
commit 55b2d01028
1 changed files with 443 additions and 67 deletions

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Reflection; using System.Reflection;
using System.Threading;
using System.Collections.Generic; using System.Collections.Generic;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Services.Interfaces; using OpenSim.Services.Interfaces;
@ -37,54 +38,17 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
{ {
public class RegionInfoCache public class RegionInfoCache
{ {
private const double CACHE_EXPIRATION_SECONDS = 300.0; // 5 minutes private const double CACHE_EXPIRATION_SECONDS = 120; // 2 minutes opensim regions change a lot
// private static readonly ILog m_log = // private static readonly ILog m_log =
// LogManager.GetLogger( // LogManager.GetLogger(
// MethodBase.GetCurrentMethod().DeclaringType); // MethodBase.GetCurrentMethod().DeclaringType);
internal struct ScopedRegionUUID private RegionsExpiringCache m_Cache;
{
public UUID m_scopeID;
public UUID m_regionID;
public ScopedRegionUUID(UUID scopeID, UUID regionID)
{
m_scopeID = scopeID;
m_regionID = regionID;
}
}
internal struct ScopedRegionName
{
public UUID m_scopeID;
public string m_name;
public ScopedRegionName(UUID scopeID, string name)
{
m_scopeID = scopeID;
m_name = name;
}
}
internal struct ScopedRegionPosition
{
public UUID m_scopeID;
public ulong m_regionHandle;
public ScopedRegionPosition(UUID scopeID, ulong handle)
{
m_scopeID = scopeID;
m_regionHandle = handle;
}
}
private ExpiringCache<ScopedRegionUUID, GridRegion> m_UUIDCache;
private ExpiringCache<ScopedRegionName, ScopedRegionUUID> m_NameCache;
private ExpiringCache<ScopedRegionPosition, GridRegion> m_PositionCache;
public RegionInfoCache() public RegionInfoCache()
{ {
m_UUIDCache = new ExpiringCache<ScopedRegionUUID, GridRegion>(); m_Cache = new RegionsExpiringCache();
m_NameCache = new ExpiringCache<ScopedRegionName, ScopedRegionUUID>();
m_PositionCache = new ExpiringCache<ScopedRegionPosition, GridRegion>();
} }
public void Cache(GridRegion rinfo) public void Cache(GridRegion rinfo)
@ -101,18 +65,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
if (rinfo == null) if (rinfo == null)
return; return;
ScopedRegionUUID id = new ScopedRegionUUID(scopeID,regionID); m_Cache.AddOrUpdate(scopeID, rinfo, CACHE_EXPIRATION_SECONDS);
// Cache even null accounts
m_UUIDCache.AddOrUpdate(id, rinfo, CACHE_EXPIRATION_SECONDS);
if (rinfo != null)
{
ScopedRegionName name = new ScopedRegionName(scopeID,rinfo.RegionName);
m_NameCache.AddOrUpdate(name, id, CACHE_EXPIRATION_SECONDS);
ScopedRegionPosition pos = new ScopedRegionPosition(scopeID, rinfo.RegionHandle);
m_PositionCache.AddOrUpdate(pos, rinfo, CACHE_EXPIRATION_SECONDS);
}
} }
public GridRegion Get(UUID scopeID, UUID regionID, out bool inCache) public GridRegion Get(UUID scopeID, UUID regionID, out bool inCache)
@ -120,8 +73,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
inCache = false; inCache = false;
GridRegion rinfo = null; GridRegion rinfo = null;
ScopedRegionUUID id = new ScopedRegionUUID(scopeID,regionID); if (m_Cache.TryGetValue(scopeID, regionID, out rinfo))
if (m_UUIDCache.TryGetValue(id, out rinfo))
{ {
inCache = true; inCache = true;
return rinfo; return rinfo;
@ -135,8 +87,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
inCache = false; inCache = false;
GridRegion rinfo = null; GridRegion rinfo = null;
ScopedRegionPosition pos = new ScopedRegionPosition(scopeID, handle); if (m_Cache.TryGetValue(scopeID, handle, out rinfo))
if (m_PositionCache.TryGetValue(pos, out rinfo))
{ {
inCache = true; inCache = true;
return rinfo; return rinfo;
@ -145,25 +96,450 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
return null; return null;
} }
public GridRegion Get(UUID scopeID, string name, out bool inCache) public GridRegion Get(UUID scopeID, string name, out bool inCache)
{ {
inCache = false; inCache = false;
ScopedRegionName sname = new ScopedRegionName(scopeID,name); GridRegion rinfo = null;
if (m_Cache.TryGetValue(scopeID, name, out rinfo))
ScopedRegionUUID id;
if (m_NameCache.TryGetValue(sname, out id))
{ {
GridRegion rinfo = null; inCache = true;
if (m_UUIDCache.TryGetValue(id, out rinfo)) return rinfo;
{
inCache = true;
return rinfo;
}
} }
return null; return null;
} }
} }
// following code partialy adapted from lib OpenMetaverse
public class RegionKey : IComparable<RegionKey>
{
private UUID m_scopeID;
private UUID m_RegionID;
private DateTime m_expirationDate;
public RegionKey(UUID scopeID, UUID id)
{
m_scopeID = scopeID;
m_RegionID = id;
}
public UUID ScopeID
{
get { return m_scopeID; }
}
public DateTime ExpirationDate
{
get { return m_expirationDate; }
set { m_expirationDate = value; }
}
public int GetHaskCode()
{
int hash = m_scopeID.GetHashCode();
hash += hash * 23 + m_RegionID.GetHashCode();
return hash;
}
public int CompareTo(RegionKey other)
{
return GetHashCode().CompareTo(other.GetHashCode());
}
}
public class RegionInfoByScope
{
private Dictionary<string, RegionKey> byname;
private Dictionary<ulong, RegionKey> byhandle;
public RegionInfoByScope()
{
byname = new Dictionary<string, RegionKey>();
byhandle = new Dictionary<ulong, RegionKey>();
}
public RegionInfoByScope(GridRegion region, RegionKey key)
{
byname = new Dictionary<string, RegionKey>();
byhandle = new Dictionary<ulong, RegionKey>();
byname[region.RegionName] = key;
byhandle[region.RegionHandle] = key;
}
public void AddRegion(GridRegion region, RegionKey key)
{
if(byname == null)
byname = new Dictionary<string, RegionKey>();
if(byhandle == null)
byhandle = new Dictionary<ulong, RegionKey>();
byname[region.RegionName] = key;
byhandle[region.RegionHandle] = key;
}
public void RemoveRegion(GridRegion region)
{
if(byname != null)
byname.Remove(region.RegionName);
if(byhandle != null)
byhandle.Remove(region.RegionHandle);
}
public void Clear()
{
if(byname != null)
byname.Clear();
if(byhandle != null)
byhandle.Clear();
byname = null;
byhandle = null;
}
public RegionKey get(string name)
{
if(byname == null || !byname.ContainsKey(name))
return null;
return byname[name];
}
public RegionKey get(ulong handle)
{
if(byhandle == null || !byhandle.ContainsKey(handle))
return null;
return byhandle[handle];
}
public int Count()
{
if(byname == null)
return 0;
else
return byname.Count;
}
}
public sealed class RegionsExpiringCache
{
const double CACHE_PURGE_HZ = 60;
const int MAX_LOCK_WAIT = 5000; // milliseconds
/// <summary>For thread safety</summary>
object syncRoot = new object();
/// <summary>For thread safety</summary>
object isPurging = new object();
Dictionary<RegionKey, GridRegion> timedStorage = new Dictionary<RegionKey, GridRegion>();
Dictionary<UUID, RegionInfoByScope> InfobyScope = new Dictionary<UUID, RegionInfoByScope>();
private System.Timers.Timer timer = new System.Timers.Timer(TimeSpan.FromSeconds(CACHE_PURGE_HZ).TotalMilliseconds);
public RegionsExpiringCache()
{
timer.Elapsed += PurgeCache;
timer.Start();
}
public bool Add(UUID scope, GridRegion region, double expirationSeconds)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
RegionKey key = new RegionKey(scope , region.RegionID);
try
{
if (timedStorage.ContainsKey(key))
return false;
key.ExpirationDate = DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds);
timedStorage.Add(key, region);
RegionInfoByScope ris = null;
if(!InfobyScope.TryGetValue(scope, out ris) || ris == null)
{
ris = new RegionInfoByScope(region, key);
InfobyScope[scope] = ris;
}
else
ris.AddRegion(region, key);
return true;
}
finally { Monitor.Exit(syncRoot);}
}
public bool AddOrUpdate(UUID scope, GridRegion region, double expirationSeconds)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
RegionKey key = new RegionKey(scope, region.RegionID);
key.ExpirationDate = DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds);
if (timedStorage.ContainsKey(key))
{
timedStorage.Remove(key);
timedStorage.Add(key, region);
if(!InfobyScope.ContainsKey(scope))
{
RegionInfoByScope ris = new RegionInfoByScope(region, key);
InfobyScope[scope] = ris;
}
return false;
}
else
{
timedStorage.Add(key, region);
RegionInfoByScope ris = null;
if(!InfobyScope.TryGetValue(scope, out ris) || ris == null)
{
ris = new RegionInfoByScope(region,key);
InfobyScope[scope] = ris;
}
else
ris.AddRegion(region,key);
return true;
}
}
finally { Monitor.Exit(syncRoot); }
}
public void Clear()
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
timedStorage.Clear();
InfobyScope.Clear();
}
finally { Monitor.Exit(syncRoot); }
}
public bool Contains(UUID scope, GridRegion region)
{
RegionKey key = new RegionKey(scope, region.RegionID);
return Contains(key);
}
public bool Contains(RegionKey key)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
return timedStorage.ContainsKey(key);
}
finally { Monitor.Exit(syncRoot); }
}
public int Count
{
get
{
return timedStorage.Count;
}
}
public bool Remove(UUID scope, GridRegion region)
{
RegionKey key = new RegionKey(scope, region.RegionID);
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
if (timedStorage.ContainsKey(key))
{
RegionInfoByScope ris = null;
if(InfobyScope.TryGetValue(scope, out ris) && ris != null)
{
GridRegion r = timedStorage[key];
if(r != null)
ris.RemoveRegion(r);
if(ris.Count() == 0)
InfobyScope.Remove(scope);
}
timedStorage.Remove(key);
return true;
}
else
return false;
}
finally { Monitor.Exit(syncRoot); }
}
public bool TryGetValue(RegionKey key, out GridRegion value)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
if (timedStorage.ContainsKey(key))
{
value = timedStorage[key];
return true;
}
}
finally { Monitor.Exit(syncRoot); }
value = null;
return false;
}
public bool TryGetValue(UUID scope, UUID id, out GridRegion value)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
RegionKey rk = new RegionKey(scope, id);
if(timedStorage.ContainsKey(rk))
{
value = timedStorage[rk];
return true;
}
}
finally { Monitor.Exit(syncRoot); }
value = null;
return false;
}
public bool TryGetValue(UUID scope, string name, out GridRegion value)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
value = null;
RegionInfoByScope ris = null;
if(!InfobyScope.TryGetValue(scope, out ris) || ris == null)
return false;
RegionKey key = ris.get(name);
if(key == null)
return false;
if(timedStorage.ContainsKey(key))
{
value = timedStorage[key];
return true;
}
}
finally { Monitor.Exit(syncRoot); }
return false;
}
public bool TryGetValue(UUID scope, ulong handle, out GridRegion value)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
try
{
value = null;
RegionInfoByScope ris = null;
if(!InfobyScope.TryGetValue(scope, out ris) || ris == null)
return false;
RegionKey key = ris.get(handle);
if(key == null)
return false;
if(timedStorage.ContainsKey(key))
{
value = timedStorage[key];
return true;
}
}
finally { Monitor.Exit(syncRoot); }
value = null;
return false;
}
public bool Update(UUID scope, GridRegion region, double expirationSeconds)
{
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
throw new ApplicationException("Lock could not be acquired after " + MAX_LOCK_WAIT + "ms");
RegionKey key = new RegionKey(scope, region.RegionID);
try
{
if (!timedStorage.ContainsKey(key))
return false;
timedStorage.Remove(key);
key.ExpirationDate = DateTime.UtcNow + TimeSpan.FromSeconds(expirationSeconds);
timedStorage.Add(key, region);
return true;
}
finally { Monitor.Exit(syncRoot); }
}
/// <summary>
/// Purges expired objects from the cache. Called automatically by the purge timer.
/// </summary>
private void PurgeCache(object sender, System.Timers.ElapsedEventArgs e)
{
// Only let one thread purge at once - a buildup could cause a crash
// This could cause the purge to be delayed while there are lots of read/write ops
// happening on the cache
if (!Monitor.TryEnter(isPurging))
return;
DateTime signalTime = DateTime.UtcNow;
try
{
// If we fail to acquire a lock on the synchronization root after MAX_LOCK_WAIT, skip this purge cycle
if (!Monitor.TryEnter(syncRoot, MAX_LOCK_WAIT))
return;
try
{
OpenMetaverse.Lazy<List<object>> expiredItems = new OpenMetaverse.Lazy<List<object>>();
foreach (RegionKey timedKey in timedStorage.Keys)
{
if (timedKey.ExpirationDate < signalTime)
{
// Mark the object for purge
expiredItems.Value.Add(timedKey);
}
else
{
break;
}
}
RegionInfoByScope ris;
if (expiredItems.IsValueCreated)
{
foreach (RegionKey key in expiredItems.Value)
{
ris = null;
if(InfobyScope.TryGetValue(key.ScopeID, out ris) && ris != null)
{
GridRegion r = timedStorage[key];
if(r != null)
ris.RemoveRegion(r);
if(ris.Count() == 0)
InfobyScope.Remove(key.ScopeID);
}
timedStorage.Remove(key);
}
}
}
finally { Monitor.Exit(syncRoot); }
}
finally { Monitor.Exit(isPurging); }
}
}
} }