fix(core): update
This commit is contained in:
		
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							| @@ -22,18 +22,20 @@ | |||||||
|   }, |   }, | ||||||
|   "homepage": "https://gitlab.com/pushrocks/smartdata#README", |   "homepage": "https://gitlab.com/pushrocks/smartdata#README", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@pushrocks/lik": "^6.0.0", |     "@pushrocks/lik": "^6.0.2", | ||||||
|     "@pushrocks/smartdelay": "^2.0.13", |     "@pushrocks/smartdelay": "^2.0.13", | ||||||
|     "@pushrocks/smartlog": "^3.0.1", |     "@pushrocks/smartlog": "^3.0.1", | ||||||
|     "@pushrocks/smartmongo": "^2.0.7", |     "@pushrocks/smartmongo": "^2.0.7", | ||||||
|     "@pushrocks/smartpromise": "^3.1.7", |     "@pushrocks/smartpromise": "^3.1.7", | ||||||
|     "@pushrocks/smartrx": "^3.0.0", |     "@pushrocks/smartrx": "^3.0.0", | ||||||
|     "@pushrocks/smartstring": "^4.0.2", |     "@pushrocks/smartstring": "^4.0.2", | ||||||
|  |     "@pushrocks/smarttime": "^4.0.1", | ||||||
|     "@pushrocks/smartunique": "^3.0.3", |     "@pushrocks/smartunique": "^3.0.3", | ||||||
|     "@tsclass/tsclass": "^4.0.19", |     "@pushrocks/taskbuffer": "^3.0.10", | ||||||
|     "@types/lodash": "^4.14.188", |     "@tsclass/tsclass": "^4.0.28", | ||||||
|  |     "@types/lodash": "^4.14.191", | ||||||
|     "lodash": "^4.17.21", |     "lodash": "^4.17.21", | ||||||
|     "mongodb": "^4.9.1" |     "mongodb": "^4.13.0" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "@gitzone/tsbuild": "^2.1.65", |     "@gitzone/tsbuild": "^2.1.65", | ||||||
| @@ -41,7 +43,7 @@ | |||||||
|     "@gitzone/tstest": "^1.0.74", |     "@gitzone/tstest": "^1.0.74", | ||||||
|     "@pushrocks/qenv": "^5.0.2", |     "@pushrocks/qenv": "^5.0.2", | ||||||
|     "@pushrocks/tapbundle": "^5.0.4", |     "@pushrocks/tapbundle": "^5.0.4", | ||||||
|     "@types/node": "^18.7.16", |     "@types/node": "^18.11.18", | ||||||
|     "@types/shortid": "0.0.29" |     "@types/shortid": "0.0.29" | ||||||
|   }, |   }, | ||||||
|   "files": [ |   "files": [ | ||||||
|   | |||||||
							
								
								
									
										1395
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1395
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -100,7 +100,7 @@ tap.test('should save the car to the db', async (toolsArg) => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| tap.test('expect to get instance of Car with shallow match', async () => { | tap.test('expect to get instance of Car with shallow match', async () => { | ||||||
|   const totalQueryCycles = totalCars / 6; |   const totalQueryCycles = totalCars / 2; | ||||||
|   let counter = 0; |   let counter = 0; | ||||||
|   do { |   do { | ||||||
|     const timeStart = Date.now(); |     const timeStart = Date.now(); | ||||||
|   | |||||||
| @@ -3,6 +3,6 @@ | |||||||
|  */ |  */ | ||||||
| export const commitinfo = { | export const commitinfo = { | ||||||
|   name: '@pushrocks/smartdata', |   name: '@pushrocks/smartdata', | ||||||
|   version: '5.0.10', |   version: '5.0.11', | ||||||
|   description: 'do more with data' |   description: 'do more with data' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,7 +4,13 @@ export * from './smartdata.classes.doc.js'; | |||||||
| export * from './smartdata.classes.easystore.js'; | export * from './smartdata.classes.easystore.js'; | ||||||
| export * from './smartdata.classes.cursor.js'; | export * from './smartdata.classes.cursor.js'; | ||||||
|  |  | ||||||
|  | import * as convenience from './smartadata.convenience.js'; | ||||||
|  |  | ||||||
|  | export { | ||||||
|  |   convenience | ||||||
|  | } | ||||||
|  |  | ||||||
| // to be removed with the next breaking update | // to be removed with the next breaking update | ||||||
| import * as plugins from './smartdata.plugins.js'; | import type * as plugins from './smartdata.plugins.js'; | ||||||
| type IMongoDescriptor = plugins.tsclass.database.IMongoDescriptor; | type IMongoDescriptor = plugins.tsclass.database.IMongoDescriptor; | ||||||
| export type { IMongoDescriptor }; | export type { IMongoDescriptor }; | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								ts/smartadata.convenience.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								ts/smartadata.convenience.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | import * as plugins from './smartdata.plugins.js'; | ||||||
|  |  | ||||||
|  | export const getNewUniqueId = async (prefixArg?: string) => { | ||||||
|  |   return plugins.smartunique.uni(prefixArg); | ||||||
|  | } | ||||||
| @@ -16,6 +16,7 @@ export class SmartdataDb { | |||||||
|   mongoDbClient: plugins.mongodb.MongoClient; |   mongoDbClient: plugins.mongodb.MongoClient; | ||||||
|   mongoDb: plugins.mongodb.Db; |   mongoDb: plugins.mongodb.Db; | ||||||
|   status: TConnectionStatus; |   status: TConnectionStatus; | ||||||
|  |   statusConnectedDeferred = plugins.smartpromise.defer(); | ||||||
|   smartdataCollectionMap = new ObjectMap<SmartdataCollection<any>>(); |   smartdataCollectionMap = new ObjectMap<SmartdataCollection<any>>(); | ||||||
|  |  | ||||||
|   constructor(smartdataOptions: plugins.tsclass.database.IMongoDescriptor) { |   constructor(smartdataOptions: plugins.tsclass.database.IMongoDescriptor) { | ||||||
| @@ -51,6 +52,7 @@ export class SmartdataDb { | |||||||
|     }); |     }); | ||||||
|     this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName); |     this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName); | ||||||
|     this.status = 'connected'; |     this.status = 'connected'; | ||||||
|  |     this.statusConnectedDeferred.resolve(); | ||||||
|     console.log(`Connected to database ${this.smartdataOptions.mongoDbName}`); |     console.log(`Connected to database ${this.smartdataOptions.mongoDbName}`); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										201
									
								
								ts/smartdata.classes.distributedcoordinator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								ts/smartdata.classes.distributedcoordinator.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | |||||||
|  | import * as plugins from './smartdata.plugins.js'; | ||||||
|  | import { SmartdataDb } from './smartdata.classes.db.js'; | ||||||
|  | import { Manager, setDefaultManagerForDoc } from './smartdata.classes.collection.js'; | ||||||
|  | import { SmartDataDbDoc, svDb, unI } from './smartdata.classes.doc.js'; | ||||||
|  |  | ||||||
|  | @Manager() | ||||||
|  | class DistributedClass extends SmartDataDbDoc<DistributedClass, DistributedClass> { | ||||||
|  |   // INSTANCE | ||||||
|  |   @unI() | ||||||
|  |   public id: string; | ||||||
|  |  | ||||||
|  |   @svDb() | ||||||
|  |   public data: { | ||||||
|  |     status: 'bidding' | 'settled' | 'initializing' | 'stopped'; | ||||||
|  |     biddingShortcode?: string; | ||||||
|  |     lastUpdated: number; | ||||||
|  |     elected: boolean; | ||||||
|  |     /** | ||||||
|  |      * used to store request | ||||||
|  |      */ | ||||||
|  |     taskRequests: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest[], | ||||||
|  |    | ||||||
|  |     /** | ||||||
|  |      * only used by the leader to convey consultation results | ||||||
|  |      */ | ||||||
|  |     taskRequestResults: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequestResult[]; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This file implements a distributed coordinator according to the @pushrocks/taskbuffer standard. | ||||||
|  |  * you should not set up this yourself. Instead, there is a factory on the SmartdataDb class | ||||||
|  |  * that will take care of setting this up. | ||||||
|  |  */ | ||||||
|  | export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distributedCoordination | ||||||
|  |   .AbstractDistributedCoordinator { | ||||||
|  |   public readyPromise: Promise<any>; | ||||||
|  |   public db: SmartdataDb; | ||||||
|  |   private asyncExecutionStack = new plugins.lik.AsyncExecutionStack(); | ||||||
|  |   private ownInstance: DistributedClass; | ||||||
|  |  | ||||||
|  |   constructor(dbArg?: SmartdataDb) { | ||||||
|  |     super(); | ||||||
|  |     this.db = dbArg; | ||||||
|  |     setDefaultManagerForDoc(this, DistributedClass); | ||||||
|  |     this.readyPromise = this.db.statusConnectedDeferred.promise; | ||||||
|  |     this.init(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // smartdata specific stuff | ||||||
|  |   public async start() { | ||||||
|  |     await this.init(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public async stop() { | ||||||
|  |     if (this.ownInstance?.data.elected) { | ||||||
|  |       this.ownInstance.data.elected = false; | ||||||
|  |     } else { | ||||||
|  |       console.log(`can't stop a distributed instance that has not been started yet.`); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public id = plugins.smartunique.uni('distributedInstance'); | ||||||
|  |  | ||||||
|  |   private startHeartbeat = async () => { | ||||||
|  |     while (this.ownInstance.data.status !== 'stopped') { | ||||||
|  |       await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |         await this.ownInstance.updateFromDb(); | ||||||
|  |         this.ownInstance.data.lastUpdated = Date.now(); | ||||||
|  |         await this.ownInstance.save(); | ||||||
|  |       }); | ||||||
|  |       await plugins.smartdelay.delayFor(10000); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |   public async init() { | ||||||
|  |     await this.readyPromise; | ||||||
|  |     if (!this.ownInstance) { | ||||||
|  |       await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |         this.ownInstance = new DistributedClass(); | ||||||
|  |         this.ownInstance.id = this.id; | ||||||
|  |         this.ownInstance.data = { | ||||||
|  |           elected: false, | ||||||
|  |           lastUpdated: Date.now(), | ||||||
|  |           status: 'initializing', | ||||||
|  |           taskRequests: [], | ||||||
|  |           taskRequestResults: [] | ||||||
|  |         }; | ||||||
|  |         await this.ownInstance.save(); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // lets enable the heartbeat | ||||||
|  |     this.startHeartbeat(); | ||||||
|  |  | ||||||
|  |     // lets do a leader check | ||||||
|  |     await this.checkAndMaybeLead(); | ||||||
|  |  | ||||||
|  |     return this.ownInstance; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // --> leader election | ||||||
|  |   public async checkAndMaybeLead() { | ||||||
|  |     const allInstances = await DistributedClass.getInstances({}); | ||||||
|  |     let leader = allInstances.find((instanceArg) => instanceArg.data.elected === true); | ||||||
|  |     if ( | ||||||
|  |       leader && | ||||||
|  |       leader.data.lastUpdated >= | ||||||
|  |         Date.now() - plugins.smarttime.getMilliSecondsFromUnits({ minutes: 1 }) | ||||||
|  |     ) { | ||||||
|  |       await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |         await this.ownInstance.updateFromDb(); | ||||||
|  |         this.ownInstance.data.status = 'settled'; | ||||||
|  |         await this.ownInstance.save(); | ||||||
|  |       }); | ||||||
|  |       return; | ||||||
|  |     } else { | ||||||
|  |       await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |         this.ownInstance.data.status = 'bidding'; | ||||||
|  |         this.ownInstance.data.biddingShortcode = plugins.smartunique.shortId(); | ||||||
|  |         await this.ownInstance.save(); | ||||||
|  |       }); | ||||||
|  |       await plugins.smartdelay.delayFor(plugins.smarttime.getMilliSecondsFromUnits({ minutes: 2 })); | ||||||
|  |       await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |         let biddingInstances = await DistributedClass.getInstances({}); | ||||||
|  |         biddingInstances = biddingInstances.filter( | ||||||
|  |           (instanceArg) => | ||||||
|  |             !instanceArg.data.elected && | ||||||
|  |             instanceArg.data.lastUpdated >= | ||||||
|  |               Date.now() - plugins.smarttime.getMilliSecondsFromUnits({ minutes: 1 }) | ||||||
|  |         ); | ||||||
|  |         this.ownInstance.data.elected = true; | ||||||
|  |         for (const biddingInstance of biddingInstances) { | ||||||
|  |           if (biddingInstance.data.biddingShortcode < this.ownInstance.data.biddingShortcode) { | ||||||
|  |             this.ownInstance.data.elected = false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         await this.ownInstance.save(); | ||||||
|  |       }); | ||||||
|  |       if (this.ownInstance.data.elected) { | ||||||
|  |         this.leadFunction(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * when it has been determined | ||||||
|  |    * that this instance is leading | ||||||
|  |    * the leading is implemented here | ||||||
|  |    */ | ||||||
|  |   public async leadFunction() { | ||||||
|  |     const ownInstance = await this.init(); | ||||||
|  |     const watcher = await DistributedClass.watch({}); | ||||||
|  |     /** | ||||||
|  |      * this function is started once per unique job request | ||||||
|  |      */ | ||||||
|  |     const startResultTimer = async () => { | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     watcher.changeSubject.subscribe({ | ||||||
|  |       next: async (distributedDoc) => { | ||||||
|  |         distributedDoc | ||||||
|  |       } | ||||||
|  |     }) | ||||||
|  |     while (this.ownInstance.data.status !== 'stopped') { | ||||||
|  |       await plugins.smartdelay.delayFor(1000); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   // abstract implemented methods | ||||||
|  |   public async fireDistributedTaskRequest( | ||||||
|  |     taskRequestArg: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest | ||||||
|  |   ): Promise<plugins.taskbuffer.distributedCoordination.IDistributedTaskRequestResult> { | ||||||
|  |     const ownInstance = await this.init(); | ||||||
|  |     await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |       this.ownInstance.data.taskRequests.push(taskRequestArg); | ||||||
|  |       await this.ownInstance.save(); | ||||||
|  |     }); | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public async updateDistributedTaskRequest( | ||||||
|  |     infoBasisArg: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest | ||||||
|  |   ): Promise<void> { | ||||||
|  |     await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |       const existingInfoBasis = this.ownInstance.data.taskRequests.find(infoBasisItem => { | ||||||
|  |         return infoBasisItem.taskName === infoBasisArg.taskName | ||||||
|  |           && infoBasisItem.taskExecutionTime === infoBasisArg.taskExecutionTime; | ||||||
|  |       }); | ||||||
|  |       Object.assign(existingInfoBasis, infoBasisArg); | ||||||
|  |       await this.ownInstance.save(); | ||||||
|  |       plugins.smartdelay.delayFor(60000).then(() => { | ||||||
|  |         this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { | ||||||
|  |           const indexToRemove = this.ownInstance.data.taskRequests.indexOf(existingInfoBasis); | ||||||
|  |           this.ownInstance.data.taskRequests.splice(indexToRemove, indexToRemove); | ||||||
|  |           await this.ownInstance.save(); | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -255,6 +255,16 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * updates an object from db | ||||||
|  |    */ | ||||||
|  |   public async updateFromDb() { | ||||||
|  |     const mongoDbNativeDoc = await this.collection.findOne(this.createIdentifiableObject()); | ||||||
|  |     for (const key of Object.keys(mongoDbNativeDoc)) { | ||||||
|  |       this[key] = mongoDbNativeDoc[key]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * creates a saveable object so the instance can be persisted as json in the database |    * creates a saveable object so the instance can be persisted as json in the database | ||||||
|    */ |    */ | ||||||
|   | |||||||
| @@ -17,6 +17,15 @@ export class EasyStore<T> { | |||||||
|       @unI() |       @unI() | ||||||
|       public nameId: string; |       public nameId: string; | ||||||
|  |  | ||||||
|  |       @svDb() | ||||||
|  |       public ephermal: { | ||||||
|  |         activated: boolean; | ||||||
|  |         timeout: number; | ||||||
|  |       }; | ||||||
|  |  | ||||||
|  |       @svDb() | ||||||
|  |       lastEdit: number; | ||||||
|  |  | ||||||
|       @svDb() |       @svDb() | ||||||
|       public data: Partial<T>; |       public data: Partial<T>; | ||||||
|     } |     } | ||||||
| @@ -90,4 +99,10 @@ export class EasyStore<T> { | |||||||
|     easyStore.data = {}; |     easyStore.data = {}; | ||||||
|     await easyStore.save(); |     await easyStore.save(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   public async cleanUpEphermal() { | ||||||
|  |     while(await this.smartdataDbRef.statusConnectedDeferred.promise && this.smartdataDbRef.status === 'connected') { | ||||||
|  |        | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,17 +4,21 @@ import * as tsclass from '@tsclass/tsclass'; | |||||||
| export { tsclass }; | export { tsclass }; | ||||||
|  |  | ||||||
| // @pushrocks scope | // @pushrocks scope | ||||||
| import * as smartlog from '@pushrocks/smartlog'; | import * as lik from '@pushrocks/lik'; | ||||||
| import * as lodash from 'lodash'; |  | ||||||
| import * as mongodb from 'mongodb'; |  | ||||||
| import * as smartdelay from '@pushrocks/smartdelay'; | import * as smartdelay from '@pushrocks/smartdelay'; | ||||||
|  | import * as smartlog from '@pushrocks/smartlog'; | ||||||
| import * as smartpromise from '@pushrocks/smartpromise'; | import * as smartpromise from '@pushrocks/smartpromise'; | ||||||
| import * as smartq from '@pushrocks/smartpromise'; | import * as smartq from '@pushrocks/smartpromise'; | ||||||
| import * as smartrx from '@pushrocks/smartrx'; | import * as smartrx from '@pushrocks/smartrx'; | ||||||
| import * as smartstring from '@pushrocks/smartstring'; | import * as smartstring from '@pushrocks/smartstring'; | ||||||
|  | import * as smarttime from '@pushrocks/smarttime'; | ||||||
| import * as smartunique from '@pushrocks/smartunique'; | import * as smartunique from '@pushrocks/smartunique'; | ||||||
|  | import * as taskbuffer from '@pushrocks/taskbuffer'; | ||||||
|  | import * as lodash from 'lodash'; | ||||||
|  | import * as mongodb from 'mongodb'; | ||||||
|  |  | ||||||
| export { | export { | ||||||
|  |   lik, | ||||||
|   smartdelay, |   smartdelay, | ||||||
|   smartpromise, |   smartpromise, | ||||||
|   smartlog, |   smartlog, | ||||||
| @@ -23,5 +27,7 @@ export { | |||||||
|   smartrx, |   smartrx, | ||||||
|   mongodb, |   mongodb, | ||||||
|   smartstring, |   smartstring, | ||||||
|  |   smarttime, | ||||||
|   smartunique, |   smartunique, | ||||||
|  |   taskbuffer, | ||||||
| }; | }; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user