aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs371
1 files changed, 294 insertions, 77 deletions
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
index 17646fb..c1bf544 100644
--- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
@@ -25,7 +25,7 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28// Uncomment to make asset Get requests for existing 28// Uncomment to make asset Get requests for existing
29// #define WAIT_ON_INPROGRESS_REQUESTS 29// #define WAIT_ON_INPROGRESS_REQUESTS
30 30
31using System; 31using System;
@@ -55,16 +55,18 @@ using OpenSim.Services.Interfaces;
55namespace OpenSim.Region.CoreModules.Asset 55namespace OpenSim.Region.CoreModules.Asset
56{ 56{
57 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FlotsamAssetCache")] 57 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "FlotsamAssetCache")]
58 public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache, IAssetService 58 public class FlotsamAssetCache : ISharedRegionModule, IAssetCache, IAssetService
59 { 59 {
60 private static readonly ILog m_log = 60 private static readonly ILog m_log =
61 LogManager.GetLogger( 61 LogManager.GetLogger(
62 MethodBase.GetCurrentMethod().DeclaringType); 62 MethodBase.GetCurrentMethod().DeclaringType);
63 63
64 private bool m_Enabled; 64 private bool m_Enabled;
65 private bool m_timerRunning;
66 private bool m_cleanupRunning;
65 67
66 private const string m_ModuleName = "FlotsamAssetCache"; 68 private const string m_ModuleName = "FlotsamAssetCache";
67 private const string m_DefaultCacheDirectory = "../caches/assetcache"; 69 private const string m_DefaultCacheDirectory = "./assetcache";
68 private string m_CacheDirectory = m_DefaultCacheDirectory; 70 private string m_CacheDirectory = m_DefaultCacheDirectory;
69 71
70 private readonly List<char> m_InvalidChars = new List<char>(); 72 private readonly List<char> m_InvalidChars = new List<char>();
@@ -76,6 +78,7 @@ namespace OpenSim.Region.CoreModules.Asset
76 private static ulong m_RequestsForInprogress; 78 private static ulong m_RequestsForInprogress;
77 private static ulong m_DiskHits; 79 private static ulong m_DiskHits;
78 private static ulong m_MemoryHits; 80 private static ulong m_MemoryHits;
81 private static ulong m_weakRefHits;
79 82
80#if WAIT_ON_INPROGRESS_REQUESTS 83#if WAIT_ON_INPROGRESS_REQUESTS
81 private Dictionary<string, ManualResetEvent> m_CurrentlyWriting = new Dictionary<string, ManualResetEvent>(); 84 private Dictionary<string, ManualResetEvent> m_CurrentlyWriting = new Dictionary<string, ManualResetEvent>();
@@ -89,12 +92,17 @@ namespace OpenSim.Region.CoreModules.Asset
89 private ExpiringCache<string, AssetBase> m_MemoryCache; 92 private ExpiringCache<string, AssetBase> m_MemoryCache;
90 private bool m_MemoryCacheEnabled = false; 93 private bool m_MemoryCacheEnabled = false;
91 94
95 private ExpiringCache<string, object> m_negativeCache;
96 private bool m_negativeCacheEnabled = true;
97 private bool m_negativeCacheSliding = false;
98
92 // Expiration is expressed in hours. 99 // Expiration is expressed in hours.
93 private const double m_DefaultMemoryExpiration = 2; 100 private double m_MemoryExpiration = 0.016;
94 private const double m_DefaultFileExpiration = 48; 101 private const double m_DefaultFileExpiration = 48;
95 private TimeSpan m_MemoryExpiration = TimeSpan.FromHours(m_DefaultMemoryExpiration); 102 // Negative cache is in seconds
103 private int m_negativeExpiration = 120;
96 private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration); 104 private TimeSpan m_FileExpiration = TimeSpan.FromHours(m_DefaultFileExpiration);
97 private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(0.166); 105 private TimeSpan m_FileExpirationCleanupTimer = TimeSpan.FromHours(1.0);
98 106
99 private static int m_CacheDirectoryTiers = 1; 107 private static int m_CacheDirectoryTiers = 1;
100 private static int m_CacheDirectoryTierLen = 3; 108 private static int m_CacheDirectoryTierLen = 3;
@@ -104,6 +112,11 @@ namespace OpenSim.Region.CoreModules.Asset
104 112
105 private IAssetService m_AssetService; 113 private IAssetService m_AssetService;
106 private List<Scene> m_Scenes = new List<Scene>(); 114 private List<Scene> m_Scenes = new List<Scene>();
115 private object timerLock = new object();
116
117 private Dictionary<string,WeakReference> weakAssetReferences = new Dictionary<string, WeakReference>();
118 private object weakAssetReferencesLock = new object();
119 private bool m_updateFileTimeOnCacheHit = false;
107 120
108 public FlotsamAssetCache() 121 public FlotsamAssetCache()
109 { 122 {
@@ -111,7 +124,7 @@ namespace OpenSim.Region.CoreModules.Asset
111 m_InvalidChars.AddRange(Path.GetInvalidFileNameChars()); 124 m_InvalidChars.AddRange(Path.GetInvalidFileNameChars());
112 } 125 }
113 126
114 public Type ReplaceableInterface 127 public Type ReplaceableInterface
115 { 128 {
116 get { return null; } 129 get { return null; }
117 } 130 }
@@ -124,7 +137,7 @@ namespace OpenSim.Region.CoreModules.Asset
124 public void Initialise(IConfigSource source) 137 public void Initialise(IConfigSource source)
125 { 138 {
126 IConfig moduleConfig = source.Configs["Modules"]; 139 IConfig moduleConfig = source.Configs["Modules"];
127 140
128 if (moduleConfig != null) 141 if (moduleConfig != null)
129 { 142 {
130 string name = moduleConfig.GetString("AssetCaching", String.Empty); 143 string name = moduleConfig.GetString("AssetCaching", String.Empty);
@@ -132,6 +145,7 @@ namespace OpenSim.Region.CoreModules.Asset
132 if (name == Name) 145 if (name == Name)
133 { 146 {
134 m_MemoryCache = new ExpiringCache<string, AssetBase>(); 147 m_MemoryCache = new ExpiringCache<string, AssetBase>();
148 m_negativeCache = new ExpiringCache<string, object>();
135 m_Enabled = true; 149 m_Enabled = true;
136 150
137 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name); 151 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} enabled", this.Name);
@@ -148,12 +162,18 @@ namespace OpenSim.Region.CoreModules.Asset
148 m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); 162 m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory);
149 163
150 m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); 164 m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled);
151 m_MemoryExpiration = TimeSpan.FromHours(assetConfig.GetDouble("MemoryCacheTimeout", m_DefaultMemoryExpiration)); 165 m_MemoryExpiration = assetConfig.GetDouble("MemoryCacheTimeout", m_MemoryExpiration);
152 166 m_MemoryExpiration *= 3600.0; // config in hours to seconds
167
168 m_negativeCacheEnabled = assetConfig.GetBoolean("NegativeCacheEnabled", m_negativeCacheEnabled);
169 m_negativeExpiration = assetConfig.GetInt("NegativeCacheTimeout", m_negativeExpiration);
170 m_negativeCacheSliding = assetConfig.GetBoolean("NegativeCacheSliding", m_negativeCacheSliding);
171 m_updateFileTimeOnCacheHit = assetConfig.GetBoolean("UpdateFileTimeOnCacheHit", m_updateFileTimeOnCacheHit);
172
153 #if WAIT_ON_INPROGRESS_REQUESTS 173 #if WAIT_ON_INPROGRESS_REQUESTS
154 m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000); 174 m_WaitOnInprogressTimeout = assetConfig.GetInt("WaitOnInprogressTimeout", 3000);
155 #endif 175 #endif
156 176
157 m_LogLevel = assetConfig.GetInt("LogLevel", m_LogLevel); 177 m_LogLevel = assetConfig.GetInt("LogLevel", m_LogLevel);
158 m_HitRateDisplay = (ulong)assetConfig.GetLong("HitRateDisplay", (long)m_HitRateDisplay); 178 m_HitRateDisplay = (ulong)assetConfig.GetLong("HitRateDisplay", (long)m_HitRateDisplay);
159 179
@@ -170,14 +190,6 @@ namespace OpenSim.Region.CoreModules.Asset
170 190
171 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); 191 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
172 192
173 if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero))
174 {
175 m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds);
176 m_CacheCleanTimer.AutoReset = true;
177 m_CacheCleanTimer.Elapsed += CleanupExpiredFiles;
178 lock (m_CacheCleanTimer)
179 m_CacheCleanTimer.Start();
180 }
181 193
182 if (m_CacheDirectoryTiers < 1) 194 if (m_CacheDirectoryTiers < 1)
183 { 195 {
@@ -217,9 +229,8 @@ namespace OpenSim.Region.CoreModules.Asset
217 { 229 {
218 if (m_Enabled) 230 if (m_Enabled)
219 { 231 {
220 scene.RegisterModuleInterface<IImprovedAssetCache>(this); 232 scene.RegisterModuleInterface<IAssetCache>(this);
221 m_Scenes.Add(scene); 233 m_Scenes.Add(scene);
222
223 } 234 }
224 } 235 }
225 236
@@ -227,23 +238,61 @@ namespace OpenSim.Region.CoreModules.Asset
227 { 238 {
228 if (m_Enabled) 239 if (m_Enabled)
229 { 240 {
230 scene.UnregisterModuleInterface<IImprovedAssetCache>(this); 241 scene.UnregisterModuleInterface<IAssetCache>(this);
231 m_Scenes.Remove(scene); 242 m_Scenes.Remove(scene);
243 lock(timerLock)
244 {
245 if(m_timerRunning && m_Scenes.Count <= 0)
246 {
247 m_timerRunning = false;
248 m_CacheCleanTimer.Stop();
249 m_CacheCleanTimer.Close();
250 }
251 }
232 } 252 }
233 } 253 }
234 254
235 public void RegionLoaded(Scene scene) 255 public void RegionLoaded(Scene scene)
236 { 256 {
237 if (m_Enabled && m_AssetService == null) 257 if (m_Enabled)
238 m_AssetService = scene.RequestModuleInterface<IAssetService>(); 258 {
259 if(m_AssetService == null)
260 m_AssetService = scene.RequestModuleInterface<IAssetService>();
261 lock(timerLock)
262 {
263 if(!m_timerRunning)
264 {
265 if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero))
266 {
267 m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds);
268 m_CacheCleanTimer.AutoReset = false;
269 m_CacheCleanTimer.Elapsed += CleanupExpiredFiles;
270 m_CacheCleanTimer.Start();
271 m_timerRunning = true;
272 }
273 }
274 }
275 if (m_MemoryCacheEnabled)
276 m_MemoryCache = new ExpiringCache<string, AssetBase>();
277
278 lock(weakAssetReferencesLock)
279 weakAssetReferences = new Dictionary<string, WeakReference>();
280 }
239 } 281 }
240 282
241 //////////////////////////////////////////////////////////// 283 ////////////////////////////////////////////////////////////
242 // IImprovedAssetCache 284 // IAssetCache
243 // 285 //
286 private void UpdateWeakReference(string key, AssetBase asset)
287 {
288 WeakReference aref = new WeakReference(asset);
289 lock(weakAssetReferencesLock)
290 weakAssetReferences[key] = aref;
291 }
244 292
245 private void UpdateMemoryCache(string key, AssetBase asset) 293 private void UpdateMemoryCache(string key, AssetBase asset)
246 { 294 {
295 // NOTE DO NOT USE SLIDEEXPIRE option on current libomv
247 m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration); 296 m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration);
248 } 297 }
249 298
@@ -257,11 +306,11 @@ namespace OpenSim.Region.CoreModules.Asset
257 if (File.Exists(filename)) 306 if (File.Exists(filename))
258 { 307 {
259 UpdateFileLastAccessTime(filename); 308 UpdateFileLastAccessTime(filename);
260 } 309 }
261 else 310 else
262 { 311 {
263 // Once we start writing, make sure we flag that we're writing 312 // Once we start writing, make sure we flag that we're writing
264 // that object to the cache so that we don't try to write the 313 // that object to the cache so that we don't try to write the
265 // same file multiple times. 314 // same file multiple times.
266 lock (m_CurrentlyWriting) 315 lock (m_CurrentlyWriting)
267 { 316 {
@@ -306,6 +355,7 @@ namespace OpenSim.Region.CoreModules.Asset
306 if (asset != null) 355 if (asset != null)
307 { 356 {
308 //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID); 357 //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID);
358 UpdateWeakReference(asset.ID, asset);
309 359
310 if (m_MemoryCacheEnabled) 360 if (m_MemoryCacheEnabled)
311 UpdateMemoryCache(asset.ID, asset); 361 UpdateMemoryCache(asset.ID, asset);
@@ -315,6 +365,17 @@ namespace OpenSim.Region.CoreModules.Asset
315 } 365 }
316 } 366 }
317 367
368 public void CacheNegative(string id)
369 {
370 if (m_negativeCacheEnabled)
371 {
372 if (m_negativeCacheSliding)
373 m_negativeCache.AddOrUpdate(id, null, TimeSpan.FromSeconds(m_negativeExpiration));
374 else
375 m_negativeCache.AddOrUpdate(id, null, m_negativeExpiration);
376 }
377 }
378
318 /// <summary> 379 /// <summary>
319 /// Updates the cached file with the current time. 380 /// Updates the cached file with the current time.
320 /// </summary> 381 /// </summary>
@@ -333,6 +394,25 @@ namespace OpenSim.Region.CoreModules.Asset
333 } 394 }
334 } 395 }
335 396
397 private AssetBase GetFromWeakReference(string id)
398 {
399 AssetBase asset = null;
400 WeakReference aref;
401
402 lock(weakAssetReferencesLock)
403 {
404 if (weakAssetReferences.TryGetValue(id, out aref))
405 {
406 asset = aref.Target as AssetBase;
407 if(asset == null)
408 weakAssetReferences.Remove(id);
409 else
410 m_weakRefHits++;
411 }
412 }
413 return asset;
414 }
415
336 /// <summary> 416 /// <summary>
337 /// Try to get an asset from the in-memory cache. 417 /// Try to get an asset from the in-memory cache.
338 /// </summary> 418 /// </summary>
@@ -359,7 +439,7 @@ namespace OpenSim.Region.CoreModules.Asset
359 /// <param name="id"></param> 439 /// <param name="id"></param>
360 /// <returns>An asset retrieved from the file cache. null if there was a problem retrieving an asset.</returns> 440 /// <returns>An asset retrieved from the file cache. null if there was a problem retrieving an asset.</returns>
361 private AssetBase GetFromFileCache(string id) 441 private AssetBase GetFromFileCache(string id)
362 { 442 {
363 string filename = GetFileName(id); 443 string filename = GetFileName(id);
364 444
365#if WAIT_ON_INPROGRESS_REQUESTS 445#if WAIT_ON_INPROGRESS_REQUESTS
@@ -394,6 +474,8 @@ namespace OpenSim.Region.CoreModules.Asset
394 { 474 {
395 using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) 475 using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
396 { 476 {
477 if (stream.Length == 0) // Empty file will trigger exception below
478 return null;
397 BinaryFormatter bformatter = new BinaryFormatter(); 479 BinaryFormatter bformatter = new BinaryFormatter();
398 480
399 asset = (AssetBase)bformatter.Deserialize(stream); 481 asset = (AssetBase)bformatter.Deserialize(stream);
@@ -451,23 +533,57 @@ namespace OpenSim.Region.CoreModules.Asset
451 return found; 533 return found;
452 } 534 }
453 535
536 // For IAssetService
454 public AssetBase Get(string id) 537 public AssetBase Get(string id)
455 { 538 {
539 AssetBase asset;
540 Get(id, out asset);
541 return asset;
542 }
543
544 public bool Get(string id, out AssetBase asset)
545 {
546 asset = null;
547
456 m_Requests++; 548 m_Requests++;
457 549
458 AssetBase asset = null; 550 object dummy;
551 if (m_negativeCache.TryGetValue(id, out dummy))
552 {
553 return false;
554 }
459 555
460 if (m_MemoryCacheEnabled) 556 asset = GetFromWeakReference(id);
557 if (asset != null && m_updateFileTimeOnCacheHit)
558 {
559 string filename = GetFileName(id);
560 UpdateFileLastAccessTime(filename);
561 }
562
563 if (m_MemoryCacheEnabled && asset == null)
564 {
461 asset = GetFromMemoryCache(id); 565 asset = GetFromMemoryCache(id);
566 if(asset != null)
567 {
568 UpdateWeakReference(id,asset);
569 if (m_updateFileTimeOnCacheHit)
570 {
571 string filename = GetFileName(id);
572 UpdateFileLastAccessTime(filename);
573 }
574 }
575 }
462 576
463 if (asset == null && m_FileCacheEnabled) 577 if (asset == null && m_FileCacheEnabled)
464 { 578 {
465 asset = GetFromFileCache(id); 579 asset = GetFromFileCache(id);
466 580 if(asset != null)
467 if (m_MemoryCacheEnabled && asset != null) 581 UpdateWeakReference(id,asset);
468 UpdateMemoryCache(id, asset);
469 } 582 }
470 583
584 if (m_MemoryCacheEnabled && asset != null)
585 UpdateMemoryCache(id, asset);
586
471 if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) 587 if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0))
472 { 588 {
473 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); 589 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit");
@@ -475,7 +591,7 @@ namespace OpenSim.Region.CoreModules.Asset
475 GenerateCacheHitReport().ForEach(l => m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0}", l)); 591 GenerateCacheHitReport().ForEach(l => m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0}", l));
476 } 592 }
477 593
478 return asset; 594 return true;
479 } 595 }
480 596
481 public bool Check(string id) 597 public bool Check(string id)
@@ -490,7 +606,9 @@ namespace OpenSim.Region.CoreModules.Asset
490 606
491 public AssetBase GetCached(string id) 607 public AssetBase GetCached(string id)
492 { 608 {
493 return Get(id); 609 AssetBase asset;
610 Get(id, out asset);
611 return asset;
494 } 612 }
495 613
496 public void Expire(string id) 614 public void Expire(string id)
@@ -511,6 +629,9 @@ namespace OpenSim.Region.CoreModules.Asset
511 629
512 if (m_MemoryCacheEnabled) 630 if (m_MemoryCacheEnabled)
513 m_MemoryCache.Remove(id); 631 m_MemoryCache.Remove(id);
632
633 lock(weakAssetReferencesLock)
634 weakAssetReferences.Remove(id);
514 } 635 }
515 catch (Exception e) 636 catch (Exception e)
516 { 637 {
@@ -525,7 +646,7 @@ namespace OpenSim.Region.CoreModules.Asset
525 if (m_LogLevel >= 2) 646 if (m_LogLevel >= 2)
526 m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches."); 647 m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches.");
527 648
528 if (m_FileCacheEnabled) 649 if (m_FileCacheEnabled && Directory.Exists(m_CacheDirectory))
529 { 650 {
530 foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) 651 foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
531 { 652 {
@@ -534,7 +655,12 @@ namespace OpenSim.Region.CoreModules.Asset
534 } 655 }
535 656
536 if (m_MemoryCacheEnabled) 657 if (m_MemoryCacheEnabled)
537 m_MemoryCache.Clear(); 658 m_MemoryCache = new ExpiringCache<string, AssetBase>();
659 if (m_negativeCacheEnabled)
660 m_negativeCache = new ExpiringCache<string, object>();
661
662 lock(weakAssetReferencesLock)
663 weakAssetReferences = new Dictionary<string, WeakReference>();
538 } 664 }
539 665
540 private void CleanupExpiredFiles(object source, ElapsedEventArgs e) 666 private void CleanupExpiredFiles(object source, ElapsedEventArgs e)
@@ -542,6 +668,12 @@ namespace OpenSim.Region.CoreModules.Asset
542 if (m_LogLevel >= 2) 668 if (m_LogLevel >= 2)
543 m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration); 669 m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Checking for expired files older then {0}.", m_FileExpiration);
544 670
671 lock(timerLock)
672 {
673 if(!m_timerRunning || m_cleanupRunning)
674 return;
675 m_cleanupRunning = true;
676 }
545 // Purge all files last accessed prior to this point 677 // Purge all files last accessed prior to this point
546 DateTime purgeLine = DateTime.Now - m_FileExpiration; 678 DateTime purgeLine = DateTime.Now - m_FileExpiration;
547 679
@@ -549,27 +681,34 @@ namespace OpenSim.Region.CoreModules.Asset
549 // before cleaning up expired files we must scan the objects in the scene to make sure that we retain 681 // before cleaning up expired files we must scan the objects in the scene to make sure that we retain
550 // such local assets if they have not been recently accessed. 682 // such local assets if they have not been recently accessed.
551 TouchAllSceneAssets(false); 683 TouchAllSceneAssets(false);
684 if(Directory.Exists(m_CacheDirectory))
685 {
686 foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
687 CleanExpiredFiles(dir, purgeLine);
688 }
552 689
553 foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) 690 lock(timerLock)
554 { 691 {
555 CleanExpiredFiles(dir, purgeLine); 692 if(m_timerRunning)
693 m_CacheCleanTimer.Start();
694 m_cleanupRunning = false;
556 } 695 }
557 } 696 }
558 697
559 /// <summary> 698 /// <summary>
560 /// Recurses through specified directory checking for asset files last 699 /// Recurses through specified directory checking for asset files last
561 /// accessed prior to the specified purge line and deletes them. Also 700 /// accessed prior to the specified purge line and deletes them. Also
562 /// removes empty tier directories. 701 /// removes empty tier directories.
563 /// </summary> 702 /// </summary>
564 /// <param name="dir"></param> 703 /// <param name="dir"></param>
565 /// <param name="purgeLine"></param> 704 /// <param name="purgeLine"></param>
566 private void CleanExpiredFiles(string dir, DateTime purgeLine) 705 private void CleanExpiredFiles(string dir, DateTime purgeLine)
567 { 706 {
568 // Yet another "directory we are trying to remove doesn't exist, so don't complain" idiocy fix.
569 if (!Directory.Exists(dir))
570 return;
571 try 707 try
572 { 708 {
709 if(!Directory.Exists(dir))
710 return;
711
573 foreach (string file in Directory.GetFiles(dir)) 712 foreach (string file in Directory.GetFiles(dir))
574 { 713 {
575 if (File.GetLastAccessTime(file) < purgeLine) 714 if (File.GetLastAccessTime(file) < purgeLine)
@@ -593,14 +732,19 @@ namespace OpenSim.Region.CoreModules.Asset
593 else if (dirSize >= m_CacheWarnAt) 732 else if (dirSize >= m_CacheWarnAt)
594 { 733 {
595 m_log.WarnFormat( 734 m_log.WarnFormat(
596 "[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", 735 "[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration",
597 dir, dirSize); 736 dir, dirSize);
598 } 737 }
599 } 738 }
739 catch (DirectoryNotFoundException)
740 {
741 // If we get here, another node on the same box has
742 // already removed the directory. Continue with next.
743 }
600 catch (Exception e) 744 catch (Exception e)
601 { 745 {
602//// TODO - I'm almost sure this is another case of failing to delete something that doesn't actually exist. 746 m_log.Warn(
603//// m_log.Warn(string.Format("[FLOTSAM ASSET CACHE]: Could not complete clean of expired files in {0}", dir)); 747 string.Format("[FLOTSAM ASSET CACHE]: Could not complete clean of expired files in {0}, exception ", dir), e);
604 } 748 }
605 } 749 }
606 750
@@ -628,7 +772,7 @@ namespace OpenSim.Region.CoreModules.Asset
628 } 772 }
629 773
630 /// <summary> 774 /// <summary>
631 /// Writes a file to the file cache, creating any nessesary 775 /// Writes a file to the file cache, creating any nessesary
632 /// tier directories along the way 776 /// tier directories along the way
633 /// </summary> 777 /// </summary>
634 /// <param name="filename"></param> 778 /// <param name="filename"></param>
@@ -640,7 +784,7 @@ namespace OpenSim.Region.CoreModules.Asset
640 // Make sure the target cache directory exists 784 // Make sure the target cache directory exists
641 string directory = Path.GetDirectoryName(filename); 785 string directory = Path.GetDirectoryName(filename);
642 786
643 // Write file first to a temp name, so that it doesn't look 787 // Write file first to a temp name, so that it doesn't look
644 // like it's already cached while it's still writing. 788 // like it's already cached while it's still writing.
645 string tempname = Path.Combine(directory, Path.GetRandomFileName()); 789 string tempname = Path.Combine(directory, Path.GetRandomFileName());
646 790
@@ -652,7 +796,7 @@ namespace OpenSim.Region.CoreModules.Asset
652 { 796 {
653 Directory.CreateDirectory(directory); 797 Directory.CreateDirectory(directory);
654 } 798 }
655 799
656 stream = File.Open(tempname, FileMode.Create); 800 stream = File.Open(tempname, FileMode.Create);
657 BinaryFormatter bformatter = new BinaryFormatter(); 801 BinaryFormatter bformatter = new BinaryFormatter();
658 bformatter.Serialize(stream, asset); 802 bformatter.Serialize(stream, asset);
@@ -665,6 +809,9 @@ namespace OpenSim.Region.CoreModules.Asset
665 809
666 return; 810 return;
667 } 811 }
812 catch (UnauthorizedAccessException)
813 {
814 }
668 finally 815 finally
669 { 816 {
670 if (stream != null) 817 if (stream != null)
@@ -686,7 +833,7 @@ namespace OpenSim.Region.CoreModules.Asset
686 // This situation occurs fairly rarely anyway. We assume in this that moves are atomic on the 833 // This situation occurs fairly rarely anyway. We assume in this that moves are atomic on the
687 // filesystem. 834 // filesystem.
688 File.Move(tempname, filename); 835 File.Move(tempname, filename);
689 836
690 if (m_LogLevel >= 2) 837 if (m_LogLevel >= 2)
691 m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Cache Stored :: {0}", asset.ID); 838 m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Cache Stored :: {0}", asset.ID);
692 } 839 }
@@ -725,6 +872,9 @@ namespace OpenSim.Region.CoreModules.Asset
725 /// <returns></returns> 872 /// <returns></returns>
726 private int GetFileCacheCount(string dir) 873 private int GetFileCacheCount(string dir)
727 { 874 {
875 if(!Directory.Exists(dir))
876 return 0;
877
728 int count = Directory.GetFiles(dir).Length; 878 int count = Directory.GetFiles(dir).Length;
729 879
730 foreach (string subdir in Directory.GetDirectories(dir)) 880 foreach (string subdir in Directory.GetDirectories(dir))
@@ -743,7 +893,7 @@ namespace OpenSim.Region.CoreModules.Asset
743 { 893 {
744 string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + regionID.ToString() + ".fac"); 894 string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + regionID.ToString() + ".fac");
745 895
746 try 896 try
747 { 897 {
748 if (File.Exists(RegionCacheStatusFile)) 898 if (File.Exists(RegionCacheStatusFile))
749 { 899 {
@@ -752,7 +902,7 @@ namespace OpenSim.Region.CoreModules.Asset
752 else 902 else
753 { 903 {
754 File.WriteAllText( 904 File.WriteAllText(
755 RegionCacheStatusFile, 905 RegionCacheStatusFile,
756 "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache."); 906 "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache.");
757 } 907 }
758 } 908 }
@@ -760,14 +910,14 @@ namespace OpenSim.Region.CoreModules.Asset
760 { 910 {
761 m_log.Warn( 911 m_log.Warn(
762 string.Format( 912 string.Format(
763 "[FLOTSAM ASSET CACHE]: Could not stamp region status file for region {0}. Exception ", 913 "[FLOTSAM ASSET CACHE]: Could not stamp region status file for region {0}. Exception ",
764 regionID), 914 regionID),
765 e); 915 e);
766 } 916 }
767 } 917 }
768 918
769 /// <summary> 919 /// <summary>
770 /// Iterates through all Scenes, doing a deep scan through assets 920 /// Iterates through all Scenes, doing a deep scan through assets
771 /// to update the access time of all assets present in the scene or referenced by assets 921 /// to update the access time of all assets present in the scene or referenced by assets
772 /// in the scene. 922 /// in the scene.
773 /// </summary> 923 /// </summary>
@@ -786,10 +936,16 @@ namespace OpenSim.Region.CoreModules.Asset
786 StampRegionStatusFile(s.RegionInfo.RegionID); 936 StampRegionStatusFile(s.RegionInfo.RegionID);
787 937
788 s.ForEachSOG(delegate(SceneObjectGroup e) 938 s.ForEachSOG(delegate(SceneObjectGroup e)
789 { 939 {
940 if(!m_timerRunning && !storeUncached)
941 return;
942
790 gatherer.AddForInspection(e); 943 gatherer.AddForInspection(e);
791 gatherer.GatherAll(); 944 gatherer.GatherAll();
792 945
946 if(!m_timerRunning && !storeUncached)
947 return;
948
793 foreach (UUID assetID in gatherer.GatheredUuids.Keys) 949 foreach (UUID assetID in gatherer.GatheredUuids.Keys)
794 { 950 {
795 if (!assetsFound.ContainsKey(assetID)) 951 if (!assetsFound.ContainsKey(assetID))
@@ -799,6 +955,7 @@ namespace OpenSim.Region.CoreModules.Asset
799 if (File.Exists(filename)) 955 if (File.Exists(filename))
800 { 956 {
801 UpdateFileLastAccessTime(filename); 957 UpdateFileLastAccessTime(filename);
958 assetsFound[assetID] = true;
802 } 959 }
803 else if (storeUncached) 960 else if (storeUncached)
804 { 961 {
@@ -818,7 +975,14 @@ namespace OpenSim.Region.CoreModules.Asset
818 } 975 }
819 976
820 gatherer.GatheredUuids.Clear(); 977 gatherer.GatheredUuids.Clear();
978 if(!m_timerRunning && !storeUncached)
979 return;
980
981 if(!storeUncached)
982 Thread.Sleep(50);
821 }); 983 });
984 if(!m_timerRunning && !storeUncached)
985 break;
822 } 986 }
823 987
824 return assetsFound.Count; 988 return assetsFound.Count;
@@ -829,6 +993,9 @@ namespace OpenSim.Region.CoreModules.Asset
829 /// </summary> 993 /// </summary>
830 private void ClearFileCache() 994 private void ClearFileCache()
831 { 995 {
996 if(!Directory.Exists(m_CacheDirectory))
997 return;
998
832 foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) 999 foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
833 { 1000 {
834 try 1001 try
@@ -862,22 +1029,35 @@ namespace OpenSim.Region.CoreModules.Asset
862 { 1029 {
863 List<string> outputLines = new List<string>(); 1030 List<string> outputLines = new List<string>();
864 1031
865 double fileHitRate = (double)m_DiskHits / m_Requests * 100.0; 1032 double invReq = 100.0 / m_Requests;
1033
1034 double weakHitRate = m_weakRefHits * invReq;
1035 int weakEntries = weakAssetReferences.Count;
1036
1037 double fileHitRate = m_DiskHits * invReq;
1038 double TotalHitRate = weakHitRate + fileHitRate;
1039
1040 outputLines.Add(
1041 string.Format("Total requests: {0}", m_Requests));
866 outputLines.Add( 1042 outputLines.Add(
867 string.Format("File Hit Rate: {0}% for {1} requests", fileHitRate.ToString("0.00"), m_Requests)); 1043 string.Format("unCollected Hit Rate: {0}% ({1} entries)", weakHitRate.ToString("0.00"),weakEntries));
1044 outputLines.Add(
1045 string.Format("File Hit Rate: {0}%", fileHitRate.ToString("0.00")));
868 1046
869 if (m_MemoryCacheEnabled) 1047 if (m_MemoryCacheEnabled)
870 { 1048 {
871 double memHitRate = (double)m_MemoryHits / m_Requests * 100.0; 1049 double HitRate = m_MemoryHits * invReq;
872
873 outputLines.Add( 1050 outputLines.Add(
874 string.Format("Memory Hit Rate: {0}% for {1} requests", memHitRate.ToString("0.00"), m_Requests)); 1051 string.Format("Memory Hit Rate: {0}%", HitRate.ToString("0.00")));
1052
1053 TotalHitRate += HitRate;
875 } 1054 }
1055 outputLines.Add(
1056 string.Format("Total Hit Rate: {0}%", TotalHitRate.ToString("0.00")));
876 1057
877 outputLines.Add( 1058 outputLines.Add(
878 string.Format( 1059 string.Format(
879 "Unnecessary requests due to requests for assets that are currently downloading: {0}", 1060 "Requests overlap during file writing: {0}", m_RequestsForInprogress));
880 m_RequestsForInprogress));
881 1061
882 return outputLines; 1062 return outputLines;
883 } 1063 }
@@ -914,9 +1094,9 @@ namespace OpenSim.Region.CoreModules.Asset
914 if (m_FileCacheEnabled) 1094 if (m_FileCacheEnabled)
915 { 1095 {
916 con.Output("Deep scans have previously been performed on the following regions:"); 1096 con.Output("Deep scans have previously been performed on the following regions:");
917 1097
918 foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) 1098 foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac"))
919 { 1099 {
920 string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); 1100 string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac","");
921 DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); 1101 DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s);
922 con.OutputFormat("Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); 1102 con.OutputFormat("Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss"));
@@ -959,7 +1139,7 @@ namespace OpenSim.Region.CoreModules.Asset
959 con.Output("Memory cache not enabled."); 1139 con.Output("Memory cache not enabled.");
960 } 1140 }
961 } 1141 }
962 1142
963 if (clearFile) 1143 if (clearFile)
964 { 1144 {
965 if (m_FileCacheEnabled) 1145 if (m_FileCacheEnabled)
@@ -976,13 +1156,44 @@ namespace OpenSim.Region.CoreModules.Asset
976 break; 1156 break;
977 1157
978 case "assets": 1158 case "assets":
979 con.Output("Ensuring assets are cached for all scenes."); 1159 lock(timerLock)
1160 {
1161 if(m_cleanupRunning)
1162 {
1163 con.OutputFormat("FloatSam assets check already running");
1164 return;
1165 }
1166 m_cleanupRunning = true;
1167 }
980 1168
981 WorkManager.RunInThread(delegate 1169 con.Output("FloatSam Ensuring assets are cached for all scenes.");
1170
1171 WorkManager.RunInThreadPool(delegate
982 { 1172 {
1173 bool wasRunning= false;
1174 lock(timerLock)
1175 {
1176 if(m_timerRunning)
1177 {
1178 m_CacheCleanTimer.Stop();
1179 m_timerRunning = false;
1180 wasRunning = true;
1181 Thread.Sleep(100);
1182 }
1183 }
983 int assetReferenceTotal = TouchAllSceneAssets(true); 1184 int assetReferenceTotal = TouchAllSceneAssets(true);
1185 GC.Collect();
1186 lock(timerLock)
1187 {
1188 if(wasRunning)
1189 {
1190 m_CacheCleanTimer.Start();
1191 m_timerRunning = true;
1192 }
1193 m_cleanupRunning = false;
1194 }
984 con.OutputFormat("Completed check with {0} assets.", assetReferenceTotal); 1195 con.OutputFormat("Completed check with {0} assets.", assetReferenceTotal);
985 }, null, "TouchAllSceneAssets"); 1196 }, null, "TouchAllSceneAssets", false);
986 1197
987 break; 1198 break;
988 1199
@@ -1037,19 +1248,23 @@ namespace OpenSim.Region.CoreModules.Asset
1037 1248
1038 public AssetMetadata GetMetadata(string id) 1249 public AssetMetadata GetMetadata(string id)
1039 { 1250 {
1040 AssetBase asset = Get(id); 1251 AssetBase asset;
1252 Get(id, out asset);
1041 return asset.Metadata; 1253 return asset.Metadata;
1042 } 1254 }
1043 1255
1044 public byte[] GetData(string id) 1256 public byte[] GetData(string id)
1045 { 1257 {
1046 AssetBase asset = Get(id); 1258 AssetBase asset;
1259 Get(id, out asset);
1047 return asset.Data; 1260 return asset.Data;
1048 } 1261 }
1049 1262
1050 public bool Get(string id, object sender, AssetRetrieved handler) 1263 public bool Get(string id, object sender, AssetRetrieved handler)
1051 { 1264 {
1052 AssetBase asset = Get(id); 1265 AssetBase asset;
1266 if (!Get(id, out asset))
1267 return false;
1053 handler(id, sender, asset); 1268 handler(id, sender, asset);
1054 return true; 1269 return true;
1055 } 1270 }
@@ -1057,12 +1272,12 @@ namespace OpenSim.Region.CoreModules.Asset
1057 public bool[] AssetsExist(string[] ids) 1272 public bool[] AssetsExist(string[] ids)
1058 { 1273 {
1059 bool[] exist = new bool[ids.Length]; 1274 bool[] exist = new bool[ids.Length];
1060 1275
1061 for (int i = 0; i < ids.Length; i++) 1276 for (int i = 0; i < ids.Length; i++)
1062 { 1277 {
1063 exist[i] = Check(ids[i]); 1278 exist[i] = Check(ids[i]);
1064 } 1279 }
1065 1280
1066 return exist; 1281 return exist;
1067 } 1282 }
1068 1283
@@ -1080,7 +1295,9 @@ namespace OpenSim.Region.CoreModules.Asset
1080 1295
1081 public bool UpdateContent(string id, byte[] data) 1296 public bool UpdateContent(string id, byte[] data)
1082 { 1297 {
1083 AssetBase asset = Get(id); 1298 AssetBase asset;
1299 if (!Get(id, out asset))
1300 return false;
1084 asset.Data = data; 1301 asset.Data = data;
1085 Cache(asset); 1302 Cache(asset);
1086 return true; 1303 return true;