nope-js-node
Version:
NoPE Runtime for Nodejs. For Browser-Support please use nope-browser
220 lines (219 loc) • 7.7 kB
JavaScript
;
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateDefineMaster = exports.waitForDispatcher = exports.enableTimeSyncing = exports.generatePingAccessors = exports.generatePingServices = void 0;
const arrayMethods_1 = require("../../helpers/arrayMethods");
const getLogger_1 = require("../../logger/getLogger");
const logger = (0, getLogger_1.getNopeLogger)("baseService");
/**
* Generate and registers a ping service.
*
* @author M.Karkowski
* @export
* @param {INopeDispatcher} dispatcher
* @return {*} The function to ping all dispatchers.
*/
async function generatePingServices(dispatcher) {
// Name of the Service
const serviceName = `nope/baseService/ping`;
const ping = async () => {
return {
dispatcherId: dispatcher.id,
timestamp: dispatcher.connectivityManager.now,
};
};
logger.info("Adding 'ping' service!");
if (!dispatcher.rpcManager.isProviding(serviceName)) {
// Registers the Ping Method at the Dispatcher.
await dispatcher.rpcManager.registerService(ping, {
id: serviceName,
schema: {
inputs: [],
outputs: {
type: "object",
properties: {
dispatcherId: {
type: "string",
description: "Id of the responding Dispatcher",
},
timestamp: {
type: "number",
description: "UTC-Timestamp of the system which is responding",
},
},
},
type: "function",
description: "Ping",
},
});
}
return await generatePingAccessors(dispatcher);
}
exports.generatePingServices = generatePingServices;
/**
* Helper to generate the Accessors to ping dispatchers or ping all
* systems
*
* @author M.Karkowski
* @export
* @param {INopeDispatcher} dispatcher The Dispatcher, used to perform the calls.
* @return {*}
*/
async function generatePingAccessors(dispatcher) {
const serviceName = `nope/baseService/ping`;
logger.info("Adding 'determinePing' service!");
// Function to determine the ping in the services.
const determinePing = async (target) => {
// Call the Pings
const start = dispatcher.connectivityManager.now;
const result = await dispatcher.rpcManager.performCall(serviceName, [], { target });
const delay = dispatcher.connectivityManager.now;
const ping = delay - start;
return {
ping,
...result,
};
};
logger.info("Adding 'pingAll' service!");
// Function to Ping all Services
const pingAll = async () => {
const dispatchers = Array.from(dispatcher.rpcManager.services.keyMappingReverse.get(serviceName));
const promises = dispatchers.map((target) => {
return determinePing(target);
});
const pings = await Promise.all(promises);
const avg = (0, arrayMethods_1.avgOfArray)(pings, "ping");
const max = (0, arrayMethods_1.maxOfArray)(pings, "ping");
const min = (0, arrayMethods_1.minOfArray)(pings, "ping");
return {
pings,
avg,
max,
min,
};
};
return {
determinePing,
pingAll,
};
}
exports.generatePingAccessors = generatePingAccessors;
/**
* Registers a sync service, which will syncronize the time
*
* @author M.Karkowski
* @export
* @param {INopeDispatcher} dispatcher
* @return {*} The Sync-Method to call
*/
async function enableTimeSyncing(dispatcher) {
if (!dispatcher.rpcManager.isProviding(`nope/baseService/ping`)) {
await generatePingServices(dispatcher);
}
const determinePing = (await generatePingAccessors(dispatcher)).determinePing;
// Function to determine the ping in the services.
const manualSyncTime = async () => {
const masterId = dispatcher.connectivityManager.master.id;
const ping = await determinePing(masterId);
// Now use the delay to synchronize the time.
dispatcher.connectivityManager.syncTime(ping.timestamp, ping.ping / 2);
};
dispatcher.connectivityManager.dispatchers.onChange.subscribe((changes) => {
if (dispatcher.disposing) {
return;
}
if (!dispatcher.connectivityManager.isMaster) {
manualSyncTime()
.catch((e) => {
logger.error("Failed synchronizing time");
logger.error(e);
})
.then((_) => {
logger.info(`Synchronized time with master=${dispatcher.connectivityManager.master.id}`);
});
}
});
logger.info("Adding 'manualSyncTime' service!");
return {
manualSyncTime,
};
}
exports.enableTimeSyncing = enableTimeSyncing;
/**
* Helper, that lets enables waiting for a required dispatcher.
*
* @author M.Karkowski
* @export
* @param {INopeDispatcher} dispatcher
* @return {*}
*/
async function waitForDispatcher(dispatcher, name) {
dispatcher.ready.getter = (value) => {
return (dispatcher.connectivityManager.dispatchers.data
.getContent()
.includes(name) && value);
};
return {};
}
exports.waitForDispatcher = waitForDispatcher;
/**
* A Helper to create a Service to manually define a master.
*
* @author M.Karkowski
* @export
* @param {INopeDispatcher} dispatcher The Dispatcher to use.
* @return {*}
*/
async function generateDefineMaster(dispatcher) {
/**
* Create a Ping service.
*
* @author M.Karkowski
* @param {string} requestId
* @return {*}
*/
const defineAsMaster = async (value = true) => {
dispatcher.connectivityManager.isMaster = value;
};
// Registers the Ping Method at the Dispatcher.
await dispatcher.rpcManager.registerService(defineAsMaster, {
id: `nope/baseService/defineAsMaster`,
schema: {
description: "Defines the desired Node as Master. Propagets, that this system is a master",
inputs: [
{
name: "value",
optional: true,
schema: {
type: "boolean",
},
},
],
outputs: [],
},
});
const setMaster = async () => {
// Set the Master to True
dispatcher.connectivityManager.isMaster = true;
// Get the Master ID
const masterId = dispatcher.id;
const service = `nope/baseService/defineAsMaster`;
// Get the Matching Dispatchers.
const relevantDispatchers = dispatcher.rpcManager.services.keyMappingReverse.get(`nope/baseService/defineAsMaster`);
relevantDispatchers.delete(masterId);
const targets = Array.from(relevantDispatchers);
await dispatcher.rpcManager.performCall(targets.map((_) => {
return service;
}), [false], targets.map((target) => {
return { target, timeout: 200 };
}));
};
logger.info("Adding 'setMaster' service!");
return {
setMaster,
};
}
exports.generateDefineMaster = generateDefineMaster;