/// <summary> /// Gets the file size of a given file /// </summary> /// <param name="root">The root folder of the cache</param> /// <param name="id">The Id of the file to get the size of</param> /// <param name="hash">The has of the file</param> /// <returns>The file size in bytes</returns> public static ulong GetFileSizeBytes(string root, Guid id, string hash) { // TODO: Replace all string.Format with Path.Join FileInfo info = new FileInfo(CacheFile.GetFullFilePath(root, id, hash)); return((ulong)info.Length); }
/// <summary> /// Walks through all assets in the file system and evicts any items that occur in the past /// to take the cache file system below the requested limit. /// This should only be called in a background thread since this is a time intensive operation. /// </summary> /// <param name="state">Ignored, object state</param> private void Evict(object state) { ulong cacheLimitBytes = (ulong)Settings.Default.MaxCacheSizeMB * 1048576; if (this.cacheSizeBytes < cacheLimitBytes) { // Cache isn't big enough to evict logger.Info( "Cache isn't large enough to require eviction. Max: {0} MB, Current: {1} MB", Settings.Default.MaxCacheSizeMB, this.cacheSizeBytes / 1048576); return; } logger.Warn( "Cache eviction is starting to prune. Max: {0} MB, Current: {1} MB", Settings.Default.MaxCacheSizeMB, this.cacheSizeBytes / 1048576); List <CacheFile> fileData = this.EnumerateAllCacheFiles(); // Run the list of elements and delete the files that were accessed the furthest in the past var filesSorted = from a in fileData orderby a.LastAccessed ascending select a; IEnumerator <CacheFile> fileEnumerator = filesSorted.GetEnumerator(); fileEnumerator.MoveNext(); // Delete files until we are within 90% of the limit while (this.cacheSizeBytes > (cacheLimitBytes * Settings.Default.CacheFreePercentage)) { CacheFile file = fileEnumerator.Current; lock (this.cacheSizeBytesLock) { this.cacheSizeBytes -= (ulong)file.Length; } logger.Warn( "Deleting last accessed {1} file {0}", file.LastAccessed, CacheFile.GetFileName(file.Id, file.Hash)); File.Delete(CacheFile.GetFullFilePath(this.root, file.Id, file.Hash)); if (!fileEnumerator.MoveNext()) { // There are no more files to clean break; } } logger.Warn( "Cache eviction is complete. Max: {0} MB, Current: {1} MB", Settings.Default.MaxCacheSizeMB, this.cacheSizeBytes / 1048576); this.evictingCache = false; }
/// <summary> /// Loads the details of the files /// </summary> /// <param name="root">The root folder of the cache</param> /// <param name="id">The id of the file</param> /// <param name="hash">The hash of the file</param> public void Load(string root, Guid id, string hash) { string path = CacheFile.GetFullFilePath(root, id, hash); FileInfo info = new FileInfo(path); this.Id = id; this.Hash = hash; this.LastAccessed = File.GetLastAccessTime(path); this.Length = (int)info.Length; }
/// <summary> /// Moves the file from the incoming temporary folder and moves it to permanent storage. /// The file is automatically marked as recently accessed in order to prevent it from being evicted. /// </summary> /// <param name="id">The Id of the file</param> /// <param name="hash">The hash of the file</param> public void CompleteFile(Guid id, string hash) { string fileName = CacheFile.GetFileName(id, hash); string src = Path.Combine(this.incoming, fileName); string dest = CacheFile.GetFullFilePath(this.root, id, hash); if (!Directory.Exists(Path.GetDirectoryName(dest))) { Directory.CreateDirectory(Path.GetDirectoryName(dest)); } // For some reason the cache server is asking to overwrite the file, if (CacheFile.IsFileCached(this.root, id, hash)) { File.Delete(CacheFile.GetFullFilePath(this.root, id, hash)); } File.Move(src, dest); File.SetLastAccessTime(dest, DateTime.Now); FileInfo info = new FileInfo(dest); lock (this.cacheSizeBytesLock) { // Increment the cache size by adding a file this.cacheSizeBytes += (ulong)info.Length; int limit = Settings.Default.MaxCacheSizeMB * 1048576; // Check we haven't exceeded the cap if (this.cacheSizeBytes > (ulong)limit && !this.evictingCache) { // We've exceeded the cache cap, request a cleanup this.evictingCache = true; ThreadPool.QueueUserWorkItem(new WaitCallback(this.Evict)); } } // Store a hit on the ojbect File.SetLastAccessTime(dest, DateTime.Now); logger.Info(CultureInfo.CurrentCulture, "Moving {0} to permanent cache", fileName); }
/// <summary> /// Determines if the given file is cached or not /// </summary> /// <param name="root">The root folder of the cache</param> /// <param name="id">The Id of the file</param> /// <param name="hash">The hash of the file</param> /// <returns>True if the file is cached, false otherwise</returns> public static bool IsFileCached(string root, Guid id, string hash) { return(File.Exists(CacheFile.GetFullFilePath(root, id, hash))); }