aotrautils-srv 0.0.1057 → 0.0.1059
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
2
|
|
3
|
-
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (
|
3
|
+
/*utils COMMONS library associated with aotra version : «1_29072022-2359 (19/02/2025-23:50:43)»*/
|
4
4
|
/*-----------------------------------------------------------------------------*/
|
5
5
|
|
6
6
|
|
@@ -1551,6 +1551,13 @@ aotest.profile=function(rootObject,methodName,visited=[]){
|
|
1551
1551
|
|
1552
1552
|
|
1553
1553
|
|
1554
|
+
//================================================================
|
1555
|
+
//================= Tjread control utility methods =================
|
1556
|
+
//================================================================
|
1557
|
+
|
1558
|
+
|
1559
|
+
window.sleep = (millis) => new Promise( resolve => setTimeout(resolve, millis) );
|
1560
|
+
|
1554
1561
|
|
1555
1562
|
|
1556
1563
|
//================================================================
|
@@ -1988,6 +1995,15 @@ window.compare=function(value1,value2){
|
|
1988
1995
|
if(value1==value2) return 0;
|
1989
1996
|
}
|
1990
1997
|
|
1998
|
+
window.pushInArrayAsQueue=function(array, maxSize, item){
|
1999
|
+
if(maxSize<=getArraySize(array)){
|
2000
|
+
array.shift();
|
2001
|
+
}
|
2002
|
+
array.push(item);
|
2003
|
+
return item;
|
2004
|
+
}
|
2005
|
+
|
2006
|
+
|
1991
2007
|
|
1992
2008
|
/*KNOWN LIMITATIONS : You will have an incomplete browsing loop if there are the string values "break" or "continue" in your array or associative array !*/
|
1993
2009
|
// CAUTION : Only use «return foreach» syntax in client code loops with SINGLE-LEVEL nested loops ONLY !
|
@@ -1998,7 +2014,7 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
|
|
1998
2014
|
// -1: if(a<b)
|
1999
2015
|
// 1: if(a>b)
|
2000
2016
|
// for instance this is a valid javascript compare function : function(a, b){return a-b} so (100,20) returns positive result and (20,100) returns negative result !
|
2001
|
-
/*OPTIONAL*/compareFunction=null){
|
2017
|
+
/*OPTIONAL*/compareFunction=null, sortKeys=false){
|
2002
2018
|
|
2003
2019
|
// TODO : FIXME : Add the possibility to iterate over a string characters ?
|
2004
2020
|
|
@@ -2054,7 +2070,8 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
|
|
2054
2070
|
|
2055
2071
|
// Else : associative array
|
2056
2072
|
|
2057
|
-
|
2073
|
+
let keysToIterateOn=[];
|
2074
|
+
const associativeArray=arrayOfValues;
|
2058
2075
|
if(compareFunction){
|
2059
2076
|
|
2060
2077
|
// if(!associativeArray.hasOwnProperty){
|
@@ -2075,24 +2092,26 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
|
|
2075
2092
|
}
|
2076
2093
|
sortedKeysAndValues.sort(compareFunction);
|
2077
2094
|
|
2078
|
-
associativeArray={};
|
2095
|
+
// OLD : associativeArray={};
|
2079
2096
|
for(var i=0;i<sortedKeysAndValues.length;i++){
|
2080
2097
|
var item=sortedKeysAndValues[i];
|
2081
|
-
associativeArray[item.key]=item.value;
|
2098
|
+
// OLD : associativeArray[item.key]=item.value;
|
2099
|
+
keysToIterateOn.push(item.key);
|
2082
2100
|
}
|
2083
2101
|
|
2102
|
+
}else{
|
2103
|
+
keysToIterateOn=Object.keys(associativeArray);
|
2084
2104
|
}
|
2085
2105
|
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2090
|
-
//}
|
2091
|
-
|
2106
|
+
if(!compareFunction && sortKeys){
|
2107
|
+
keysToIterateOn=keysToIterateOn.sort();
|
2108
|
+
}
|
2109
|
+
|
2092
2110
|
var cnt=0;
|
2093
|
-
for(var key
|
2111
|
+
for(var key of keysToIterateOn){
|
2094
2112
|
if(associativeArray.hasOwnProperty && !associativeArray.hasOwnProperty(key)) continue;
|
2095
2113
|
var item=associativeArray[key];
|
2114
|
+
|
2096
2115
|
if(!compareFunction){ // If we have a compare function, then array is already filtered !
|
2097
2116
|
if(filterFunction && !filterFunction(item,key)) continue;
|
2098
2117
|
}
|
@@ -2108,6 +2127,8 @@ window.foreach=function(arrayOfValues,doOnEachFunction,
|
|
2108
2127
|
}// else continue looping...
|
2109
2128
|
cnt++;
|
2110
2129
|
}
|
2130
|
+
|
2131
|
+
|
2111
2132
|
|
2112
2133
|
//
|
2113
2134
|
return null;
|
@@ -2124,7 +2145,7 @@ window.getAt=function(associativeOrNormalArray,index,returnKey=false){
|
|
2124
2145
|
return foreach(associativeOrNormalArray,(item, key)=>{
|
2125
2146
|
if(index<=i) return returnKey?key:item;
|
2126
2147
|
i++;
|
2127
|
-
});
|
2148
|
+
},null,null,returnKey);
|
2128
2149
|
}
|
2129
2150
|
|
2130
2151
|
window.getFirst=function(associativeOrNormalArray){
|
@@ -2856,24 +2877,46 @@ Math.averageInArray=function(arrayOfValues){
|
|
2856
2877
|
return Math.sumInArray(arrayOfValues) / arrayOfValues.length;
|
2857
2878
|
};
|
2858
2879
|
|
2859
|
-
Math.maxInArray=function(arrayOfValues){
|
2880
|
+
Math.maxInArray=function(arrayOfValues, returnKey=false){
|
2860
2881
|
if(empty(arrayOfValues))
|
2861
2882
|
return null;
|
2862
|
-
|
2863
|
-
|
2864
|
-
|
2865
|
-
|
2866
|
-
|
2883
|
+
|
2884
|
+
// var max=arrayOfValues[0];
|
2885
|
+
// for (var i=0; i<arrayOfValues.length; i++)
|
2886
|
+
// if(max<arrayOfValues[i])
|
2887
|
+
// max=arrayOfValues[i];
|
2888
|
+
|
2889
|
+
let returnedKey=getKeyAt(arrayOfValues,0);
|
2890
|
+
let max=getAt(arrayOfValues,0);
|
2891
|
+
foreach(arrayOfValues,(item, key)=>{
|
2892
|
+
if(max<item){
|
2893
|
+
returnedKey=key;
|
2894
|
+
max=item;
|
2895
|
+
}
|
2896
|
+
},null,null,true);
|
2897
|
+
|
2898
|
+
return (returnKey?returnedKey:max);
|
2867
2899
|
};
|
2868
2900
|
|
2869
|
-
Math.minInArray=function(arrayOfValues){
|
2901
|
+
Math.minInArray=function(arrayOfValues, returnKey=false){
|
2870
2902
|
if(empty(arrayOfValues))
|
2871
2903
|
return null;
|
2872
|
-
|
2873
|
-
|
2874
|
-
|
2875
|
-
|
2876
|
-
|
2904
|
+
|
2905
|
+
// var min=arrayOfValues[0];
|
2906
|
+
// for (var i=0; i<arrayOfValues.length; i++)
|
2907
|
+
// if(arrayOfValues[i]<min)
|
2908
|
+
// min=arrayOfValues[i];
|
2909
|
+
|
2910
|
+
let returnedKey=getKeyAt(arrayOfValues,0);
|
2911
|
+
let min=getAt(arrayOfValues,0);
|
2912
|
+
foreach(arrayOfValues,(item, key)=>{
|
2913
|
+
if(item<min){
|
2914
|
+
returnedKey=key;
|
2915
|
+
min=item;
|
2916
|
+
}
|
2917
|
+
},null,null,true);
|
2918
|
+
|
2919
|
+
return (returnKey?returnedKey:min);
|
2877
2920
|
};
|
2878
2921
|
|
2879
2922
|
|
@@ -5248,6 +5291,25 @@ window.stringifyObject=function(objectToStringify){
|
|
5248
5291
|
}
|
5249
5292
|
|
5250
5293
|
|
5294
|
+
window.splitURL=(urlOrigin)=>{
|
5295
|
+
let protocol=null;
|
5296
|
+
let hostAndPort=urlOrigin;
|
5297
|
+
if(contains(urlOrigin,"://")){
|
5298
|
+
const hostAndPortSplits=urlOrigin.split("://");
|
5299
|
+
protocol=hostAndPortSplits[0];
|
5300
|
+
hostAndPort=hostAndPortSplits[1];
|
5301
|
+
}
|
5302
|
+
let host=null, port=null;
|
5303
|
+
if(contains(hostAndPort,":")){
|
5304
|
+
const splits=hostAndPort.split(":");
|
5305
|
+
host=splits[0];
|
5306
|
+
port=splits[1];
|
5307
|
+
}else{
|
5308
|
+
host=hostAndPort;
|
5309
|
+
}
|
5310
|
+
const isSecure=(protocol && contains(protocol,"s"));
|
5311
|
+
return {protocol:protocol, host:host, port:port, isSecure:isSecure};
|
5312
|
+
};
|
5251
5313
|
|
5252
5314
|
|
5253
5315
|
|
@@ -5259,7 +5321,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
|
|
5259
5321
|
|
5260
5322
|
|
5261
5323
|
|
5262
|
-
/*utils AI library associated with aotra version : «1_29072022-2359 (
|
5324
|
+
/*utils AI library associated with aotra version : «1_29072022-2359 (19/02/2025-23:50:43)»*/
|
5263
5325
|
/*-----------------------------------------------------------------------------*/
|
5264
5326
|
|
5265
5327
|
|
@@ -5403,7 +5465,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
|
|
5403
5465
|
|
5404
5466
|
|
5405
5467
|
|
5406
|
-
/*utils SERVER library associated with aotra version : «1_29072022-2359 (
|
5468
|
+
/*utils SERVER library associated with aotra version : «1_29072022-2359 (19/02/2025-23:50:43)»*/
|
5407
5469
|
/*-----------------------------------------------------------------------------*/
|
5408
5470
|
|
5409
5471
|
|
@@ -5581,12 +5643,12 @@ getServerParams=function(portParam=null, certPathParam=null, keyPathParam=null,
|
|
5581
5643
|
// https=require("https");
|
5582
5644
|
// fs=require("fs");
|
5583
5645
|
|
5584
|
-
if(
|
5646
|
+
if(typeof(https)==="undefined"){
|
5585
5647
|
// TRACE
|
5586
5648
|
console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
|
5587
5649
|
return null;
|
5588
5650
|
}
|
5589
|
-
if(
|
5651
|
+
if(typeof(fs)==="undefined"){
|
5590
5652
|
// TRACE
|
5591
5653
|
console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
|
5592
5654
|
return null;
|
@@ -5629,7 +5691,17 @@ getServerParams=function(portParam=null, certPathParam=null, keyPathParam=null,
|
|
5629
5691
|
return result;
|
5630
5692
|
}
|
5631
5693
|
|
5632
|
-
|
5694
|
+
getConsoleParam=function(index=0, argsOffset=0){
|
5695
|
+
let result=null;
|
5696
|
+
if(!process){
|
5697
|
+
throw new Error("ERROR : Cannot extract console parameter in this context !");
|
5698
|
+
}
|
5699
|
+
process.argv.forEach((val, i)=>{
|
5700
|
+
if(i<=argsOffset+1) return;
|
5701
|
+
else if(i==argsOffset+index+1) result=val;
|
5702
|
+
});
|
5703
|
+
return result;
|
5704
|
+
}
|
5633
5705
|
|
5634
5706
|
|
5635
5707
|
|
@@ -5697,6 +5769,7 @@ WebsocketImplementation={
|
|
5697
5769
|
if(typeof(WebSocket)==="undefined"){
|
5698
5770
|
// TRACE
|
5699
5771
|
console.log("«ws» SERVER library not called yet, calling it now.");
|
5772
|
+
|
5700
5773
|
WebSocket=require("ws");
|
5701
5774
|
if(typeof(WebSocket)==="undefined"){
|
5702
5775
|
// TRACE
|
@@ -5798,7 +5871,7 @@ WebsocketImplementation={
|
|
5798
5871
|
receive:(channelNameParam, doOnIncomingMessage, clientsRoomsTag=null)=>{
|
5799
5872
|
|
5800
5873
|
// DBG
|
5801
|
-
lognow("(SERVER) Registering receive for «"+channelNameParam+"»..."
|
5874
|
+
lognow("(SERVER) Registering receive for «"+channelNameParam+"»...");
|
5802
5875
|
|
5803
5876
|
|
5804
5877
|
|
@@ -5836,10 +5909,11 @@ WebsocketImplementation={
|
|
5836
5909
|
|
5837
5910
|
if(!isClientInRoom) return;
|
5838
5911
|
|
5839
|
-
|
5840
|
-
|
5841
|
-
|
5842
|
-
|
5912
|
+
if(doOnIncomingMessage){
|
5913
|
+
// DBG
|
5914
|
+
lognow("(SERVER) doOnIncomingMessage:");
|
5915
|
+
doOnIncomingMessage(dataWrapped.data, clientSocketParam);
|
5916
|
+
}
|
5843
5917
|
|
5844
5918
|
},
|
5845
5919
|
};
|
@@ -6097,7 +6171,7 @@ WebsocketImplementation={
|
|
6097
6171
|
return nodeServerInstance;
|
6098
6172
|
},
|
6099
6173
|
|
6100
|
-
|
6174
|
+
// DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
|
6101
6175
|
// NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
|
6102
6176
|
connectToServer:(serverURL, port, isSecure=false, timeout)=>{
|
6103
6177
|
|
@@ -6725,7 +6799,7 @@ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /
|
|
6725
6799
|
server.onFinalize((serverParam)=>{
|
6726
6800
|
|
6727
6801
|
// DBG
|
6728
|
-
lognow("onFinalize
|
6802
|
+
lognow("onFinalize() server");
|
6729
6803
|
|
6730
6804
|
if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
|
6731
6805
|
});
|
@@ -6863,7 +6937,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
6863
6937
|
// Eventual encryption options :
|
6864
6938
|
let sslOptions=null;
|
6865
6939
|
if(!isForceUnsecure){
|
6866
|
-
if(
|
6940
|
+
if(typeof(fs)==="undefined"){
|
6867
6941
|
// TRACE
|
6868
6942
|
lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
|
6869
6943
|
}else{
|
@@ -6914,7 +6988,7 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
6914
6988
|
//
|
6915
6989
|
// const server=initNodeServerInfrastructureWrapper(
|
6916
6990
|
// // On each client connection :
|
6917
|
-
|
6991
|
+
// // (serverParam, clientSocketParam)=>{},
|
6918
6992
|
// null,
|
6919
6993
|
// // On server finalization :
|
6920
6994
|
// (serverParam)=>{
|
@@ -6947,15 +7021,15 @@ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFina
|
|
6947
7021
|
// },portParam,certPathParam,keyPathParam);
|
6948
7022
|
//
|
6949
7023
|
//
|
6950
|
-
|
6951
|
-
|
6952
|
-
|
6953
|
-
|
6954
|
-
|
6955
|
-
|
6956
|
-
|
6957
|
-
|
6958
|
-
|
7024
|
+
// // const doOnConnect=(serverParam, clientSocketParam)=>{
|
7025
|
+
// // };
|
7026
|
+
// // const doOnFinalizeServer=(serverParam)=>{
|
7027
|
+
// // /*DO NOTHING*/
|
7028
|
+
// // };
|
7029
|
+
// // const server={};
|
7030
|
+
// // server.start=(port=6080)=>{
|
7031
|
+
// // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
|
7032
|
+
// // };
|
6959
7033
|
//
|
6960
7034
|
// return server;
|
6961
7035
|
//}
|
@@ -7299,23 +7373,47 @@ appendGetParameters=function(apiURL, namedArgs){
|
|
7299
7373
|
|
7300
7374
|
//*********************************** AUTO-ORGANIZING REAL-TIME AORTAC CLUSTERIZATION (AORTAC) *********************************** */
|
7301
7375
|
|
7376
|
+
AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT=30000;
|
7377
|
+
REQUESTS_IDS_HISTORY_SIZE=10;
|
7378
|
+
|
7302
7379
|
class AORTACNode{
|
7303
|
-
|
7380
|
+
|
7381
|
+
constructor(nodeId, selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[], model, controller, allowAnonymousNodes=true, sslConfig={/*OPTIONAL*/certPath:null,/*OPTIONAL*/keyPath:null}){
|
7382
|
+
|
7383
|
+
this.nodeId=nodeId;
|
7304
7384
|
this.selfOrigin=selfOrigin;
|
7385
|
+
this.sslConfig=sslConfig;
|
7305
7386
|
this.outcomingNodesOrigins=outcomingNodesOrigins;
|
7387
|
+
this.allowAnonymousNodes=allowAnonymousNodes;
|
7306
7388
|
|
7307
|
-
this.model=model; // must implement
|
7308
|
-
this.controller=controller; // Must implement
|
7309
|
-
this.serverId=getUUID();
|
7389
|
+
this.model=model; // must implement functions
|
7390
|
+
this.controller=controller; // Must implement functions
|
7310
7391
|
|
7311
7392
|
this.server=null;
|
7312
7393
|
this.clients={};
|
7313
7394
|
this.incomingServers={};
|
7314
7395
|
this.outcomingServers={};
|
7396
|
+
this.authorizedNodesIds=[this.nodeId];
|
7315
7397
|
|
7398
|
+
this.listeners={"protocol":[],"cluster":[],"neighbors":[],"inputs":[]};
|
7399
|
+
|
7400
|
+
this.executedRequestIdsHistory=[];
|
7401
|
+
|
7402
|
+
this.isConnectedToCluster=false;
|
7403
|
+
|
7404
|
+
this.modelObjectsDirectory={};
|
7405
|
+
this.objectsIndexesForClients={};
|
7406
|
+
|
7407
|
+
this.addProtocolListeners();
|
7408
|
+
|
7409
|
+
// TRACE
|
7410
|
+
lognow(`AORTAC node created with id ${this.nodeId}.`);
|
7316
7411
|
}
|
7317
7412
|
|
7318
7413
|
start(){
|
7414
|
+
|
7415
|
+
const splits=splitURL(this.selfOrigin);
|
7416
|
+
let port=nonull(splits.port,"40000");
|
7319
7417
|
|
7320
7418
|
const self=this;
|
7321
7419
|
this.server = initNodeServerInfrastructureWrapper(
|
@@ -7323,62 +7421,819 @@ class AORTACNode{
|
|
7323
7421
|
null,
|
7324
7422
|
// On client finalization :
|
7325
7423
|
function(server){
|
7326
|
-
|
7327
7424
|
self.server=server;
|
7328
7425
|
|
7329
|
-
|
7330
|
-
|
7331
|
-
|
7332
|
-
console.log("INFO : SERVER : Client has sent a protocol message: ", message);
|
7333
|
-
|
7334
|
-
// ============== OTHER SERVER / CLIENT REGISTRATION PHASE ==============
|
7335
|
-
|
7336
|
-
if(message.type==="registerOtherServer"){
|
7337
|
-
const incomingServerId=message.serverId;
|
7426
|
+
// Listeners handling ;
|
7427
|
+
foreach(Object.keys(self.listeners), channelName=>{
|
7428
|
+
self.server.receive(channelName, (message, clientSocket)=>{
|
7338
7429
|
|
7339
|
-
|
7340
|
-
|
7341
|
-
|
7342
|
-
|
7430
|
+
// INCOMING NODES LISTENERS HOOK :
|
7431
|
+
// TRACE
|
7432
|
+
console.log("INFO : SERVER : Client or incoming node has sent a "+channelName+" message: ", message);
|
7433
|
+
foreach(self.listeners[channelName], listener => {
|
7434
|
+
listener.execute(self, message, self.server, clientSocket);
|
7435
|
+
},(listener)=>(message.type===listener.messageType));
|
7343
7436
|
|
7344
|
-
|
7345
|
-
}
|
7346
|
-
|
7437
|
+
});
|
7347
7438
|
});
|
7439
|
+
|
7440
|
+
// OLD : self.connectToOutcomingServers(self, server).then((server)=>{ self.doOnConnectedToCluster(); });
|
7441
|
+
|
7442
|
+
|
7443
|
+
}, port, this.sslConfig.certPath, this.sslConfig.keyPath);
|
7444
|
+
|
7445
|
+
this.server.serverManager.start();
|
7446
|
+
|
7447
|
+
// TRACE
|
7448
|
+
lognow(`AORTAC node started with id ${this.nodeId}.`);
|
7449
|
+
|
7348
7450
|
|
7451
|
+
return this;
|
7452
|
+
}
|
7453
|
+
|
7454
|
+
|
7455
|
+
connect(){
|
7456
|
+
|
7457
|
+
const self=this;
|
7458
|
+
self.connectToOutcomingServers(self).then((server)=>{ self.doOnConnectedToCluster(); });
|
7349
7459
|
|
7460
|
+
return this;
|
7461
|
+
}
|
7462
|
+
|
7463
|
+
|
7464
|
+
|
7465
|
+
|
7466
|
+
/*private*/addProtocolListeners(){
|
7467
|
+
|
7468
|
+
const self=this;
|
7469
|
+
// ============== OTHER SERVER / CLIENT REGISTRATION PHASE ==============
|
7470
|
+
// - OTHER SERVER REGISTRATION PHASE :
|
7471
|
+
this.listeners["protocol"].push({
|
7472
|
+
messageType:"request.register.server.incoming",
|
7473
|
+
execute:(self, message, server, clientSocket)=>{
|
7474
|
+
const incomingServerNodeId=message.nodeId;
|
7475
|
+
|
7476
|
+
// TRACE
|
7477
|
+
lognow(` (${self.nodeId}) Receiving registering request from node ${incomingServerNodeId}...`);
|
7478
|
+
|
7479
|
+
if(!self.allowAnonymousNodes && self.isInAuthorizedNodes(incomingServerNodeId)){
|
7480
|
+
// TRACE
|
7481
|
+
lognow("WARN : Cannot accept incoming server with node id «"+incomingServerNodeId+"» because it's not in the authorized nodes list.");
|
7482
|
+
return;
|
7483
|
+
}
|
7484
|
+
if(!contains(self.authorizedNodesIds,incomingServerNodeId)) self.authorizedNodesIds.push(incomingServerNodeId);
|
7485
|
+
|
7486
|
+
// TRACE
|
7487
|
+
lognow(` (${self.nodeId}) Adding registering node ${incomingServerNodeId} to incoming servers...`);
|
7488
|
+
|
7489
|
+
self.incomingServers[incomingServerNodeId]={clientSocket:clientSocket};
|
7490
|
+
|
7491
|
+
const welcomeResponse={
|
7492
|
+
nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
|
7493
|
+
type:"response.register.server.incoming",
|
7494
|
+
authorizedNodesIds:self.authorizedNodesIds
|
7495
|
+
};
|
7496
|
+
server.send("protocol", welcomeResponse, null, clientSocket);
|
7497
|
+
|
7498
|
+
// TRACE
|
7499
|
+
lognow(` (${self.nodeId}) Sent registering response to node ${incomingServerNodeId}.`);
|
7500
|
+
}
|
7501
|
+
});
|
7502
|
+
|
7503
|
+
// - CLIENT REGISTRATION PHASE : (for clients to the server, NOT INCOMING NODES !)
|
7504
|
+
this.handleClients();
|
7505
|
+
|
7506
|
+
}
|
7507
|
+
|
7508
|
+
/*private*/handleClients(){
|
7509
|
+
const self=this;
|
7510
|
+
|
7511
|
+
// --------- PROTOCOL HANDLING ---------
|
7512
|
+
|
7513
|
+
this.listeners["protocol"].push({
|
7514
|
+
messageType:"request.register.client",
|
7515
|
+
execute:(self, message, server, clientSocket)=>{
|
7516
|
+
const clientId=message.clientId;
|
7517
|
+
|
7518
|
+
self.clients[clientId]={clientSocket:clientSocket};
|
7519
|
+
|
7520
|
+
// TRACE
|
7521
|
+
lognow(` (${self.nodeId}) Receiving non-cluster client registration request from client ${clientId}.`);
|
7522
|
+
|
7523
|
+
const welcomeClientResponse={
|
7524
|
+
nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
|
7525
|
+
type:"response.register.client",
|
7526
|
+
// If necessary we also send them our model part :
|
7527
|
+
partialModelString:(message.isReferenceNode?null:JSON.stringifyDecycle(self.model)),
|
7528
|
+
isReferenceNode:(!!message.isReferenceNode),
|
7529
|
+
};
|
7530
|
+
server.send("protocol", welcomeClientResponse, null, clientSocket);
|
7531
|
+
}
|
7532
|
+
});
|
7533
|
+
|
7534
|
+
this.listeners["protocol"].push({
|
7535
|
+
messageType:"request.unregister.client",
|
7536
|
+
execute:(self, message, server, clientSocket)=>{
|
7537
|
+
const clientId=message.clientId;
|
7538
|
+
|
7539
|
+
delete self.clients[clientId];
|
7540
|
+
|
7541
|
+
// TRACE
|
7542
|
+
lognow(` (${self.nodeId}) Receiving non-cluster client unregistration request from client ${clientId}.`);
|
7543
|
+
|
7544
|
+
const unwelcomeClientResponse={
|
7545
|
+
nodeId:self.nodeId, // MUST BE SELF ! ELSE THE REGISTERING CLIENT WON'T KNOW WHICH ID IT HAS BEEN REGISTERED TO !
|
7546
|
+
type:"response.unregister.client",
|
7547
|
+
};
|
7548
|
+
server.send("protocol", unwelcomeClientResponse, null, clientSocket);
|
7549
|
+
}
|
7550
|
+
});
|
7551
|
+
|
7552
|
+
|
7553
|
+
this.listeners["protocol"].push({
|
7554
|
+
messageType:"request.interrogation.client",
|
7555
|
+
execute:(self, message, server, clientSocket)=>{
|
7556
|
+
const clientId=message.clientId;
|
7557
|
+
|
7558
|
+
// TRACE
|
7559
|
+
lognow(` (${self.nodeId}) Receiving non-cluster client interrogation request from client ${clientId}.`);
|
7560
|
+
|
7561
|
+
const clientBoundaries=message.boundaries;
|
7562
|
+
|
7563
|
+
// We ask the whole cluster to find the requested objects :
|
7564
|
+
const modelSeekObjectsRequest={
|
7565
|
+
type:"request.model.seekObjects",
|
7566
|
+
originatingClientId:clientId,
|
7567
|
+
clientBoundaries:clientBoundaries,
|
7568
|
+
};
|
7569
|
+
this.propagateToCluster(modelSeekObjectsRequest);
|
7570
|
+
|
7571
|
+
self.objectsIndexesForClients[clientId]={};
|
7572
|
+
|
7573
|
+
}
|
7574
|
+
});
|
7575
|
+
|
7576
|
+
// --------- INPUTS HANDLING ---------
|
7577
|
+
|
7578
|
+
this.listeners["inputs"].push({
|
7579
|
+
messageType:"request.inputs.client",
|
7580
|
+
execute:(self, message, server, clientSocket)=>{
|
7581
|
+
const clientId=message.clientId;
|
7582
|
+
|
7583
|
+
// TRACE
|
7584
|
+
lognow(` (${self.nodeId}) Receiving non-cluster client inputs request from client ${clientId}.`);
|
7585
|
+
|
7586
|
+
const clientInputs=message.inputs;
|
7587
|
+
const clientSubBoundaries=message.subBoundaries;
|
7588
|
+
|
7589
|
+
|
7590
|
+
// We let the controller interpret these inputs :
|
7591
|
+
const modifiedObjects=self.controller.interpretInputs(clientInputs, clientSubBoundaries);
|
7592
|
+
|
7593
|
+
|
7594
|
+
}
|
7595
|
+
});
|
7596
|
+
|
7597
|
+
}
|
7598
|
+
|
7599
|
+
|
7600
|
+
/*private*/addPropagationForClientsListeners(){
|
7601
|
+
|
7602
|
+
const self=this;
|
7603
|
+
|
7604
|
+
// ------------------------------------- CLIENTS PROTOCOL HANDLING -------------------------------------
|
7605
|
+
// Adding propagation listener :
|
7606
|
+
this.setPropagation("cluster","request.model.seekObjects",(self, message, server, clientSocket)=>{
|
7607
|
+
|
7608
|
+
// If we receive this request from cluster, then we try to find the requested objects
|
7609
|
+
const relayNodeid=message.nodeId;
|
7610
|
+
const originatingClientId=message.originatingClientId;
|
7611
|
+
const clientBoundaries=message.clientBoundaries
|
7612
|
+
const objectsIds=self.model.getObjectsIdsWithinBoundaries(clientBoundaries);
|
7613
|
+
|
7614
|
+
// If server has not the seeked objects, it answers with an empty array.
|
7615
|
+
|
7616
|
+
// TRACE
|
7617
|
+
lognow(`(${self.nodeId}) Node receiving a seek objects request from relay node ${message.nodeId}...objectsIds=`,objectsIds);
|
7618
|
+
|
7619
|
+
// TRACE
|
7620
|
+
lognow(`(${self.nodeId}) Sending objects ids to relay node ${relayNodeid}...`);
|
7621
|
+
|
7622
|
+
const modelObjectsSeekResponse={
|
7623
|
+
type:"response.model.seekObject",
|
7624
|
+
originatingClientId:originatingClientId,
|
7625
|
+
clientBoundaries:clientBoundaries,
|
7626
|
+
objectsIds:objectsIds,
|
7627
|
+
nodeServerInfo:self.selfOrigin
|
7628
|
+
};
|
7629
|
+
this.propagateToCluster(modelObjectsSeekResponse, relayNodeid/*destination node*/);
|
7630
|
+
|
7631
|
+
});
|
7632
|
+
|
7633
|
+
// Adding propagation listener :
|
7634
|
+
this.setPropagation("cluster","response.model.seekObject",(self, message, server, clientSocket)=>{
|
7635
|
+
|
7636
|
+
// Here the relay server gathers the information from the other nodes, and sends it back to client ;
|
7637
|
+
|
7638
|
+
const referenceNodeId=self.nodeId;
|
7639
|
+
const originatingClientId=message.originatingClientId;
|
7640
|
+
const originatingNodeId=message.nodeId;
|
7641
|
+
const clientBoundaries=message.clientBoundaries;
|
7642
|
+
const objectsIds=message.objectsIds;
|
7643
|
+
const nodeServerInfo=message.nodeServerInfo;
|
7644
|
+
|
7645
|
+
// TRACE
|
7646
|
+
lognow(`(${self.nodeId}) Node receiving a seek objects request from relay node ${message.nodeId}...objectsIds=`,objectsIds);
|
7647
|
+
|
7648
|
+
// Here the currently answering node fills its information :
|
7649
|
+
const objectsIndexForClient=self.objectsIndexesForClients[originatingClientId];
|
7650
|
+
objectsIndexForClient[originatingNodeId]={nodeServerInfo:nodeServerInfo, objectsIds:objectsIds};
|
7651
|
+
|
7652
|
+
|
7653
|
+
// We check if all known nodes have answered :
|
7654
|
+
if(getArraySize(self.authorizedNodesIds)-1/*Because we exclude the reference node*/<=getArraySize(objectsIndexForClient)){
|
7655
|
+
|
7656
|
+
// The reference node also answers to the request to its direct client :
|
7657
|
+
const objectsIdsFromReferenceNode=self.model.getObjectsIdsWithinBoundaries(clientBoundaries);
|
7658
|
+
const referenceNodeServerInfo=self.selfOrigin;
|
7659
|
+
objectsIndexForClient[referenceNodeId]={nodeServerInfo:referenceNodeServerInfo, objectsIds:objectsIdsFromReferenceNode};
|
7660
|
+
|
7661
|
+
const client=self.clients[originatingClientId];
|
7662
|
+
const clientSocket=client.clientSocket;
|
7663
|
+
|
7664
|
+
// TRACE
|
7665
|
+
lognow(`(${self.nodeId}) Sending objects ids to the client ${originatingClientId}...objectsIndexForClient=`,objectsIndexForClient);
|
7666
|
+
|
7667
|
+
// We filter all the index entries with an empty objetcs ids array :
|
7668
|
+
const objectsIndexForClientWithoutEmptyObjects={};
|
7669
|
+
foreach(objectsIndexForClient,(nodeInfoAndObjectsIds, serverNodeId)=>{
|
7670
|
+
objectsIndexForClientWithoutEmptyObjects[serverNodeId]=nodeInfoAndObjectsIds;
|
7671
|
+
},(nodeInfoAndObjectsIds,serverNodeId)=>(!empty(nodeInfoAndObjectsIds.objectsIds)));
|
7672
|
+
|
7673
|
+
// We send the final result information (objects ids) to client :
|
7674
|
+
const interrogationClientResponse={
|
7675
|
+
nodeId:self.nodeId,
|
7676
|
+
type:"response.interrogation.client",
|
7677
|
+
serversNodesIdsForModelObjectsForClient:objectsIndexForClientWithoutEmptyObjects
|
7678
|
+
};
|
7679
|
+
self.server.send("protocol", interrogationClientResponse, null, clientSocket);
|
7680
|
+
}
|
7681
|
+
|
7682
|
+
});
|
7683
|
+
|
7684
|
+
|
7685
|
+
// ------------------------------------- CLIENTS INPUTS HANDLING -------------------------------------
|
7686
|
+
// Adding propagation listener :
|
7687
|
+
|
7688
|
+
|
7689
|
+
|
7690
|
+
// DBG
|
7691
|
+
lognow(">>>>>>>>FOR CLIENTS this.listeners",this.listeners);
|
7692
|
+
|
7693
|
+
}
|
7694
|
+
|
7695
|
+
// /*private*/getServersNodesIdsForModelObjectsForClient(clientObjectsIds){
|
7696
|
+
// const serversNodesIdsForModelObjectsForClient={};
|
7697
|
+
// const self=this;
|
7698
|
+
// // TODO : FIXME : INEFFICIENT !!!
|
7699
|
+
// foreach(clientObjectsIds,(objectId)=>{
|
7700
|
+
// foreach(self.modelObjectsDirectory,(objectsInNode, nodeId)=>{
|
7701
|
+
// if(contains(objectsInNode,objectId))
|
7702
|
+
// serversNodesIdsForModelObjectsForClient[objectId]=nodeId;
|
7703
|
+
// });
|
7704
|
+
// });
|
7705
|
+
//
|
7706
|
+
// return serversNodesIdsForModelObjectsForClient;
|
7707
|
+
// }
|
7708
|
+
|
7709
|
+
/*private*/connectToOutcomingServers(self){
|
7710
|
+
|
7711
|
+
const server=self.server;
|
7712
|
+
|
7713
|
+
const theoreticNumberOfOutcomingServers=getArraySize(self.outcomingNodesOrigins);
|
7714
|
+
|
7715
|
+
return new Promise((resolve,error)=>{
|
7716
|
+
|
7717
|
+
// In case we timeout :
|
7718
|
+
setTimeout(()=>{
|
7719
|
+
if(self.isConnectedToCluster) return;
|
7720
|
+
self.isConnectedToCluster=true;
|
7721
|
+
resolve(server);
|
7722
|
+
},AORTAC_OUTCOMING_SERVERS_CONNECTION_GLOBAL_TIMEOUT);
|
7723
|
+
|
7350
7724
|
// We try to connect to all the other outcoming servers :
|
7725
|
+
|
7726
|
+
// Special case : if node has no outcoming serves, (it's the seed node), then
|
7727
|
+
// it should be considered as connected to cluster nonetheless :
|
7728
|
+
if(empty(self.outcomingNodesOrigins)){
|
7729
|
+
self.finalizeClusterConnection(self.nodeId);
|
7730
|
+
resolve(server);
|
7731
|
+
return;
|
7732
|
+
}
|
7733
|
+
|
7734
|
+
// We try to connect to all outcoming nodes :
|
7351
7735
|
foreach(self.outcomingNodesOrigins, outcomingNodeOrigin=>{
|
7352
|
-
let outcomingNodeHost;
|
7353
|
-
let outcomingNodePort;
|
7354
|
-
if(contains(outcomingNodeOrigin,":")){
|
7355
|
-
const splits=outcomingNodeOrigin.split(":");
|
7356
|
-
outcomingNodeHost=splits[0];
|
7357
|
-
outcomingNodePort=splits[1];
|
7358
|
-
}
|
7359
|
-
const isSecure=(contains(outcomingNodeOrigin,"wss"));
|
7360
|
-
const clientInstance=connectToServer(outcomingNodeHost,outcomingNodePort, isSecure, 5000);
|
7361
7736
|
|
7362
|
-
//
|
7737
|
+
// TRACE
|
7738
|
+
lognow(` (${self.nodeId}) Sending registering response to node ${outcomingNodeOrigin}...`);
|
7739
|
+
|
7740
|
+
const splits=splitURL(outcomingNodeOrigin);
|
7741
|
+
const outcomingNodeProtocol=nonull(splits.protocol,"ws");
|
7742
|
+
const outcomingNodeHost=nonull(splits.host,"localhost");
|
7743
|
+
const outcomingNodePort=nonull(splits.port,"40000");
|
7744
|
+
//const isSecure=splits.isSecure; // UNUSED
|
7745
|
+
|
7746
|
+
const clientInstance=initClient(true, false, (socketToServer)=>{
|
7747
|
+
|
7748
|
+
// TODO : FIXME : IF CONNECTION TO SERVER FAILS, IT MUST BE ABLE TO TRY AGAIN LATER !
|
7749
|
+
|
7750
|
+
// TRACE
|
7751
|
+
lognow(` (${self.nodeId}) Sending registering request to outcoming node...`);
|
7752
|
+
|
7753
|
+
const helloRequest={
|
7754
|
+
nodeId:self.nodeId,
|
7755
|
+
type:"request.register.server.incoming"
|
7756
|
+
};
|
7757
|
+
socketToServer.send("protocol", helloRequest);
|
7758
|
+
|
7759
|
+
|
7760
|
+
// We place a listener from outcoming node on this client instance to this outcoming node :
|
7761
|
+
socketToServer.receive("protocol",(message)=>{
|
7762
|
+
if(message.type==="response.register.server.incoming"){
|
7763
|
+
|
7764
|
+
// TRACE
|
7765
|
+
lognow(` (${self.nodeId}) Receving registering response from requested outcoming node...`);
|
7766
|
+
|
7767
|
+
const welcomeNodeId=message.nodeId;
|
7768
|
+
const duplicatesFreeAuthorizedNodesIds=[...new Set([...self.authorizedNodesIds, ...message.authorizedNodesIds, welcomeNodeId ])];
|
7769
|
+
self.authorizedNodesIds=duplicatesFreeAuthorizedNodesIds;
|
7770
|
+
|
7771
|
+
self.outcomingServers[welcomeNodeId]={clientInstance:clientInstance};
|
7772
|
+
|
7773
|
+
// Once we have registered to all the outcoming nodes :
|
7774
|
+
const currentNumberOfOutcomingServers=getArraySize(self.outcomingServers);
|
7775
|
+
if(!self.isConnectedToCluster && theoreticNumberOfOutcomingServers<=currentNumberOfOutcomingServers){
|
7776
|
+
//self.finalizeClusterConnection(welcomeNodeId);
|
7777
|
+
self.finalizeClusterConnection(self.nodeId);
|
7778
|
+
resolve(server);
|
7779
|
+
}
|
7780
|
+
}
|
7781
|
+
});
|
7782
|
+
|
7783
|
+
// Listeners handling ;
|
7784
|
+
foreach(Object.keys(self.listeners), channelName=>{
|
7785
|
+
socketToServer.receive(channelName, (message, clientSocket)=>{
|
7786
|
+
|
7787
|
+
// OUTCOMING NODES LISTENERS HOOK :
|
7788
|
+
// TRACE
|
7789
|
+
console.log("INFO : SERVER : Outcoming node has sent a "+channelName+" message: ", message);
|
7790
|
+
foreach(self.listeners[channelName], listener => {
|
7791
|
+
listener.execute(self, message, self.server, clientSocket);
|
7792
|
+
},(listener)=>(message.type===listener.messageType));
|
7793
|
+
|
7794
|
+
});
|
7795
|
+
});
|
7796
|
+
|
7797
|
+
|
7798
|
+
}, outcomingNodeProtocol+"://"+outcomingNodeHost, outcomingNodePort, false);
|
7799
|
+
clientInstance.client.start();
|
7363
7800
|
|
7364
7801
|
});
|
7365
7802
|
|
7803
|
+
});
|
7804
|
+
|
7805
|
+
}
|
7806
|
+
|
7807
|
+
/*private*/finalizeClusterConnection(welcomeNodeId){
|
7808
|
+
// TRACE
|
7809
|
+
lognow(` (${this.nodeId}) Propagating this new node to the whole cluster...`);
|
7810
|
+
|
7811
|
+
// We propagate the new node id to the whole cluster :
|
7812
|
+
const newNodeRequest={
|
7813
|
+
nodeId:welcomeNodeId,
|
7814
|
+
type:"request.node.new"
|
7815
|
+
};
|
7816
|
+
this.propagateToCluster(newNodeRequest);
|
7817
|
+
|
7818
|
+
this.isConnectedToCluster=true;
|
7819
|
+
|
7820
|
+
this.addPropagationListeners();
|
7821
|
+
this.addPropagationForClientsListeners();
|
7822
|
+
}
|
7823
|
+
|
7824
|
+
|
7825
|
+
|
7826
|
+
|
7827
|
+
/*public*/doOnConnectedToCluster(){
|
7366
7828
|
|
7829
|
+
// TRACE
|
7830
|
+
lognow(`(${this.nodeId}) Node is connected to cluster...`);
|
7831
|
+
|
7832
|
+
// The node asks the cluster for its model partition :
|
7833
|
+
|
7834
|
+
// At this point, the new node does not know the state of the model in the cluster, only cluster nodes know.
|
7835
|
+
|
7836
|
+
// TRACE
|
7837
|
+
lognow(`(${this.nodeId}) Node is asking to its model partition...`);
|
7838
|
+
|
7839
|
+
// New node asks for its model partition :
|
7840
|
+
|
7841
|
+
// - First we need to know which server has the biggest model :
|
7842
|
+
const modelSizeRequest={
|
7843
|
+
type:"request.model.getSize",
|
7844
|
+
};
|
7845
|
+
this.propagateToCluster(modelSizeRequest);
|
7367
7846
|
|
7847
|
+
}
|
7848
|
+
|
7849
|
+
|
7850
|
+
/*private*/addPropagationListeners(){
|
7851
|
+
|
7852
|
+
// Adding propagation listener for acknowledging new node added to cluster :
|
7853
|
+
this.setPropagation("cluster","request.node.new", (self, message, server, clientSocket)=>{
|
7854
|
+
const newNodeId=message.nodeId;
|
7855
|
+
// TRACE
|
7856
|
+
lognow(` (${self.nodeId}) Acknowledging new node ${newNodeId} added to cluster...`);
|
7857
|
+
if(!contains(self.authorizedNodesIds,newNodeId)) self.authorizedNodesIds.push(newNodeId);
|
7858
|
+
});
|
7859
|
+
|
7860
|
+
// Adding propagation listener if a node receives a model size request :
|
7861
|
+
this.setPropagation("cluster","request.model.getSize",(self, message, server, clientSocket)=>{
|
7862
|
+
const modelSize=self.model.getModelSize();
|
7863
|
+
// TRACE
|
7864
|
+
lognow(`(${self.nodeId}) Node gives its model size to node ${message.nodeId}, modelSize=${modelSize}...`);
|
7865
|
+
return modelSize;
|
7866
|
+
},"response.model.getSize");
|
7867
|
+
|
7868
|
+
// Adding propagation listener :
|
7869
|
+
this.modelSizes={};
|
7870
|
+
this.setPropagation("cluster","response.model.getSize",(self, message, server, clientSocket)=>{
|
7368
7871
|
|
7369
|
-
|
7370
|
-
|
7872
|
+
const modelSizes=self.modelSizes;
|
7873
|
+
const currentClusterSize=getArraySize(self.authorizedNodesIds);
|
7371
7874
|
|
7875
|
+
const concernedNodeId=message.nodeId;
|
7876
|
+
const modelSize=message.result;
|
7372
7877
|
|
7373
|
-
|
7878
|
+
let currentNumberOfAnswers=getArraySize(modelSizes);
|
7879
|
+
|
7880
|
+
if(currentNumberOfAnswers<=currentClusterSize-1){// -1 because we want to exclude this node also !
|
7881
|
+
modelSizes[concernedNodeId]=modelSize;
|
7882
|
+
}
|
7883
|
+
|
7884
|
+
currentNumberOfAnswers=getArraySize(modelSizes);
|
7885
|
+
// TRACE
|
7886
|
+
lognow(`(${self.nodeId}) Node receiving a model size from node ${message.nodeId}...currentClusterSize=${currentClusterSize} ; modelSizes:`,modelSizes);
|
7887
|
+
if(currentClusterSize-1<=currentNumberOfAnswers){
|
7888
|
+
const nodeIdWithBiggestModel=Math.maxInArray(modelSizes, true);
|
7889
|
+
// TRACE
|
7890
|
+
lognow(`(${self.nodeId}) Node with biggest model is nodeIdWithBiggestModel=${nodeIdWithBiggestModel}...`);
|
7891
|
+
|
7892
|
+
const modelPartitionRequest={
|
7893
|
+
type:"request.model.partition",
|
7894
|
+
};
|
7895
|
+
this.propagateToCluster(modelPartitionRequest, nodeIdWithBiggestModel);
|
7896
|
+
}
|
7897
|
+
});
|
7374
7898
|
|
7899
|
+
// Adding propagation listener :
|
7900
|
+
// If a node receives a model partition request :
|
7901
|
+
this.setPropagation("cluster","request.model.partition",(self, message, server, clientSocket)=>{
|
7902
|
+
// Each node will give a little portion of its model, according to its size :
|
7903
|
+
// TRACE
|
7904
|
+
lognow(`(${self.nodeId}) Node splits its model for node ${message.nodeId}...`);
|
7905
|
+
|
7906
|
+
const ownModel=self.model;
|
7907
|
+
const splittedModel=ownModel.split();
|
7908
|
+
|
7909
|
+
// Node shares also its model objects directory with the newcomer node :
|
7910
|
+
const ownModelObjectsIds=ownModel.getAllObjectsIds();
|
7911
|
+
self.modelObjectsDirectory[self.nodeId]=ownModelObjectsIds;
|
7912
|
+
|
7913
|
+
const modelString=JSON.stringifyDecycle(splittedModel);
|
7914
|
+
const modelPartitionResponse={
|
7915
|
+
type:"response.model.partition",
|
7916
|
+
modelString:modelString,
|
7917
|
+
modelObjectsDirectory:self.modelObjectsDirectory
|
7918
|
+
};
|
7919
|
+
this.propagateToCluster(modelPartitionResponse, message.nodeId);
|
7920
|
+
|
7921
|
+
// This node also advertises that its model has changed to all other nodes (not the newcomer, because it's unnecessary) :
|
7922
|
+
const updatemodelObjectsDirectoryRequest={
|
7923
|
+
type:"request.update.modelObjectsDirectory",
|
7924
|
+
ownModelObjectsIds:ownModelObjectsIds
|
7925
|
+
};
|
7926
|
+
this.propagateToCluster(updatemodelObjectsDirectoryRequest, null, [message.nodeId]);
|
7927
|
+
|
7928
|
+
});
|
7929
|
+
|
7930
|
+
// Adding propagation listener :
|
7931
|
+
this.setPropagation("cluster","response.model.partition",(self, message, server, clientSocket)=>{
|
7932
|
+
// TRACE
|
7933
|
+
lognow(`(${self.nodeId}) Node receives a model partition from node ${message.nodeId}...`,message);
|
7934
|
+
|
7935
|
+
const newModel=JSON.parseRecycle(message.modelString);
|
7936
|
+
self.model.replaceBy(newModel);
|
7375
7937
|
|
7938
|
+
// We initialize this node's model objects directory from the one froom the node tha provided it its model partition :
|
7939
|
+
self.modelObjectsDirectory=message.modelObjectsDirectory;
|
7940
|
+
|
7941
|
+
// Once this node has received its model partition, it updates its model objects directory
|
7942
|
+
const ownModelObjectsIds=newModel.getAllObjectsIds();
|
7943
|
+
self.modelObjectsDirectory[self.nodeId]=ownModelObjectsIds;
|
7944
|
+
|
7945
|
+
// TRACE
|
7946
|
+
lognow(`(${self.nodeId}) Node sends a model objects directory update request...ownModelObjectsIds=${ownModelObjectsIds}`);
|
7947
|
+
|
7948
|
+
// And notifies the other nodes of the change :
|
7949
|
+
const updatemodelObjectsDirectoryRequest={
|
7950
|
+
type:"request.update.modelObjectsDirectory",
|
7951
|
+
ownModelObjectsIds:ownModelObjectsIds
|
7952
|
+
};
|
7953
|
+
this.propagateToCluster(updatemodelObjectsDirectoryRequest);
|
7954
|
+
|
7955
|
+
});
|
7956
|
+
|
7957
|
+
this.addModelObjectsDirectoryListeners();
|
7958
|
+
|
7959
|
+
|
7960
|
+
// DBG
|
7961
|
+
lognow(">>>>>>>>FOR OTHER NODES this.listeners",this.listeners);
|
7962
|
+
|
7376
7963
|
}
|
7377
7964
|
|
7965
|
+
|
7966
|
+
|
7967
|
+
/*private*/addModelObjectsDirectoryListeners(){
|
7968
|
+
|
7969
|
+
const self=this;
|
7970
|
+
|
7971
|
+
// Adding propagation listener :
|
7972
|
+
// If a node receives a model objects directory update request :
|
7973
|
+
this.setPropagation("cluster","request.update.modelObjectsDirectory",(self, message, server, clientSocket)=>{
|
7974
|
+
|
7975
|
+
// TRACE
|
7976
|
+
lognow(`(${self.nodeId}) !!!!!! Node receives a model objects directory update request from node ${message.nodeId}...`,message);
|
7977
|
+
|
7978
|
+
const modelObjectsIds=message.ownModelObjectsIds;
|
7979
|
+
self.modelObjectsDirectory[message.nodeId]=modelObjectsIds;
|
7980
|
+
|
7981
|
+
});
|
7982
|
+
|
7983
|
+
|
7984
|
+
// Adding propagation listener :
|
7985
|
+
// If a node receives a model objects directory update request :
|
7986
|
+
this.setPropagation("cluster","request.add.modelObjectsDirectory",(self, message, server, clientSocket)=>{
|
7987
|
+
|
7988
|
+
// TRACE
|
7989
|
+
lognow(`(${self.nodeId}) !!!!!! Node receives a model objects directory add request from node ${message.nodeId}...`,message);
|
7990
|
+
|
7991
|
+
const newObjectsIds=message.newObjectsIds;
|
7992
|
+
self.modelObjectsDirectory[message.nodeId].push(...newObjectsIds);
|
7993
|
+
|
7994
|
+
});
|
7995
|
+
|
7996
|
+
}
|
7997
|
+
|
7998
|
+
|
7999
|
+
/*public*/sendUpdatedObjects(modifiedObjects, modifiedObjectsIds){
|
8000
|
+
|
8001
|
+
if(empty(modifiedObjects)) return modifiedObjects;
|
8002
|
+
|
8003
|
+
// Then we send back the modified objects to all the concerned clients : (which are all clients connected to this node)
|
8004
|
+
return this.sendObjectsUpdatesToClients(modifiedObjects);
|
8005
|
+
}
|
8006
|
+
|
8007
|
+
/*public*/sendNewObjects(newObjects, newObjectsIds){
|
8008
|
+
|
8009
|
+
if(empty(newObjects)) return newObjects;
|
8010
|
+
|
8011
|
+
// At this point, node controller has already added the objects to its model.
|
8012
|
+
// We just want to update the objects directories of all the nodes in the cluster:
|
8013
|
+
|
8014
|
+
// !!! CAUTION : NOTE THAT THE EMITTER NODE IS ALWAYS INCLUDED IN THE THE VISITED NODES !!!
|
8015
|
+
// So this is why ir needs to update its mode objects directory itself, before sending the add request to the cluster :
|
8016
|
+
this.modelObjectsDirectory[this.nodeId].push(...newObjectsIds);
|
8017
|
+
|
8018
|
+
// This node also advertises that its model has changed to all other nodes (not the newcomer, because it's unnecessary) :
|
8019
|
+
const updatemodelObjectsDirectoryRequest={
|
8020
|
+
type:"request.add.modelObjectsDirectory",
|
8021
|
+
newObjectsIds:newObjectsIds
|
8022
|
+
};
|
8023
|
+
this.propagateToCluster(updatemodelObjectsDirectoryRequest);
|
8024
|
+
|
8025
|
+
|
8026
|
+
// But we also want all the concerned clients to update their local models : (which are all clients connected to this node)
|
8027
|
+
return this.sendObjectsUpdatesToClients(newObjects, true);
|
8028
|
+
}
|
8029
|
+
|
8030
|
+
/*private*/sendObjectsUpdatesToClients(modifiedOrAddedObjects, isAddingObjects=false){
|
8031
|
+
|
8032
|
+
const self=this;
|
8033
|
+
foreach(this.clients, client=>{
|
8034
|
+
const clientSocket=client.clientSocket;
|
8035
|
+
const objectsModifiedOrAddedClientResponse={
|
8036
|
+
nodeId:self.nodeId,
|
8037
|
+
type:"response.objectsModifiedOrAdded.client",
|
8038
|
+
objectsString:JSON.stringifyDecycle(modifiedOrAddedObjects),
|
8039
|
+
isAddingObjects:isAddingObjects,
|
8040
|
+
};
|
8041
|
+
self.server.send("inputs", objectsModifiedOrAddedClientResponse, null, clientSocket);
|
8042
|
+
});
|
8043
|
+
|
8044
|
+
return modifiedOrAddedObjects;
|
8045
|
+
}
|
8046
|
+
|
8047
|
+
|
8048
|
+
|
8049
|
+
|
8050
|
+
/*private*/getNeighborsNodesIds(){
|
8051
|
+
const neighborsNodesIds=[];
|
8052
|
+
foreach(this.incomingServers,(incomingServer,nodeId)=>{
|
8053
|
+
neighborsNodesIds.push(nodeId);
|
8054
|
+
});
|
8055
|
+
foreach(this.outcomingServers,(outcomingServer,nodeId)=>{
|
8056
|
+
neighborsNodesIds.push(nodeId);
|
8057
|
+
});
|
8058
|
+
return neighborsNodesIds;
|
8059
|
+
}
|
8060
|
+
|
8061
|
+
/*private*/getLeastOccupiedNeighborNodeId(){
|
8062
|
+
const self=this;
|
8063
|
+
|
8064
|
+
// We send the new objects to the neighbor node with the least amount of objects :
|
8065
|
+
let leastOccupiedNodeId=null;
|
8066
|
+
foreach(this.getNeighborsNodesIds(), (nodeId)=>{
|
8067
|
+
const entry=self.modelObjectsDirectory[nodeId];
|
8068
|
+
const numberOfObjects=getArraySize(entry);
|
8069
|
+
if(!leastOccupiedNodeId || numberOfObjects<getArraySize(self.modelObjectsDirectory[leastOccupiedNodeId]))
|
8070
|
+
leastOccupiedNodeId=nodeId;
|
8071
|
+
});
|
8072
|
+
|
8073
|
+
if(!leastOccupiedNodeId){
|
8074
|
+
// TRACE
|
8075
|
+
lognow("WARN : Cannot find the least occupied node. Aborting.");
|
8076
|
+
}
|
8077
|
+
return leastOccupiedNodeId;
|
8078
|
+
}
|
8079
|
+
|
8080
|
+
|
8081
|
+
// ****************************************************************************************************
|
8082
|
+
/*private*/setPropagation(channelName, requestType, doOnReception=null, responseRequestType=null, propagateToAllCluster=true){
|
8083
|
+
const self=this;
|
8084
|
+
this.setUniqueReceptionPoint(channelName, requestType, (self, message, server, clientSocket)=>{
|
8085
|
+
|
8086
|
+
const requestId=message.requestId;
|
8087
|
+
if(contains(self.executedRequestIdsHistory, requestId)){
|
8088
|
+
// TRACE
|
8089
|
+
lognow(`WARN : Request of type ${message.type} already answered. Aborting.`);
|
8090
|
+
return;
|
8091
|
+
}
|
8092
|
+
if(message.visitedNodeIds){
|
8093
|
+
if(contains(message.visitedNodeIds, self.nodeId)){
|
8094
|
+
return;
|
8095
|
+
}
|
8096
|
+
message.visitedNodeIds.push(self.nodeId);
|
8097
|
+
}
|
8098
|
+
|
8099
|
+
const hasNoExclusionsOrExclusionDoesNotApplyOnThisNode=(!message.excludedNodesIds || !contains(message.excludedNodesIds,self.nodeId));
|
8100
|
+
let response=null;
|
8101
|
+
if(!message.destinationNodeId && hasNoExclusionsOrExclusionDoesNotApplyOnThisNode){
|
8102
|
+
// Case broadcast message :
|
8103
|
+
if(doOnReception)
|
8104
|
+
response=doOnReception(self, message, server, clientSocket);
|
8105
|
+
pushInArrayAsQueue(self.executedRequestIdsHistory, REQUESTS_IDS_HISTORY_SIZE, requestId);
|
8106
|
+
if(propagateToAllCluster) self.sendToOtherNodes(channelName, message);
|
8107
|
+
}else{
|
8108
|
+
if(message.destinationNodeId===self.nodeId && hasNoExclusionsOrExclusionDoesNotApplyOnThisNode){
|
8109
|
+
if(doOnReception)
|
8110
|
+
response=doOnReception(self, message, server, clientSocket);
|
8111
|
+
pushInArrayAsQueue(self.executedRequestIdsHistory, REQUESTS_IDS_HISTORY_SIZE, requestId);
|
8112
|
+
}else{
|
8113
|
+
if(propagateToAllCluster) self.sendToOtherNodes(channelName, message);
|
8114
|
+
}
|
8115
|
+
}
|
8116
|
+
|
8117
|
+
// We send back the answer to the originating node :
|
8118
|
+
if(response!=null && responseRequestType){
|
8119
|
+
// TRACE
|
8120
|
+
lognow(`(${self.nodeId}) Node sending back a response of type ${responseRequestType}...`);
|
8121
|
+
|
8122
|
+
const clusterResponse={
|
8123
|
+
type:responseRequestType,
|
8124
|
+
result:response
|
8125
|
+
};
|
8126
|
+
const originNodeId=message.nodeId;
|
8127
|
+
if(propagateToAllCluster) self.propagateToCluster(clusterResponse, originNodeId);
|
8128
|
+
else self.propagateToSingleNeighbor(clusterResponse, originNodeId);
|
8129
|
+
}
|
8130
|
+
|
8131
|
+
});
|
8132
|
+
}
|
8133
|
+
// ****************************************************************************************************
|
8134
|
+
|
8135
|
+
/*private*/propagateToCluster(request, destinationNodeId=null, excludedNodesIds=null){
|
8136
|
+
request.requestId=getUUID();
|
8137
|
+
const originNodeId=this.nodeId;
|
8138
|
+
request.nodeId=originNodeId;
|
8139
|
+
if(destinationNodeId) request.destinationNodeId=destinationNodeId;
|
8140
|
+
if(excludedNodesIds) request.excludedNodesIds=excludedNodesIds;
|
8141
|
+
request.visitedNodeIds=[originNodeId];
|
8142
|
+
this.sendToOtherNodes("cluster",request);
|
8143
|
+
}
|
8144
|
+
|
8145
|
+
/*private*/propagateToNeighbors(request){
|
8146
|
+
request.requestId=getUUID();
|
8147
|
+
const originNodeId=this.nodeId;
|
8148
|
+
request.nodeId=originNodeId;
|
8149
|
+
this.sendToOtherNodes("neightbors", request);
|
8150
|
+
}
|
8151
|
+
/*private*/propagateToSingleNeighbor(request, destinationNodeId){
|
8152
|
+
request.requestId=getUUID();
|
8153
|
+
const originNodeId=this.nodeId;
|
8154
|
+
request.nodeId=originNodeId;
|
8155
|
+
request.destinationNodeId=destinationNodeId;
|
8156
|
+
this.sendToOtherSingleNode("neightbors", request, destinationNodeId);
|
8157
|
+
}
|
8158
|
+
|
8159
|
+
// ****************************************************************************************************
|
8160
|
+
|
8161
|
+
/*private*/sendToOtherNodes(channelName, message){
|
8162
|
+
let hasBeenSentIncoming=this.sendToIncomingServers(channelName, message);
|
8163
|
+
let hasBeenSentOutcoming=this.sendToOutcomingServers(channelName, message);
|
8164
|
+
return (hasBeenSentIncoming || hasBeenSentOutcoming);
|
8165
|
+
}
|
8166
|
+
/*private*/sendToIncomingServers(channelName, message){
|
8167
|
+
const self=this;
|
8168
|
+
let hasBeenSent=false;
|
8169
|
+
foreach(this.incomingServers, (client)=>{
|
8170
|
+
const clientSocket=client.clientSocket;
|
8171
|
+
self.server.send(channelName, message, null, clientSocket);
|
8172
|
+
hasBeenSent=true;
|
8173
|
+
});
|
8174
|
+
return hasBeenSent;
|
8175
|
+
}
|
8176
|
+
/*private*/sendToOutcomingServers(channelName, message){
|
8177
|
+
let hasBeenSent=false;
|
8178
|
+
foreach(this.outcomingServers, (client)=>{
|
8179
|
+
const clientInstanceToOtherServer=client.clientInstance;
|
8180
|
+
clientInstanceToOtherServer.client.socketToServer.send(channelName, message);
|
8181
|
+
hasBeenSent=true;
|
8182
|
+
});
|
8183
|
+
return hasBeenSent;
|
8184
|
+
}
|
8185
|
+
/*private*/sendToOtherSingleNode(channelName, request, destinationNodeId){
|
8186
|
+
const self=this;
|
8187
|
+
let hasBeenSent=false;
|
8188
|
+
foreach(this.incomingServers, (client)=>{
|
8189
|
+
const clientSocket=client.clientSocket;
|
8190
|
+
self.server.send(channelName, message, null, clientSocket);
|
8191
|
+
hasBeenSent=true;
|
8192
|
+
},(c, nodeId)=>(nodeId===destinationNodeId));
|
8193
|
+
if(!hasBeenSent){
|
8194
|
+
foreach(this.outcomingServers, (client)=>{
|
8195
|
+
const clientInstanceToOtherServer=client.clientInstance;
|
8196
|
+
clientInstanceToOtherServer.client.socketToServer.send(channelName, message);
|
8197
|
+
hasBeenSent=true;
|
8198
|
+
},(c, nodeId)=>(nodeId===destinationNodeId));
|
8199
|
+
}
|
8200
|
+
return hasBeenSent;
|
8201
|
+
}
|
8202
|
+
|
8203
|
+
/*private*/setUniqueReceptionPoint(channelName, messageType, doOnReception){
|
8204
|
+
const self=this;
|
8205
|
+
const listeners=this.listeners[channelName];
|
8206
|
+
if(foreach(listeners,listener=>{
|
8207
|
+
if(listener.messageType===messageType) return true;
|
8208
|
+
})) return;
|
8209
|
+
listeners.push({
|
8210
|
+
messageType:messageType,
|
8211
|
+
execute:(self, message, server, clientSocket)=>{
|
8212
|
+
doOnReception(self, message, server, clientSocket);
|
8213
|
+
}
|
8214
|
+
});
|
8215
|
+
}
|
8216
|
+
|
8217
|
+
// ****************************************************************************************************
|
8218
|
+
|
8219
|
+
/*private*/isInAuthorizedNodes(incomingServerNodeId){
|
8220
|
+
return contains(this.authorizedNodesIds,incomingServerNodeId);
|
8221
|
+
}
|
8222
|
+
|
8223
|
+
/*public*/traceNode(){
|
8224
|
+
// TRACE
|
8225
|
+
lognow("-------------------------------------------------------------");
|
8226
|
+
lognow(`(${this.nodeId}) :`,this);
|
8227
|
+
lognow("-------------------------------------------------------------");
|
8228
|
+
}
|
8229
|
+
|
8230
|
+
|
8231
|
+
|
7378
8232
|
}
|
7379
8233
|
|
7380
|
-
|
7381
|
-
|
8234
|
+
|
8235
|
+
getAORTACNode=function(nodeId=getUUID(), selfOrigin="ws://127.0.0.1:40000", outcomingNodesOrigins=[], model, controller){
|
8236
|
+
return new AORTACNode(nodeId, selfOrigin, outcomingNodesOrigins, model, controller);
|
7382
8237
|
}
|
7383
8238
|
|
7384
8239
|
|
@@ -7388,6 +8243,7 @@ getAORTACNode=function(selfOrigin="127.0.0.1:40000",outcomingNodesOrigins=[]){
|
|
7388
8243
|
|
7389
8244
|
|
7390
8245
|
|
8246
|
+
|
7391
8247
|
/* INCLUDED EXTERNAL LIBRAIRIES
|
7392
8248
|
*/
|
7393
8249
|
"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
|
aotrautils-srv/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "aotrautils-srv",
|
3
|
-
"version": "0.0.
|
3
|
+
"version": "0.0.1059",
|
4
4
|
"main": "aotrautils-srv.build.js",
|
5
5
|
"description": "A library for vanilla javascript utils (server-side) used in aotra javascript CMS",
|
6
6
|
"author": "Jeremie Ratomposon <info@alqemia.com> (https://alqemia.com)",
|