Add negative caching to flotsam cache. Prevents scripts from hammering the asset server

melanie
Melanie Thielker 2016-12-29 15:47:46 +00:00
parent ec4c258794
commit 07b48fd58c
7 changed files with 92 additions and 2 deletions

View File

@ -38,6 +38,12 @@ namespace OpenSim.Framework
void Cache(AssetBase asset); void Cache(AssetBase asset);
/// <summary> /// <summary>
/// Cache that the specified asset wasn't found.
/// </summary>
/// <param name='id'></param>
/// <summary>
void CacheNegative(string id);
/// Get an asset by its id. /// Get an asset by its id.
/// </summary> /// </summary>
/// <param name='id'></param> /// <param name='id'></param>

View File

@ -221,6 +221,11 @@ namespace OpenSim.Region.CoreModules.Asset
} }
public void CacheNegative(string id)
{
// We don't do negative caching
}
/// <summary> /// <summary>
/// Clear asset cache. /// Clear asset cache.
/// </summary> /// </summary>

View File

@ -124,6 +124,11 @@ namespace OpenSim.Region.CoreModules.Asset
m_Cache.Store(asset.ID, asset); m_Cache.Store(asset.ID, asset);
} }
public void CacheNegative(string id)
{
// We don't do negative caching
}
public AssetBase Get(string id) public AssetBase Get(string id)
{ {
return (AssetBase)m_Cache.Get(id); return (AssetBase)m_Cache.Get(id);

View File

@ -92,9 +92,15 @@ namespace OpenSim.Region.CoreModules.Asset
private ExpiringCache<string, AssetBase> m_MemoryCache; private ExpiringCache<string, AssetBase> m_MemoryCache;
private bool m_MemoryCacheEnabled = false; private bool m_MemoryCacheEnabled = false;
private ExpiringCache<string, object> m_negativeCache;
private bool m_negativeCacheEnabled = true;
private bool m_negativeCacheSliding = false;
// Expiration is expressed in hours. // Expiration is expressed in hours.
private double m_MemoryExpiration = 0.016; private double m_MemoryExpiration = 0.016;
private const double m_DefaultFileExpiration = 48; private const double m_DefaultFileExpiration = 48;
// Negative cache is in seconds
private int m_negativeExpiration = 120;
private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration); private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration);
private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(1.0); private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(1.0);
@ -139,6 +145,7 @@ namespace OpenSim.Region.CoreModules.Asset
if (name == Name) if (name == Name)
{ {
m_MemoryCache = new ExpiringCache<string, AssetBase>(); m_MemoryCache = new ExpiringCache<string, AssetBase>();
m_negativeCache = new ExpiringCache<string, object>();
m_Enabled = true; m_Enabled = true;
m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name); m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name);
@ -158,6 +165,9 @@ namespace OpenSim.Region.CoreModules.Asset
m_MemoryExpiration = assetConfig.GetDouble("MemoryCacheTimeout", m_MemoryExpiration); m_MemoryExpiration = assetConfig.GetDouble("MemoryCacheTimeout", m_MemoryExpiration);
m_MemoryExpiration *= 3600.0; // config in hours to seconds m_MemoryExpiration *= 3600.0; // config in hours to seconds
m_negativeCacheEnabled = assetConfig.GetBoolean("NegativeCacheEnabled", m_negativeCacheEnabled);
m_negativeExpiration = assetConfig.GetInt("NegativeCacheTimeout", m_negativeExpiration);
m_negativeCacheSliding = assetConfig.GetBoolean("NegativeCacheSliding", m_negativeCacheSliding);
m_updateFileTimeOnCacheHit = assetConfig.GetBoolean("UpdateFileTimeOnCacheHit", m_updateFileTimeOnCacheHit); m_updateFileTimeOnCacheHit = assetConfig.GetBoolean("UpdateFileTimeOnCacheHit", m_updateFileTimeOnCacheHit);
#if WAIT_ON_INPROGRESS_REQUESTS #if WAIT_ON_INPROGRESS_REQUESTS
@ -355,6 +365,17 @@ namespace OpenSim.Region.CoreModules.Asset
} }
} }
public void CacheNegative(string id)
{
if (m_negativeCacheEnabled)
{
if (m_negativeCacheSliding)
m_negativeCache.AddOrUpdate(id, null, TimeSpan.FromSeconds(m_negativeExpiration));
else
m_negativeCache.AddOrUpdate(id, null, m_negativeExpiration);
}
}
/// <summary> /// <summary>
/// Updates the cached file with the current time. /// Updates the cached file with the current time.
/// </summary> /// </summary>
@ -514,6 +535,10 @@ namespace OpenSim.Region.CoreModules.Asset
{ {
m_Requests++; m_Requests++;
object dummy;
if (m_negativeCache.TryGetValue(id, out dummy))
return null;
AssetBase asset = null; AssetBase asset = null;
asset = GetFromWeakReference(id); asset = GetFromWeakReference(id);
if (asset != null && m_updateFileTimeOnCacheHit) if (asset != null && m_updateFileTimeOnCacheHit)
@ -622,6 +647,8 @@ namespace OpenSim.Region.CoreModules.Asset
if (m_MemoryCacheEnabled) if (m_MemoryCacheEnabled)
m_MemoryCache = new ExpiringCache<string, AssetBase>(); m_MemoryCache = new ExpiringCache<string, AssetBase>();
if (m_negativeCacheEnabled)
m_negativeCache = new ExpiringCache<string, object>();
lock(weakAssetReferencesLock) lock(weakAssetReferencesLock)
weakAssetReferences = new Dictionary<string, WeakReference>(); weakAssetReferences = new Dictionary<string, WeakReference>();

View File

@ -126,6 +126,11 @@ namespace OpenSim.Region.CoreModules.Asset
m_Cache.AddOrUpdate(asset.ID, asset); m_Cache.AddOrUpdate(asset.ID, asset);
} }
public void CacheNegative(string id)
{
// We don't do negative caching
}
public AssetBase Get(string id) public AssetBase Get(string id)
{ {
Object asset = null; Object asset = null;

View File

@ -260,8 +260,13 @@ namespace OpenSim.Services.Connectors
asset = SynchronousRestObjectRequester.MakeRequest<int, AssetBase>("GET", uri, 0, m_Auth); asset = SynchronousRestObjectRequester.MakeRequest<int, AssetBase>("GET", uri, 0, m_Auth);
if (asset != null && m_Cache != null) if (m_Cache != null)
m_Cache.Cache(asset); {
if (asset != null)
m_Cache.Cache(asset);
else
m_Cache.CacheNegative(id);
}
} }
return asset; return asset;
} }

