Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,19 @@ public interface ICacheAside
The Add and remove methods are implemented with fire and forget, hence it does not need to be Async as this is handled by the StackExchange.Redis client.

DoubleCache comes with the following implementations of this interface
* LocalCache.MemCache - using System.Runtime.Memory
* *[obsolete]* LocalCache.MemCache - using System.Runtime.Memory, does not support null items.
* LocalCache.WrappingMemoryCache - Allows storage of null items in Memory cache.*
* SystemWebCaching.HttpCache - An in memory cache using HttpContext.Cache
* Redis.RedisCache - using StackExchange.Redis client
* Redis.RedisStaleCache - Use redis as a stale cache in order to mitigate cache stampede.
* SubscribingCache - a decorator supporting push notifications of cache updates
* PublishingCache - a decorator publishing cache changes
* DoubleCache - a decorator wrapping a local and a remote cache

\* using a custom proxy object holding the cache items. This is transparent to the client.

Depending on your cache need, you can combine these implementations and decorators as you need. The most complete example would be a DoubleCache which takes a local cache decorated with a SubscribingCache and a RedisCache decorated with a PublishingCache. This will result in a local cache that will be in sync with the other local caches; if a value isn't found the value will be retrieved from Redis before ultimately being resolved using the func provided to the cache.

###Redis Stale Cache
Extends TTL for all objects stored in Redis. Verifies TTL when reading data from redis. If this is less than the default TTL on the redis cache, the cached item will be returned and a new fetch from the dataretriever will be extecuted on a separate thread. Storing the result in the cache.

2 changes: 1 addition & 1 deletion build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let references = !! "source/DoubleCache/*.csproj"
++ "source/DoubleCache.SystemWebCaching/DoubleCache.SystemWebCaching.csproj"
let testReferences = !! "source/DoubleCacheTests/*.csproj"

let version = "1.6.0"
let version = "2.0.0-beta4"
let commitHash = Information.getCurrentSHA1(".")

let projectName = "DoubleCache"
Expand Down
75 changes: 45 additions & 30 deletions source/DoubleCache.SystemWebCaching/HttpCache.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Caching;

