Compare commits

..

59 Commits

Author SHA1 Message Date
bef7f68360 5.0.42 2024-03-30 12:26:43 +01:00
56e9754725 fix(TypeScript): improve tsconfig.json for ES Module use 2024-03-30 12:26:42 +01:00
30d81581cf 5.0.41 2024-03-27 17:32:02 +01:00
5e9db12955 fix(core): update 2024-03-27 17:32:01 +01:00
ad2f422c86 5.0.40 2024-03-27 17:30:51 +01:00
17ce14bcb9 fix(core): update 2024-03-27 17:30:50 +01:00
32319e6e77 5.0.39 2024-03-27 17:30:15 +01:00
4cd284eaa9 fix(core): update 2024-03-27 17:30:14 +01:00
00ec2e57c2 5.0.38 2024-03-27 16:24:58 +01:00
765356ce3d fix(core): update 2024-03-27 16:24:57 +01:00
56b8581d2b 5.0.37 2024-03-26 13:22:34 +01:00
37a9df9086 fix(core): update 2024-03-26 13:22:33 +01:00
090fb668cd 5.0.36 2024-03-26 13:21:37 +01:00
a1c807261c fix(core): update 2024-03-26 13:21:36 +01:00
a2ccf15f69 5.0.35 2024-03-26 00:25:06 +01:00
84d48f1914 fix(core): update 2024-03-26 00:25:06 +01:00
1e258e5ffb 5.0.34 2024-03-22 18:36:35 +01:00
19d5f553b9 fix(core): update 2024-03-22 18:36:34 +01:00
7a257ea925 5.0.33 2023-08-21 12:39:49 +02:00
2fa1e89f34 fix(core): update 2023-08-21 12:39:48 +02:00
d6b3896dd3 5.0.32 2023-08-16 13:16:40 +02:00
49b11b17ce fix(core): update 2023-08-16 13:16:39 +02:00
4ac8a4c0cd 5.0.31 2023-08-16 12:08:28 +02:00
7f9983382a fix(core): update 2023-08-16 12:08:27 +02:00
54f529b0a7 5.0.30 2023-08-15 19:55:53 +02:00
f542463bf6 fix(core): update 2023-08-15 19:55:52 +02:00
1235ae2eb3 5.0.29 2023-08-15 19:55:23 +02:00
8166d2f7c2 fix(core): update 2023-08-15 19:55:22 +02:00
7c9f27e02f 5.0.28 2023-08-15 01:24:30 +02:00
842e4b280b fix(core): update 2023-08-15 01:24:29 +02:00
009f3297b2 5.0.27 2023-08-15 01:01:16 +02:00
2ff3a4e0b7 fix(core): update 2023-08-15 01:01:16 +02:00
0e55cd8876 5.0.26 2023-08-12 23:32:40 +02:00
eccdf3f00a fix(core): update 2023-08-12 23:32:39 +02:00
c7544133d9 5.0.25 2023-08-12 23:32:02 +02:00
c7c9acf5bd fix(core): update 2023-08-12 23:32:02 +02:00
c99ec50853 5.0.24 2023-08-10 18:06:46 +02:00
4dd9557e1d fix(core): update 2023-08-10 18:06:45 +02:00
52b34a6da1 5.0.23 2023-07-21 20:09:51 +02:00
1bf74fe04d fix(core): update 2023-07-21 20:09:51 +02:00
fdd875ad31 5.0.22 2023-07-21 20:08:50 +02:00
a7bf0c0298 fix(core): update 2023-07-21 20:08:50 +02:00
59d6336e43 5.0.21 2023-07-21 20:08:18 +02:00
e0fc81179a fix(core): update 2023-07-21 20:08:18 +02:00
5aa81a56a2 switch to new org scheme 2023-07-10 02:43:00 +02:00
9ae26177b8 5.0.20 2023-06-25 02:01:20 +02:00
26ac52d6c5 fix(core): update 2023-06-25 02:01:19 +02:00
fb39463b7d 5.0.19 2023-06-25 01:53:17 +02:00
44acba80c1 fix(core): update 2023-06-25 01:53:17 +02:00
8cf8315577 5.0.18 2023-06-25 01:51:44 +02:00
9b44b64a50 fix(core): update 2023-06-25 01:51:43 +02:00
699e25201c 5.0.17 2023-06-25 01:30:14 +02:00
2ef9aace68 fix(core): update 2023-06-25 01:30:14 +02:00
cc55a57dfd 5.0.16 2023-06-25 01:29:40 +02:00
b2df512552 fix(core): update 2023-06-25 01:29:39 +02:00
23c62fbd69 5.0.15 2023-06-25 01:28:30 +02:00
5f70ea0b05 fix(core): update 2023-06-25 01:28:29 +02:00
49a595876a 5.0.14 2023-06-25 01:27:10 +02:00
db38a1ef85 fix(core): update 2023-06-25 01:27:09 +02:00
24 changed files with 3332 additions and 2000 deletions

View File

@ -0,0 +1,66 @@
name: Default (not tags)
on:
push:
tags-ignore:
- '**'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Install pnpm and npmci
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
- name: Run npm prepare
run: npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build

View File

