Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distributed cache #63

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,4 @@ $RECYCLE.BIN/

packages/
.vs/
/.vscode
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using LazyCache;
using Microsoft.AspNetCore.Mvc;

namespace CacheDatabaseQueriesApiSample.Controllers
{
public class DbTimeDistributedController : Controller
{
private readonly IDistributedAppCache _distributedCache;
private readonly string cacheKey = "DbTimeController.Get";
private readonly DbTimeContext dbContext;


public DbTimeDistributedController(DbTimeContext context, IDistributedAppCache distributedCache)
{
dbContext = context;
_distributedCache = distributedCache;
}

[HttpGet]
[Route("api/ddbtime")]
public DbTimeEntity Get()
{
Func<DbTimeEntity> actionThatWeWantToCache = () => dbContext.GeDbTime();

var cachedDatabaseTime = _distributedCache.GetOrAdd(cacheKey, actionThatWeWantToCache);

return cachedDatabaseTime;
}

[HttpDelete]
[Route("api/ddbtime")]
public IActionResult DeleteFromCache()
{
_distributedCache.Remove(cacheKey);
var friendlyMessage = new { Message = $"Item with key '{cacheKey}' removed from server in-memory cache" };
return Ok(friendlyMessage);
}
}
}
2 changes: 1 addition & 1 deletion CacheDatabaseQueriesApiSample/DbTimeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public DbTimeEntity GeDbTime()
// get the current time from SQL server right now asynchronously (simulating a slow query)
var result = Times
.FromSql("WAITFOR DELAY '00:00:00:500'; SELECT 1 as [ID], GETDATE() as [TimeNowInTheDatabase]")
.Single();
.SingleOrDefault();

databaseRequestCounter++;

Expand Down
11 changes: 10 additions & 1 deletion CacheDatabaseQueriesApiSample/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Microsoft.AspNetCore.Builder;
using LazyCache;
using LazyCache.Providers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -29,6 +32,12 @@ public void ConfigureServices(IServiceCollection services)

// Register IAppCache as a singleton CachingService
services.AddLazyCache();

services.AddMemoryCache();
services.AddDistributedMemoryCache();
services.AddSingleton<DistributedCacheProvider>();
services.AddTransient<IDistributedCacheProvider>(provider => new HybridCacheProvider(provider.GetRequiredService<DistributedCacheProvider>(), provider.GetRequiredService<IMemoryCache>()));
services.AddDistributedHybridLazyCache(provider => new HybridCachingService(provider.GetRequiredService<IDistributedCacheProvider>()));
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down
159 changes: 159 additions & 0 deletions CacheDatabaseQueriesApiSample/wwwroot/index-d.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Lazy cache sample app</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"> </script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"> </script>
<![endif]-->
</head>
<body>
<div class="container">
<div class="centre-box">
<div class="counter pull-right"><span class="counter-val">0</span> Database query(s)</div>

<h1>Sample app to demonstrate using a distributed cache in your API to save database SQL queries and speed up API calls</h1>

<p>
Every 3 seconds we fetch the current time from the database, however because the sql query
result is cached on the server on the first call, the time stays the same untill you clear
the cache and no more SQL queries are made. <br />
<br />

After you clear the cache you should see the time change because a real SQL query is made.<br />
<br />

Also note how real SQL queryies are slower than cache hits.
</p>

<div class="button-bar">
<button class="btn btn-default btn-primary btn-clear-cache">Clear Cache</button>
</div>

<label>Log</label>
<p class="log"></p>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script>
// util for n digit numbers with leading zeros
Number.prototype.pad = function (len) {
return (new Array(len + 1).join("0") + this).slice(-len);
}
</script>
<script>
"use strict";

(function () {
var app = {
log: function (message) {
var now = new Date();
var prefix = "[" + now.getHours().pad(2) + ":" + now.getMinutes().pad(2) + ":" + now.getSeconds().pad(2) + "] ";
var currentLog = $(".log").text();
$(".log").text(prefix + message + "\n" + currentLog);
},

getDbTime: function () {
return $.get("/api/ddbtime");
},

updateDbQueryCount: function () {
return $.get("/api/dbQueries")
.done(function (data) {
$(".counter-val").text(data);
});
},

updateLogWithTime: function (dbTime, duration) {
var message = "API reports that the time now in the database is " + dbTime.timeNowInTheDatabase + " (" + duration + "ms)";
app.log(message);
},

clearCache: function () {
$.ajax({
method: "DELETE",
url: "/api/ddbtime"
})
.done(function (data) {
app.log(data.message);
});
},

updateTimeEvent: function () {

var start_time = new Date().getTime();

return app.getDbTime()
.done(function(data) {
var duration = new Date().getTime() - start_time;
app.updateLogWithTime(data, duration);
})
.then(app.updateDbQueryCount);
},

startPollingForTime: function () {
var threeSecs = 3000;
setInterval(app.updateTimeEvent, threeSecs);
},

bindEvents: function () {
$(".btn-clear-cache").click(app.clearCache);

$(document).ajaxError(function (event, request, settings) {
app.log("Error requesting page " + settings.url);
});
}

};

// setup the application
app.bindEvents();

// fetch the db time now
app.log("Starting app... Fetching the current time from SQL server every 3 secs");
app.updateTimeEvent()
.then(app.startPollingForTime);
})();

