Compare commits

...

5 Commits

Author SHA1 Message Date
ab4d73dba6 [Persistence] Add bundle definition
...for multi-persistence support.
2015-11-05 12:23:49 -08:00
92755f040c [Persistence] Initially implement methods
...of MultiPersistenceDecorator.
2015-11-05 11:55:42 -08:00
551f95363f [Persistence] Begin implementing decorator
Begin implementing decorator to map to multiple underlying
persistence spaces, nasa/openmctweb#245.
2015-11-05 11:45:02 -08:00
7f5ce2e712 [Persistence] Simplify queuing 2015-11-05 11:30:01 -08:00
e0c5cb099d [Persistence] Sketch in persistence table 2015-11-04 17:00:51 -08:00
5 changed files with 384 additions and 0 deletions

View File

@ -0,0 +1,32 @@
{
"extensions": {
"components": [
{
"type": "decorator",
"provides": "persistenceService",
"implementation": "MultiPersistenceDecorator.js",
"depends": [
"persistenceService",
"$q",
"MULTI_PERSISTENCE_SPACE_MAPPINGS",
"MULTI_PERSISTENCE_DEFAULT_SPACE",
"PERSISTENCE_SPACE"
]
}
],
"constants": [
{
"key": "MULTI_PERSISTENCE_SPACE_MAPPINGS",
"value": {},
"priority": "fallback",
"comment": "Maps identifiers to persistence spaces (statically.)"
},
{
"key": "MULTI_PERSISTENCE_DEFAULT_SPACE",
"value": "mct",
"priority": "fallback",
"comment": "Should be overridden with a reasonable default."
}
]
}
}

View File

@ -0,0 +1,73 @@
/*****************************************************************************
* 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*/
define(
[],
function () {
'use strict';
function AsyncMutex($q) {
this.queue = [];
this.$q = $q;
}
AsyncMutex.prototype.acquire = function (callback) {
var deferred = this.$q.defer(),
queue = this.queue;
function advance() {
if (queue.length > 0) {
queue.shift()();
}
}
function release(result) {
deferred.resolve(result);
advance();
}
function next() {
try {
callback(release);
} catch (e) {
deferred.reject(e);
advance();
}
}
queue.push(next);
if (queue.length === 1) {
advance();
}
return deferred.promise;
};
AsyncMutex.prototype.use = function (callback) {
return this.acquire(function (release) {
release(callback);
});
};
}
);

View File

@ -0,0 +1,125 @@
/*****************************************************************************
* 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 implements a persistence service which uses ElasticSearch to
* store documents.
* @namespace platform/persistence/elastic
*/
define(
['./PersistenceTable'],
function (PersistenceTable) {
'use strict';
function MultiPersistenceDecorator(
persistenceService,
$q,
spaceMappings,
defaultSpace,
spaceToRemap
) {
this.table = new PersistenceTable(
persistenceService,
$q,
spaceMappings,
defaultSpace
);
this.spaceToRemap = spaceToRemap;
this.persistenceService = persistenceService;
this.$q = $q;
}
// Public API
MultiPersistenceDecorator.prototype.listSpaces = function () {
var spaceToRemap = this.spaceToRemap,
mappedSpaces = this.table.getSpaces();
return this.persistenceService.listSpaces.then(function (spaces) {
// Hide the existence of alternate spaces; make them
// appear as the one global space for storing domain objects.
return spaces.filter(function (space) {
return mappedSpaces.indexOf(space) === -1;
}).concat([spaceToRemap]);
});
};
MultiPersistenceDecorator.prototype.listObjects = function (space) {
var persistenceService = this.persistenceService;
if (space === this.spaceToRemap) {
return this.$q.all(this.mappedSpaces.map(function (s) {
return persistenceService.listObjects(s);
})).then(function (lists) {
return lists.reduce(function (a, b) {
return a.concat(b);
}, []);
});
}
return persistenceService.listObjects(space);
};
MultiPersistenceDecorator.prototype.createObject = function (space, key, value) {
var persistenceService = this.persistenceService,
table = this.table;
if (space === this.spaceToRemap) {
return table.getSpace(value.location).then(function (s) {
return table.setSpace(key, s).then(function () {
return persistenceService.createObject(s, key, value);
});
});
}
return persistenceService.createObject(space, key, value);
};
MultiPersistenceDecorator.prototype.readObject = function (space, key) {
var persistenceService = this.persistenceService;
if (space === this.spaceToRemap) {
return this.table.getSpace(key).then(function (s) {
return persistenceService.readObject(s, key);
});
}
return persistenceService.readObject(space, key);
};
MultiPersistenceDecorator.prototype.updateObject = function (space, key, value) {
var persistenceService = this.persistenceService,
table = this.table,
self = this;
if (space === this.spaceToRemap) {
return this.table.getSpace(key).then(function (currentSpace) {
return this.table.getSpace(value.location).then(function (newSpace) {
// TODO: Also move children when space change happens?
return (newSpace === currentSpace) ?
persistenceService.updateObject(newSpace, key, value) :
self.createObject(space, key, value);
});
});
}
return persistenceService.createObject(space, key, value);
};
MultiPersistenceDecorator.prototype.deleteObject = function (space, key, value) {
};
return MultiPersistenceDecorator;
}
);