View File

@ -24,6 +24,43 @@
; so even a small memory cache is useful ; so even a small memory cache is useful
MemoryCacheEnabled = false MemoryCacheEnabled = false
; If a memory cache hit happens, or the asset is still in memory
; due to other causes, update the timestamp on the disk file anyway.
; Don't turn this on unless you share your asset cache between simulators
; AND use an external process, e.g. cron job, to clean it up.
UpdateFileTimeOnCacheHit = false
; Enabling this will cache negative fetches. If an asset is negative-cached
; it will not be re-requested from the asset server again for a while.
; Generally, this is a good thing.
;
; Regular expiration settings (non-sliding) mean that the asset will be
; retried after the time has expired. Sliding expiration means that
; the time the negative cache will keep the asset is refreshed each
; time a fetch is attempted. Use sliding expiration if you have rogue
; scripts hammering the asset server with requests for nonexistent
; assets.
;
; There are two cases where negative caching may cause issues:
;
; 1 - If an invalid asset is repeatedly requested by a script and that asset is
; subsequently created, it will not be seen until fcache clear
; is used. This is a very theoretical scenario since UUID collisions
; are deemed to be not occuring in practice.
; This can only become an issue with sliding expiration time.
;
; 2 - If the asset service is clustered, an asset may not have propagated
; to all cluster members when it is first attempted to fetch it.
; This may theoretically occur with networked vendor systems and
; would lead to an asset not found message. However, after the
; expiration time has elapsed, the asset will the be fetchable.
;
; The defaults below are suitable for all small to medium installations
; including grids.
NegativeCacheEnabled = true
NegativeCacheTimeout = 120
NegativeCacheSliding = false
; Set to false for no file cache ; Set to false for no file cache
FileCacheEnabled = true FileCacheEnabled = true