2
platform/persistence/cache/README.md
vendored
2
platform/persistence/cache/README.md
vendored
@@ -1,2 +0,0 @@
|
|||||||
This bundle introduces a persistence cache to Open MCT Web to
|
|
||||||
limit the number of persistence requests issued by the platform.
|
|
||||||
49
platform/persistence/cache/bundle.js
vendored
49
platform/persistence/cache/bundle.js
vendored
@@ -1,49 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT Web includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define*/
|
|
||||||
|
|
||||||
define([
|
|
||||||
"./src/CachingPersistenceDecorator",
|
|
||||||
'legacyRegistry'
|
|
||||||
], function (
|
|
||||||
CachingPersistenceDecorator,
|
|
||||||
legacyRegistry
|
|
||||||
) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
legacyRegistry.register("platform/persistence/cache", {
|
|
||||||
"name": "Persistence cache",
|
|
||||||
"description": "Cache to improve availability of persisted objects.",
|
|
||||||
"extensions": {
|
|
||||||
"components": [
|
|
||||||
{
|
|
||||||
"provides": "persistenceService",
|
|
||||||
"type": "decorator",
|
|
||||||
"implementation": CachingPersistenceDecorator,
|
|
||||||
"depends": [
|
|
||||||
"PERSISTENCE_SPACE"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT Web includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This bundle decorates the persistence service to maintain a local cache
|
|
||||||
* of persisted documents.
|
|
||||||
* @namespace platform/persistence/cache
|
|
||||||
*/
|
|
||||||
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.
|
|
||||||
*
|
|
||||||
* @memberof platform/persistence/cache
|
|
||||||
* @constructor
|
|
||||||
* @param {string[]} cacheSpaces persistence space names which
|
|
||||||
* should be cached
|
|
||||||
* @param {PersistenceService} persistenceService the service which
|
|
||||||
* implements object persistence, whose inputs/outputs
|
|
||||||
* should be cached.
|
|
||||||
* @implements {PersistenceService}
|
|
||||||
*/
|
|
||||||
function CachingPersistenceDecorator(cacheSpaces, persistenceService) {
|
|
||||||
var spaces = cacheSpaces || [], // List of spaces to cache
|
|
||||||
cache = {}; // Where objects will be stored
|
|
||||||
|
|
||||||
// Arrayify list of spaces to cache, if necessary.
|
|
||||||
spaces = Array.isArray(spaces) ? spaces : [ spaces ];
|
|
||||||
|
|
||||||
// Initialize caches
|
|
||||||
spaces.forEach(function (space) {
|
|
||||||
cache[space] = {};
|
|
||||||
});
|
|
||||||
|
|
||||||
this.spaces = spaces;
|
|
||||||
this.cache = cache;
|
|
||||||
this.persistenceService = persistenceService;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the cached instance of an object to a new value
|
|
||||||
function replaceValue(valueHolder, newValue) {
|
|
||||||
var v = valueHolder.value;
|
|
||||||
|
|
||||||
// If it's a JS object, we want to replace contents, so that
|
|
||||||
// everybody gets the same instance.
|
|
||||||
if (typeof v === 'object' && v !== null) {
|
|
||||||
// Only update contents if these are different instances
|
|
||||||
if (v !== newValue) {
|
|
||||||
// Clear prior contents
|
|
||||||
Object.keys(v).forEach(function (k) {
|
|
||||||
delete v[k];
|
|
||||||
});
|
|
||||||
// Shallow-copy contents
|
|
||||||
Object.keys(newValue).forEach(function (k) {
|
|
||||||
v[k] = newValue[k];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise, just store the new value
|
|
||||||
valueHolder.value = newValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Place value in the cache for space, if there is one.
|
|
||||||
CachingPersistenceDecorator.prototype.addToCache = function (space, key, value) {
|
|
||||||
var cache = this.cache;
|
|
||||||
if (cache[space]) {
|
|
||||||
if (cache[space][key]) {
|
|
||||||
replaceValue(cache[space][key], value);
|
|
||||||
} else {
|
|
||||||
cache[space][key] = { value: value };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a function for putting value into a cache;
|
|
||||||
// useful for then-chaining.
|
|
||||||
CachingPersistenceDecorator.prototype.putCache = function (space, key) {
|
|
||||||
var self = this;
|
|
||||||
return function (value) {
|
|
||||||
self.addToCache(space, key, value);
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CachingPersistenceDecorator.prototype.listSpaces = function () {
|
|
||||||
return this.persistenceService.listSpaces();
|
|
||||||
};
|
|
||||||
|
|
||||||
CachingPersistenceDecorator.prototype.listObjects = function (space) {
|
|
||||||
return this.persistenceService.listObjects(space);
|
|
||||||
};
|
|
||||||
|
|
||||||
CachingPersistenceDecorator.prototype.createObject = function (space, key, value) {
|
|
||||||
this.addToCache(space, key, value);
|
|
||||||
return this.persistenceService.createObject(space, key, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
CachingPersistenceDecorator.prototype.readObject = function (space, key) {
|
|
||||||
var cache = this.cache;
|
|
||||||
return (cache[space] && cache[space][key]) ?
|
|
||||||
fastPromise(cache[space][key].value) :
|
|
||||||
this.persistenceService.readObject(space, key)
|
|
||||||
.then(this.putCache(space, key));
|
|
||||||
};
|
|
||||||
|
|
||||||
CachingPersistenceDecorator.prototype.updateObject = function (space, key, value) {
|
|
||||||
var self = this;
|
|
||||||
return this.persistenceService.updateObject(space, key, value)
|
|
||||||
.then(function (result) {
|
|
||||||
self.addToCache(space, key, value);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
CachingPersistenceDecorator.prototype.deleteObject = function (space, key, value) {
|
|
||||||
if (this.cache[space]) {
|
|
||||||
delete this.cache[space][key];
|
|
||||||
}
|
|
||||||
return this.persistenceService.deleteObject(space, key, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
return CachingPersistenceDecorator;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
||||||
* as represented by the Administrator of the National Aeronautics and Space
|
|
||||||
* Administration. All rights reserved.
|
|
||||||
*
|
|
||||||
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*
|
|
||||||
* Open MCT Web includes source code licensed under additional open source
|
|
||||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
||||||
* this source code distribution or the Licensing information page available
|
|
||||||
* at runtime from the About dialog for additional information.
|
|
||||||
*****************************************************************************/
|
|
||||||
/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/
|
|
||||||
|
|
||||||
define(
|
|
||||||
["../src/CachingPersistenceDecorator"],
|
|
||||||
function (CachingPersistenceDecorator) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var PERSISTENCE_METHODS = [
|
|
||||||
"listSpaces",
|
|
||||||
"listObjects",
|
|
||||||
"createObject",
|
|
||||||
"readObject",
|
|
||||||
"updateObject",
|
|
||||||
"deleteObject"
|
|
||||||
];
|
|
||||||
|
|
||||||
describe("The caching persistence decorator", function () {
|
|
||||||
var testSpace,
|
|
||||||
mockPersistence,
|
|
||||||
mockCallback,
|
|
||||||
decorator;
|
|
||||||
|
|
||||||
function mockPromise(value) {
|
|
||||||
return {
|
|
||||||
then: function (callback) {
|
|
||||||
return mockPromise(callback(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
|
||||||
|
|
||||||
|
|
||||||
testSpace = "TEST";
|
|
||||||
mockPersistence = jasmine.createSpyObj(
|
|
||||||
"persistenceService",
|
|
||||||
PERSISTENCE_METHODS
|
|
||||||
);
|
|
||||||
mockCallback = jasmine.createSpy("callback");
|
|
||||||
|
|
||||||
PERSISTENCE_METHODS.forEach(function (m) {
|
|
||||||
mockPersistence[m].andReturn(mockPromise({
|
|
||||||
method: m
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
decorator = new CachingPersistenceDecorator(
|
|
||||||
testSpace,
|
|
||||||
mockPersistence
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("delegates all methods", function () {
|
|
||||||
PERSISTENCE_METHODS.forEach(function (m) {
|
|
||||||
// Reset the callback
|
|
||||||
mockCallback = jasmine.createSpy("callback");
|
|
||||||
// Invoke the method; avoid using a key that will be cached
|
|
||||||
decorator[m](testSpace, "testKey" + m, "testValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
// Should have gotten that method's plain response
|
|
||||||
expect(mockCallback).toHaveBeenCalledWith({ method: m });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does not repeat reads of cached objects", function () {
|
|
||||||
// Perform two reads
|
|
||||||
decorator.readObject(testSpace, "someKey", "someValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
decorator.readObject(testSpace, "someKey", "someValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
|
|
||||||
// Should have only delegated once
|
|
||||||
expect(mockPersistence.readObject.calls.length).toEqual(1);
|
|
||||||
|
|
||||||
// But both promises should have resolved
|
|
||||||
expect(mockCallback.calls.length).toEqual(2);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it("gives a single instance of cached objects", function () {
|
|
||||||
// Perform two reads
|
|
||||||
decorator.readObject(testSpace, "someKey", "someValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
decorator.readObject(testSpace, "someKey", "someValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
|
|
||||||
// Results should have been pointer-identical
|
|
||||||
expect(mockCallback.calls[0].args[0])
|
|
||||||
.toBe(mockCallback.calls[1].args[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("maintains the same cached instance between reads/writes", function () {
|
|
||||||
var testObject = { abc: "XYZ!" };
|
|
||||||
|
|
||||||
// Perform two reads with a write in between
|
|
||||||
decorator.readObject(testSpace, "someKey", "someValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
decorator.updateObject(testSpace, "someKey", testObject);
|
|
||||||
decorator.readObject(testSpace, "someKey", "someValue")
|
|
||||||
.then(mockCallback);
|
|
||||||
|
|
||||||
// Results should have been pointer-identical
|
|
||||||
expect(mockCallback.calls[0].args[0])
|
|
||||||
.toBe(mockCallback.calls[1].args[0]);
|
|
||||||
|
|
||||||
// But contents should have been equal to the written object
|
|
||||||
expect(mockCallback).toHaveBeenCalledWith(testObject);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("is capable of reading/writing strings", function () {
|
|
||||||
// Efforts made to keep cached objects pointer-identical
|
|
||||||
// would break on strings - so make sure cache isn't
|
|
||||||
// breaking when we read/write strings.
|
|
||||||
decorator.createObject(testSpace, "someKey", "someValue");
|
|
||||||
decorator.updateObject(testSpace, "someKey", "someOtherValue");
|
|
||||||
decorator.readObject(testSpace, "someKey").then(mockCallback);
|
|
||||||
expect(mockCallback).toHaveBeenCalledWith("someOtherValue");
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
Reference in New Issue
Block a user