in RegionInfoCache, replace lib omv ExpireCaches by a single dedicated one
parent
3d58198c55
commit
55b2d01028
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Services.Interfaces;
|
||||
|
@ -37,54 +38,17 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
{
|
||||
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 =
|
||||
// LogManager.GetLogger(
|
||||
// MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
internal struct ScopedRegionUUID
|
||||
{
|
||||
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;
|
||||
private RegionsExpiringCache m_Cache;
|
||||
|
||||
public RegionInfoCache()
|
||||
{
|
||||
m_UUIDCache = new ExpiringCache<ScopedRegionUUID, GridRegion>();
|
||||
m_NameCache = new ExpiringCache<ScopedRegionName, ScopedRegionUUID>();
|
||||
m_PositionCache = new ExpiringCache<ScopedRegionPosition, GridRegion>();
|
||||
m_Cache = new RegionsExpiringCache();
|
||||
}
|
||||
|
||||
public void Cache(GridRegion rinfo)
|
||||
|
@ -101,18 +65,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
if (rinfo == null)
|
||||
return;
|
||||
|
||||
ScopedRegionUUID id = new ScopedRegionUUID(scopeID,regionID);
|
||||
|
||||
// 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);
|
||||
}
|
||||
m_Cache.AddOrUpdate(scopeID, rinfo, CACHE_EXPIRATION_SECONDS);
|
||||
}
|
||||
|
||||
public GridRegion Get(UUID scopeID, UUID regionID, out bool inCache)
|
||||
|
@ -120,8 +73,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
inCache = false;
|
||||
|
||||
GridRegion rinfo = null;
|
||||
ScopedRegionUUID id = new ScopedRegionUUID(scopeID,regionID);
|
||||
if (m_UUIDCache.TryGetValue(id, out rinfo))
|
||||
if (m_Cache.TryGetValue(scopeID, regionID, out rinfo))
|
||||
{
|
||||
inCache = true;
|
||||
return rinfo;
|
||||
|
@ -135,8 +87,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
inCache = false;
|
||||
|
||||
GridRegion rinfo = null;
|
||||
ScopedRegionPosition pos = new ScopedRegionPosition(scopeID, handle);
|
||||
if (m_PositionCache.TryGetValue(pos, out rinfo))
|
||||
if (m_Cache.TryGetValue(scopeID, handle, out rinfo))
|
||||
{
|
||||
inCache = true;
|
||||
return rinfo;
|
||||
|
@ -145,25 +96,450 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
public GridRegion Get(UUID scopeID, string name, out bool inCache)
|
||||
{
|
||||
inCache = false;
|
||||
|
||||
ScopedRegionName sname = new ScopedRegionName(scopeID,name);
|
||||
|
||||
ScopedRegionUUID id;
|
||||
if (m_NameCache.TryGetValue(sname, out id))
|
||||
{
|
||||
GridRegion rinfo = null;
|
||||
if (m_UUIDCache.TryGetValue(id, out rinfo))
|
||||
if (m_Cache.TryGetValue(scopeID, name, out rinfo))
|
||||
{
|
||||
inCache = true;
|
||||
return rinfo;
|
||||
}
|
||||
}
|
||||
|
||||
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); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue