From 6ee834a147a6af84032501de0bcc7e3219319f82 Mon Sep 17 00:00:00 2001 From: tysonrm Date: Sat, 7 May 2022 19:04:05 -0500 Subject: [PATCH] add support for federated queries --- .gitignore | 9 +++++++++ public/aegis.config.test.json | 1 + src/domain/datasource-factory.js | 8 +++++--- src/domain/datasource.js | 2 +- src/domain/federated-query.js | 34 ++++++++++++++++++++++++++++++++ src/domain/make-relations.js | 24 +++++++++++++++++----- src/domain/shared-memory.js | 6 ++++-- src/domain/use-cases/index.js | 21 ++++++++++++++++---- 8 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 src/domain/federated-query.js diff --git a/.gitignore b/.gitignore index 429f4787..2e79f1fe 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,12 @@ node_modules .idea .DS_Store tst-rtry.js +.VSCodeCounter/2022-05-07_18-48-46/details.md +.VSCodeCounter/2022-05-07_18-48-46/diff-details.md +.VSCodeCounter/2022-05-07_18-48-46/diff.csv +.VSCodeCounter/2022-05-07_18-48-46/diff.md +.VSCodeCounter/2022-05-07_18-48-46/diff.txt +.VSCodeCounter/2022-05-07_18-48-46/results.csv +.VSCodeCounter/2022-05-07_18-48-46/results.json +.VSCodeCounter/2022-05-07_18-48-46/results.md +.VSCodeCounter/2022-05-07_18-48-46/results.txt diff --git a/public/aegis.config.test.json b/public/aegis.config.test.json index 51196f88..e5e819b8 100644 --- a/public/aegis.config.test.json +++ b/public/aegis.config.test.json @@ -14,6 +14,7 @@ "adapters": { "desc": "adapter config", "cacheSize": 3000, + "enableFederatedQueries": false, "defaultDatasource": "DataSourceFile", "datasources": { "DataSourceMemory": { diff --git a/src/domain/datasource-factory.js b/src/domain/datasource-factory.js index c07173dd..33bce02b 100644 --- a/src/domain/datasource-factory.js +++ b/src/domain/datasource-factory.js @@ -15,6 +15,7 @@ import dsconfig from '../adapters/datasources' import sysconf from '../config' import DataSource from './datasource' import { withSharedMemory } from './shared-memory' +import compose from './util/compose' const defaultAdapter = sysconf.hostConfig.adapters.defaultDatasource const DefaultDataSource = adapters[defaultAdapter] @@ -54,7 +55,7 @@ const DataSourceFactory = (() => { * @property {string} adapterName specify the adapter to use * @property {Map|import('sharedmap').SharedMap} dsMap * data source location and structure for private or shared memory - * @property {function(typeof DataSource):typeof DataSource} mixin + * @property {function(typeof DataSource):typeof DataSource[]} mixins */ /** @@ -93,8 +94,9 @@ const DataSourceFactory = (() => { const spec = ModelFactory.getModelSpec(name) const dsMap = options.dsMap || new Map() const DsClass = createDataSourceClass(spec, options) - const MixinClass = options.mixin ? options.mixin(DsClass) : DsClass - const newDs = new MixinClass(dsMap, this, name) + const DsMixinClass = + options.mixins.length > 0 ? compose(...options.mixins)(DsClass) : DsClass + const newDs = new DsMixinClass(dsMap, this, name) if (!options.ephemeral) dataSources.set(name, newDs) return newDs } diff --git a/src/domain/datasource.js b/src/domain/datasource.js index 5d9447e4..15cde02c 100644 --- a/src/domain/datasource.js +++ b/src/domain/datasource.js @@ -111,7 +111,7 @@ export default class DataSource { } getCacheSizeBytes () { - return this.count() * roughSizeOfObject(this.dsMap.reduce(a => a)) + return this.count() * roughSizeOfObject(this.findSync({ count: 1 })) } /** diff --git a/src/domain/federated-query.js b/src/domain/federated-query.js new file mode 100644 index 00000000..7cf0e636 --- /dev/null +++ b/src/domain/federated-query.js @@ -0,0 +1,34 @@ +'use strict' + +import { EventBrokerFactory } from '.' +import { requireRemoteObject } from './make-relations' + +export const FederationMixin = superclass => + class extends superclass { + /** + * Deserialize + * @override + * @param {*} id + * @returns {import('.').Model} + */ + async find (id) { + try { + const result = await super.find(id) + if (!result || result.length < 1) return this.findRemote(id) + return result + } catch (error) { + console.error({ fn: 'federatedFindById', error }) + } + } + + async findRemote (id) { + const event = await requireRemoteObject( + null, + { type: 'findById', modelName: this.name, id }, + EventBrokerFactory.getInstance() + ) + if (event?.model) return event.model + console.debug('federated findById: no model found') + return {} + } + } diff --git a/src/domain/make-relations.js b/src/domain/make-relations.js index 8e5fd1b8..0ee2b60f 100644 --- a/src/domain/make-relations.js +++ b/src/domain/make-relations.js @@ -43,7 +43,8 @@ export const relationType = { containsMany: async (model, ds, rel) => await Promise.all( model[rel.arrayKey].map(arrayItem => ds.find(arrayItem[rel.foreignKey])) - ) + ), + findById: async (model, ds, rel) => ds.find(id) } const referentialIntegrity = { @@ -134,13 +135,26 @@ export function requireRemoteObject (model, relation, broker, ...args) { console.debug({ fn: requireRemoteObject.name }) + if (!model && relation.type !== 'findById') { + console.error({ + fn: requireRemoteObject.name, + error: 'model param missing' + }) + return + } + + const name = (model ? model.getName() : relation.modelName).toUpperCase() + const id = model ? model.getId() : relation.id + const eventSource = name + const eventTarget = model ? relation.modelName.toUpperCase() : null + const requestData = { eventName: request, - modelName: model.getName().toUpperCase(), + modelName: name, eventType: externalCacheRequest.name, - eventSource: model.getName().toUpperCase(), - eventTarget: relation.modelName.toUpperCase(), - modelId: model.getId(), + eventSource, + eventTarget, + modelId: id, relation, model, args diff --git a/src/domain/shared-memory.js b/src/domain/shared-memory.js index 93fc81c5..65c7f5e2 100644 --- a/src/domain/shared-memory.js +++ b/src/domain/shared-memory.js @@ -126,8 +126,10 @@ export function withSharedMemory ( return createDataSource.call(factory, name, { ...options, dsMap: sharedMap, - mixin: DsClass => - class DataSourceSharedMemory extends SharedMemoryMixin(DsClass) {} + mixins: [ + DsClass => + class DataSourceSharedMemory extends SharedMemoryMixin(DsClass) {} + ].concat(options.mixins) }) return createDataSource.call(factory, name, options) diff --git a/src/domain/use-cases/index.js b/src/domain/use-cases/index.js index e494d548..67068afd 100644 --- a/src/domain/use-cases/index.js +++ b/src/domain/use-cases/index.js @@ -17,6 +17,13 @@ import makeHotReload from './hot-reload' import brokerEvents from './broker-events' import { isMainThread } from 'worker_threads' +import { FederationMixin } from '../federated-query' + +const dsOpts = { + mixins: [ + DsClass => class DataSourceFederated extends FederationMixin(DsClass) {} + ] +} export function registerEvents () { // main thread handles event dispatch @@ -44,7 +51,7 @@ function findLocalRelatedModels (modelName) { function findLocalRelatedDatasources (modelName) { return findLocalRelatedModels(modelName).map(modelName => ({ modelName, - dsMap: DataSourceFactory.getSharedDataSource(modelName).dsMap + dsMap: DataSourceFactory.getSharedDataSource(modelName, dsOpts).dsMap })) } @@ -64,12 +71,18 @@ function buildOptions (model) { return { ...options, // main thread does not write to persistent store - repository: DataSourceFactory.getSharedDataSource(model.modelName), + repository: DataSourceFactory.getSharedDataSource( + model.modelName, + dsOpts + ), // only main thread knows about thread pools (no nesting) threadpool: ThreadPoolFactory.getThreadPool(model.modelName, { preload: false, - sharedMap: DataSourceFactory.getSharedDataSource(model.modelName).dsMap, + sharedMap: DataSourceFactory.getSharedDataSource( + model.modelName, + dsOpts + ).dsMap, dsRelated: findLocalRelatedDatasources(model.modelName) }) } @@ -77,7 +90,7 @@ function buildOptions (model) { return { ...options, // only worker threads can write to persistent storage - repository: DataSourceFactory.getSharedDataSource(model.modelName) + repository: DataSourceFactory.getSharedDataSource(model.modelName, dsOpts) } } }