Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split cache and middlewares that use cache
API is changed.
Before:
application.use(droonga.cache({
size: cacheSize,
rules: [
{
regex: /^\//,
ttlInMilliSeconds: 1000
}
]
}));
After:
var cache = new droonga.cache({
size: cache
});
application.use('/cache/statistics',
droonga.middleware.cacheStatistics(cache));
application.use(droonga.middleware.cache(cache, {
rules: [
{
regex: /^\//,
ttlInMilliSeconds: 1000
}
]
}));- Loading branch information
Showing
14 changed files
with
400 additions
and
339 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| var createCache = require('uber-cache'); | ||
|
|
||
| var defaultSize = 100; | ||
|
|
||
| function normalizeOptions(options) { | ||
| options = options || {}; | ||
|
|
||
| options.size = options.size || defaultSize; | ||
|
|
||
| return options; | ||
| } | ||
|
|
||
| function Cache(options) { | ||
| options = normalizeOptions(options); | ||
| this.cache = createCache({ | ||
| size: options.size | ||
| }); | ||
| this.nGets = 0; | ||
| this.nHits = 0; | ||
| } | ||
| Cache.prototype = { | ||
| 'get': function(key, callback) { | ||
| return this.cache.get(key, function(error, cachedResponse) { | ||
| this.nGets++; | ||
| if (cachedResponse) { | ||
| this.nHits++; | ||
| } | ||
| callback(error, cachedResponse); | ||
| }.bind(this)); | ||
| }, | ||
|
|
||
| 'set': function(key, value, ttl, callback) { | ||
| return this.cache.set(key, value, ttl, callback); | ||
| }, | ||
|
|
||
| getStatistics: function() { | ||
| var hitRatio; | ||
| if (this.nGets == 0) { | ||
| hitRatio = 0.0; | ||
| } else { | ||
| hitRatio = (this.nHits / this.nGets) * 100; | ||
| } | ||
| return { | ||
| "nGets": this.nGets, | ||
| "nHits": this.nHits, | ||
| "hitRatio": hitRatio | ||
| }; | ||
| } | ||
| }; | ||
| module.exports = Cache; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module.exports = function middleware(cache) { | ||
| return function(request, response, next) { | ||
| response.jsonp(200, cache.getStatistics()); | ||
| } | ||
| } |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| var Entry = require('./entry'); | ||
| var Rule = require('./rule'); | ||
|
|
||
| var defaultTTLInMilliSeconds = 60 * 1000; | ||
|
|
||
| function getNormalizedTTLInMilliSeconds(options) { | ||
| var ttlInSeconds = options.ttlInSeconds || 0; | ||
| return options.ttl || | ||
| options.ttlInMilliSeconds || | ||
| (ttlInSeconds * 1000) || | ||
| 0; | ||
| } | ||
|
|
||
| function validateRules(rules) { | ||
| if (!Array.isArray(rules)) | ||
| throw new Error('rules must be an array'); | ||
|
|
||
| if (!rules.length) | ||
| throw new Error('you must specify one or more rules'); | ||
| } | ||
|
|
||
| function createRules(options) { | ||
| var ttlInMilliSeconds = | ||
| getNormalizedTTLInMilliSeconds(options) || | ||
| defaultTTLInMilliSeconds; | ||
|
|
||
| validateRules(options.rules); | ||
|
|
||
| return options.rules.map(function(rule) { | ||
| rule.ttlInMilliSeconds = getNormalizedTTLInMilliSeconds(rule) || | ||
| ttlInMilliSeconds; | ||
| return new Rule(rule); | ||
| }); | ||
| } | ||
|
|
||
| function findRule(rules, request) { | ||
| if (request.method != 'GET') | ||
| return null; | ||
|
|
||
| var foundRule = null; | ||
| rules.some(function(rule) { | ||
| if (rule.match(request)) | ||
| return foundRule = rule; | ||
| }); | ||
| return foundRule; | ||
| } | ||
|
|
||
| function generateKey(request) { | ||
| return request.method + '\n' + request.url; | ||
| } | ||
|
|
||
| function sendCachedResponse(response, cached) { | ||
| response.statusCode = cached.status; | ||
| Object.keys(cached.headers).forEach(function(key) { | ||
| response.setHeader(key, cached.headers[key]); | ||
| }); | ||
| response.setHeader('X-Droonga-Cached', 'yes'); | ||
| cached.body.forEach(function(chunk) { | ||
| response.write(chunk.data, chunk.encoding); | ||
| }); | ||
| response.end(); | ||
| } | ||
|
|
||
| module.exports = function cacheMiddleware(cache, options) { | ||
| var rules = createRules(options); | ||
|
|
||
| return function(request, response, next) { | ||
| var rule = findRule(rules, request); | ||
| if (!rule) { | ||
| next(); | ||
| return; | ||
| } | ||
|
|
||
| var cacheKey = generateKey(request); | ||
| cache.get(cacheKey, function(error, cachedResponse) { | ||
| if (error) { | ||
| console.error(error); | ||
| return; | ||
| } | ||
|
|
||
| if (cachedResponse) { | ||
| sendCachedResponse(response, cachedResponse); | ||
| } else { | ||
| var entry = new Entry(); | ||
| entry.hook(response, function(cachedResponse) { | ||
| cache.set(cacheKey, cachedResponse, rule.ttlInMilliSeconds); | ||
| }); | ||
| next(); | ||
| } | ||
| }); | ||
| }; | ||
| }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| var assert = require('chai').assert; | ||
|
|
||
| var Cache = require('../lib/cache'); | ||
|
|
||
| suite('Cache', function() { | ||
| suite('statistics', function() { | ||
| var cache; | ||
| setup(function() { | ||
| cache = new Cache(); | ||
| }); | ||
|
|
||
| test('nGets', function() { | ||
| assert.equal(cache.getStatistics().nGets, 0); | ||
| cache.get('key', function(error, cachedResponse) { | ||
| }); | ||
| assert.equal(cache.getStatistics().nGets, 1); | ||
| }); | ||
|
|
||
| test('nHits', function() { | ||
| cache.set('key', 'value'); | ||
| assert.equal(cache.getStatistics().nHits, 0); | ||
| cache.get('key', function(error, cachedResponse) { | ||
| }); | ||
| assert.equal(cache.getStatistics().nHits, 1); | ||
| }); | ||
|
|
||
| suite('hitRatio', function() { | ||
| test('0 gets', function() { | ||
| assert.equal(cache.getStatistics().hitRatio, 0.0); | ||
| }); | ||
|
|
||
| test('0 hits', function() { | ||
| cache.get('key', function(error, cachedResponse) { | ||
| }); | ||
| assert.equal(cache.getStatistics().hitRatio, 0.0); | ||
| }); | ||
|
|
||
| test('1/2 hits', function() { | ||
| cache.get('key', function(error, cachedResponse) { | ||
| }); | ||
| cache.set('key', 'value'); | ||
| cache.get('key', function(error, cachedResponse) { | ||
| }); | ||
| assert.equal(cache.getStatistics().hitRatio, 50.0); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
Oops, something went wrong.