public void TestBasicOperations() { DefaultDiskStorage storage = GetStorageSupplier(1).Get(); string resourceId1 = "R1"; string resourceId2 = "R2"; // No file - get should fail IBinaryResource resource1 = storage.GetResource(resourceId1, null); Assert.IsNull(resource1); // Write out the file now byte[] key1Contents = new byte[] { 0, 1, 2 }; WriteToStorage(storage, resourceId1, key1Contents); // Get should succeed now resource1 = storage.GetResource(resourceId1, null); Assert.IsNotNull(resource1); FileInfo underlyingFile = ((FileBinaryResource)resource1).File; CollectionAssert.AreEqual(key1Contents, Files.ToByteArray(underlyingFile)); // Remove the file now - get should fail again underlyingFile.Delete(); resource1 = storage.GetResource(resourceId1, null); Assert.IsNull(resource1); // No file IBinaryResource resource2 = storage.GetResource(resourceId2, null); Assert.IsNull(resource2); }
public void TestVersioning() { // Define data that will be written to cache ICacheKey key = new SimpleCacheKey("version_test"); byte[] value = new byte[32]; value[0] = 118; // 'v' // Set up cache with version == 1 IDiskStorage storage1 = CreateDiskStorage(TESTCACHE_CURRENT_VERSION); DiskStorageCache cache1 = CreateDiskCache(storage1, false); // Write test data to cache 1 cache1.Insert(key, WriterCallbacks.From(value)); // Get cached file IBinaryResource resource1 = GetResource(storage1, key); Assert.IsNotNull(resource1); // Set up cache with version == 2 IDiskStorage storageSupplier2 = CreateDiskStorage(TESTCACHE_NEXT_VERSION); DiskStorageCache cache2 = CreateDiskCache(storageSupplier2, false); // Write test data to cache 2 cache2.Insert(key, WriterCallbacks.From(value)); // Get cached file IBinaryResource resource2 = GetResource(storageSupplier2, key); Assert.IsNotNull(resource2); // Make sure filenames of the two file are different Assert.IsFalse(resource2.Equals(resource1)); }
private byte[] GetContents(IBinaryResource resource) { using (Stream file = resource.OpenStream()) { byte[] contents = ByteStreams.ToByteArray(file); return(contents); } }
public void TestCacheFileWithIOException() { ICacheKey key1 = new SimpleCacheKey("aaa"); // Before inserting, make sure files not exist. IBinaryResource resource1 = GetResource(key1); Assert.IsNull(resource1); // 1. Should not create cache files if IOException happens in the middle. IOException writeException = new IOException(); try { _cache.Insert(key1, new WriterCallbackImpl(os => { throw writeException; })); Assert.Fail(); } catch (IOException) { Assert.IsNull(GetResource(key1)); } VerifyListenerOnWriteAttempt(key1); VerifyListenerOnWriteException(key1, writeException); // 2. Test a read failure from DiskStorage ICacheKey key2 = new SimpleCacheKey("bbb"); int value2Size = 42; byte[] value2 = new byte[value2Size]; value2[25] = 98; // 'b' _cache.Insert(key2, WriterCallbacks.From(value2)); VerifyListenerOnWriteAttempt(key2); string resourceId2 = VerifyListenerOnWriteSuccessAndGetResourceId(key2, value2Size); ((DiskStorageWithReadFailures)_storage).SetPoisonResourceId(resourceId2); Assert.IsNull(_cache.GetResource(key2)); VerifyListenerOnReadException(key2, new IOException("Poisoned resource requested")); Assert.IsFalse(_cache.Probe(key2)); VerifyListenerOnReadException(key2, new IOException("Poisoned resource requested")); DuplicatingCacheEventListener listener = (DuplicatingCacheEventListener)_cacheEventListener; Assert.IsTrue(listener.GetEvents("OnCleared").Count == 0); Assert.IsTrue(listener.GetEvents("OnHit").Count == 0); Assert.IsTrue(listener.GetEvents("OnMiss").Count == 0); Assert.IsTrue(listener.GetEvents("OnEviction").Count == 0); }
/// <summary> /// Inserts resource into file with key. /// </summary> /// <param name="key">Cache key.</param> /// <param name="callback"> /// Callback that writes to an output stream. /// </param> /// <returns>A sequence of bytes.</returns> public IBinaryResource Insert(ICacheKey key, IWriterCallback callback) { // Write to a temp file, then move it into place. // This allows more parallelism when writing files. SettableCacheEvent cacheEvent = SettableCacheEvent.Obtain().SetCacheKey(key); _cacheEventListener.OnWriteAttempt(cacheEvent); string resourceId; lock (_lock) { // For multiple resource ids associated with the same image, // we only write one file resourceId = CacheKeyUtil.GetFirstResourceId(key); } cacheEvent.SetResourceId(resourceId); try { // Getting the file is synchronized IInserter inserter = StartInsert(resourceId, key); try { inserter.WriteData(callback, key); // Committing the file is synchronized IBinaryResource resource = EndInsert(inserter, key, resourceId); cacheEvent.SetItemSize(resource.GetSize()) .SetCacheSize(_cacheStats.Size); _cacheEventListener.OnWriteSuccess(cacheEvent); return(resource); } finally { if (!inserter.CleanUp()) { Debug.WriteLine("Failed to delete temp file"); } } } catch (IOException ioe) { cacheEvent.SetException(ioe); _cacheEventListener.OnWriteException(cacheEvent); Debug.WriteLine("Failed inserting a file into the cache"); throw; } finally { cacheEvent.Recycle(); } }
/// <summary> /// Retrieves the file corresponding to the key, if it is in the cache. /// Also touches the item, thus changing its LRU timestamp. If the file /// is not present in the file cache, returns null. /// <para /> /// This should NOT be called on the UI thread. /// </summary> /// <param name="key">The key to check.</param> /// <returns> /// The resource if present in cache, otherwise null. /// </returns> public IBinaryResource GetResource(ICacheKey key) { SettableCacheEvent cacheEvent = SettableCacheEvent .Obtain() .SetCacheKey(key); try { lock (_lock) { IBinaryResource resource = null; IList <string> resourceIds = CacheKeyUtil.GetResourceIds(key); string resourceId = default(string); foreach (var entry in resourceIds) { resourceId = entry; cacheEvent.SetResourceId(resourceId); resource = _storage.GetResource(resourceId, key); if (resource != null) { break; } } if (resource == null) { _cacheEventListener.OnMiss(cacheEvent); _resourceIndex.Remove(resourceId); } else { _cacheEventListener.OnHit(cacheEvent); _resourceIndex.Add(resourceId); } return(resource); } } catch (IOException ioe) { _cacheErrorLogger.LogError( CacheErrorCategory.GENERIC_IO, typeof(DiskStorageCache), "GetResource"); cacheEvent.SetException(ioe); _cacheEventListener.OnReadException(cacheEvent); return(null); } finally { cacheEvent.Recycle(); } }
/// <summary> /// Commits the provided temp file to the cache, renaming it to match /// the cache's hashing convention. /// </summary> private IBinaryResource EndInsert( IInserter inserter, ICacheKey key, string resourceId) { lock (_lock) { IBinaryResource resource = inserter.Commit(key); _resourceIndex.Add(resourceId); _cacheStats.Increment(resource.GetSize(), 1); return(resource); } }
public void TestCacheEventListener() { // 1. Add first cache file ICacheKey key1 = new SimpleCacheKey("foo"); int value1Size = 101; byte[] value1 = new byte[value1Size]; value1[80] = 99; // 'c', just so it's not all zeros for the equality test below. IBinaryResource resource1 = _cache.Insert(key1, WriterCallbacks.From(value1)); VerifyListenerOnWriteAttempt(key1); string resourceId1 = VerifyListenerOnWriteSuccessAndGetResourceId(key1, value1Size); IBinaryResource resource1Again = _cache.GetResource(key1); Assert.AreEqual(((FileBinaryResource)resource1).File.FullName, ((FileBinaryResource)resource1Again).File.FullName); VerifyListenerOnHit(key1, resourceId1); IBinaryResource resource1Again2 = _cache.GetResource(key1); Assert.AreEqual(((FileBinaryResource)resource1).File.FullName, ((FileBinaryResource)resource1Again2).File.FullName); VerifyListenerOnHit(key1, resourceId1); SimpleCacheKey missingKey = new SimpleCacheKey("nonexistent_key"); IBinaryResource res2 = _cache.GetResource(missingKey); Assert.IsNull(res2); VerifyListenerOnMiss(missingKey); DuplicatingCacheEventListener listener = (DuplicatingCacheEventListener)_cacheEventListener; listener.Clear(); _cache.ClearAll(); Assert.IsTrue(listener.GetEvents("OnCleared").Count != 0); Assert.IsTrue(listener.GetEvents("OnHit").Count == 0); Assert.IsTrue(listener.GetEvents("OnMiss").Count == 0); Assert.IsTrue(listener.GetEvents("OnWriteAttempt").Count == 0); Assert.IsTrue(listener.GetEvents("OnWriteSuccess").Count == 0); Assert.IsTrue(listener.GetEvents("OnReadException").Count == 0); Assert.IsTrue(listener.GetEvents("OnWriteException").Count == 0); Assert.IsTrue(listener.GetEvents("OnEviction").Count == 0); }
public void TestEntryIds() { DefaultDiskStorage storage = GetStorageSupplier(1).Get(); byte[] value1 = new byte[101]; byte[] value2 = new byte[102]; byte[] value3 = new byte[103]; value1[80] = 123; value2[80] = 45; value3[80] = 67; WriteFileToStorage(storage, "resourceId1", value1); WriteFileToStorage(storage, "resourceId2", value2); WriteFileToStorage(storage, "resourceId3", value3); // Check that resources are retrieved by the right name, before testing getEntries IBinaryResource res1 = storage.GetResource("resourceId1", null); IBinaryResource res2 = storage.GetResource("resourceId2", null); IBinaryResource res3 = storage.GetResource("resourceId3", null); CollectionAssert.AreEqual(value1, res1.Read()); CollectionAssert.AreEqual(value2, res2.Read()); CollectionAssert.AreEqual(value3, res3.Read()); // Obtain entries and sort by name List <IEntry> entries = new List <IEntry>(storage.GetEntries()); entries.Sort((a, b) => { return(a.Id.CompareTo(b.Id)); }); Assert.AreEqual(3, entries.Count); Assert.AreEqual("resourceId1", entries[0].Id); Assert.AreEqual("resourceId2", entries[1].Id); Assert.AreEqual("resourceId3", entries[2].Id); CollectionAssert.AreEqual(value1, entries[0].Resource.Read()); CollectionAssert.AreEqual(value2, entries[1].Resource.Read()); CollectionAssert.AreEqual(value3, entries[2].Resource.Read()); }
/// <summary> /// Performs disk cache read. In case of any exception null is returned. /// </summary> private IPooledByteBuffer ReadFromDiskCache(ICacheKey key) { try { Debug.WriteLine($"Disk cache read for { key.ToString() }"); IBinaryResource diskCacheResource = _fileCache.GetResource(key); if (diskCacheResource == null) { Debug.WriteLine($"Disk cache miss for { key.ToString() }"); _imageCacheStatsTracker.OnDiskCacheMiss(); return(null); } else { Debug.WriteLine($"Found entry in disk cache for { key.ToString() }"); _imageCacheStatsTracker.OnDiskCacheHit(); } IPooledByteBuffer byteBuffer; using (Stream inputStream = diskCacheResource.OpenStream()) { byteBuffer = _pooledByteBufferFactory.NewByteBuffer( inputStream, (int)diskCacheResource.GetSize()); } Debug.WriteLine($"Successful read from disk cache for { key.ToString() }"); return(byteBuffer); } catch (Exception) { // TODO: 3697790 log failures // TODO: 5258772 - uncomment line below // _fileCache.Remove(key); Debug.WriteLine($"Exception reading from cache for { key.ToString() }"); _imageCacheStatsTracker.OnDiskCacheGetFail(); throw; } }
/// <summary> /// Gets the file cache path. /// </summary> /// <param name="uri">The image uri.</param> public Task <FileInfo> GetFileCachePath(Uri uri) { ICacheKey cacheKey = _cacheKeyFactory.GetEncodedCacheKey( ImageRequest.FromUri(uri), null); Task writeTask = _mainBufferedDiskCache.GetWriteToDiskCacheTask(cacheKey); if (writeTask != default(Task)) { return(writeTask.ContinueWith( task => { IBinaryResource resource = ImagePipelineFactory.Instance.GetMainDiskStorageCache().GetResource(cacheKey); return ((FileBinaryResource)resource).File; }, TaskContinuationOptions.ExecuteSynchronously)); } else { return(_mainBufferedDiskCache.Contains(cacheKey).ContinueWith( task => { bool fileExists = task.Result; if (fileExists) { IBinaryResource resource = ImagePipelineFactory.Instance.GetMainDiskStorageCache().GetResource(cacheKey); return ((FileBinaryResource)resource).File; } else { return default(FileInfo); } }, TaskContinuationOptions.ExecuteSynchronously)); } }