View File

@ -0,0 +1,77 @@
/*****************************************************************************
* 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*/
define(
['./AsyncMutex', './PersistenceTableInitializer'],
function (AsyncMutex, PersistenceTableInitializer) {
'use strict';
function PersistenceTable(
persistenceService,
$q,
spaceMappings,
defaultSpace
) {
var spaces = Object.keys(spaceMappings).map(function (id) {
return spaceMappings[id];
}).sort().filter(function (item, i, arr) {
return i === 0 || arr[i - 1] !== item;
}),
initializer =
new PersistenceTableInitializer($q, persistenceService),
self = this;
this.mutex = new AsyncMutex($q);
this.$q = $q;
this.staticSpaceMappings = spaceMappings;
this.defaultSpace = defaultSpace;
this.spaces = spaces;
this.mutex.acquire(function (release) {
initializer.initialTable(spaces).then(function (table) {
self.observedSpaceMappings = table;
}).then(release);
});
}
PersistenceTable.prototype.setSpace = function (id, space) {
var mappings = this.observedSpaceMappings;
return this.mutex.use(function () {
this.observedSpaceMappings[id] = space;
});
};
PersistenceTable.prototype.getSpace = function (id) {
var self = this;
return this.mutex.use(function () {
return self.staticSpaceMappings[id] ||
self.observedSpaceMappings[id];
});
};
PersistenceTable.prototype.getSpaces = function () {
return this.spaces;
};
}
);

View File

@ -0,0 +1,77 @@
/*global define*/
define(
[],
function () {
'use strict';
function PersistenceTableInitializer($q, persistenceService) {
this.$q = $q;
this.persistenceService = persistenceService;
}
PersistenceTableInitializer.prototype.initialTable = function (spaces) {
var persistenceService = this.persistenceService,
$q = this.$q,
unreconciledSpaceMappings = {},
reconciledSpaceMappings = {};
function initializeSpace(space) {
return persistenceService.listObjects().then(function (ids) {
ids.forEach(function (id) {
unreconciledSpaceMappings[id] =
unreconciledSpaceMappings[id] || [];
unreconciledSpaceMappings[id].push(space);
});
});
}
function choose(models) {
var index = 0,
greatest = Number.NEGATIVE_INFINITY;
models.forEach(function (model, i) {
if (model.persisted !== undefined &&
model.persisted > greatest) {
greatest = model.persisted;
index = i;
}
});
return index;
}
function reconcileConflict(id) {
var candidateSpaces = unreconciledSpaceMappings[id];
return $q.all(candidateSpaces.map(function (space) {
return persistenceService.readObject(space, id);
})).then(choose).then(function (index) {
reconciledSpaceMappings[id] = candidateSpaces[index];
});
}
function reconcileConflicts() {
var toReconcile = [];
Object.keys(unreconciledSpaceMappings).forEach(function (id) {
if (unreconciledSpaceMappings[id].length > 1) {
toReconcile.push(id);
} else {
reconciledSpaceMappings[id] =
unreconciledSpaceMappings[id][0];
}
});
return $q.all(toReconcile.map(reconcileConflict));
}
function giveResult() {
return reconciledSpaceMappings;
}
return $q.all(spaces.map(initializeSpace))
.then(reconcileConflicts)
.then(giveResult);
};
}
);