diff --git a/README.md b/README.md
index ba2a0eb..ed6f98c 100644
--- a/README.md
+++ b/README.md
@@ -187,6 +187,12 @@ public string[] GetKeys(string regionName = null)
///
public CacheItemPolicy GetPolicy(string key, string regionName = null)
+///
+/// Returns a list of regions, including the root region.
+///
+///
+public IEnumerable GetRegions()
+
///
/// Used to specify the disk size, in bytes, that can be used by the File Cache.
/// Defaults to long.MaxValue
diff --git a/src/FileCache.UnitTests/FileCacheTest.cs b/src/FileCache.UnitTests/FileCacheTest.cs
index 1c389b6..b455c4b 100644
--- a/src/FileCache.UnitTests/FileCacheTest.cs
+++ b/src/FileCache.UnitTests/FileCacheTest.cs
@@ -11,6 +11,7 @@ FileCache is distributed under the Apache License 2.0.
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Runtime.Caching;
using System.Threading;
@@ -165,12 +166,12 @@ public void ShrinkCacheTest()
// Test empty case
_cache.ShrinkCacheToSize(0).Should().Be(0);
- // Insert 4 items, and keep track of their size
+ // Insert 4 items, one of them in a region, and keep track of their size
//sleep to make sure that oldest item gets removed first
_cache["item1"] = "bar1asdfasdfdfskjslkjlkjsdf sdlfkjasdlf asdlfkjskjfkjs d sdkfjksjd";
Thread.Sleep(500);
long size1 = _cache.GetCacheSize();
- _cache["item2"] = "bar2sdfjkjk skdfj sdflkj sdlkj lkjkjkjkjkjssss";
+ _cache.Add("item2", "bar2sdfjkjk skdfj sdflkj sdlkj lkjkjkjkjkjssss", _cache.DefaultPolicy, "region");
Thread.Sleep(500);
long size2 = _cache.GetCacheSize() - size1;
_cache["item3"] = "bar3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
@@ -183,7 +184,7 @@ public void ShrinkCacheTest()
long newSize = _cache.ShrinkCacheToSize(size4 + size3 + size2);
newSize.Should().Be(size4 + size3 + size2);
- // Shrink to just smaller than two items (should keep just item4, delete item2 and item3)
+ // Shrink to just smaller than two items (should keep just item4, delete item2 (in a region) and item3)
newSize = _cache.ShrinkCacheToSize(size3 + size4 - 1);
newSize.Should().Be(size4);
@@ -229,7 +230,8 @@ public void FlushTest()
{
_cache = new FileCache("FlushTest");
- _cache["foo"] = "bar";
+ _cache.Add("Key1", "Value1", _cache.DefaultPolicy);
+ _cache.Add("Key2", "Value2", DateTime.Now.AddDays(1), "Region1");
Thread.Sleep(500);
//attempt flush
@@ -241,6 +243,26 @@ public void FlushTest()
_cache.GetCacheSize().Should().Be(0);
}
+ [TestMethod]
+ public void FlushRegionTest()
+ {
+ _cache = new FileCache("FlushRegionTest");
+
+ _cache.Add("Key1", "Value1", _cache.DefaultPolicy);
+ _cache.Add("Key2", "Value2", _cache.DefaultPolicy, "Region1");
+ Thread.Sleep(500);
+
+ //attempt flush
+ _cache.Flush("Region1");
+
+ Thread.Sleep(500);
+
+ object result = _cache["Key1"];
+ result.Should().Be("Value1");
+ object result2 = _cache.Get("Key2", "Region1");
+ result2.Should().BeNull();
+ }
+
[TestMethod]
public void RemoveTest()
{
@@ -295,8 +317,16 @@ public void ClearCache()
{
_cache = new FileCache();
_cache["MyFirstKey"] = "MyFirstValue";
+ _cache.Add("MySecondKey", "MySecondValue", _cache.DefaultPolicy, "MyFirstRegion");
object pull = _cache.Get("MyFirstKey");
pull.ToString().Should().Be("MyFirstValue");
+ object pull2 = _cache.Get("MySecondKey", "MyFirstRegion");
+ pull2.ToString().Should().Be("MySecondValue");
+ _cache.Clear();
+ object result = _cache.Get("MyFirstKey");
+ result.Should().BeNull();
+ object result2 = _cache.Get("MySecondKey", "MyFirstRegion");
+ result2.Should().BeNull();
}
///
@@ -381,11 +411,24 @@ public void CleanCacheTest()
_cache.Add("foo", 1, DateTime.Now); // expires immediately
_cache.Add("bar", 2, DateTime.Now + TimeSpan.FromDays(1)); // set to expire tomorrow
+ _cache.Add("foo", 1, DateTime.Now, "region"); // expires immediately
+ _cache.Add("bar", 2, DateTime.Now + TimeSpan.FromDays(1), "region"); // set to expire tomorrow
+
+ var keys = _cache.GetKeys().ToList();
+ keys.Should().Contain("foo");
+ keys.Should().Contain("bar");
+ keys = _cache.GetKeys("region").ToList();
+ keys.Should().Contain("foo");
+ keys.Should().Contain("bar");
_cache.CleanCache();
- _cache["foo"].Should().BeNull();
- _cache["bar"].Should().NotBeNull();
+ keys = _cache.GetKeys().ToList();
+ keys.Should().NotContain("foo");
+ keys.Should().Contain("bar");
+ keys = _cache.GetKeys("region").ToList();
+ keys.Should().NotContain("foo");
+ keys.Should().Contain("bar");
}
[TestMethod]
diff --git a/src/FileCache.UnitTests/HashedFileCacheTest.cs b/src/FileCache.UnitTests/HashedFileCacheTest.cs
index ed54e9c..69d6a3f 100644
--- a/src/FileCache.UnitTests/HashedFileCacheTest.cs
+++ b/src/FileCache.UnitTests/HashedFileCacheTest.cs
@@ -9,6 +9,7 @@ FileCache is distributed under the Apache License 2.0.
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Runtime.Caching;
using System.Threading;
@@ -165,12 +166,12 @@ public void ShrinkCacheTest()
// Test empty case
_cache.ShrinkCacheToSize(0).Should().Be(0);
- // Insert 4 items, and keep track of their size
+ // Insert 4 items, one of them in a region, and keep track of their size
//sleep to make sure that oldest item gets removed first
_cache["item1"] = "bar1asdfasdfdfskjslkjlkjsdf sdlfkjasdlf asdlfkjskjfkjs d sdkfjksjd";
Thread.Sleep(500);
long size1 = _cache.GetCacheSize();
- _cache["item2"] = "bar2sdfjkjk skdfj sdflkj sdlkj lkjkjkjkjkjssss";
+ _cache.Add("item2", "bar2sdfjkjk skdfj sdflkj sdlkj lkjkjkjkjkjssss", _cache.DefaultPolicy, "region");
Thread.Sleep(500);
long size2 = _cache.GetCacheSize() - size1;
_cache["item3"] = "bar3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
@@ -183,7 +184,7 @@ public void ShrinkCacheTest()
long newSize = _cache.ShrinkCacheToSize(size4 + size3 + size2);
newSize.Should().Be(size4 + size3 + size2);
- // Shrink to just smaller than two items (should keep just item4, delete item2 and item3)
+ // Shrink to just smaller than two items (should keep just item4, delete item2 (in a region) and item3)
newSize = _cache.ShrinkCacheToSize(size3 + size4 - 1);
newSize.Should().Be(size4);
@@ -225,7 +226,8 @@ public void FlushTest()
{
_cache = new FileCache("FlushTest");
- _cache["foo"] = "bar";
+ _cache.Add("Key1", "Value1", _cache.DefaultPolicy);
+ _cache.Add("Key2", "Value2", DateTime.Now.AddDays(1), "Region1");
Thread.Sleep(500);
//attempt flush
@@ -237,6 +239,26 @@ public void FlushTest()
_cache.GetCacheSize().Should().Be(0);
}
+ [TestMethod]
+ public void FlushRegionTest()
+ {
+ _cache = new FileCache("FlushRegionTest");
+
+ _cache.Add("Key1", "Value1", _cache.DefaultPolicy);
+ _cache.Add("Key2", "Value2", _cache.DefaultPolicy, "Region1");
+ Thread.Sleep(500);
+
+ //attempt flush
+ _cache.Flush("Region1");
+
+ Thread.Sleep(500);
+
+ object result = _cache["Key1"];
+ result.Should().Be("Value1");
+ object result2 = _cache.Get("Key2", "Region1");
+ result2.Should().BeNull();
+ }
+
[TestMethod]
public void RemoveTest()
{
@@ -291,8 +313,16 @@ public void ClearCache()
{
_cache = new FileCache();
_cache["MyFirstKey"] = "MyFirstValue";
+ _cache.Add("MySecondKey", "MySecondValue", _cache.DefaultPolicy, "MyFirstRegion");
object pull = _cache.Get("MyFirstKey");
pull.ToString().Should().Be("MyFirstValue");
+ object pull2 = _cache.Get("MySecondKey", "MyFirstRegion");
+ pull2.ToString().Should().Be("MySecondValue");
+ _cache.Clear();
+ object result = _cache.Get("MyFirstKey");
+ result.Should().BeNull();
+ object result2 = _cache.Get("MySecondKey", "MyFirstRegion");
+ result2.Should().BeNull();
}
///
@@ -377,11 +407,24 @@ public void CleanCacheTest()
_cache.Add("foo", 1, DateTime.Now); // expires immediately
_cache.Add("bar", 2, DateTime.Now + TimeSpan.FromDays(1)); // set to expire tomorrow
+ _cache.Add("foo", 1, DateTime.Now, "region"); // expires immediately
+ _cache.Add("bar", 2, DateTime.Now + TimeSpan.FromDays(1), "region"); // set to expire tomorrow
+
+ var keys = _cache.GetKeys().ToList();
+ keys.Should().Contain("foo");
+ keys.Should().Contain("bar");
+ keys = _cache.GetKeys("region").ToList();
+ keys.Should().Contain("foo");
+ keys.Should().Contain("bar");
_cache.CleanCache();
- _cache["foo"].Should().BeNull();
- _cache["bar"].Should().NotBeNull();
+ keys = _cache.GetKeys().ToList();
+ keys.Should().NotContain("foo");
+ keys.Should().Contain("bar");
+ keys = _cache.GetKeys("region").ToList();
+ keys.Should().NotContain("foo");
+ keys.Should().Contain("bar");
}
}
}
diff --git a/src/FileCache/FileCache.cs b/src/FileCache/FileCache.cs
index 7dd465d..9d3de91 100644
--- a/src/FileCache/FileCache.cs
+++ b/src/FileCache/FileCache.cs
@@ -461,22 +461,30 @@ public long CleanCache(string regionName = null)
{
if (cLock == null)
return 0;
-
- foreach (string key in GetKeys(regionName))
+
+ IEnumerable regions =
+ string.IsNullOrEmpty(regionName)
+ ? CacheManager.GetRegions()
+ : new List(1) { regionName };
+
+ foreach (var region in regions)
{
- CacheItemPolicy policy = GetPolicy(key, regionName);
- if (policy.AbsoluteExpiration < DateTime.Now)
+ foreach (string key in GetKeys(region))
{
- try
+ CacheItemPolicy policy = GetPolicy(key, region);
+ if (policy.AbsoluteExpiration < DateTime.Now)
{
- string cachePath = CacheManager.GetCachePath(key, regionName);
- string policyPath = CacheManager.GetPolicyPath(key, regionName);
- CacheItemReference ci = new CacheItemReference(key, cachePath, policyPath);
- Remove(key, regionName); // CT note: Remove will update CurrentCacheSize
- removed += ci.Length;
+ try
+ {
+ string cachePath = CacheManager.GetCachePath(key, region);
+ string policyPath = CacheManager.GetPolicyPath(key, region);
+ CacheItemReference ci = new CacheItemReference(key, region, cachePath, policyPath);
+ Remove(key, region); // CT note: Remove will update CurrentCacheSize
+ removed += ci.Length;
+ }
+ catch (Exception) // skip if the file cannot be accessed
+ { }
}
- catch (Exception) // skip if the file cannot be accessed
- { }
}
}
@@ -506,19 +514,27 @@ private long DeleteOldestFiles(long amount, string regionName = null)
//Heap of all CacheReferences
PriortyQueue cacheReferences = new PriortyQueue();
- //build a heap of all files in cache region
- foreach (string key in GetKeys(regionName))
+ IEnumerable regions =
+ string.IsNullOrEmpty(regionName)
+ ? CacheManager.GetRegions()
+ : new List(1) { regionName };
+
+ foreach (var region in regions)
{
- try
- {
- //build item reference
- string cachePath = CacheManager.GetCachePath(key, regionName);
- string policyPath = CacheManager.GetPolicyPath(key, regionName);
- CacheItemReference ci = new CacheItemReference(key, cachePath, policyPath);
- cacheReferences.Enqueue(ci);
- }
- catch(FileNotFoundException)
+ //build a heap of all files in cache region
+ foreach (string key in GetKeys(region))
{
+ try
+ {
+ //build item reference
+ string cachePath = CacheManager.GetCachePath(key, region);
+ string policyPath = CacheManager.GetPolicyPath(key, region);
+ CacheItemReference ci = new CacheItemReference(key, region, cachePath, policyPath);
+ cacheReferences.Enqueue(ci);
+ }
+ catch (FileNotFoundException)
+ {
+ }
}
}
@@ -529,7 +545,7 @@ private long DeleteOldestFiles(long amount, string regionName = null)
//remove oldest item
CacheItemReference oldest = cacheReferences.Dequeue();
removedBytes += oldest.Length;
- Remove(oldest.Key, regionName);
+ Remove(oldest.Key, oldest.Region);
}
return removedBytes;
}
@@ -648,18 +664,26 @@ public void Flush(DateTime minDate, string regionName = null)
return;
}
- IEnumerable keys = CacheManager.GetKeys();
- foreach (string key in keys)
- {
- string policyPath = CacheManager.GetPolicyPath(key, regionName);
- string cachePath = CacheManager.GetCachePath(key, regionName);
+ IEnumerable regions =
+ string.IsNullOrEmpty(regionName)
+ ? CacheManager.GetRegions()
+ : new List(1) { regionName };
- // Update the Cache size
- CurrentCacheSize = GetCacheSize();
- //if either policy or cache are stale, delete both
- if (File.GetLastAccessTime(policyPath) < minDate || File.GetLastAccessTime(cachePath) < minDate)
+ foreach (var region in regions)
+ {
+ IEnumerable keys = CacheManager.GetKeys(region);
+ foreach (string key in keys)
{
- CurrentCacheSize -= CacheManager.DeleteFile(key, regionName);
+ string policyPath = CacheManager.GetPolicyPath(key, region);
+ string cachePath = CacheManager.GetCachePath(key, region);
+
+ // Update the Cache size
+ CurrentCacheSize = GetCacheSize();
+ //if either policy or cache are stale, delete both
+ if (File.GetLastAccessTime(policyPath) < minDate || File.GetLastAccessTime(cachePath) < minDate)
+ {
+ CurrentCacheSize -= CacheManager.DeleteFile(key, region);
+ }
}
}
@@ -1003,10 +1027,12 @@ private class CacheItemReference : IComparable
public readonly DateTime LastAccessTime;
public readonly long Length;
public readonly string Key;
+ public readonly string Region;
- public CacheItemReference(string key, string cachePath, string policyPath)
+ public CacheItemReference(string key, string region, string cachePath, string policyPath)
{
Key = key;
+ Region = region;
FileInfo cfi = new FileInfo(cachePath);
FileInfo pfi = new FileInfo(policyPath);
cfi.Refresh();
@@ -1027,8 +1053,12 @@ public int CompareTo(CacheItemReference other)
// that way we delete smaller files first)
i = -1 * Length.CompareTo(other.Length);
if (i == 0)
- {
- i = Key.CompareTo(other.Key);
+ {
+ i = string.Compare(Region, other.Region);
+ if (i == 0)
+ {
+ i = Key.CompareTo(other.Key);
+ }
}
}
diff --git a/src/FileCache/FileCacheManager.cs b/src/FileCache/FileCacheManager.cs
index 9db4475..a7e600a 100644
--- a/src/FileCache/FileCacheManager.cs
+++ b/src/FileCache/FileCacheManager.cs
@@ -234,6 +234,24 @@ public virtual long WriteFile(FileCache.PayloadMode mode, string key, FileCacheP
///
public abstract IEnumerable GetKeys(string regionName = null);
+ ///
+ /// Returns a list of regions, including the root region.
+ ///
+ ///
+ public IEnumerable GetRegions()
+ {
+ string directory = Path.Combine(CacheDir, CacheSubFolder);
+ DirectoryInfo di = new DirectoryInfo(directory);
+ if (di.Exists)
+ {
+ yield return null;
+ foreach (var d in di.EnumerateDirectories())
+ {
+ yield return d.Name;
+ }
+ }
+ }
+
///
/// Builds a string that will get the path to the supplied file's policy file
///