@ -0,0 +1,124 @@
name: Default (tags)
on:
push:
tags:
- '*'
env:
IMAGE: registry.gitlab.com/hosttoday/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@gitea.lossless.digital/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
jobs:
security:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Audit production dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --prod
continue-on-error: true
- name: Audit development dependencies
run: |
npmci command npm config set registry https://registry.npmjs.org
npmci command pnpm audit --audit-level=high --dev
continue-on-error: true
test:
if: ${{ always() }}
needs: security
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Test stable
run: |
npmci node install stable
npmci npm install
npmci npm test
- name: Test build
run: |
npmci node install stable
npmci npm install
npmci npm build
release:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Release
run: |
npmci node install stable
npmci npm publish
metadata:
needs: test
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
container:
image: ${{ env.IMAGE }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Prepare
run: |
pnpm install -g pnpm
pnpm install -g @shipzone/npmci
npmci npm prepare
- name: Code quality
run: |
npmci command npm install -g typescript
npmci npm install
- name: Trigger
run: npmci trigger
- name: Build docs and upload artifacts
run: |
npmci node install stable
npmci npm install
pnpm install -g @gitzone/tsdoc
npmci command tsdoc
continue-on-error: true

View File

@ -1,128 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
before_script:
- pnpm install -g pnpm
- pnpm install -g @shipzone/npmci
- npmci npm prepare
# ====================
# security stage
# ====================
# ====================
# security stage
# ====================
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --prod
tags:
- lossless
- docker
allow_failure: true
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci command npm config set registry https://registry.npmjs.org
- npmci command pnpm audit --audit-level=high --dev
tags:
- lossless
- docker
allow_failure: true
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
testBuild:
stage: test
script:
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- docker
release:
stage: release
script:
- npmci node install stable
- npmci npm publish
only:
- tags
tags:
- lossless
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
only:
- tags
script:
- npmci command npm install -g typescript
- npmci npm prepare
- npmci npm install
tags:
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
stage: metadata
script:
- npmci node install stable
- npmci npm install
- npmci command npm run buildDocs
tags:
- lossless
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

View File

@ -13,10 +13,10 @@
"projectType": "npm", "projectType": "npm",
"module": { "module": {
"githost": "gitlab.com", "githost": "gitlab.com",
"gitscope": "pushrocks", "gitscope": "push.rocks",
"gitrepo": "smartdata", "gitrepo": "smartdata",
"description": "do more with data", "description": "do more with data",
"npmPackagename": "@pushrocks/smartdata", "npmPackagename": "@push.rocks/smartdata",
"license": "MIT" "license": "MIT"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartdata", "name": "@push.rocks/smartdata",
"version": "5.0.13", "version": "5.0.42",
"private": false, "private": false,
"description": "do more with data", "description": "do more with data",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
@ -22,27 +22,26 @@
}, },
"homepage": "https://gitlab.com/pushrocks/smartdata#README", "homepage": "https://gitlab.com/pushrocks/smartdata#README",
"dependencies": { "dependencies": {
"@pushrocks/lik": "^6.0.2", "@push.rocks/lik": "^6.0.14",
"@pushrocks/smartdelay": "^3.0.1", "@push.rocks/smartdelay": "^3.0.1",
"@pushrocks/smartlog": "^3.0.1", "@push.rocks/smartlog": "^3.0.2",
"@pushrocks/smartmongo": "^2.0.7", "@push.rocks/smartmongo": "^2.0.10",
"@pushrocks/smartpromise": "^4.0.2", "@push.rocks/smartpromise": "^4.0.2",
"@pushrocks/smartrx": "^3.0.2", "@push.rocks/smartrx": "^3.0.7",
"@pushrocks/smartstring": "^4.0.2", "@push.rocks/smartstring": "^4.0.15",
"@pushrocks/smarttime": "^4.0.1", "@push.rocks/smarttime": "^4.0.6",
"@pushrocks/smartunique": "^3.0.3", "@push.rocks/smartunique": "^3.0.8",
"@pushrocks/taskbuffer": "^3.0.10", "@push.rocks/taskbuffer": "^3.1.7",
"@tsclass/tsclass": "^4.0.42", "@tsclass/tsclass": "^4.0.52",
"mongodb": "^5.6.0" "mongodb": "^6.5.0"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.66", "@gitzone/tsbuild": "^2.1.66",
"@gitzone/tsrun": "^1.2.42", "@gitzone/tsrun": "^1.2.44",
"@gitzone/tstest": "^1.0.74", "@gitzone/tstest": "^1.0.77",
"@pushrocks/qenv": "^5.0.2", "@push.rocks/qenv": "^6.0.5",
"@pushrocks/tapbundle": "^5.0.8", "@push.rocks/tapbundle": "^5.0.22",
"@types/node": "^20.3.1", "@types/node": "^20.11.30"
"@types/shortid": "0.0.29"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",

4471
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1 @@
required: required:
- MONGO_URL
- MONGO_DBNAME
- MONGO_PASS

View File

@ -1,27 +1,26 @@
# @pushrocks/smartdata # @push.rocks/smartdata
do more with data do more with data
## Availabililty and Links ## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@pushrocks/smartdata) * [npmjs.org (npm package)](https://www.npmjs.com/package/@push.rocks/smartdata)
* [gitlab.com (source)](https://gitlab.com/pushrocks/smartdata) * [gitlab.com (source)](https://gitlab.com/push.rocks/smartdata)
* [github.com (source mirror)](https://github.com/pushrocks/smartdata) * [github.com (source mirror)](https://github.com/push.rocks/smartdata)
* [docs (typedoc)](https://pushrocks.gitlab.io/smartdata/) * [docs (typedoc)](https://push.rocks.gitlab.io/smartdata/)
## Status for master ## Status for master
Status Category | Status Badge Status Category | Status Badge
-- | -- -- | --
GitLab Pipelines | [![pipeline status](https://gitlab.com/pushrocks/smartdata/badges/master/pipeline.svg)](https://lossless.cloud) GitLab Pipelines | [![pipeline status](https://gitlab.com/push.rocks/smartdata/badges/master/pipeline.svg)](https://lossless.cloud)
GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/pushrocks/smartdata/badges/master/coverage.svg)](https://lossless.cloud) GitLab Pipline Test Coverage | [![coverage report](https://gitlab.com/push.rocks/smartdata/badges/master/coverage.svg)](https://lossless.cloud)
npm | [![npm downloads per month](https://badgen.net/npm/dy/@pushrocks/smartdata)](https://lossless.cloud) npm | [![npm downloads per month](https://badgen.net/npm/dy/@push.rocks/smartdata)](https://lossless.cloud)
Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/pushrocks/smartdata)](https://lossless.cloud) Snyk | [![Known Vulnerabilities](https://badgen.net/snyk/push.rocks/smartdata)](https://lossless.cloud)
TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud) TypeScript Support | [![TypeScript](https://badgen.net/badge/TypeScript/>=%203.x/blue?icon=typescript)](https://lossless.cloud)
node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/) node Support | [![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud) Code Style | [![Code Style](https://badgen.net/badge/style/prettier/purple)](https://lossless.cloud)
PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@pushrocks/smartdata)](https://lossless.cloud) PackagePhobia (total standalone install weight) | [![PackagePhobia](https://badgen.net/packagephobia/install/@push.rocks/smartdata)](https://lossless.cloud)
PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@pushrocks/smartdata)](https://lossless.cloud) PackagePhobia (package size on registry) | [![PackagePhobia](https://badgen.net/packagephobia/publish/@push.rocks/smartdata)](https://lossless.cloud)
BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@pushrocks/smartdata)](https://lossless.cloud) BundlePhobia (total size when bundled) | [![BundlePhobia](https://badgen.net/bundlephobia/minzip/@push.rocks/smartdata)](https://lossless.cloud)
Platform support | [![Supports Windows 10](https://badgen.net/badge/supports%20Windows%2010/yes/green?icon=windows)](https://lossless.cloud) [![Supports Mac OS X](https://badgen.net/badge/supports%20Mac%20OS%20X/yes/green?icon=apple)](https://lossless.cloud)
## Usage ## Usage
@ -150,7 +149,6 @@ We are always happy for code contributions. If you are not the code contributing
For further information read the linked docs at the top of this readme. For further information read the linked docs at the top of this readme.
> MIT licensed | **©** [Lossless GmbH](https://lossless.gmbh) ## Legal
> MIT licensed | **©** [Task Venture Capital GmbH](https://task.vc)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy) | By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)

View File

@ -0,0 +1,112 @@
import { tap, expect } from '@push.rocks/tapbundle';
import * as smartmongo from '@push.rocks/smartmongo';
import type * as taskbuffer from '@push.rocks/taskbuffer';
import * as smartdata from '../ts/index.js';
import { SmartdataDistributedCoordinator, DistributedClass } from '../ts/smartdata.classes.distributedcoordinator.js'; // path might need adjusting
const totalInstances = 10;
// =======================================
// Connecting to the database server
// =======================================
let smartmongoInstance: smartmongo.SmartMongo;
let testDb: smartdata.SmartdataDb;
tap.test('should create a testinstance as database', async () => {
smartmongoInstance = await smartmongo.SmartMongo.createAndStart();
testDb = new smartdata.SmartdataDb(await smartmongoInstance.getMongoDescriptor());
await testDb.init();
});
tap.test('should instantiate DistributedClass', async (tools) => {
const instance = new DistributedClass();
expect(instance).toBeInstanceOf(DistributedClass);
});
tap.test('DistributedClass should update the time', async (tools) => {
const distributedCoordinator = new SmartdataDistributedCoordinator(testDb);
await distributedCoordinator.start();
const initialTime = distributedCoordinator.ownInstance.data.lastUpdated;
await distributedCoordinator.sendHeartbeat();
const updatedTime = distributedCoordinator.ownInstance.data.lastUpdated;
expect(updatedTime).toBeGreaterThan(initialTime);
await distributedCoordinator.stop();
});
tap.test('should instantiate SmartdataDistributedCoordinator', async (tools) => {
const distributedCoordinator = new SmartdataDistributedCoordinator(testDb);
await distributedCoordinator.start();
expect(distributedCoordinator).toBeInstanceOf(SmartdataDistributedCoordinator);
await distributedCoordinator.stop();
});
tap.test('SmartdataDistributedCoordinator should update leader status', async (tools) => {
const distributedCoordinator = new SmartdataDistributedCoordinator(testDb);
await distributedCoordinator.start();
await distributedCoordinator.checkAndMaybeLead();
expect(distributedCoordinator.ownInstance.data.elected).toBeOneOf([true, false]);
await distributedCoordinator.stop();
});
tap.test('SmartdataDistributedCoordinator should handle distributed task requests', async (tools) => {
const distributedCoordinator = new SmartdataDistributedCoordinator(testDb);
await distributedCoordinator.start();
const mockTaskRequest: taskbuffer.distributedCoordination.IDistributedTaskRequest = {
submitterId: "mockSubmitter12345", // Some unique mock submitter ID
requestResponseId: 'uni879873462hjhfkjhsdf', // Some unique ID for the request-response
taskName: "SampleTask",
taskVersion: "1.0.0", // Assuming it's a version string
taskExecutionTime: Date.now(),
taskExecutionTimeout: 60000, // Let's say the timeout is 1 minute (60000 ms)
taskExecutionParallel: 5, // Let's assume max 5 parallel executions
status: 'requesting'
};
const response = await distributedCoordinator.fireDistributedTaskRequest(mockTaskRequest);
console.log(response) // based on your expected structure for the response
await distributedCoordinator.stop();
});
tap.test('SmartdataDistributedCoordinator should update distributed task requests', async (tools) => {
const distributedCoordinator = new SmartdataDistributedCoordinator(testDb);
await distributedCoordinator.start();
const mockTaskRequest: taskbuffer.distributedCoordination.IDistributedTaskRequest = {
submitterId: "mockSubmitter12345", // Some unique mock submitter ID
requestResponseId: 'uni879873462hjhfkjhsdf', // Some unique ID for the request-response
taskName: "SampleTask",
taskVersion: "1.0.0", // Assuming it's a version string
taskExecutionTime: Date.now(),
taskExecutionTimeout: 60000, // Let's say the timeout is 1 minute (60000 ms)
taskExecutionParallel: 5, // Let's assume max 5 parallel executions
status: 'requesting'
};
await distributedCoordinator.updateDistributedTaskRequest(mockTaskRequest);
// Here, we can potentially check if a DB entry got updated or some other side-effect of the update method.
await distributedCoordinator.stop();
});
tap.test('should elect only one leader amongst multiple instances', async (tools) => {
const coordinators = Array.from({ length: totalInstances }).map(() => new SmartdataDistributedCoordinator(testDb));
await Promise.all(coordinators.map(coordinator => coordinator.start()));
const leaders = coordinators.filter(coordinator => coordinator.ownInstance.data.elected);
for (const leader of leaders) {
console.log(leader.ownInstance);
}
expect(leaders.length).toEqual(1);
// stopping clears a coordinator from being elected.
await Promise.all(coordinators.map(coordinator => coordinator.stop()));
});
tap.test('should clean up', async () => {
await smartmongoInstance.stopAndDumpToDir(`.nogit/testdata/`);
setTimeout(() => process.exit(), 2000);
})
tap.start({ throwOnError: true });

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import { Qenv } from '@pushrocks/qenv'; import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@pushrocks/smartmongo'; import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/smartdata.plugins.js'; import { smartunique } from '../ts/smartdata.plugins.js';
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/'); const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
@ -26,7 +26,7 @@ tap.test('should create a testinstance as database', async () => {
tap.skip.test('should connect to atlas', async (tools) => { tap.skip.test('should connect to atlas', async (tools) => {
const databaseName = `test-smartdata-${smartunique.shortId()}`; const databaseName = `test-smartdata-${smartunique.shortId()}`;
testDb = new smartdata.SmartdataDb({ testDb = new smartdata.SmartdataDb({
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'), mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGO_URL'),
mongoDbName: databaseName, mongoDbName: databaseName,
}); });
await testDb.init(); await testDb.init();

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import { Qenv } from '@pushrocks/qenv'; import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@pushrocks/smartmongo'; import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/smartdata.plugins.js'; import { smartunique } from '../ts/smartdata.plugins.js';
import * as mongodb from 'mongodb'; import * as mongodb from 'mongodb';
@ -30,7 +30,7 @@ tap.test('should create a testinstance as database', async () => {
tap.skip.test('should connect to atlas', async (tools) => { tap.skip.test('should connect to atlas', async (tools) => {
const databaseName = `test-smartdata-${smartunique.shortId()}`; const databaseName = `test-smartdata-${smartunique.shortId()}`;
testDb = new smartdata.SmartdataDb({ testDb = new smartdata.SmartdataDb({
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'), mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGO_URL'),
mongoDbName: databaseName, mongoDbName: databaseName,
}); });
await testDb.init(); await testDb.init();
@ -72,6 +72,11 @@ class Car extends smartdata.SmartDataDbDoc<Car, Car> {
} }
} }
tap.test('should create a new id', async () => {
const newid = await Car.getNewId();
console.log(newid);
})
tap.test('should save the car to the db', async (toolsArg) => { tap.test('should save the car to the db', async (toolsArg) => {
const myCar = new Car('red', 'Volvo'); const myCar = new Car('red', 'Volvo');
await myCar.save(); await myCar.save();

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import { Qenv } from '@pushrocks/qenv'; import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@pushrocks/smartmongo'; import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/smartdata.plugins.js'; import { smartunique } from '../ts/smartdata.plugins.js';
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/'); const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
@ -28,7 +28,7 @@ tap.test('should create a testinstance as database', async () => {
tap.skip.test('should connect to atlas', async (tools) => { tap.skip.test('should connect to atlas', async (tools) => {
const databaseName = `test-smartdata-${smartunique.shortId()}`; const databaseName = `test-smartdata-${smartunique.shortId()}`;
testDb = new smartdata.SmartdataDb({ testDb = new smartdata.SmartdataDb({
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'), mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGO_URL'),
mongoDbName: databaseName, mongoDbName: databaseName,
}); });
await testDb.init(); await testDb.init();

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@pushrocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import { Qenv } from '@pushrocks/qenv'; import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@pushrocks/smartmongo'; import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/smartdata.plugins.js'; import { smartunique } from '../ts/smartdata.plugins.js';
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/'); const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
@ -28,7 +28,7 @@ tap.test('should create a testinstance as database', async () => {
tap.skip.test('should connect to atlas', async (tools) => { tap.skip.test('should connect to atlas', async (tools) => {
const databaseName = `test-smartdata-${smartunique.shortId()}`; const databaseName = `test-smartdata-${smartunique.shortId()}`;
testDb = new smartdata.SmartdataDb({ testDb = new smartdata.SmartdataDb({
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'), mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGO_URL'),
mongoDbName: databaseName, mongoDbName: databaseName,
}); });
await testDb.init(); await testDb.init();

View File

@ -2,7 +2,7 @@
* autocreated commitinfo by @pushrocks/commitinfo * autocreated commitinfo by @pushrocks/commitinfo
*/ */
export const commitinfo = { export const commitinfo = {
name: '@pushrocks/smartdata', name: '@push.rocks/smartdata',
version: '5.0.13', version: '5.0.42',
description: 'do more with data' description: 'do more with data'
} }

View File

@ -6,9 +6,7 @@ export * from './smartdata.classes.cursor.js';
import * as convenience from './smartadata.convenience.js'; import * as convenience from './smartadata.convenience.js';
export { export { convenience };
convenience
}
// to be removed with the next breaking update // to be removed with the next breaking update
import type * as plugins from './smartdata.plugins.js'; import type * as plugins from './smartdata.plugins.js';

View File

@ -2,4 +2,4 @@ import * as plugins from './smartdata.plugins.js';
export const getNewUniqueId = async (prefixArg?: string) => { export const getNewUniqueId = async (prefixArg?: string) => {
return plugins.smartunique.uni(prefixArg); return plugins.smartunique.uni(prefixArg);
} };

View File

@ -26,7 +26,8 @@ const collectionFactory = new CollectionFactory();
*/ */
export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) { export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) { return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor { const decoratedClass = class extends constructor {
public static className = constructor.name;
public static get collection() { public static get collection() {
if (!(dbArg instanceof SmartdataDb)) { if (!(dbArg instanceof SmartdataDb)) {
dbArg = dbArg(); dbArg = dbArg();
@ -40,6 +41,7 @@ export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
return collectionFactory.getCollection(constructor.name, dbArg); return collectionFactory.getCollection(constructor.name, dbArg);
} }
}; };
return decoratedClass;
}; };
} }
@ -56,9 +58,10 @@ export const setDefaultManagerForDoc = <T>(managerArg: IManager, dbDocArg: T): T
* This is a decorator that will tell the decorated class what dbTable to use * This is a decorator that will tell the decorated class what dbTable to use
* @param dbArg * @param dbArg
*/ */
export function Manager<TManager extends IManager>(managerArg?: TManager | TDelayed<TManager>) { export function managed<TManager extends IManager>(managerArg?: TManager | TDelayed<TManager>) {
return function classDecorator<T extends { new (...args: any[]): any }>(constructor: T) { return function classDecorator<T extends { new (...args: any[]): any }>(constructor: T) {
return class extends constructor { const decoratedClass = class extends constructor {
public static className = constructor.name;
public static get collection() { public static get collection() {
let dbArg: SmartdataDb; let dbArg: SmartdataDb;
if (!managerArg) { if (!managerArg) {
@ -106,9 +109,15 @@ export function Manager<TManager extends IManager>(managerArg?: TManager | TDela
return manager; return manager;
} }
}; };
return decoratedClass;
}; };
} }
/**
* @dpecrecated use @managed instead
*/
export const Manager = managed;
export class SmartdataCollection<T> { export class SmartdataCollection<T> {
/** /**
* the collection that is used * the collection that is used
@ -269,7 +278,7 @@ export class SmartdataCollection<T> {
* if this.objectValidation is not set it passes. * if this.objectValidation is not set it passes.
*/ */
private checkDoc(docArg: T): Promise<void> { private checkDoc(docArg: T): Promise<void> {
const done = plugins.smartq.defer<void>(); const done = plugins.smartpromise.defer<void>();
let validationResult = true; let validationResult = true;
if (this.objectValidation) { if (this.objectValidation) {
validationResult = this.objectValidation(docArg); validationResult = this.objectValidation(docArg);

View File

@ -1,5 +1,4 @@
import * as plugins from './smartdata.plugins.js'; import * as plugins from './smartdata.plugins.js';
import { ObjectMap } from '@pushrocks/lik';
import { SmartdataCollection } from './smartdata.classes.collection.js'; import { SmartdataCollection } from './smartdata.classes.collection.js';
import { EasyStore } from './smartdata.classes.easystore.js'; import { EasyStore } from './smartdata.classes.easystore.js';
@ -17,7 +16,7 @@ export class SmartdataDb {
mongoDb: plugins.mongodb.Db; mongoDb: plugins.mongodb.Db;
status: TConnectionStatus; status: TConnectionStatus;
statusConnectedDeferred = plugins.smartpromise.defer(); statusConnectedDeferred = plugins.smartpromise.defer();
smartdataCollectionMap = new ObjectMap<SmartdataCollection<any>>(); smartdataCollectionMap = new plugins.lik.ObjectMap<SmartdataCollection<any>>();
constructor(smartdataOptions: plugins.tsclass.database.IMongoDescriptor) { constructor(smartdataOptions: plugins.tsclass.database.IMongoDescriptor) {
this.smartdataOptions = smartdataOptions; this.smartdataOptions = smartdataOptions;

View File

@ -1,30 +1,32 @@
import * as plugins from './smartdata.plugins.js'; import * as plugins from './smartdata.plugins.js';
import { SmartdataDb } from './smartdata.classes.db.js'; import { SmartdataDb } from './smartdata.classes.db.js';
import { Manager, setDefaultManagerForDoc } from './smartdata.classes.collection.js'; import { managed, setDefaultManagerForDoc } from './smartdata.classes.collection.js';
import { SmartDataDbDoc, svDb, unI } from './smartdata.classes.doc.js'; import { SmartDataDbDoc, svDb, unI } from './smartdata.classes.doc.js';
import { SmartdataDbWatcher } from './smartdata.classes.watcher.js';
@Manager() @managed()
class DistributedClass extends SmartDataDbDoc<DistributedClass, DistributedClass> { export class DistributedClass extends SmartDataDbDoc<DistributedClass, DistributedClass> {
// INSTANCE // INSTANCE
@unI() @unI()
public id: string; public id: string;
@svDb() @svDb()
public data: { public data: {
status: 'bidding' | 'settled' | 'initializing' | 'stopped'; status: 'initializing' | 'bidding' | 'settled' | 'stopped';
biddingShortcode?: string; biddingShortcode?: string;
biddingStartTime?: number;
lastUpdated: number; lastUpdated: number;
elected: boolean; elected: boolean;
/** /**
* used to store request * used to store request
*/ */
taskRequests: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest[], taskRequests: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest[];
/** /**
* only used by the leader to convey consultation results * only used by the leader to convey consultation results
*/ */
taskRequestResults: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequestResult[]; taskRequestResults: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequestResult[];
} };
} }
/** /**
@ -37,14 +39,14 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
public readyPromise: Promise<any>; public readyPromise: Promise<any>;
public db: SmartdataDb; public db: SmartdataDb;
private asyncExecutionStack = new plugins.lik.AsyncExecutionStack(); private asyncExecutionStack = new plugins.lik.AsyncExecutionStack();
private ownInstance: DistributedClass; public ownInstance: DistributedClass;
public distributedWatcher: SmartdataDbWatcher<DistributedClass>;
constructor(dbArg?: SmartdataDb) { constructor(dbArg: SmartdataDb) {
super(); super();
this.db = dbArg; this.db = dbArg;
setDefaultManagerForDoc(this, DistributedClass); setDefaultManagerForDoc(this, DistributedClass);
this.readyPromise = this.db.statusConnectedDeferred.promise; this.readyPromise = this.db.statusConnectedDeferred.promise;
this.init();
} }
// smartdata specific stuff // smartdata specific stuff
@ -53,26 +55,56 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
} }
public async stop() { public async stop() {
if (this.ownInstance?.data.elected) { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
this.ownInstance.data.elected = false; if (this.distributedWatcher) {
} else { await this.distributedWatcher.close();
console.log(`can't stop a distributed instance that has not been started yet.`); }
} if (this.ownInstance?.data.elected) {
this.ownInstance.data.elected = false;
}
if (this.ownInstance?.data.status === 'stopped') {
console.log(`stopping a distributed instance that has not been started yet.`);
}
this.ownInstance.data.status = 'stopped';
await this.ownInstance.save();
console.log(`stopped ${this.ownInstance.id}`);
});
} }
public id = plugins.smartunique.uni('distributedInstance'); public id = plugins.smartunique.uni('distributedInstance');
private startHeartbeat = async () => { private startHeartbeat = async () => {
while (this.ownInstance.data.status !== 'stopped') { while (this.ownInstance.data.status !== 'stopped') {
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.sendHeartbeat();
await this.ownInstance.updateFromDb(); await plugins.smartdelay.delayForRandom(5000, 10000);
this.ownInstance.data.lastUpdated = Date.now();
await this.ownInstance.save();
});
await plugins.smartdelay.delayFor(10000);
} }
}; };
public async init() {
public async sendHeartbeat() {
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
if (this.ownInstance.data.status === 'stopped') {
console.log(`aborted sending heartbeat because status is stopped`);
return;
}
await this.ownInstance.updateFromDb();
this.ownInstance.data.lastUpdated = Date.now();
await this.ownInstance.save();
console.log(`sent heartbeat for ${this.ownInstance.id}`);
const allInstances = DistributedClass.getInstances({});
});
if (this.ownInstance.data.status === 'stopped') {
console.log(`aborted sending heartbeat because status is stopped`);
return;
}
const eligibleLeader = await this.getEligibleLeader();
// not awaiting here because we don't want to block the heartbeat
this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
if (!eligibleLeader && this.ownInstance.data.status === 'settled') {
this.checkAndMaybeLead();
}
});
}
private async init() {
await this.readyPromise; await this.readyPromise;
if (!this.ownInstance) { if (!this.ownInstance) {
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
@ -83,10 +115,12 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
lastUpdated: Date.now(), lastUpdated: Date.now(),
status: 'initializing', status: 'initializing',
taskRequests: [], taskRequests: [],
taskRequestResults: [] taskRequestResults: [],
}; };
await this.ownInstance.save(); await this.ownInstance.save();
}); });
} else {
console.warn(`distributed instance already initialized`);
} }
// lets enable the heartbeat // lets enable the heartbeat
@ -98,42 +132,73 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
return this.ownInstance; return this.ownInstance;
} }
public async getEligibleLeader() {
return this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
const allInstances = await DistributedClass.getInstances({});
let leaders = allInstances.filter((instanceArg) => instanceArg.data.elected === true);
const eligibleLeader = leaders.find(
(leader) =>
leader.data.lastUpdated >=
Date.now() - plugins.smarttime.getMilliSecondsFromUnits({ seconds: 20 })
);
return eligibleLeader;
});
}
// --> leader election // --> leader election
public async checkAndMaybeLead() { public async checkAndMaybeLead() {
const allInstances = await DistributedClass.getInstances({}); await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
let leader = allInstances.find((instanceArg) => instanceArg.data.elected === true); this.ownInstance.data.status = 'initializing';
if ( this.ownInstance.save();
leader && });
leader.data.lastUpdated >= if (await this.getEligibleLeader()) {
Date.now() - plugins.smarttime.getMilliSecondsFromUnits({ minutes: 1 })
) {
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
await this.ownInstance.updateFromDb(); await this.ownInstance.updateFromDb();
this.ownInstance.data.status = 'settled'; this.ownInstance.data.status = 'settled';
await this.ownInstance.save(); await this.ownInstance.save();
console.log(`${this.ownInstance.id} settled as follower`);
}); });
return; return;
} else if (
(await DistributedClass.getInstances({})).find((instanceArg) => {
instanceArg.data.status === 'bidding' &&
instanceArg.data.biddingStartTime <= Date.now() - 4000 &&
instanceArg.data.biddingStartTime >= Date.now() - 30000;
})
) {
console.log('too late to the bidding party... waiting for next round.');
return;
} else { } else {
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
await this.ownInstance.updateFromDb();
this.ownInstance.data.status = 'bidding'; this.ownInstance.data.status = 'bidding';
this.ownInstance.data.biddingStartTime = Date.now();
this.ownInstance.data.biddingShortcode = plugins.smartunique.shortId(); this.ownInstance.data.biddingShortcode = plugins.smartunique.shortId();
await this.ownInstance.save(); await this.ownInstance.save();
console.log('bidding code stored.');
}); });
await plugins.smartdelay.delayFor(plugins.smarttime.getMilliSecondsFromUnits({ minutes: 2 })); console.log(`bidding for leadership...`);
await plugins.smartdelay.delayFor(
plugins.smarttime.getMilliSecondsFromUnits({ seconds: 5 })
);
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
let biddingInstances = await DistributedClass.getInstances({}); let biddingInstances = await DistributedClass.getInstances({});
biddingInstances = biddingInstances.filter( biddingInstances = biddingInstances.filter(
(instanceArg) => (instanceArg) =>
!instanceArg.data.elected && instanceArg.data.status === 'bidding' &&
instanceArg.data.lastUpdated >= instanceArg.data.lastUpdated >=
Date.now() - plugins.smarttime.getMilliSecondsFromUnits({ minutes: 1 }) Date.now() - plugins.smarttime.getMilliSecondsFromUnits({ seconds: 10 })
); );
console.log(`found ${biddingInstances.length} bidding instances...`);
this.ownInstance.data.elected = true; this.ownInstance.data.elected = true;
for (const biddingInstance of biddingInstances) { for (const biddingInstance of biddingInstances) {
if (biddingInstance.data.biddingShortcode < this.ownInstance.data.biddingShortcode) { if (biddingInstance.data.biddingShortcode < this.ownInstance.data.biddingShortcode) {
this.ownInstance.data.elected = false; this.ownInstance.data.elected = false;
} }
} }
await plugins.smartdelay.delayFor(5000);
console.log(`settling with status elected = ${this.ownInstance.data.elected}`);
this.ownInstance.data.status = 'settled';
await this.ownInstance.save(); await this.ownInstance.save();
}); });
if (this.ownInstance.data.elected) { if (this.ownInstance.data.elected) {
@ -148,45 +213,83 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
* the leading is implemented here * the leading is implemented here
*/ */
public async leadFunction() { public async leadFunction() {
const ownInstance = await this.init(); this.distributedWatcher = await DistributedClass.watch({});
const watcher = await DistributedClass.watch({});
/**
* this function is started once per unique job request
*/
const startResultTimer = async () => {
} const currentTaskRequests: Array<{
taskName: string;
watcher.changeSubject.subscribe({ taskExecutionTime: number;
/**
* all instances that requested this task
*/
requestingDistibutedInstanceIds: string[];
responseTimeout: plugins.smartdelay.Timeout<any>;
}> = [];
this.distributedWatcher.changeSubject.subscribe({
next: async (distributedDoc) => { next: async (distributedDoc) => {
distributedDoc if (!distributedDoc) {
console.log(`registered deletion of instance...`);
return;
}
console.log(distributedDoc);
console.log(`registered change for ${distributedDoc.id}`);
distributedDoc;
},
});
while (this.ownInstance.data.status !== 'stopped' && this.ownInstance.data.elected) {
const allInstances = await DistributedClass.getInstances({});
for (const instance of allInstances) {
if (instance.data.status === 'stopped') {
await instance.delete();
};
} }
}) await plugins.smartdelay.delayFor(10000);
while (this.ownInstance.data.status !== 'stopped') {
await plugins.smartdelay.delayFor(1000);
} }
}; }
// abstract implemented methods // abstract implemented methods
public async fireDistributedTaskRequest( public async fireDistributedTaskRequest(
taskRequestArg: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest taskRequestArg: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest
): Promise<plugins.taskbuffer.distributedCoordination.IDistributedTaskRequestResult> { ): Promise<plugins.taskbuffer.distributedCoordination.IDistributedTaskRequestResult> {
const ownInstance = await this.init();
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
if (!this.ownInstance) {
console.error('instance need to be started first...');
return;
}
await this.ownInstance.updateFromDb();
this.ownInstance.data.taskRequests.push(taskRequestArg); this.ownInstance.data.taskRequests.push(taskRequestArg);
await this.ownInstance.save(); await this.ownInstance.save();
}); });
return null; await plugins.smartdelay.delayFor(10000);
const result = await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
await this.ownInstance.updateFromDb();
const taskRequestResult = this.ownInstance.data.taskRequestResults.find((resultItem) => {
return resultItem.requestResponseId === taskRequestArg.requestResponseId;
});
return taskRequestResult;
});
if (!result) {
console.warn('no result found for task request...');
return null;
}
return result;
} }
public async updateDistributedTaskRequest( public async updateDistributedTaskRequest(
infoBasisArg: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest infoBasisArg: plugins.taskbuffer.distributedCoordination.IDistributedTaskRequest
): Promise<void> { ): Promise<void> {
await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => { await this.asyncExecutionStack.getExclusiveExecutionSlot(async () => {
const existingInfoBasis = this.ownInstance.data.taskRequests.find(infoBasisItem => { const existingInfoBasis = this.ownInstance.data.taskRequests.find((infoBasisItem) => {
return infoBasisItem.taskName === infoBasisArg.taskName return (
&& infoBasisItem.taskExecutionTime === infoBasisArg.taskExecutionTime; infoBasisItem.taskName === infoBasisArg.taskName &&
infoBasisItem.taskExecutionTime === infoBasisArg.taskExecutionTime
);
}); });
if (!existingInfoBasis) {
console.warn('trying to update a non existing task request... aborting!');
return;
}
Object.assign(existingInfoBasis, infoBasisArg); Object.assign(existingInfoBasis, infoBasisArg);
await this.ownInstance.save(); await this.ownInstance.save();
plugins.smartdelay.delayFor(60000).then(() => { plugins.smartdelay.delayFor(60000).then(() => {
@ -194,8 +297,8 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
const indexToRemove = this.ownInstance.data.taskRequests.indexOf(existingInfoBasis); const indexToRemove = this.ownInstance.data.taskRequests.indexOf(existingInfoBasis);
this.ownInstance.data.taskRequests.splice(indexToRemove, indexToRemove); this.ownInstance.data.taskRequests.splice(indexToRemove, indexToRemove);
await this.ownInstance.save(); await this.ownInstance.save();
}) });
}) });
}); });
} }
} }

View File

@ -1,7 +1,5 @@
import * as plugins from './smartdata.plugins.js'; import * as plugins from './smartdata.plugins.js';
import { ObjectMap } from '@pushrocks/lik';
import { SmartdataDb } from './smartdata.classes.db.js'; import { SmartdataDb } from './smartdata.classes.db.js';
import { SmartdataDbCursor } from './smartdata.classes.cursor.js'; import { SmartdataDbCursor } from './smartdata.classes.cursor.js';
import { type IManager, SmartdataCollection } from './smartdata.classes.collection.js'; import { type IManager, SmartdataCollection } from './smartdata.classes.collection.js';
@ -129,6 +127,13 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
} }
} }
/**
* get a unique id prefixed with the class name
*/
public static async getNewId<T = any>(this: plugins.tsclass.typeFest.Class<T>, lengthArg: number = 20) {
return `${(this as any).className}:${plugins.smartunique.shortId(lengthArg)}`;
}
/** /**
* get cursor * get cursor
* @returns * @returns
@ -241,9 +246,9 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
* also store any referenced objects to DB * also store any referenced objects to DB
* better for data consistency * better for data consistency
*/ */
public saveDeep(savedMapArg: ObjectMap<SmartDataDbDoc<any, any>> = null) { public saveDeep(savedMapArg: plugins.lik.ObjectMap<SmartDataDbDoc<any, any>> = null) {
if (!savedMapArg) { if (!savedMapArg) {
savedMapArg = new ObjectMap<SmartDataDbDoc<any, any>>(); savedMapArg = new plugins.lik.ObjectMap<SmartDataDbDoc<any, any>>();
} }
savedMapArg.add(this); savedMapArg.add(this);
this.save(); this.save();
@ -259,7 +264,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
* updates an object from db * updates an object from db
*/ */
public async updateFromDb() { public async updateFromDb() {
const mongoDbNativeDoc = await this.collection.findOne(this.createIdentifiableObject()); const mongoDbNativeDoc = await this.collection.findOne(await this.createIdentifiableObject());
for (const key of Object.keys(mongoDbNativeDoc)) { for (const key of Object.keys(mongoDbNativeDoc)) {
this[key] = mongoDbNativeDoc[key]; this[key] = mongoDbNativeDoc[key];
} }

View File

@ -37,7 +37,16 @@ export class EasyStore<T> {
this.nameId = nameIdArg; this.nameId = nameIdArg;
} }
private async getEasyStore() { private easyStorePromise: Promise<InstanceType<typeof this.easyStoreClass>>;
private async getEasyStore(): Promise<InstanceType<typeof this.easyStoreClass>> {
if (this.easyStorePromise) {
return this.easyStorePromise;
};
// first run from here
const deferred = plugins.smartpromise.defer<InstanceType<typeof this.easyStoreClass>>();
this.easyStorePromise = deferred.promise;
let easyStore = await this.easyStoreClass.getInstance({ let easyStore = await this.easyStoreClass.getInstance({
nameId: this.nameId, nameId: this.nameId,
}); });
@ -48,7 +57,8 @@ export class EasyStore<T> {
easyStore.data = {}; easyStore.data = {};
await easyStore.save(); await easyStore.save();
} }
return easyStore; deferred.resolve(easyStore);
return this.easyStorePromise;
} }
/** /**
@ -70,7 +80,7 @@ export class EasyStore<T> {
/** /**
* writes a specific key to the keyValueStore * writes a specific key to the keyValueStore
*/ */
public async writeKey(keyArg: keyof T, valueArg: any) { public async writeKey<TKey extends keyof T>(keyArg: TKey, valueArg: T[TKey]) {
const easyStore = await this.getEasyStore(); const easyStore = await this.getEasyStore();
easyStore.data[keyArg] = valueArg; easyStore.data[keyArg] = valueArg;
await easyStore.save(); await easyStore.save();
@ -101,8 +111,9 @@ export class EasyStore<T> {
} }
public async cleanUpEphermal() { public async cleanUpEphermal() {
while(await this.smartdataDbRef.statusConnectedDeferred.promise && this.smartdataDbRef.status === 'connected') { while (
(await this.smartdataDbRef.statusConnectedDeferred.promise) &&
} this.smartdataDbRef.status === 'connected'
) {}
} }
} }

View File

@ -17,9 +17,13 @@ export class SmartdataDbWatcher<T = any> {
smartdataDbDocArg: typeof SmartDataDbDoc smartdataDbDocArg: typeof SmartDataDbDoc
) { ) {
this.changeStream = changeStreamArg; this.changeStream = changeStreamArg;
this.changeStream.on('change', async (item: T) => { this.changeStream.on('change', async (item: any) => {
if (!item.fullDocument) {
this.changeSubject.next(null);
return;
}
this.changeSubject.next( this.changeSubject.next(
smartdataDbDocArg.createInstanceFromMongoDbNativeDoc(item) as any as T smartdataDbDocArg.createInstanceFromMongoDbNativeDoc(item.fullDocument) as any as T
); );
}); });
plugins.smartdelay.delayFor(0).then(() => { plugins.smartdelay.delayFor(0).then(() => {

View File

@ -4,16 +4,15 @@ import * as tsclass from '@tsclass/tsclass';
export { tsclass }; export { tsclass };
// @pushrocks scope // @pushrocks scope
import * as lik from '@pushrocks/lik'; import * as lik from '@push.rocks/lik';
import * as smartdelay from '@pushrocks/smartdelay'; import * as smartdelay from '@push.rocks/smartdelay';
import * as smartlog from '@pushrocks/smartlog'; import * as smartlog from '@push.rocks/smartlog';
import * as smartpromise from '@pushrocks/smartpromise'; import * as smartpromise from '@push.rocks/smartpromise';
import * as smartq from '@pushrocks/smartpromise'; import * as smartrx from '@push.rocks/smartrx';
import * as smartrx from '@pushrocks/smartrx'; import * as smartstring from '@push.rocks/smartstring';
import * as smartstring from '@pushrocks/smartstring'; import * as smarttime from '@push.rocks/smarttime';
import * as smarttime from '@pushrocks/smarttime'; import * as smartunique from '@push.rocks/smartunique';
import * as smartunique from '@pushrocks/smartunique'; import * as taskbuffer from '@push.rocks/taskbuffer';
import * as taskbuffer from '@pushrocks/taskbuffer';
import * as mongodb from 'mongodb'; import * as mongodb from 'mongodb';
export { export {
@ -21,7 +20,6 @@ export {
smartdelay, smartdelay,
smartpromise, smartpromise,
smartlog, smartlog,
smartq,
smartrx, smartrx,
mongodb, mongodb,
smartstring, smartstring,

View File

@ -3,7 +3,12 @@
"experimentalDecorators": true, "experimentalDecorators": true,
"useDefineForClassFields": false, "useDefineForClassFields": false,
"target": "ES2022", "target": "ES2022",
"module": "ES2022", "module": "NodeNext",
"moduleResolution": "nodenext" "moduleResolution": "NodeNext",
} "esModuleInterop": true,
"verbatimModuleSyntax": true
},
"exclude": [
"dist_*/**/*.d.ts"
]
} }