├── .nuget
├── NuGet.exe
├── NuGet.Config
└── NuGet.targets
├── CacheR.Model
├── CacheCommandType.cs
├── CacheEntry.cs
├── CacheCommand.cs
├── CacheEntryKeyComparer.cs
├── Properties
│ └── AssemblyInfo.cs
└── CacheR.Model.csproj
├── CacheR.Server
├── app.config
├── ICacheStore.cs
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── MemoryCacheStore.cs
├── Program.cs
├── CacheServer.cs
└── CacheR.Server.csproj
├── CacheR.Client.Sample
├── app.config
├── Properties
│ └── AssemblyInfo.cs
├── Program.cs
└── CacheR.Client.Sample.csproj
├── CacheR.Client
├── packages.config
├── Properties
│ └── AssemblyInfo.cs
├── CacheR.Client.csproj
└── CacheClient.cs
├── .gitignore
├── README.md
├── Sakefile.shade
└── CacheR.sln
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidfowl/CacheR/HEAD/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/CacheR.Model/CacheCommandType.cs:
--------------------------------------------------------------------------------
1 | namespace CacheR.Model
2 | {
3 | public enum CacheCommandType
4 | {
5 | Add,
6 | Remove
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CacheR.Server/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CacheR.Client.Sample/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CacheR.Model/CacheEntry.cs:
--------------------------------------------------------------------------------
1 | namespace CacheR.Model
2 | {
3 | public class CacheEntry
4 | {
5 | public string Key { get; set; }
6 | public object Value { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CacheR.Model/CacheCommand.cs:
--------------------------------------------------------------------------------
1 | namespace CacheR.Model
2 | {
3 | public class CacheCommand
4 | {
5 | public CacheCommandType Type { get; set; }
6 | public CacheEntry[] Entries { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/CacheR.Client/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Oo]bj/
2 | [Bb]in/
3 | *.user
4 | *.suo
5 | *.cache
6 | *.docstates
7 | _ReSharper.*
8 | *.csproj.user
9 | *[Rr]e[Ss]harper.user
10 | _ReSharper.*/
11 | packages/*
12 | target/*
13 | msbuild.log
14 | *.psess
15 | *.vsp
16 | *.pidb
17 | *.userprefs
18 | *DS_Store
19 | *.ncrunchsolution
--------------------------------------------------------------------------------
/CacheR.Server/ICacheStore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 | using CacheR.Model;
5 |
6 | namespace CacheR.Server
7 | {
8 | public interface ICacheStore
9 | {
10 | Task Save(CacheEntry entry);
11 | IEnumerable GetAll();
12 | Task Delete(string key);
13 |
14 | Action OnEntryRemoved { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## What is this?
2 | My attempt at writing a scalable distributed cache on top of SignalR.
3 |
4 | ## How do I run it?
5 |
6 | 1. Run `CacheR.Server.exe` (by default it'll run on port 91).
7 | 2. Run 2 instances of the `CacheR.Client.Sample.exe`.
8 |
9 | The instructions in the client sample should be clear. You should be able to add a value in one client and have it be available in the other immediately
10 |
11 | ## Can I use this?
12 | This is far from production ready but if you're interesting in using it, let me know, I'd love the feedback.
--------------------------------------------------------------------------------
/CacheR.Model/CacheEntryKeyComparer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CacheR.Model
4 | {
5 | public class CacheEntryKeyComparer : IEqualityComparer
6 | {
7 | public readonly static CacheEntryKeyComparer Instance = new CacheEntryKeyComparer();
8 |
9 | private CacheEntryKeyComparer()
10 | {
11 | }
12 |
13 | public bool Equals(CacheEntry x, CacheEntry y)
14 | {
15 | return x.Key.Equals(y.Key);
16 | }
17 |
18 | public int GetHashCode(CacheEntry obj)
19 | {
20 | return obj.Key.GetHashCode();
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/CacheR.Server/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Sakefile.shade:
--------------------------------------------------------------------------------
1 | use-standard-lifecycle
2 | use namespace="System"
3 | use namespace="System.IO"
4 | use import="Files"
5 |
6 | var PROJECT='CacheR'
7 | var VERSION='0.0.1'
8 | var FULL_VERSION='${VERSION}'
9 | var AUTHORS='${PROJECT} contributors'
10 |
11 | var BASE_DIR='${Directory.GetCurrentDirectory()}'
12 | var TARGET_DIR='${Path.Combine(BASE_DIR, "target")}'
13 | var BUILD_DIR='${Path.Combine(TARGET_DIR, "build")}'
14 | var TEST_DIR='${Path.Combine(TARGET_DIR, "test")}'
15 | var PACKAGE_DIR='${Path.Combine(TARGET_DIR, "package")}'
16 |
17 | var HOME_DIR='${Environment.GetEnvironmentVariable("HOME")}'
18 | set HOME_DIR='${Environment.GetEnvironmentVariable("HOMEDRIVE") + Environment.GetEnvironmentVariable("HOMEPATH")}' if='string.IsNullOrEmpty(HOME_DIR)'
19 |
20 | #clean-target-dir target='clean'
21 | directory delete='${TARGET_DIR}'
22 |
23 | #apply-version target='initialize'
24 | for each='var file in Files.Include("**/AssemblyInfo.cs")'
25 | assemblyinfo updateFile='${file}' assemblyVersion='${VERSION}' assemblyInformationalVersion='${FULL_VERSION}'
26 |
27 | #build-sln target='compile'
28 | build projectFile='${PROJECT}.sln' outputDir='${BUILD_DIR}' configuration='Release'
29 |
30 |
--------------------------------------------------------------------------------
/CacheR.Client.Sample/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CacheR")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("CacheR")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("74f9200f-8998-424c-9b35-eaca5373c972")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("0.0.1")]
35 | [assembly: AssemblyVersion("0.0.1")]
36 | [assembly: AssemblyFileVersion("0.0.1")]
37 |
--------------------------------------------------------------------------------
/CacheR.Client/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CacheR.Client")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("CacheR.Client")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("8e8980ed-2bf3-47f0-b4ee-62332f9d4ae8")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("0.0.1")]
35 | [assembly: AssemblyVersion("0.0.1")]
36 | [assembly: AssemblyFileVersion("0.0.1")]
37 |
--------------------------------------------------------------------------------
/CacheR.Model/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CacheR.Model")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("CacheR.Model")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("9841c0a4-827d-4351-a7f9-076fa909ae2e")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("0.0.1")]
35 | [assembly: AssemblyVersion("0.0.1")]
36 | [assembly: AssemblyFileVersion("0.0.1")]
37 |
--------------------------------------------------------------------------------
/CacheR.Server/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CacheR.Server")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("Microsoft")]
12 | [assembly: AssemblyProduct("CacheR.Server")]
13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("27f1d034-6a0e-4b21-80be-44c08cac77f6")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("0.0.1")]
35 | [assembly: AssemblyVersion("0.0.1")]
36 | [assembly: AssemblyFileVersion("0.0.1")]
37 |
--------------------------------------------------------------------------------
/CacheR.Client.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using CacheR.Client;
4 |
5 | namespace CacheR
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | var cache = new Cache("http://localhost:8087");
12 |
13 | RunCacheTest(cache).Wait();
14 | }
15 |
16 | private static async Task RunCacheTest(Cache cache)
17 | {
18 | await cache.ConnectAsync();
19 |
20 | Console.WriteLine("Enter 'key=value' to add a value to the cache.");
21 | Console.WriteLine("Enter 'key' to get a value from the cache.");
22 | Console.WriteLine("Enter '-key' to delete a value from the cache.");
23 |
24 | string line = null;
25 | while ((line = Console.ReadLine()) != null)
26 | {
27 | var values = line.Split('=');
28 | if (values.Length == 2)
29 | {
30 | string key = values[0].Trim();
31 | string value = values[1].Trim();
32 | await cache.AddAsync(key, value);
33 |
34 | Console.WriteLine("Added '{0}' to the cache with value '{1}'.", key, value);
35 | }
36 | else if (line.StartsWith("-"))
37 | {
38 | string key = line.Substring(1).Trim();
39 | await cache.DeleteAsync(key);
40 |
41 | Console.WriteLine("Deleting entry for key '{0}'", key);
42 | }
43 | else
44 | {
45 | string key = line.Trim();
46 | Console.WriteLine("Value for '{0}' is " + cache.Get(key), key);
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/CacheR.Server/MemoryCacheStore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Runtime.Caching;
5 | using System.Threading.Tasks;
6 | using CacheR.Model;
7 |
8 | namespace CacheR.Server
9 | {
10 | public class MemoryCacheStore : ICacheStore
11 | {
12 | private readonly MemoryCache _cache = MemoryCache.Default;
13 | private readonly Task _completedTask = CompletedTask();
14 |
15 | public Action OnEntryRemoved { get; set; }
16 |
17 | public Task Save(CacheEntry entry)
18 | {
19 | var policy = new CacheItemPolicy();
20 |
21 | // TODO: Allow this to be configured
22 | // Why 8 minutes? Because it feels right.
23 | policy.AbsoluteExpiration = DateTimeOffset.Now + TimeSpan.FromMinutes(8);
24 | policy.RemovedCallback = OnCacheEntryRemoved;
25 | _cache.Set(entry.Key, entry.Value, policy);
26 |
27 | return _completedTask;
28 | }
29 |
30 | public IEnumerable GetAll()
31 | {
32 | foreach (var entry in _cache)
33 | {
34 | yield return new CacheEntry
35 | {
36 | Key = entry.Key,
37 | Value = entry.Value
38 | };
39 | }
40 | }
41 |
42 | public Task Delete(string key)
43 | {
44 | _cache.Remove(key);
45 |
46 | return _completedTask;
47 | }
48 |
49 | private static Task CompletedTask()
50 | {
51 | var tcs = new TaskCompletionSource