[Persistence] Add persistence cache

Add a decorator to handle the caching of objects stored to
and/or read from persistence (bring over from pre-Angular
branch.) Supports the WARP Telemetry Adapter; the absence
of such a cache has been observed to result in significant
latency in instantiating autoflow tabular views for packets.
WTD-644.

Also, include an empty test file to ensure that the added
decorator is included in code coverage estimation (and to
provide a location for tests to be written later.)
This commit is contained in:
Victor Woeltjen 2015-01-06 12:44:54 -08:00
parent 22d2be8942
commit 75a0cbe43d
4 changed files with 119 additions and 0 deletions

View File

@ -8,6 +8,12 @@
"type": "provider",
"implementation": "CouchPersistenceProvider.js",
"depends": [ "$http", "$q", "PERSISTENCE_SPACE", "COUCHDB_PATH" ]
},
{
"provides": "persistenceService",
"type": "decorator",
"implementation": "CachingPersistenceDecorator.js",
"depends": [ "PERSISTENCE_SPACE" ]
}
],
"constants": [

View File

@ -0,0 +1,100 @@
/*global define*/
define(
[],
function () {
'use strict';
/**
* A caching persistence decorator maintains local copies of persistent objects
* that have been loaded, and keeps them in sync after writes. This allows
* retrievals to occur more quickly after the first load.
*
* @constructor
* @param {string[]} CACHE_SPACES persistence space names which
* should be cached
* @param {PersistenceService} persistenceService the service which
* implements object persistence, whose inputs/outputs
* should be cached.
*/
function CachingPersistenceDecorator(CACHE_SPACES, persistenceService) {
var spaces = CACHE_SPACES || [], // List of spaces to cache
cache = {}; // Where objects will be stored
// Utility function; avoid sharing one instance everywhere.
function clone(value) {
// Only clone truthy values (no need to clone undefined, false...)
return value && JSON.parse(JSON.stringify(value));
}
// Place value in the cache for space, if there is one.
function addToCache(space, key, value) {
if (cache[space]) {
cache[space][key] = { value: clone(value) };
}
}
// Create a function for putting value into a cache;
// useful for then-chaining.
function putCache(space, key) {
return function (value) {
addToCache(space, key, value);
return value;
};
}
// Wrap as a thenable; used instead of $q.when because that
// will resolve on a future tick, which can cause latency
// issues (which this decorator is intended to address.)
function fastPromise(value) {
return {
then: function (callback) {
return fastPromise(callback(value));
}
};
}
// Arrayify list of spaces to cache, if necessary.
spaces = Array.isArray(spaces) ? spaces : [ spaces ];
// Initialize caches
spaces.forEach(function (space) {
cache[space] = {};
});
// Provide PersistenceService interface; mostly delegate to the
// decorated service, intervene and cache where appropriate.
return {
listSpaces: function () {
return persistenceService.listSpaces();
},
listObjects: function (space) {
return persistenceService.listObjects(space);
},
createObject: function (space, key, value) {
addToCache(space, key, value);
return persistenceService.createObject(space, key, value);
},
readObject: function (space, key) {
return (cache[space] && cache[space][key]) ?
fastPromise(clone(cache[space][key].value)) :
persistenceService.readObject(space, key)
.then(putCache(space, key));
},
updateObject: function (space, key, value) {
addToCache(space, key, value);
return persistenceService.updateObject(space, key, value);
},
deleteObject: function (space, key, value) {
if (cache[space]) {
delete cache[space][key];
}
return persistenceService.deleteObject(space, key, value);
}
};
}
return CachingPersistenceDecorator;
}
);

View File

@ -0,0 +1,12 @@
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
define(
["../src/CachingPersistenceDecorator"],
function (CachingPersistenceDecorator) {
"use strict";
describe("The caching persistence decorator", function () {
});
}
);

View File

@ -1,4 +1,5 @@
[
"CachingPersistenceDecorator",
"CouchDocument",
"CouchIndicator",
"CouchPersistenceProvider"