diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | 400 |
1 files changed, 264 insertions, 136 deletions
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs index 8e800cb..7d9c9a9 100644 --- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs +++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs | |||
@@ -31,26 +31,26 @@ | |||
31 | using System; | 31 | using System; |
32 | using System.IO; | 32 | using System.IO; |
33 | using System.Collections.Generic; | 33 | using System.Collections.Generic; |
34 | using System.Linq; | ||
34 | using System.Reflection; | 35 | using System.Reflection; |
35 | using System.Runtime.Serialization; | 36 | using System.Runtime.Serialization; |
36 | using System.Runtime.Serialization.Formatters.Binary; | 37 | using System.Runtime.Serialization.Formatters.Binary; |
37 | using System.Threading; | 38 | using System.Threading; |
38 | using System.Timers; | 39 | using System.Timers; |
39 | |||
40 | using log4net; | 40 | using log4net; |
41 | using Nini.Config; | 41 | using Nini.Config; |
42 | using Mono.Addins; | 42 | using Mono.Addins; |
43 | using OpenMetaverse; | 43 | using OpenMetaverse; |
44 | |||
45 | using OpenSim.Framework; | 44 | using OpenSim.Framework; |
46 | using OpenSim.Framework.Console; | 45 | using OpenSim.Framework.Console; |
46 | using OpenSim.Framework.Monitoring; | ||
47 | using OpenSim.Region.Framework.Interfaces; | 47 | using OpenSim.Region.Framework.Interfaces; |
48 | using OpenSim.Region.Framework.Scenes; | 48 | using OpenSim.Region.Framework.Scenes; |
49 | using OpenSim.Services.Interfaces; | 49 | using OpenSim.Services.Interfaces; |
50 | 50 | ||
51 | 51 | ||
52 | //[assembly: Addin("FlotsamAssetCache", "1.1")] | 52 | //[assembly: Addin("FlotsamAssetCache", "1.1")] |
53 | //[assembly: AddinDependency("OpenSim", "0.5")] | 53 | //[assembly: AddinDependency("OpenSim", "0.8.1")] |
54 | 54 | ||
55 | namespace OpenSim.Region.CoreModules.Asset | 55 | namespace OpenSim.Region.CoreModules.Asset |
56 | { | 56 | { |
@@ -76,8 +76,6 @@ namespace OpenSim.Region.CoreModules.Asset | |||
76 | private static ulong m_RequestsForInprogress; | 76 | private static ulong m_RequestsForInprogress; |
77 | private static ulong m_DiskHits; | 77 | private static ulong m_DiskHits; |
78 | private static ulong m_MemoryHits; | 78 | private static ulong m_MemoryHits; |
79 | private static double m_HitRateMemory; | ||
80 | private static double m_HitRateFile; | ||
81 | 79 | ||
82 | #if WAIT_ON_INPROGRESS_REQUESTS | 80 | #if WAIT_ON_INPROGRESS_REQUESTS |
83 | private Dictionary<string, ManualResetEvent> m_CurrentlyWriting = new Dictionary<string, ManualResetEvent>(); | 81 | private Dictionary<string, ManualResetEvent> m_CurrentlyWriting = new Dictionary<string, ManualResetEvent>(); |
@@ -251,23 +249,19 @@ namespace OpenSim.Region.CoreModules.Asset | |||
251 | 249 | ||
252 | private void UpdateFileCache(string key, AssetBase asset) | 250 | private void UpdateFileCache(string key, AssetBase asset) |
253 | { | 251 | { |
254 | string filename = GetFileName(asset.ID); | 252 | string filename = GetFileName(key); |
255 | 253 | ||
256 | try | 254 | try |
257 | { | 255 | { |
258 | // If the file is already cached just update access time. | 256 | // If the file is already cached, don't cache it, just touch it so access time is updated |
259 | if (File.Exists(filename)) | 257 | if (File.Exists(filename)) |
260 | { | 258 | { |
261 | lock (m_CurrentlyWriting) | 259 | UpdateFileLastAccessTime(filename); |
262 | { | 260 | } |
263 | if (!m_CurrentlyWriting.Contains(filename)) | 261 | else |
264 | File.SetLastAccessTime(filename, DateTime.Now); | ||
265 | } | ||
266 | } | ||
267 | else | ||
268 | { | 262 | { |
269 | // Once we start writing, make sure we flag that we're writing | 263 | // Once we start writing, make sure we flag that we're writing |
270 | // that object to the cache so that we don't try to write the | 264 | // that object to the cache so that we don't try to write the |
271 | // same file multiple times. | 265 | // same file multiple times. |
272 | lock (m_CurrentlyWriting) | 266 | lock (m_CurrentlyWriting) |
273 | { | 267 | { |
@@ -279,7 +273,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
279 | else | 273 | else |
280 | { | 274 | { |
281 | m_CurrentlyWriting.Add(filename, new ManualResetEvent(false)); | 275 | m_CurrentlyWriting.Add(filename, new ManualResetEvent(false)); |
282 | } | 276 | } |
283 | 277 | ||
284 | #else | 278 | #else |
285 | if (m_CurrentlyWriting.Contains(filename)) | 279 | if (m_CurrentlyWriting.Contains(filename)) |
@@ -291,10 +285,11 @@ namespace OpenSim.Region.CoreModules.Asset | |||
291 | m_CurrentlyWriting.Add(filename); | 285 | m_CurrentlyWriting.Add(filename); |
292 | } | 286 | } |
293 | #endif | 287 | #endif |
288 | |||
294 | } | 289 | } |
295 | 290 | ||
296 | Util.FireAndForget( | 291 | Util.FireAndForget( |
297 | delegate { WriteFileCache(filename, asset); }); | 292 | delegate { WriteFileCache(filename, asset); }, null, "FlotsamAssetCache.UpdateFileCache"); |
298 | } | 293 | } |
299 | } | 294 | } |
300 | catch (Exception e) | 295 | catch (Exception e) |
@@ -321,6 +316,24 @@ namespace OpenSim.Region.CoreModules.Asset | |||
321 | } | 316 | } |
322 | 317 | ||
323 | /// <summary> | 318 | /// <summary> |
319 | /// Updates the cached file with the current time. | ||
320 | /// </summary> | ||
321 | /// <param name="filename">Filename.</param> | ||
322 | /// <returns><c>true</c>, if the update was successful, false otherwise.</returns> | ||
323 | private bool UpdateFileLastAccessTime(string filename) | ||
324 | { | ||
325 | try | ||
326 | { | ||
327 | File.SetLastAccessTime(filename, DateTime.Now); | ||
328 | return true; | ||
329 | } | ||
330 | catch | ||
331 | { | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | /// <summary> | ||
324 | /// Try to get an asset from the in-memory cache. | 337 | /// Try to get an asset from the in-memory cache. |
325 | /// </summary> | 338 | /// </summary> |
326 | /// <param name="id"></param> | 339 | /// <param name="id"></param> |
@@ -335,31 +348,62 @@ namespace OpenSim.Region.CoreModules.Asset | |||
335 | return asset; | 348 | return asset; |
336 | } | 349 | } |
337 | 350 | ||
351 | private bool CheckFromMemoryCache(string id) | ||
352 | { | ||
353 | return m_MemoryCache.Contains(id); | ||
354 | } | ||
355 | |||
338 | /// <summary> | 356 | /// <summary> |
339 | /// Try to get an asset from the file cache. | 357 | /// Try to get an asset from the file cache. |
340 | /// </summary> | 358 | /// </summary> |
341 | /// <param name="id"></param> | 359 | /// <param name="id"></param> |
342 | /// <returns></returns> | 360 | /// <returns>An asset retrieved from the file cache. null if there was a problem retrieving an asset.</returns> |
343 | private AssetBase GetFromFileCache(string id) | 361 | private AssetBase GetFromFileCache(string id) |
344 | { | 362 | { |
363 | string filename = GetFileName(id); | ||
364 | |||
365 | #if WAIT_ON_INPROGRESS_REQUESTS | ||
366 | // Check if we're already downloading this asset. If so, try to wait for it to | ||
367 | // download. | ||
368 | if (m_WaitOnInprogressTimeout > 0) | ||
369 | { | ||
370 | m_RequestsForInprogress++; | ||
371 | |||
372 | ManualResetEvent waitEvent; | ||
373 | if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) | ||
374 | { | ||
375 | waitEvent.WaitOne(m_WaitOnInprogressTimeout); | ||
376 | return Get(id); | ||
377 | } | ||
378 | } | ||
379 | #else | ||
380 | // Track how often we have the problem that an asset is requested while | ||
381 | // it is still being downloaded by a previous request. | ||
382 | if (m_CurrentlyWriting.Contains(filename)) | ||
383 | { | ||
384 | m_RequestsForInprogress++; | ||
385 | return null; | ||
386 | } | ||
387 | #endif | ||
388 | |||
345 | AssetBase asset = null; | 389 | AssetBase asset = null; |
346 | 390 | ||
347 | string filename = GetFileName(id); | ||
348 | if (File.Exists(filename)) | 391 | if (File.Exists(filename)) |
349 | { | 392 | { |
350 | FileStream stream = null; | ||
351 | try | 393 | try |
352 | { | 394 | { |
353 | stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read); | 395 | using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) |
354 | BinaryFormatter bformatter = new BinaryFormatter(); | 396 | { |
397 | BinaryFormatter bformatter = new BinaryFormatter(); | ||
355 | 398 | ||
356 | asset = (AssetBase)bformatter.Deserialize(stream); | 399 | asset = (AssetBase)bformatter.Deserialize(stream); |
357 | 400 | ||
358 | m_DiskHits++; | 401 | m_DiskHits++; |
402 | } | ||
359 | } | 403 | } |
360 | catch (System.Runtime.Serialization.SerializationException e) | 404 | catch (System.Runtime.Serialization.SerializationException e) |
361 | { | 405 | { |
362 | m_log.ErrorFormat( | 406 | m_log.WarnFormat( |
363 | "[FLOTSAM ASSET CACHE]: Failed to get file {0} for asset {1}. Exception {2} {3}", | 407 | "[FLOTSAM ASSET CACHE]: Failed to get file {0} for asset {1}. Exception {2} {3}", |
364 | filename, id, e.Message, e.StackTrace); | 408 | filename, id, e.Message, e.StackTrace); |
365 | 409 | ||
@@ -371,40 +415,40 @@ namespace OpenSim.Region.CoreModules.Asset | |||
371 | } | 415 | } |
372 | catch (Exception e) | 416 | catch (Exception e) |
373 | { | 417 | { |
374 | m_log.ErrorFormat( | 418 | m_log.WarnFormat( |
375 | "[FLOTSAM ASSET CACHE]: Failed to get file {0} for asset {1}. Exception {2} {3}", | 419 | "[FLOTSAM ASSET CACHE]: Failed to get file {0} for asset {1}. Exception {2} {3}", |
376 | filename, id, e.Message, e.StackTrace); | 420 | filename, id, e.Message, e.StackTrace); |
377 | } | 421 | } |
378 | finally | ||
379 | { | ||
380 | if (stream != null) | ||
381 | stream.Close(); | ||
382 | } | ||
383 | } | 422 | } |
384 | 423 | ||
385 | #if WAIT_ON_INPROGRESS_REQUESTS | 424 | return asset; |
386 | // Check if we're already downloading this asset. If so, try to wait for it to | 425 | } |
387 | // download. | ||
388 | if (m_WaitOnInprogressTimeout > 0) | ||
389 | { | ||
390 | m_RequestsForInprogress++; | ||
391 | 426 | ||
392 | ManualResetEvent waitEvent; | 427 | private bool CheckFromFileCache(string id) |
393 | if (m_CurrentlyWriting.TryGetValue(filename, out waitEvent)) | 428 | { |
429 | bool found = false; | ||
430 | |||
431 | string filename = GetFileName(id); | ||
432 | |||
433 | if (File.Exists(filename)) | ||
434 | { | ||
435 | try | ||
394 | { | 436 | { |
395 | waitEvent.WaitOne(m_WaitOnInprogressTimeout); | 437 | using (FileStream stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) |
396 | return Get(id); | 438 | { |
439 | if (stream != null) | ||
440 | found = true; | ||
441 | } | ||
442 | } | ||
443 | catch (Exception e) | ||
444 | { | ||
445 | m_log.ErrorFormat( | ||
446 | "[FLOTSAM ASSET CACHE]: Failed to check file {0} for asset {1}. Exception {2} {3}", | ||
447 | filename, id, e.Message, e.StackTrace); | ||
397 | } | 448 | } |
398 | } | 449 | } |
399 | #else | 450 | |
400 | // Track how often we have the problem that an asset is requested while | 451 | return found; |
401 | // it is still being downloaded by a previous request. | ||
402 | if (m_CurrentlyWriting.Contains(filename)) | ||
403 | { | ||
404 | m_RequestsForInprogress++; | ||
405 | } | ||
406 | #endif | ||
407 | return asset; | ||
408 | } | 452 | } |
409 | 453 | ||
410 | public AssetBase Get(string id) | 454 | public AssetBase Get(string id) |
@@ -426,23 +470,24 @@ namespace OpenSim.Region.CoreModules.Asset | |||
426 | 470 | ||
427 | if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) | 471 | if (((m_LogLevel >= 1)) && (m_HitRateDisplay != 0) && (m_Requests % m_HitRateDisplay == 0)) |
428 | { | 472 | { |
429 | m_HitRateFile = (double)m_DiskHits / m_Requests * 100.0; | ||
430 | |||
431 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); | 473 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Get :: {0} :: {1}", id, asset == null ? "Miss" : "Hit"); |
432 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File Hit Rate {0}% for {1} requests", m_HitRateFile.ToString("0.00"), m_Requests); | ||
433 | |||
434 | if (m_MemoryCacheEnabled) | ||
435 | { | ||
436 | m_HitRateMemory = (double)m_MemoryHits / m_Requests * 100.0; | ||
437 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory Hit Rate {0}% for {1} requests", m_HitRateMemory.ToString("0.00"), m_Requests); | ||
438 | } | ||
439 | 474 | ||
440 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} unnessesary requests due to requests for assets that are currently downloading.", m_RequestsForInprogress); | 475 | GenerateCacheHitReport().ForEach(l => m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0}", l)); |
441 | } | 476 | } |
442 | 477 | ||
443 | return asset; | 478 | return asset; |
444 | } | 479 | } |
445 | 480 | ||
481 | public bool Check(string id) | ||
482 | { | ||
483 | if (m_MemoryCacheEnabled && CheckFromMemoryCache(id)) | ||
484 | return true; | ||
485 | |||
486 | if (m_FileCacheEnabled && CheckFromFileCache(id)) | ||
487 | return true; | ||
488 | return false; | ||
489 | } | ||
490 | |||
446 | public AssetBase GetCached(string id) | 491 | public AssetBase GetCached(string id) |
447 | { | 492 | { |
448 | return Get(id); | 493 | return Get(id); |
@@ -469,7 +514,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
469 | } | 514 | } |
470 | catch (Exception e) | 515 | catch (Exception e) |
471 | { | 516 | { |
472 | m_log.ErrorFormat( | 517 | m_log.WarnFormat( |
473 | "[FLOTSAM ASSET CACHE]: Failed to expire cached file {0}. Exception {1} {2}", | 518 | "[FLOTSAM ASSET CACHE]: Failed to expire cached file {0}. Exception {1} {2}", |
474 | id, e.Message, e.StackTrace); | 519 | id, e.Message, e.StackTrace); |
475 | } | 520 | } |
@@ -520,29 +565,39 @@ namespace OpenSim.Region.CoreModules.Asset | |||
520 | /// <param name="purgeLine"></param> | 565 | /// <param name="purgeLine"></param> |
521 | private void CleanExpiredFiles(string dir, DateTime purgeLine) | 566 | private void CleanExpiredFiles(string dir, DateTime purgeLine) |
522 | { | 567 | { |
523 | foreach (string file in Directory.GetFiles(dir)) | 568 | try |
524 | { | 569 | { |
525 | if (File.GetLastAccessTime(file) < purgeLine) | 570 | foreach (string file in Directory.GetFiles(dir)) |
526 | { | 571 | { |
527 | File.Delete(file); | 572 | if (File.GetLastAccessTime(file) < purgeLine) |
573 | { | ||
574 | File.Delete(file); | ||
575 | } | ||
528 | } | 576 | } |
529 | } | ||
530 | 577 | ||
531 | // Recurse into lower tiers | 578 | // Recurse into lower tiers |
532 | foreach (string subdir in Directory.GetDirectories(dir)) | 579 | foreach (string subdir in Directory.GetDirectories(dir)) |
533 | { | 580 | { |
534 | CleanExpiredFiles(subdir, purgeLine); | 581 | CleanExpiredFiles(subdir, purgeLine); |
535 | } | 582 | } |
536 | 583 | ||
537 | // Check if a tier directory is empty, if so, delete it | 584 | // Check if a tier directory is empty, if so, delete it |
538 | int dirSize = Directory.GetFiles(dir).Length + Directory.GetDirectories(dir).Length; | 585 | int dirSize = Directory.GetFiles(dir).Length + Directory.GetDirectories(dir).Length; |
539 | if (dirSize == 0) | 586 | if (dirSize == 0) |
540 | { | 587 | { |
541 | Directory.Delete(dir); | 588 | Directory.Delete(dir); |
589 | } | ||
590 | else if (dirSize >= m_CacheWarnAt) | ||
591 | { | ||
592 | m_log.WarnFormat( | ||
593 | "[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", | ||
594 | dir, dirSize); | ||
595 | } | ||
542 | } | 596 | } |
543 | else if (dirSize >= m_CacheWarnAt) | 597 | catch (Exception e) |
544 | { | 598 | { |
545 | m_log.WarnFormat("[FLOTSAM ASSET CACHE]: Cache folder exceeded CacheWarnAt limit {0} {1}. Suggest increasing tiers, tier length, or reducing cache expiration", dir, dirSize); | 599 | m_log.Warn( |
600 | string.Format("[FLOTSAM ASSET CACHE]: Could not complete clean of expired files in {0}, exception ", dir), e); | ||
546 | } | 601 | } |
547 | } | 602 | } |
548 | 603 | ||
@@ -601,7 +656,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
601 | } | 656 | } |
602 | catch (IOException e) | 657 | catch (IOException e) |
603 | { | 658 | { |
604 | m_log.ErrorFormat( | 659 | m_log.WarnFormat( |
605 | "[FLOTSAM ASSET CACHE]: Failed to write asset {0} to temporary location {1} (final {2}) on cache in {3}. Exception {4} {5}.", | 660 | "[FLOTSAM ASSET CACHE]: Failed to write asset {0} to temporary location {1} (final {2}) on cache in {3}. Exception {4} {5}.", |
606 | asset.ID, tempname, filename, directory, e.Message, e.StackTrace); | 661 | asset.ID, tempname, filename, directory, e.Message, e.StackTrace); |
607 | 662 | ||
@@ -680,17 +735,31 @@ namespace OpenSim.Region.CoreModules.Asset | |||
680 | /// <summary> | 735 | /// <summary> |
681 | /// This notes the last time the Region had a deep asset scan performed on it. | 736 | /// This notes the last time the Region had a deep asset scan performed on it. |
682 | /// </summary> | 737 | /// </summary> |
683 | /// <param name="RegionID"></param> | 738 | /// <param name="regionID"></param> |
684 | private void StampRegionStatusFile(UUID RegionID) | 739 | private void StampRegionStatusFile(UUID regionID) |
685 | { | 740 | { |
686 | string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + RegionID.ToString() + ".fac"); | 741 | string RegionCacheStatusFile = Path.Combine(m_CacheDirectory, "RegionStatus_" + regionID.ToString() + ".fac"); |
687 | if (File.Exists(RegionCacheStatusFile)) | 742 | |
743 | try | ||
688 | { | 744 | { |
689 | File.SetLastWriteTime(RegionCacheStatusFile, DateTime.Now); | 745 | if (File.Exists(RegionCacheStatusFile)) |
746 | { | ||
747 | File.SetLastWriteTime(RegionCacheStatusFile, DateTime.Now); | ||
748 | } | ||
749 | else | ||
750 | { | ||
751 | File.WriteAllText( | ||
752 | RegionCacheStatusFile, | ||
753 | "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache."); | ||
754 | } | ||
690 | } | 755 | } |
691 | else | 756 | catch (Exception e) |
692 | { | 757 | { |
693 | File.WriteAllText(RegionCacheStatusFile, "Please do not delete this file unless you are manually clearing your Flotsam Asset Cache."); | 758 | m_log.Warn( |
759 | string.Format( | ||
760 | "[FLOTSAM ASSET CACHE]: Could not stamp region status file for region {0}. Exception ", | ||
761 | regionID), | ||
762 | e); | ||
694 | } | 763 | } |
695 | } | 764 | } |
696 | 765 | ||
@@ -707,32 +776,49 @@ namespace OpenSim.Region.CoreModules.Asset | |||
707 | { | 776 | { |
708 | UuidGatherer gatherer = new UuidGatherer(m_AssetService); | 777 | UuidGatherer gatherer = new UuidGatherer(m_AssetService); |
709 | 778 | ||
710 | Dictionary<UUID, AssetType> assets = new Dictionary<UUID, AssetType>(); | 779 | Dictionary<UUID, bool> assetsFound = new Dictionary<UUID, bool>(); |
780 | |||
711 | foreach (Scene s in m_Scenes) | 781 | foreach (Scene s in m_Scenes) |
712 | { | 782 | { |
713 | StampRegionStatusFile(s.RegionInfo.RegionID); | 783 | StampRegionStatusFile(s.RegionInfo.RegionID); |
714 | 784 | ||
715 | s.ForEachSOG(delegate(SceneObjectGroup e) | 785 | s.ForEachSOG(delegate(SceneObjectGroup e) |
716 | { | 786 | { |
717 | gatherer.GatherAssetUuids(e, assets); | 787 | gatherer.AddForInspection(e); |
718 | }); | 788 | gatherer.GatherAll(); |
719 | } | ||
720 | 789 | ||
721 | foreach (UUID assetID in assets.Keys) | 790 | foreach (UUID assetID in gatherer.GatheredUuids.Keys) |
722 | { | 791 | { |
723 | string filename = GetFileName(assetID.ToString()); | 792 | if (!assetsFound.ContainsKey(assetID)) |
793 | { | ||
794 | string filename = GetFileName(assetID.ToString()); | ||
724 | 795 | ||
725 | if (File.Exists(filename)) | 796 | if (File.Exists(filename)) |
726 | { | 797 | { |
727 | File.SetLastAccessTime(filename, DateTime.Now); | 798 | UpdateFileLastAccessTime(filename); |
728 | } | 799 | } |
729 | else if (storeUncached) | 800 | else if (storeUncached) |
730 | { | 801 | { |
731 | m_AssetService.Get(assetID.ToString()); | 802 | AssetBase cachedAsset = m_AssetService.Get(assetID.ToString()); |
732 | } | 803 | if (cachedAsset == null && gatherer.GatheredUuids[assetID] != (sbyte)AssetType.Unknown) |
804 | assetsFound[assetID] = false; | ||
805 | else | ||
806 | assetsFound[assetID] = true; | ||
807 | } | ||
808 | } | ||
809 | else if (!assetsFound[assetID]) | ||
810 | { | ||
811 | m_log.DebugFormat( | ||
812 | "[FLOTSAM ASSET CACHE]: Could not find asset {0}, type {1} referenced by object {2} at {3} in scene {4} when pre-caching all scene assets", | ||
813 | assetID, gatherer.GatheredUuids[assetID], e.Name, e.AbsolutePosition, s.Name); | ||
814 | } | ||
815 | } | ||
816 | |||
817 | gatherer.GatheredUuids.Clear(); | ||
818 | }); | ||
733 | } | 819 | } |
734 | 820 | ||
735 | return assets.Keys.Count; | 821 | return assetsFound.Count; |
736 | } | 822 | } |
737 | 823 | ||
738 | /// <summary> | 824 | /// <summary> |
@@ -748,7 +834,7 @@ namespace OpenSim.Region.CoreModules.Asset | |||
748 | } | 834 | } |
749 | catch (Exception e) | 835 | catch (Exception e) |
750 | { | 836 | { |
751 | m_log.ErrorFormat( | 837 | m_log.WarnFormat( |
752 | "[FLOTSAM ASSET CACHE]: Couldn't clear asset cache directory {0} from {1}. Exception {2} {3}", | 838 | "[FLOTSAM ASSET CACHE]: Couldn't clear asset cache directory {0} from {1}. Exception {2} {3}", |
753 | dir, m_CacheDirectory, e.Message, e.StackTrace); | 839 | dir, m_CacheDirectory, e.Message, e.StackTrace); |
754 | } | 840 | } |
@@ -762,52 +848,84 @@ namespace OpenSim.Region.CoreModules.Asset | |||
762 | } | 848 | } |
763 | catch (Exception e) | 849 | catch (Exception e) |
764 | { | 850 | { |
765 | m_log.ErrorFormat( | 851 | m_log.WarnFormat( |
766 | "[FLOTSAM ASSET CACHE]: Couldn't clear asset cache file {0} from {1}. Exception {1} {2}", | 852 | "[FLOTSAM ASSET CACHE]: Couldn't clear asset cache file {0} from {1}. Exception {1} {2}", |
767 | file, m_CacheDirectory, e.Message, e.StackTrace); | 853 | file, m_CacheDirectory, e.Message, e.StackTrace); |
768 | } | 854 | } |
769 | } | 855 | } |
770 | } | 856 | } |
771 | 857 | ||
858 | private List<string> GenerateCacheHitReport() | ||
859 | { | ||
860 | List<string> outputLines = new List<string>(); | ||
861 | |||
862 | double fileHitRate = (double)m_DiskHits / m_Requests * 100.0; | ||
863 | outputLines.Add( | ||
864 | string.Format("File Hit Rate: {0}% for {1} requests", fileHitRate.ToString("0.00"), m_Requests)); | ||
865 | |||
866 | if (m_MemoryCacheEnabled) | ||
867 | { | ||
868 | double memHitRate = (double)m_MemoryHits / m_Requests * 100.0; | ||
869 | |||
870 | outputLines.Add( | ||
871 | string.Format("Memory Hit Rate: {0}% for {1} requests", memHitRate.ToString("0.00"), m_Requests)); | ||
872 | } | ||
873 | |||
874 | outputLines.Add( | ||
875 | string.Format( | ||
876 | "Unnecessary requests due to requests for assets that are currently downloading: {0}", | ||
877 | m_RequestsForInprogress)); | ||
878 | |||
879 | return outputLines; | ||
880 | } | ||
881 | |||
772 | #region Console Commands | 882 | #region Console Commands |
773 | private void HandleConsoleCommand(string module, string[] cmdparams) | 883 | private void HandleConsoleCommand(string module, string[] cmdparams) |
774 | { | 884 | { |
885 | ICommandConsole con = MainConsole.Instance; | ||
886 | |||
775 | if (cmdparams.Length >= 2) | 887 | if (cmdparams.Length >= 2) |
776 | { | 888 | { |
777 | string cmd = cmdparams[1]; | 889 | string cmd = cmdparams[1]; |
890 | |||
778 | switch (cmd) | 891 | switch (cmd) |
779 | { | 892 | { |
780 | case "status": | 893 | case "status": |
781 | if (m_MemoryCacheEnabled) | 894 | if (m_MemoryCacheEnabled) |
782 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory Cache : {0} assets", m_MemoryCache.Count); | 895 | con.OutputFormat("Memory Cache: {0} assets", m_MemoryCache.Count); |
783 | else | 896 | else |
784 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Memory cache disabled"); | 897 | con.OutputFormat("Memory cache disabled"); |
785 | 898 | ||
786 | if (m_FileCacheEnabled) | 899 | if (m_FileCacheEnabled) |
787 | { | 900 | { |
788 | int fileCount = GetFileCacheCount(m_CacheDirectory); | 901 | int fileCount = GetFileCacheCount(m_CacheDirectory); |
789 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File Cache : {0} assets", fileCount); | 902 | con.OutputFormat("File Cache: {0} assets", fileCount); |
903 | } | ||
904 | else | ||
905 | { | ||
906 | con.Output("File cache disabled"); | ||
907 | } | ||
908 | |||
909 | GenerateCacheHitReport().ForEach(l => con.Output(l)); | ||
910 | |||
911 | if (m_FileCacheEnabled) | ||
912 | { | ||
913 | con.Output("Deep scans have previously been performed on the following regions:"); | ||
790 | 914 | ||
791 | foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) | 915 | foreach (string s in Directory.GetFiles(m_CacheDirectory, "*.fac")) |
792 | { | 916 | { |
793 | m_log.Info("[FLOTSAM ASSET CACHE]: Deep scans have previously been performed on the following regions:"); | ||
794 | |||
795 | string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); | 917 | string RegionID = s.Remove(0,s.IndexOf("_")).Replace(".fac",""); |
796 | DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); | 918 | DateTime RegionDeepScanTMStamp = File.GetLastWriteTime(s); |
797 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); | 919 | con.OutputFormat("Region: {0}, {1}", RegionID, RegionDeepScanTMStamp.ToString("MM/dd/yyyy hh:mm:ss")); |
798 | } | 920 | } |
799 | } | 921 | } |
800 | else | ||
801 | { | ||
802 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache disabled"); | ||
803 | } | ||
804 | 922 | ||
805 | break; | 923 | break; |
806 | 924 | ||
807 | case "clear": | 925 | case "clear": |
808 | if (cmdparams.Length < 2) | 926 | if (cmdparams.Length < 2) |
809 | { | 927 | { |
810 | m_log.Warn("[FLOTSAM ASSET CACHE]: Usage is fcache clear [file] [memory]"); | 928 | con.Output("Usage is fcache clear [file] [memory]"); |
811 | break; | 929 | break; |
812 | } | 930 | } |
813 | 931 | ||
@@ -831,11 +949,11 @@ namespace OpenSim.Region.CoreModules.Asset | |||
831 | if (m_MemoryCacheEnabled) | 949 | if (m_MemoryCacheEnabled) |
832 | { | 950 | { |
833 | m_MemoryCache.Clear(); | 951 | m_MemoryCache.Clear(); |
834 | m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache cleared."); | 952 | con.Output("Memory cache cleared."); |
835 | } | 953 | } |
836 | else | 954 | else |
837 | { | 955 | { |
838 | m_log.Info("[FLOTSAM ASSET CACHE]: Memory cache not enabled."); | 956 | con.Output("Memory cache not enabled."); |
839 | } | 957 | } |
840 | } | 958 | } |
841 | 959 | ||
@@ -844,32 +962,31 @@ namespace OpenSim.Region.CoreModules.Asset | |||
844 | if (m_FileCacheEnabled) | 962 | if (m_FileCacheEnabled) |
845 | { | 963 | { |
846 | ClearFileCache(); | 964 | ClearFileCache(); |
847 | m_log.Info("[FLOTSAM ASSET CACHE]: File cache cleared."); | 965 | con.Output("File cache cleared."); |
848 | } | 966 | } |
849 | else | 967 | else |
850 | { | 968 | { |
851 | m_log.Info("[FLOTSAM ASSET CACHE]: File cache not enabled."); | 969 | con.Output("File cache not enabled."); |
852 | } | 970 | } |
853 | } | 971 | } |
854 | 972 | ||
855 | break; | 973 | break; |
856 | 974 | ||
857 | case "assets": | 975 | case "assets": |
858 | m_log.Info("[FLOTSAM ASSET CACHE]: Ensuring assets are cached for all scenes."); | 976 | con.Output("Ensuring assets are cached for all scenes."); |
859 | 977 | ||
860 | Util.FireAndForget(delegate { | 978 | WorkManager.RunInThread(delegate |
979 | { | ||
861 | int assetReferenceTotal = TouchAllSceneAssets(true); | 980 | int assetReferenceTotal = TouchAllSceneAssets(true); |
862 | m_log.InfoFormat( | 981 | con.OutputFormat("Completed check with {0} assets.", assetReferenceTotal); |
863 | "[FLOTSAM ASSET CACHE]: Completed check with {0} assets.", | 982 | }, null, "TouchAllSceneAssets"); |
864 | assetReferenceTotal); | ||
865 | }); | ||
866 | 983 | ||
867 | break; | 984 | break; |
868 | 985 | ||
869 | case "expire": | 986 | case "expire": |
870 | if (cmdparams.Length < 3) | 987 | if (cmdparams.Length < 3) |
871 | { | 988 | { |
872 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Invalid parameters for Expire, please specify a valid date & time", cmd); | 989 | con.OutputFormat("Invalid parameters for Expire, please specify a valid date & time", cmd); |
873 | break; | 990 | break; |
874 | } | 991 | } |
875 | 992 | ||
@@ -887,28 +1004,27 @@ namespace OpenSim.Region.CoreModules.Asset | |||
887 | 1004 | ||
888 | if (!DateTime.TryParse(s_expirationDate, out expirationDate)) | 1005 | if (!DateTime.TryParse(s_expirationDate, out expirationDate)) |
889 | { | 1006 | { |
890 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: {0} is not a valid date & time", cmd); | 1007 | con.OutputFormat("{0} is not a valid date & time", cmd); |
891 | break; | 1008 | break; |
892 | } | 1009 | } |
893 | 1010 | ||
894 | if (m_FileCacheEnabled) | 1011 | if (m_FileCacheEnabled) |
895 | CleanExpiredFiles(m_CacheDirectory, expirationDate); | 1012 | CleanExpiredFiles(m_CacheDirectory, expirationDate); |
896 | else | 1013 | else |
897 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: File cache not active, not clearing."); | 1014 | con.OutputFormat("File cache not active, not clearing."); |
898 | 1015 | ||
899 | break; | 1016 | break; |
900 | default: | 1017 | default: |
901 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Unknown command {0}", cmd); | 1018 | con.OutputFormat("Unknown command {0}", cmd); |
902 | break; | 1019 | break; |
903 | } | 1020 | } |
904 | } | 1021 | } |
905 | else if (cmdparams.Length == 1) | 1022 | else if (cmdparams.Length == 1) |
906 | { | 1023 | { |
907 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache status - Display cache status"); | 1024 | con.Output("fcache assets - Attempt a deep cache of all assets in all scenes"); |
908 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearmem - Remove all assets cached in memory"); | 1025 | con.Output("fcache expire <datetime> - Purge assets older then the specified date & time"); |
909 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache clearfile - Remove all assets cached on disk"); | 1026 | con.Output("fcache clear [file] [memory] - Remove cached assets"); |
910 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache cachescenes - Attempt a deep cache of all assets in all scenes"); | 1027 | con.Output("fcache status - Display cache status"); |
911 | m_log.InfoFormat("[FLOTSAM ASSET CACHE]: fcache <datetime> - Purge assets older then the specified date & time"); | ||
912 | } | 1028 | } |
913 | } | 1029 | } |
914 | 1030 | ||
@@ -935,6 +1051,18 @@ namespace OpenSim.Region.CoreModules.Asset | |||
935 | return true; | 1051 | return true; |
936 | } | 1052 | } |
937 | 1053 | ||
1054 | public bool[] AssetsExist(string[] ids) | ||
1055 | { | ||
1056 | bool[] exist = new bool[ids.Length]; | ||
1057 | |||
1058 | for (int i = 0; i < ids.Length; i++) | ||
1059 | { | ||
1060 | exist[i] = Check(ids[i]); | ||
1061 | } | ||
1062 | |||
1063 | return exist; | ||
1064 | } | ||
1065 | |||
938 | public string Store(AssetBase asset) | 1066 | public string Store(AssetBase asset) |
939 | { | 1067 | { |
940 | if (asset.FullID == UUID.Zero) | 1068 | if (asset.FullID == UUID.Zero) |