aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs')
-rw-r--r--OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs337
1 files changed, 201 insertions, 136 deletions
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
index 48ee277..2b3f7f5 100644
--- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
@@ -86,6 +86,8 @@ namespace Flotsam.RegionModules.AssetCache
86 private List<string> m_CurrentlyWriting = new List<string>(); 86 private List<string> m_CurrentlyWriting = new List<string>();
87#endif 87#endif
88 88
89 private bool m_FileCacheEnabled = true;
90
89 private ExpiringCache<string, AssetBase> m_MemoryCache; 91 private ExpiringCache<string, AssetBase> m_MemoryCache;
90 private bool m_MemoryCacheEnabled = false; 92 private bool m_MemoryCacheEnabled = false;
91 93
@@ -146,6 +148,7 @@ namespace Flotsam.RegionModules.AssetCache
146 } 148 }
147 else 149 else
148 { 150 {
151 m_FileCacheEnabled = assetConfig.GetBoolean("FileCacheEnabled", m_FileCacheEnabled);
149 m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory); 152 m_CacheDirectory = assetConfig.GetString("CacheDirectory", m_DefaultCacheDirectory);
150 153
151 m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled); 154 m_MemoryCacheEnabled = assetConfig.GetBoolean("MemoryCacheEnabled", m_MemoryCacheEnabled);
@@ -173,7 +176,7 @@ namespace Flotsam.RegionModules.AssetCache
173 176
174 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory); 177 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
175 178
176 if ((m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero)) 179 if (m_FileCacheEnabled && (m_FileExpiration > TimeSpan.Zero) && (m_FileExpirationCleanupTimer > TimeSpan.Zero))
177 { 180 {
178 m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds); 181 m_CacheCleanTimer = new System.Timers.Timer(m_FileExpirationCleanupTimer.TotalMilliseconds);
179 m_CacheCleanTimer.AutoReset = true; 182 m_CacheCleanTimer.AutoReset = true;
@@ -226,7 +229,6 @@ namespace Flotsam.RegionModules.AssetCache
226 if (m_AssetService == null) 229 if (m_AssetService == null)
227 { 230 {
228 m_AssetService = scene.RequestModuleInterface<IAssetService>(); 231 m_AssetService = scene.RequestModuleInterface<IAssetService>();
229
230 } 232 }
231 } 233 }
232 } 234 }
@@ -250,138 +252,171 @@ namespace Flotsam.RegionModules.AssetCache
250 252
251 private void UpdateMemoryCache(string key, AssetBase asset) 253 private void UpdateMemoryCache(string key, AssetBase asset)
252 { 254 {
253 if (m_MemoryCacheEnabled) 255 m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration);
254 m_MemoryCache.AddOrUpdate(key, asset, m_MemoryExpiration);
255 } 256 }
256 257
257 public void Cache(AssetBase asset) 258 private void UpdateFileCache(string key, AssetBase asset)
258 { 259 {
259 // TODO: Spawn this off to some seperate thread to do the actual writing 260 string filename = GetFileName(asset.ID);
260 if (asset != null)
261 {
262 UpdateMemoryCache(asset.ID, asset);
263
264 string filename = GetFileName(asset.ID);
265 261
266 try 262 try
263 {
264 // If the file is already cached, don't cache it, just touch it so access time is updated
265 if (File.Exists(filename))
267 { 266 {
268 // If the file is already cached, don't cache it, just touch it so access time is updated 267 File.SetLastAccessTime(filename, DateTime.Now);
269 if (File.Exists(filename)) 268 }
269 else
270 {
271 // Once we start writing, make sure we flag that we're writing
272 // that object to the cache so that we don't try to write the
273 // same file multiple times.
274 lock (m_CurrentlyWriting)
270 { 275 {
271 File.SetLastAccessTime(filename, DateTime.Now);
272 } else {
273
274 // Once we start writing, make sure we flag that we're writing
275 // that object to the cache so that we don't try to write the
276 // same file multiple times.
277 lock (m_CurrentlyWriting)
278 {
279#if WAIT_ON_INPROGRESS_REQUESTS 276#if WAIT_ON_INPROGRESS_REQUESTS
280 if (m_CurrentlyWriting.ContainsKey(filename)) 277 if (m_CurrentlyWriting.ContainsKey(filename))
281 { 278 {
282 return; 279 return;
283 } 280 }
284 else 281 else
285 { 282 {
286 m_CurrentlyWriting.Add(filename, new ManualResetEvent(false)); 283 m_CurrentlyWriting.Add(filename, new ManualResetEvent(false));
287 } 284 }
288 285
289#else 286#else
290 if (m_CurrentlyWriting.Contains(filename)) 287 if (m_CurrentlyWriting.Contains(filename))
291 { 288 {
292 return; 289 return;
293 }
294 else
295 {
296 m_CurrentlyWriting.Add(filename);
297 }
298#endif
299
300 } 290 }
301 291 else
302 Util.FireAndForget( 292 {
303 delegate { WriteFileCache(filename, asset); }); 293 m_CurrentlyWriting.Add(filename);
294 }
295#endif
304 } 296 }
305 } 297
306 catch (Exception e) 298 Util.FireAndForget(
307 { 299 delegate { WriteFileCache(filename, asset); });
308 LogException(e);
309 } 300 }
310 } 301 }
302 catch (Exception e)
303 {
304 LogException(e);
305 }
311 } 306 }
312 307
313 public AssetBase Get(string id) 308 public void Cache(AssetBase asset)
314 { 309 {
315 m_Requests++; 310 // TODO: Spawn this off to some seperate thread to do the actual writing
311 if (asset != null)
312 {
313 //m_log.DebugFormat("[FLOTSAM ASSET CACHE]: Caching asset with id {0}", asset.ID);
314
315 if (m_MemoryCacheEnabled)
316 UpdateMemoryCache(asset.ID, asset);
317
318 if (m_FileCacheEnabled)
319 UpdateFileCache(asset.ID, asset);
320 }
321 }
316 322
323 /// <summary>
324 /// Try to get an asset from the in-memory cache.
325 /// </summary>
326 /// <param name="id"></param>
327 /// <returns></returns>
328 private AssetBase GetFromMemoryCache(string id)
329 {
317 AssetBase asset = null; 330 AssetBase asset = null;
318 331
319 if (m_MemoryCacheEnabled && m_MemoryCache.TryGetValue(id, out asset)) 332 if (m_MemoryCache.TryGetValue(id, out asset))
320 {
321 m_MemoryHits++; 333 m_MemoryHits++;
322 } 334
323 else 335 return asset;
336 }
337
338 /// <summary>
339 /// Try to get an asset from the file cache.
340 /// </summary>
341 /// <param name="id"></param>
342 /// <returns></returns>
343 private AssetBase GetFromFileCache(string id)
344 {
345 AssetBase asset = null;
346
347 string filename = GetFileName(id);
348 if (File.Exists(filename))
324 { 349 {
325 string filename = GetFileName(id); 350 FileStream stream = null;
326 if (File.Exists(filename)) 351 try
327 { 352 {
328 FileStream stream = null; 353 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
329 try 354 BinaryFormatter bformatter = new BinaryFormatter();
330 {
331 stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read);
332 BinaryFormatter bformatter = new BinaryFormatter();
333 355
334 asset = (AssetBase)bformatter.Deserialize(stream); 356 asset = (AssetBase)bformatter.Deserialize(stream);
335 357
336 UpdateMemoryCache(id, asset); 358 UpdateMemoryCache(id, asset);
337 359
338 m_DiskHits++; 360 m_DiskHits++;
339 } 361 }
340 catch (System.Runtime.Serialization.SerializationException e) 362 catch (System.Runtime.Serialization.SerializationException e)
341 { 363 {
342 LogException(e); 364 LogException(e);
343 365
344 // If there was a problem deserializing the asset, the asset may 366 // If there was a problem deserializing the asset, the asset may
345 // either be corrupted OR was serialized under an old format 367 // either be corrupted OR was serialized under an old format
346 // {different version of AssetBase} -- we should attempt to 368 // {different version of AssetBase} -- we should attempt to
347 // delete it and re-cache 369 // delete it and re-cache
348 File.Delete(filename); 370 File.Delete(filename);
349 } 371 }
350 catch (Exception e) 372 catch (Exception e)
351 { 373 {
352 LogException(e); 374 LogException(e);
353 } 375 }
354 finally 376 finally
355 { 377 {
356 if (stream != null) 378 if (stream != null)
357 stream.Close(); 379 stream.Close();
358 }
359 } 380 }
381 }
360 382
361 383
362#if WAIT_ON_INPROGRESS_REQUESTS 384#if WAIT_ON_INPROGRESS_REQUESTS
363 // Check if we're already downloading this asset. If so, try to wait for it to 385 // Check if we're already downloading this asset. If so, try to wait for it to
364 // download. 386 // download.
365 if (m_WaitOnInprogressTimeout > 0) 387 if (m_WaitOnInprogressTimeout > 0)
366 { 388 {
367 m_RequestsForInprogress++; 389 m_RequestsForInprogress++;
368 390
369 ManualResetEvent waitEvent; 391 ManualResetEvent waitEvent;
370 if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) 392 if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent))
371 {
372 waitEvent.WaitOne(m_WaitOnInprogressTimeout);
373 return Get(id);
374 }
375 }
376#else
377 // Track how often we have the problem that an asset is requested while
378 // it is still being downloaded by a previous request.
379 if (m_CurrentlyWriting.Contains(filename))
380 { 393 {
381 m_RequestsForInprogress++; 394 waitEvent.WaitOne(m_WaitOnInprogressTimeout);
395 return Get(id);
382 } 396 }
383#endif
384 } 397 }
398#else
399 // Track how often we have the problem that an asset is requested while
400 // it is still being downloaded by a previous request.
401 if (m_CurrentlyWriting.Contains(filename))
402 {
403 m_RequestsForInprogress++;
404 }
405#endif
406
407 return asset;
408 }
409
410 public AssetBase Get(string id)
411 {
412 m_Requests++;
413
414 AssetBase asset = null;
415
416 if (m_MemoryCacheEnabled)
417 asset = GetFromMemoryCache(id);
418 else if (m_FileCacheEnabled)
419 asset = GetFromFileCache(id);
385 420
386 if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) 421 if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0))
387 { 422 {
@@ -415,10 +450,13 @@ namespace Flotsam.RegionModules.AssetCache
415 450
416 try 451 try
417 { 452 {
418 string filename = GetFileName(id); 453 if (m_FileCacheEnabled)
419 if (File.Exists(filename))
420 { 454 {
421 File.Delete(filename); 455 string filename = GetFileName(id);
456 if (File.Exists(filename))
457 {
458 File.Delete(filename);
459 }
422 } 460 }
423 461
424 if (m_MemoryCacheEnabled) 462 if (m_MemoryCacheEnabled)
@@ -433,11 +471,14 @@ namespace Flotsam.RegionModules.AssetCache
433 public void Clear() 471 public void Clear()
434 { 472 {
435 if (m_LogLevel >= 2) 473 if (m_LogLevel >= 2)
436 m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing Cache."); 474 m_log.Debug("[FLOTSAM ASSET CACHE]: Clearing caches.");
437 475
438 foreach (string dir in Directory.GetDirectories(m_CacheDirectory)) 476 if (m_FileCacheEnabled)
439 { 477 {
440 Directory.Delete(dir); 478 foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
479 {
480 Directory.Delete(dir);
481 }
441 } 482 }
442 483
443 if (m_MemoryCacheEnabled) 484 if (m_MemoryCacheEnabled)
@@ -472,9 +513,9 @@ namespace Flotsam.RegionModules.AssetCache
472 /// removes empty tier directories. 513 /// removes empty tier directories.
473 /// </summary> 514 /// </summary>
474 /// <param name="dir"></param> 515 /// <param name="dir"></param>
516 /// <param name="purgeLine"></param>
475 private void CleanExpiredFiles(string dir, DateTime purgeLine) 517 private void CleanExpiredFiles(string dir, DateTime purgeLine)
476 { 518 {
477
478 foreach (string file in Directory.GetFiles(dir)) 519 foreach (string file in Directory.GetFiles(dir))
479 { 520 {
480 if (File.GetLastAccessTime(file) < purgeLine) 521 if (File.GetLastAccessTime(file) < purgeLine)
@@ -712,18 +753,28 @@ namespace Flotsam.RegionModules.AssetCache
712 switch (cmd) 753 switch (cmd)
713 { 754 {
714 case "status": 755 case "status":
715 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Memory Cache : {0} assets", m_MemoryCache.Count); 756 if (m_MemoryCacheEnabled)
716 757 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory Cache : {0} assets", m_MemoryCache.Count);
717 int fileCount = GetFileCacheCount(m_CacheDirectory); 758 else
718 m_log.InfoFormat("[FLOTSAM ASSET CACHE] File Cache : {0} assets", fileCount); 759 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory cache disabled");
719 760
720 foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) 761 if (m_FileCacheEnabled)
721 { 762 {
722 m_log.Info("[FLOTSAM ASSET CACHE] Deep Scans were performed on the following regions:"); 763 int fileCount = GetFileCacheCount(m_CacheDirectory);
723 764 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File Cache : {0} assets", fileCount);
724 string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); 765
725 DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); 766 foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac"))
726 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); 767 {
768 m_log.Info("[FLOTSAM ASSET CACHE]: Deep Scans were performed on the following regions:");
769
770 string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac","");
771 DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s);
772 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss"));
773 }
774 }
775 else
776 {
777 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache disabled");
727 } 778 }
728 779
729 break; 780 break;
@@ -731,7 +782,7 @@ namespace Flotsam.RegionModules.AssetCache
731 case "clear": 782 case "clear":
732 if (cmdparams.Length < 2) 783 if (cmdparams.Length < 2)
733 { 784 {
734 m_log.Warn("[FLOTSAM ASSET CACHE] Usage is fcache clear [file] [memory]"); 785 m_log.Warn("[FLOTSAM ASSET CACHE]: Usage is fcache clear [file] [memory]");
735 break; 786 break;
736 } 787 }
737 788
@@ -752,36 +803,48 @@ namespace Flotsam.RegionModules.AssetCache
752 803
753 if (clearMemory) 804 if (clearMemory)
754 { 805 {
755 m_MemoryCache.Clear(); 806 if (m_MemoryCacheEnabled)
756 m_log.Info("[FLOTSAM ASSET CACHE] Memory cache cleared."); 807 {
808 m_MemoryCache.Clear();
809 m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache cleared.");
810 }
811 else
812 {
813 m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache not enabled.");
814 }
757 } 815 }
758 816
759 if (clearFile) 817 if (clearFile)
760 { 818 {
761 ClearFileCache(); 819 if (m_FileCacheEnabled)
762 m_log.Info("[FLOTSAM ASSET CACHE] File cache cleared."); 820 {
821 ClearFileCache();
822 m_log.Info("[FLOTSAM ASSET CACHE]: File cache cleared.");
823 }
824 else
825 {
826 m_log.Info("[FLOTSAM ASSET CACHE]: File cache not enabled.");
827 }
763 } 828 }
764 829
765 break; 830 break;
766 831
767 832
768 case "assets": 833 case "assets":
769 m_log.Info("[FLOTSAM ASSET CACHE] Caching all assets, in all scenes."); 834 m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes.");
770 835
771 Util.FireAndForget(delegate { 836 Util.FireAndForget(delegate {
772 int assetsCached = CacheScenes(); 837 int assetsCached = CacheScenes();
773 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Completed Scene Caching, {0} assets found.", assetsCached); 838 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached);
774 839
775 }); 840 });
776 841
777 break; 842 break;
778 843
779 case "expire": 844 case "expire":
780
781
782 if (cmdparams.Length < 3) 845 if (cmdparams.Length < 3)
783 { 846 {
784 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Invalid parameters for Expire, please specify a valid date & time", cmd); 847 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Invalid parameters for Expire, please specify a valid date & time", cmd);
785 break; 848 break;
786 } 849 }
787 850
@@ -799,26 +862,28 @@ namespace Flotsam.RegionModules.AssetCache
799 862
800 if (!DateTime.TryParse(s_expirationDate, out expirationDate)) 863 if (!DateTime.TryParse(s_expirationDate, out expirationDate))
801 { 864 {
802 m_log.InfoFormat("[FLOTSAM ASSET CACHE] {0} is not a valid date & time", cmd); 865 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} is not a valid date & time", cmd);
803 break; 866 break;
804 } 867 }
805 868
806 CleanExpiredFiles(m_CacheDirectory, expirationDate); 869 if (m_FileCacheEnabled)
870 CleanExpiredFiles(m_CacheDirectory, expirationDate);
871 else
872 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache not active, not clearing.");
807 873
808 break; 874 break;
809 default: 875 default:
810 m_log.InfoFormat("[FLOTSAM ASSET CACHE] Unknown command {0}", cmd); 876 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Unknown command {0}", cmd);
811 break; 877 break;
812 } 878 }
813 } 879 }
814 else if (cmdparams.Length == 1) 880 else if (cmdparams.Length == 1)
815 { 881 {
816 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache status - Display cache status"); 882 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache status - Display cache status");
817 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearmem - Remove all assets cached in memory"); 883 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearmem - Remove all assets cached in memory");
818 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache clearfile - Remove all assets cached on disk"); 884 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearfile - Remove all assets cached on disk");
819 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache cachescenes - Attempt a deep cache of all assets in all scenes"); 885 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache cachescenes - Attempt a deep cache of all assets in all scenes");
820 m_log.InfoFormat("[FLOTSAM ASSET CACHE] flotsamcache <datetime> - Purge assets older then the specified date & time"); 886 m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache <datetime> - Purge assets older then the specified date & time");
821
822 } 887 }
823 } 888 }
824 889