</script>

<style>
body {
background-color: #EEEEEE;
padding-bottom: 40px;
padding-top: 40px;
}

.centre-box {
margin: 0 auto;
max-width: 850px;
padding: 15px;
}

.log {
font-family: consolas;
white-space: pre-line;
}

.button-bar {
min-height: 3em;
}

.counter {
margin: -3em 0 0 0;
padding: 0.5em;
max-width: 6em;
background-color: darkslategrey;
color: lightskyblue;
text-align: center;
font-size: 1.2em;
font-weight: bold;
}
</style>
</body>
</html>
9 changes: 9 additions & 0 deletions Console.Net461/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
using System.Text;
using System.Threading.Tasks;
using LazyCache;
using LazyCache.Providers;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Ninject;

namespace Console.Net461
Expand All @@ -26,6 +30,11 @@ static void Main(string[] args)
item = cache.GetOrAdd("Program.Main.Person", () => Tuple.Create("Joe Blogs", DateTime.UtcNow));

System.Console.WriteLine(item.Item1);

IDistributedAppCache distributedCache = new HybridCachingService(new HybridCacheProvider(new DistributedCacheProvider(new MemoryDistributedCache(Options.Create(new MemoryDistributedCacheOptions()))), new MemoryCache(Options.Create(new MemoryCacheOptions()))));
item = distributedCache.GetOrAdd("Program.Main.Person", () => Tuple.Create("Joe Blogs", DateTime.UtcNow));

System.Console.WriteLine(item.Item1);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using LazyCache;
using LazyCache.Providers;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection.Extensions;

// ReSharper disable once CheckNamespace - MS guidelines say put DI registration in this NS
namespace Microsoft.Extensions.DependencyInjection
{
// See https://github.com/aspnet/Caching/blob/dev/src/Microsoft.Extensions.Caching.Memory/MemoryCacheServiceCollectionExtensions.cs
public static class DistributedLazyCacheServiceCollectionExtensions
{
public static IServiceCollection AddDistributedLazyCache(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));

services.AddOptions();
services.TryAddSingleton<IDistributedCache, MemoryDistributedCache>();
services.TryAddSingleton<IDistributedCacheProvider, DistributedCacheProvider>();
services.TryAddSingleton<IDistributedAppCache, DistributedCachingService>();

return services;
}

public static IServiceCollection AddDistributedLazyCache(this IServiceCollection services,
Func<IServiceProvider, DistributedCachingService> implementationFactory)
{
if (services == null) throw new ArgumentNullException(nameof(services));
if (implementationFactory == null) throw new ArgumentNullException(nameof(implementationFactory));

services.AddOptions();
services.TryAddSingleton<IDistributedCacheProvider, DistributedCacheProvider>();
services.TryAddSingleton<IDistributedAppCache>(implementationFactory);

return services;
}

public static IServiceCollection AddDistributedHybridLazyCache(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));

services.AddOptions();
services.TryAddSingleton<IMemoryCache, MemoryCache>();
services.TryAddSingleton<IDistributedCache, MemoryDistributedCache>();
services.TryAddSingleton<DistributedCacheProvider>();
services.TryAddSingleton<IDistributedCacheProvider>(provider => new HybridCacheProvider(provider.GetRequiredService<DistributedCacheProvider>(), provider.GetRequiredService<IMemoryCache>()));
services.TryAddSingleton<IDistributedAppCache, HybridCachingService>();

return services;
}

public static IServiceCollection AddDistributedHybridLazyCache(this IServiceCollection services,
Func<IServiceProvider, HybridCachingService> implementationFactory)
{
if (services == null) throw new ArgumentNullException(nameof(services));
if (implementationFactory == null) throw new ArgumentNullException(nameof(implementationFactory));

services.AddOptions();
services.TryAddSingleton<IMemoryCache, MemoryCache>();
services.TryAddSingleton<DistributedCacheProvider>();
services.TryAddSingleton<IDistributedCacheProvider>(provider => new HybridCacheProvider(provider.GetRequiredService<DistributedCacheProvider>(), provider.GetRequiredService<IMemoryCache>()));
services.TryAddSingleton<IDistributedAppCache>(implementationFactory);

return services;
}
}
}
Loading