namespace DoubleCache.SystemWebHttpCache
namespace DoubleCache.SystemWebCaching
{
public class HttpCache : ICacheAside
{
internal class CacheItemWrapper
{
internal object Item { get; }

internal CacheItemWrapper(object item)
{
Item = item;
}
}

private readonly Cache _cache;
private readonly TimeSpan? _defaultTtl;

Expand All @@ -20,12 +27,12 @@ public HttpCache(Cache cache, TimeSpan? defaultTtl = null)

public void Add<T>(string key, T item)
{
_cache.Add(key, item, null, CalculateExpire(_defaultTtl), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
Add(key,item,_defaultTtl);
}

public void Add<T>(string key, T item, TimeSpan? timeToLive)
{
_cache.Add(key, item, null, CalculateExpire(timeToLive), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
_cache.Add(key, new CacheItemWrapper(item), null, CalculateExpire(timeToLive), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}

public T Get<T>(string key, Func<T> dataRetriever) where T : class
Expand All @@ -35,13 +42,14 @@ public T Get<T>(string key, Func<T> dataRetriever) where T : class

public T Get<T>(string key, Func<T> dataRetriever, TimeSpan? timeToLive) where T : class
{
var item = _cache.Get(key) as T;
if (item != null)
return item;
{
item = dataRetriever.Invoke();
Add(key, item, timeToLive);
}
var wrapper = _cache.Get(key) as CacheItemWrapper;
if (wrapper != null)
return wrapper.Item as T;

var item = dataRetriever.Invoke();

Add(key, item, timeToLive);

return item;
}

Expand All @@ -52,13 +60,15 @@ public object Get(string key, Type type, Func<object> dataRetriever)

public object Get(string key, Type type, Func<object> dataRetriever, TimeSpan? timeToLive)
{
var item = _cache.Get(key);
if (item != null && item.GetType() == type)
return item;
var wrapper = _cache.Get(key) as CacheItemWrapper;
if (wrapper != null)
return wrapper.Item;

var item = dataRetriever.Invoke();

item = dataRetriever.Invoke();
Add(key, item, timeToLive);
return item.GetType() == type ? item : null;

return item;
}

public Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever) where T : class
Expand All @@ -68,13 +78,14 @@ public Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever) where T : cl

public async Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever, TimeSpan? timeToLive) where T : class
{
var item = _cache.Get(key) as T;
if (item != null)
return item;
{
item = await dataRetriever.Invoke();
Add(key, item, timeToLive);
}
var wrapper = _cache.Get(key) as CacheItemWrapper;
if (wrapper != null)
return wrapper.Item as T;

var item = await dataRetriever.Invoke();

Add(key, item, timeToLive);

return item;
}

Expand All @@ -85,13 +96,17 @@ public Task<object> GetAsync(string key, Type type, Func<Task<object>> dataRetri

public async Task<object> GetAsync(string key, Type type, Func<Task<object>> dataRetriever, TimeSpan? timeToLive)
{
var item = _cache.Get(key);
if (item != null && item.GetType() == type)
return item;
var wrapper = _cache.Get(key) as CacheItemWrapper;
if (wrapper != null)
return wrapper.Item;


var item = await dataRetriever.Invoke();
item = item.GetType() == type ? item : null;

item = await dataRetriever.Invoke();
Add(key, item, timeToLive);
return item.GetType() == type ? item : null;

return item;
}

public void Remove(string key)
Expand Down
2 changes: 1 addition & 1 deletion source/DoubleCache/CacheFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static ICacheAside CreatePubSubDoubleCache(IConnectionMultiplexer redisCo
{
var remoteCache = new RedisCache(redisConnection.GetDatabase(), itemSerializer, defaultTtl);
return new DoubleCache(
new SubscribingCache(new LocalCache.MemCache(defaultTtl), new RedisSubscriber(redisConnection, remoteCache, itemSerializer)),
new SubscribingCache(new LocalCache.WrappingMemoryCache(defaultTtl), new RedisSubscriber(redisConnection, remoteCache, itemSerializer)),
new PublishingCache(remoteCache, new RedisPublisher(redisConnection, itemSerializer)));
}
}
Expand Down
122 changes: 61 additions & 61 deletions source/DoubleCache/DoubleCache.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
using System;
using System.Threading.Tasks;

namespace DoubleCache
{
public class DoubleCache : ICacheAside
{
private readonly ICacheAside _localCache;
private readonly ICacheAside _remoteCache;

public DoubleCache(ICacheAside localCache,ICacheAside remoteCache)
{
_localCache = localCache;
_remoteCache = remoteCache;
}

public void Add<T>(string key, T item)
{
_localCache.Add(key, item);
_remoteCache.Add(key, item);
}

public void Add<T>(string key, T item, TimeSpan? timeToLive)
{
_localCache.Add(key, item, timeToLive);
_remoteCache.Add(key, item, timeToLive);
using System;
using System.Threading.Tasks;
namespace DoubleCache
{
public class DoubleCache : ICacheAside
{
private readonly ICacheAside _localCache;
private readonly ICacheAside _remoteCache;
public DoubleCache(ICacheAside localCache,ICacheAside remoteCache)
{
_localCache = localCache;
_remoteCache = remoteCache;
}
public void Add<T>(string key, T item)
{
_localCache.Add(key, item);
_remoteCache.Add(key, item);
}
public void Add<T>(string key, T item, TimeSpan? timeToLive)
{
_localCache.Add(key, item, timeToLive);
_remoteCache.Add(key, item, timeToLive);
}

public T Get<T>(string key, Func<T> dataRetriever) where T : class
Expand All @@ -45,41 +45,41 @@ public object Get(string key, Type type, Func<object> dataRetriever)
public object Get(string key, Type type, Func<object> dataRetriever, TimeSpan? timeToLive)
{
return _localCache.Get(key, type, () => _remoteCache.Get(key, type, dataRetriever, timeToLive), timeToLive);
}

public Task<object> GetAsync(string key, Type type, Func<Task<object>> dataRetriever)
{
return _localCache.GetAsync(key, type, () => _remoteCache.GetAsync(key, type, dataRetriever));
}

public Task<object> GetAsync(string key, Type type, Func<Task<object>> dataRetriever, TimeSpan? timeToLive)
{
return _localCache.GetAsync(key, type, () => _remoteCache.GetAsync(key, type, dataRetriever), timeToLive);
}

public Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever) where T : class
{
return _localCache.GetAsync(key, () => _remoteCache.GetAsync(key, dataRetriever));
}

public Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever, TimeSpan? timeToLive) where T : class
{
return _localCache.GetAsync(key, () => _remoteCache.GetAsync(key, dataRetriever),timeToLive);
}

public void Remove(string key)
{
_localCache.Remove(key);
_remoteCache.Remove(key);
}

public TimeSpan? DefaultTtl { get
{
return _localCache.DefaultTtl > _remoteCache.DefaultTtl
? _localCache.DefaultTtl
: _remoteCache.DefaultTtl;
} }

public Task<object> GetAsync(string key, Type type, Func<Task<object>> dataRetriever)
{
return _localCache.GetAsync(key, type, () => _remoteCache.GetAsync(key, type, dataRetriever));
}

public Task<object> GetAsync(string key, Type type, Func<Task<object>> dataRetriever, TimeSpan? timeToLive)
{
return _localCache.GetAsync(key, type, () => _remoteCache.GetAsync(key, type, dataRetriever), timeToLive);
}

public Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever) where T : class
{
return _localCache.GetAsync(key, () => _remoteCache.GetAsync(key, dataRetriever));
}

public Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever, TimeSpan? timeToLive) where T : class
{
return _localCache.GetAsync(key, () => _remoteCache.GetAsync(key, dataRetriever),timeToLive);
}

public void Remove(string key)
{
_localCache.Remove(key);
_remoteCache.Remove(key);
}

public TimeSpan? DefaultTtl { get
{
return _localCache.DefaultTtl > _remoteCache.DefaultTtl
? _localCache.DefaultTtl
: _remoteCache.DefaultTtl;
} }


}
}
}
}
1 change: 1 addition & 0 deletions source/DoubleCache/DoubleCache.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
<Compile Include="ICachePublisher.cs" />
<Compile Include="ICacheSubscriber.cs" />
<Compile Include="LocalCache\MemCache.cs" />
<Compile Include="LocalCache\WrappingMemoryCache.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PublishingCache.cs" />
<Compile Include="Redis\RedisCache.cs" />
Expand Down
7 changes: 4 additions & 3 deletions source/DoubleCache/LocalCache/MemCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

namespace DoubleCache.LocalCache
{
[Obsolete("This implementation does not accept caching of null values, consider using WrappingMemoryCache instead")]
public class MemCache : ICacheAside
{
private readonly TimeSpan? _defaultTtl;

public MemCache(TimeSpan? defaultTtl = null)
{
_defaultTtl = defaultTtl;
Expand Down Expand Up @@ -74,7 +75,7 @@ public async Task<object> GetAsync(string key, Type type, Func<Task<object>> dat
if (item != null && item.GetType() == type)
return item;

item = await dataRetriever.Invoke();
item = await dataRetriever.Invoke().ConfigureAwait(false);
Add(key, item, timeToLive);
return item.GetType() == type ? item : null;
}
Expand All @@ -90,7 +91,7 @@ public async Task<T> GetAsync<T>(string key, Func<Task<T>> dataRetriever, TimeSp
if (item != null)
return item;
{
item = await dataRetriever.Invoke();
item = await dataRetriever.Invoke().ConfigureAwait(false);
Add(key, item, timeToLive);
}
return item;
Expand Down
Loading