@harperfast/harper-pro 5.0.0-alpha.9 → 5.0.0-beta.2
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.
- package/core/.dockerignore +9 -0
- package/core/.git-blame-ignore-revs +2 -0
- package/core/.github/workflows/create-release.yaml +4 -4
- package/core/.github/workflows/integration-tests.yml +12 -10
- package/core/.github/workflows/notify-release-published.yaml +1 -1
- package/core/.github/workflows/publish-docker.yaml +2 -2
- package/core/.github/workflows/publish-npm.yaml +4 -4
- package/core/CONTRIBUTING.md +1 -1
- package/core/Dockerfile +62 -0
- package/core/build-tools/build-studio.sh +12 -0
- package/core/build-tools/build.sh +22 -0
- package/core/build-tools/download-prebuilds.js +13 -0
- package/core/components/Logger.ts +14 -0
- package/core/components/Scope.ts +35 -11
- package/core/components/componentLoader.ts +27 -10
- package/core/components/operations.js +10 -2
- package/core/config/configUtils.js +1 -1
- package/core/dataLayer/CreateTableObject.js +2 -2
- package/core/dataLayer/schema.js +7 -5
- package/core/dataLayer/schemaDescribe.js +1 -1
- package/core/index.d.ts +11 -6
- package/core/index.js +2 -0
- package/core/integrationTests/README.md +24 -0
- package/core/integrationTests/apiTests/tests/10_otherRoleTests.mjs +6 -6
- package/core/integrationTests/apiTests/tests/12_configuration.mjs +1 -1
- package/core/integrationTests/apiTests/tests/14_tokenAuth.mjs +2 -2
- package/core/integrationTests/apiTests/tests/16_terminologyUpdates.mjs +4 -4
- package/core/integrationTests/apiTests/tests/1_environmentSetup.mjs +1 -1
- package/core/integrationTests/apiTests/tests/2_dataLoad.mjs +4 -4
- package/core/integrationTests/apiTests/tests/3_sqlTests.mjs +3 -3
- package/core/integrationTests/apiTests/tests/4_noSqlTests.mjs +12 -12
- package/core/integrationTests/apiTests/tests/5_noSqlRoleTesting.mjs +8 -8
- package/core/integrationTests/apiTests/tests/7_jobsAndJobRoleTesting.mjs +10 -12
- package/core/integrationTests/apiTests/tests/8_deleteTests.mjs +8 -8
- package/core/integrationTests/apiTests/tests/9_transactions.mjs +2 -2
- package/core/integrationTests/apiTests/utils/search.mjs +1 -1
- package/core/integrationTests/apiTests/utils/table.mjs +1 -1
- package/core/integrationTests/server/operation-user-rbac.test.ts +1 -1
- package/core/integrationTests/server/operations-server.test.ts +1 -1
- package/core/integrationTests/server/storage-reclamation.test.ts +1 -1
- package/core/integrationTests/utils/README.md +1 -15
- package/core/integrationTests/utils/harperLifecycle.ts +33 -21
- package/core/package.json +23 -5
- package/core/resources/ResourceInterface.ts +1 -1
- package/core/resources/Table.ts +26 -11
- package/core/resources/analytics/read.ts +33 -26
- package/core/resources/analytics/write.ts +3 -7
- package/core/resources/databases.ts +29 -18
- package/core/resources/search.ts +10 -5
- package/core/security/auth.ts +1 -1
- package/core/security/jsLoader.ts +302 -83
- package/core/security/keys.js +11 -12
- package/core/security/user.ts +3 -3
- package/core/server/REST.ts +18 -2
- package/core/server/Server.ts +2 -1
- package/core/server/fastifyRoutes.ts +1 -0
- package/core/server/http.ts +13 -9
- package/core/server/loadRootComponents.js +1 -0
- package/core/server/operationsServer.ts +2 -1
- package/core/server/threads/manageThreads.js +49 -35
- package/core/static/defaultConfig.yaml +3 -0
- package/core/unitTests/apiTests/RESTProperties-test.mjs +2 -2
- package/core/unitTests/apiTests/basicREST-test.mjs +2 -2
- package/core/unitTests/components/Scope.test.js +54 -16
- package/core/unitTests/components/fixtures/testJSWithDeps/child-dir/circular.js +4 -0
- package/core/unitTests/components/fixtures/testJSWithDeps/child-dir/in-child-dir.js +4 -0
- package/core/unitTests/components/fixtures/testJSWithDeps/child-dir/typestrip.ts +2 -0
- package/core/unitTests/components/fixtures/testJSWithDeps/resources.js +43 -0
- package/core/unitTests/components/fixtures/testJSWithDeps/test-child-process.js +18 -0
- package/core/unitTests/components/globalIsolation.test.js +87 -1
- package/core/unitTests/config/configUtils.test.js +1 -260
- package/core/unitTests/resources/query.test.js +16 -1
- package/core/unitTests/resources/vectorIndex.test.js +1 -1
- package/core/unitTests/server/fastifyRoutes/operations.test.js +1 -1
- package/core/unitTests/testUtils.js +0 -17
- package/core/utility/hdbTerms.ts +3 -0
- package/core/utility/installation.ts +2 -5
- package/core/utility/lmdb/commonUtility.js +21 -10
- package/dist/core/{resources/ResourceInterfaceV2.js → components/Logger.js} +1 -1
- package/dist/core/components/Logger.js.map +1 -0
- package/dist/core/components/Scope.js +18 -10
- package/dist/core/components/Scope.js.map +1 -1
- package/dist/core/components/componentLoader.js +17 -10
- package/dist/core/components/componentLoader.js.map +1 -1
- package/dist/core/components/operations.js +2 -2
- package/dist/core/components/operations.js.map +1 -1
- package/dist/core/config/configUtils.js +1 -1
- package/dist/core/config/configUtils.js.map +1 -1
- package/dist/core/dataLayer/CreateTableObject.js +2 -2
- package/dist/core/dataLayer/CreateTableObject.js.map +1 -1
- package/dist/core/dataLayer/schema.js +6 -5
- package/dist/core/dataLayer/schema.js.map +1 -1
- package/dist/core/dataLayer/schemaDescribe.js +1 -1
- package/dist/core/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/resources/Table.js +12 -4
- package/dist/core/resources/Table.js.map +1 -1
- package/dist/core/resources/analytics/read.js +32 -22
- package/dist/core/resources/analytics/read.js.map +1 -1
- package/dist/core/resources/analytics/write.js +3 -6
- package/dist/core/resources/analytics/write.js.map +1 -1
- package/dist/core/resources/databases.js +22 -19
- package/dist/core/resources/databases.js.map +1 -1
- package/dist/core/resources/search.js +11 -5
- package/dist/core/resources/search.js.map +1 -1
- package/dist/core/security/auth.js +1 -1
- package/dist/core/security/auth.js.map +1 -1
- package/dist/core/security/jsLoader.js +265 -73
- package/dist/core/security/jsLoader.js.map +1 -1
- package/dist/core/security/keys.js +11 -12
- package/dist/core/security/keys.js.map +1 -1
- package/dist/core/security/user.js +3 -3
- package/dist/core/security/user.js.map +1 -1
- package/dist/core/server/REST.js +16 -2
- package/dist/core/server/REST.js.map +1 -1
- package/dist/core/server/Server.js.map +1 -1
- package/dist/core/server/fastifyRoutes.js +2 -0
- package/dist/core/server/fastifyRoutes.js.map +1 -1
- package/dist/core/server/http.js +12 -6
- package/dist/core/server/http.js.map +1 -1
- package/dist/core/server/loadRootComponents.js +1 -0
- package/dist/core/server/loadRootComponents.js.map +1 -1
- package/dist/core/server/operationsServer.js +3 -1
- package/dist/core/server/operationsServer.js.map +1 -1
- package/dist/core/server/threads/manageThreads.js +50 -35
- package/dist/core/server/threads/manageThreads.js.map +1 -1
- package/dist/core/utility/hdbTerms.js +3 -0
- package/dist/core/utility/hdbTerms.js.map +1 -1
- package/dist/core/utility/installation.js.map +1 -1
- package/dist/core/utility/lmdb/commonUtility.js +20 -13
- package/dist/core/utility/lmdb/commonUtility.js.map +1 -1
- package/dist/licensing/usageLicensing.js.map +1 -1
- package/dist/replication/knownNodes.js +5 -37
- package/dist/replication/knownNodes.js.map +1 -1
- package/dist/replication/nodeIdMapping.js +2 -35
- package/dist/replication/nodeIdMapping.js.map +1 -1
- package/dist/replication/replicationConnection.js +15 -6
- package/dist/replication/replicationConnection.js.map +1 -1
- package/dist/replication/replicator.js +3 -2
- package/dist/replication/replicator.js.map +1 -1
- package/dist/replication/setNode.js +1 -1
- package/dist/replication/setNode.js.map +1 -1
- package/dist/security/certificate.js.map +1 -1
- package/licensing/usageLicensing.ts +3 -2
- package/npm-shrinkwrap.json +303 -282
- package/package.json +4 -3
- package/replication/knownNodes.ts +3 -2
- package/replication/nodeIdMapping.ts +1 -1
- package/replication/replicationConnection.ts +33 -8
- package/replication/replicator.ts +7 -2
- package/replication/setNode.ts +1 -1
- package/security/certificate.ts +2 -1
- package/studio/web/assets/{index-v3wIpSYx.js → index-CWN9Wp5V.js} +2 -2
- package/studio/web/assets/{index-v3wIpSYx.js.map → index-CWN9Wp5V.js.map} +1 -1
- package/studio/web/assets/{index-ChCctErQ.js → index-CzghSAn2.js} +2 -2
- package/studio/web/assets/{index-ChCctErQ.js.map → index-CzghSAn2.js.map} +1 -1
- package/studio/web/assets/{index-Qu8D43wo.js → index-DMDhGP7N.js} +5 -5
- package/studio/web/assets/{index-Qu8D43wo.js.map → index-DMDhGP7N.js.map} +1 -1
- package/studio/web/assets/{index.lazy-tVSPM7bX.js → index.lazy-C-yDTGUy.js} +2 -2
- package/studio/web/assets/{index.lazy-tVSPM7bX.js.map → index.lazy-C-yDTGUy.js.map} +1 -1
- package/studio/web/assets/{profiler-C9as4sv-.js → profiler-0fZAOscv.js} +2 -2
- package/studio/web/assets/{profiler-C9as4sv-.js.map → profiler-0fZAOscv.js.map} +1 -1
- package/studio/web/assets/{react-redux-RRIhZnM6.js → react-redux-BIxqK8O6.js} +2 -2
- package/studio/web/assets/{react-redux-RRIhZnM6.js.map → react-redux-BIxqK8O6.js.map} +1 -1
- package/studio/web/assets/{startRecording-DYa4zCXV.js → startRecording-Ca3Gf2MY.js} +2 -2
- package/studio/web/assets/{startRecording-DYa4zCXV.js.map → startRecording-Ca3Gf2MY.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/core/resources/ResourceInterfaceV2.ts +0 -53
- package/core/resources/ResourceV2.ts +0 -67
- package/core/resources/analytics/profile.ts +0 -109
- package/core/unitTests/apiTests/analytics-test.mjs +0 -38
- package/core/v1.d.ts +0 -47
- package/core/v1.js +0 -38
- package/core/v2.d.ts +0 -47
- package/core/v2.js +0 -38
- package/dist/core/resources/ResourceInterfaceV2.js.map +0 -1
- package/dist/core/resources/ResourceV2.js +0 -27
- package/dist/core/resources/ResourceV2.js.map +0 -1
- package/dist/core/resources/analytics/profile.js +0 -144
- package/dist/core/resources/analytics/profile.js.map +0 -1
|
@@ -78,7 +78,7 @@ suite('Storage reclamation', (ctx: ContextWithHarper) => {
|
|
|
78
78
|
operation: 'create_table',
|
|
79
79
|
schema: TEST_DATABASE,
|
|
80
80
|
table: TEST_TABLE,
|
|
81
|
-
|
|
81
|
+
primary_key: 'id',
|
|
82
82
|
expiration: 2, // 2 second expiration (in seconds)
|
|
83
83
|
eviction: 1, // 1 second eviction (in seconds)
|
|
84
84
|
audit: true, // Enable audit logging
|
|
@@ -68,33 +68,19 @@ suite('My test suite', (ctx: ContextWithHarper) => {
|
|
|
68
68
|
|
|
69
69
|
Configuration options for `setupHarper()`.
|
|
70
70
|
|
|
71
|
-
**Interface Definition:**
|
|
72
|
-
|
|
73
71
|
```typescript
|
|
74
72
|
export interface SetupHarperOptions {
|
|
75
|
-
/**
|
|
76
|
-
* Timeout in milliseconds to wait for Harper to start.
|
|
77
|
-
* @default 30000
|
|
78
|
-
*/
|
|
79
73
|
startupTimeoutMs?: number;
|
|
80
|
-
/**
|
|
81
|
-
* Additional configuration options to pass to the Harper CLI.
|
|
82
|
-
*/
|
|
83
74
|
config: any;
|
|
84
|
-
/**
|
|
85
|
-
* Environment variables to set when running Harper.
|
|
86
|
-
*/
|
|
87
75
|
env: any;
|
|
88
76
|
}
|
|
89
77
|
```
|
|
90
78
|
|
|
91
79
|
**Properties:**
|
|
92
80
|
|
|
93
|
-
30000 (5 seconds), or the value of the `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` environment variable if set.
|
|
94
|
-
|
|
95
81
|
- **`config`** - `object` (optional) - Additional configuration options to pass to the Harper CLI.
|
|
96
82
|
- **`env`** - `object` (optional) - Additional environment variables to set when starting Harper.
|
|
97
|
-
- **`startupTimeoutMs`** - `number` (optional) - Timeout in milliseconds to wait for Harper to start. Defaults to
|
|
83
|
+
- **`startupTimeoutMs`** - `number` (optional) - Timeout in milliseconds to wait for Harper to start. Defaults to 30000, or the value of the `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` environment variable if set.
|
|
98
84
|
|
|
99
85
|
**Environment Variables:**
|
|
100
86
|
|
|
@@ -112,9 +112,13 @@ interface RunHarperCommandOptions {
|
|
|
112
112
|
*/
|
|
113
113
|
function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCommandOptions): Promise<ChildProcess> {
|
|
114
114
|
const harperScript = getHarperScript();
|
|
115
|
-
const proc = spawn(
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const proc = spawn(
|
|
116
|
+
'node',
|
|
117
|
+
['--trace-warnings', '--force-node-api-uncaught-exceptions-policy=true', harperScript, ...args],
|
|
118
|
+
{
|
|
119
|
+
env: { ...process.env, ...env },
|
|
120
|
+
}
|
|
121
|
+
);
|
|
118
122
|
|
|
119
123
|
let stdoutStream: WriteStream | undefined;
|
|
120
124
|
let stderrStream: WriteStream | undefined;
|
|
@@ -156,12 +160,12 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
|
|
|
156
160
|
proc.on('error', (error) => {
|
|
157
161
|
reject(error);
|
|
158
162
|
});
|
|
159
|
-
proc.on('exit', (statusCode) => {
|
|
163
|
+
proc.on('exit', (statusCode, signal) => {
|
|
160
164
|
clearTimeout(timer);
|
|
161
165
|
if (statusCode === 0) {
|
|
162
166
|
resolve(proc);
|
|
163
167
|
} else {
|
|
164
|
-
let errorMessage = `Harper process failed with exit code ${statusCode}`;
|
|
168
|
+
let errorMessage = `Harper process failed with exit code/signal ${statusCode ?? signal}`;
|
|
165
169
|
stderrStream?.write(errorMessage);
|
|
166
170
|
if (stderr) {
|
|
167
171
|
errorMessage += `\n\nstderr:\n${stderr}`;
|
|
@@ -284,6 +288,29 @@ export async function startHarper(ctx: ContextWithHarper, options?: SetupHarperO
|
|
|
284
288
|
return ctx;
|
|
285
289
|
}
|
|
286
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Kill harper process (can be used for teardown, or killing it before a restart)
|
|
293
|
+
* @param ctx
|
|
294
|
+
*/
|
|
295
|
+
export async function killHarper(ctx: ContextWithHarper): Promise<void> {
|
|
296
|
+
await new Promise<void>((resolve) => {
|
|
297
|
+
let timer: NodeJS.Timeout;
|
|
298
|
+
ctx.harper.process.on('exit', () => {
|
|
299
|
+
resolve();
|
|
300
|
+
clearTimeout(timer);
|
|
301
|
+
});
|
|
302
|
+
ctx.harper.process.kill();
|
|
303
|
+
timer = setTimeout(() => {
|
|
304
|
+
try {
|
|
305
|
+
ctx.harper.process.kill('SIGKILL');
|
|
306
|
+
} catch {
|
|
307
|
+
// possible that the process terminated but the exit event hasn't fired yet
|
|
308
|
+
}
|
|
309
|
+
resolve();
|
|
310
|
+
}, 200);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
287
314
|
/**
|
|
288
315
|
* Tears down a Harper instance and cleans up all resources.
|
|
289
316
|
*
|
|
@@ -305,22 +332,7 @@ export async function startHarper(ctx: ContextWithHarper, options?: SetupHarperO
|
|
|
305
332
|
* ```
|
|
306
333
|
*/
|
|
307
334
|
export async function teardownHarper(ctx: ContextWithHarper): Promise<void> {
|
|
308
|
-
await
|
|
309
|
-
let timer: NodeJS.Timeout;
|
|
310
|
-
ctx.harper.process.on('exit', () => {
|
|
311
|
-
resolve();
|
|
312
|
-
clearTimeout(timer);
|
|
313
|
-
});
|
|
314
|
-
ctx.harper.process.kill();
|
|
315
|
-
timer = setTimeout(() => {
|
|
316
|
-
try {
|
|
317
|
-
ctx.harper.process.kill('SIGKILL');
|
|
318
|
-
} catch {
|
|
319
|
-
// possible that the process terminated but the exit event hasn't fired yet
|
|
320
|
-
}
|
|
321
|
-
resolve();
|
|
322
|
-
}, 200);
|
|
323
|
-
});
|
|
335
|
+
await killHarper(ctx);
|
|
324
336
|
|
|
325
337
|
await releaseLoopbackAddress(ctx.harper.hostname);
|
|
326
338
|
|
package/core/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harper",
|
|
3
3
|
"description": "Harper is an open-source Node.js performance platform that unifies database, cache, application, and messaging layers into one in-memory process.",
|
|
4
|
-
"version": "5.0.0-
|
|
5
|
-
"private": true,
|
|
4
|
+
"version": "5.0.0-beta.1",
|
|
6
5
|
"license": "Apache-2.0",
|
|
7
6
|
"homepage": "https://harper.fast",
|
|
8
7
|
"bugs": {
|
|
@@ -24,7 +23,25 @@
|
|
|
24
23
|
},
|
|
25
24
|
"files": [
|
|
26
25
|
"dist",
|
|
26
|
+
"bin",
|
|
27
|
+
"components",
|
|
28
|
+
"config",
|
|
29
|
+
"dataLayer",
|
|
30
|
+
"json",
|
|
31
|
+
"launchServiceScripts",
|
|
32
|
+
"resources",
|
|
33
|
+
"security",
|
|
34
|
+
"server",
|
|
35
|
+
"sqlTranslator",
|
|
36
|
+
"upgrade",
|
|
37
|
+
"utility",
|
|
38
|
+
"validation",
|
|
39
|
+
"studio",
|
|
27
40
|
"static",
|
|
41
|
+
"index.*",
|
|
42
|
+
"v1.*",
|
|
43
|
+
"v2.*",
|
|
44
|
+
"README.md",
|
|
28
45
|
"SECURITY.md",
|
|
29
46
|
"SUPPORT.md",
|
|
30
47
|
"CODE_OF_CONDUCT.md"
|
|
@@ -32,7 +49,7 @@
|
|
|
32
49
|
"scripts": {
|
|
33
50
|
"build": "tsc --project tsconfig.build.json",
|
|
34
51
|
"build:watch": "npm run build -- --watch --incremental",
|
|
35
|
-
"package": "
|
|
52
|
+
"package": "./build-tools/build.sh",
|
|
36
53
|
"lint": "oxlint --deny-warnings .",
|
|
37
54
|
"lint:required": "oxlint --quiet .",
|
|
38
55
|
"lint:fix": "npm run lint -- --fix",
|
|
@@ -140,7 +157,7 @@
|
|
|
140
157
|
"@fastify/cors": "~9.0.1",
|
|
141
158
|
"@fastify/static": "~7.0.4",
|
|
142
159
|
"@harperfast/extended-iterable": "^1.0.1",
|
|
143
|
-
"@harperfast/rocksdb-js": "^0.1.
|
|
160
|
+
"@harperfast/rocksdb-js": "^0.1.12",
|
|
144
161
|
"@turf/area": "6.5.0",
|
|
145
162
|
"@turf/boolean-contains": "6.5.0",
|
|
146
163
|
"@turf/boolean-disjoint": "6.5.0",
|
|
@@ -151,6 +168,7 @@
|
|
|
151
168
|
"@turf/helpers": "6.5.0",
|
|
152
169
|
"@turf/length": "6.5.0",
|
|
153
170
|
"alasql": "4.6.6",
|
|
171
|
+
"amaro": "^1.1.8",
|
|
154
172
|
"argon2": "0.44.0",
|
|
155
173
|
"asn1js": "3.0.7",
|
|
156
174
|
"cbor-x": "1.6.4",
|
|
@@ -175,7 +193,7 @@
|
|
|
175
193
|
"json2csv": "5.0.7",
|
|
176
194
|
"jsonata": "1.8.7",
|
|
177
195
|
"jsonwebtoken": "9.0.3",
|
|
178
|
-
"lmdb": "3.5.
|
|
196
|
+
"lmdb": "3.5.2",
|
|
179
197
|
"lodash": "4.17.21",
|
|
180
198
|
"mathjs": "11.12.0",
|
|
181
199
|
"micromatch": "^4.0.8",
|
|
@@ -30,7 +30,7 @@ export interface ResourceInterface<Record extends object = any>
|
|
|
30
30
|
allowUpdate(user: User, record: Promise<Record & RecordObject>, context: Context): boolean | Promise<boolean>;
|
|
31
31
|
put?(
|
|
32
32
|
record: Record & RecordObject,
|
|
33
|
-
target
|
|
33
|
+
target?: RequestTargetOrId
|
|
34
34
|
): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
|
|
35
35
|
patch?(
|
|
36
36
|
record: Partial<Record & RecordObject>,
|
package/core/resources/Table.ts
CHANGED
|
@@ -71,11 +71,17 @@ const { validateAttribute } = lmdbProcessRows;
|
|
|
71
71
|
|
|
72
72
|
export type Attribute = {
|
|
73
73
|
name: string;
|
|
74
|
-
type: string;
|
|
74
|
+
type: 'ID' | 'Int' | 'Float' | 'Long' | 'String' | 'Boolean' | 'Date' | 'Bytes' | 'Any' | 'BigInt' | 'Blob' | string;
|
|
75
75
|
assignCreatedTime?: boolean;
|
|
76
76
|
assignUpdatedTime?: boolean;
|
|
77
|
+
nullable?: boolean;
|
|
77
78
|
expiresAt?: boolean;
|
|
78
79
|
isPrimaryKey?: boolean;
|
|
80
|
+
indexed?: unknown;
|
|
81
|
+
relationship?: unknown;
|
|
82
|
+
computed?: unknown;
|
|
83
|
+
properties?: Array<Attribute>;
|
|
84
|
+
elements?: Attribute;
|
|
79
85
|
};
|
|
80
86
|
|
|
81
87
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -106,7 +112,7 @@ export interface Table {
|
|
|
106
112
|
databasePath: string;
|
|
107
113
|
tableName: string;
|
|
108
114
|
databaseName: string;
|
|
109
|
-
attributes:
|
|
115
|
+
attributes: Attribute[];
|
|
110
116
|
primaryKey: string;
|
|
111
117
|
splitSegments?: boolean;
|
|
112
118
|
replicate?: boolean;
|
|
@@ -141,7 +147,7 @@ export function makeTable(options) {
|
|
|
141
147
|
} = options;
|
|
142
148
|
let { expirationMS: expirationMs, evictionMS: evictionMs, audit, trackDeletes } = options;
|
|
143
149
|
evictionMs ??= 0;
|
|
144
|
-
let { attributes } = options;
|
|
150
|
+
let { attributes }: { attributes: Attribute[] } = options;
|
|
145
151
|
if (!attributes) attributes = [];
|
|
146
152
|
const updateRecord = recordUpdater(primaryStore, tableId, auditStore);
|
|
147
153
|
let sourceLoad: any; // if a source has a load function (replicator), record it here
|
|
@@ -2233,11 +2239,12 @@ export function makeTable(options) {
|
|
|
2233
2239
|
entries = transformToEntries(entries, select, context, readTxn, null);
|
|
2234
2240
|
let ordered;
|
|
2235
2241
|
// if we are doing post-ordering, we need to get records first, then sort them
|
|
2236
|
-
results.iterate = function () {
|
|
2242
|
+
results.iterate = function (options: { async: boolean }) {
|
|
2237
2243
|
let sortedArrayIterator: IterableIterator<any>;
|
|
2238
|
-
const dbIterator =
|
|
2239
|
-
|
|
2240
|
-
|
|
2244
|
+
const dbIterator =
|
|
2245
|
+
options?.async && entries[Symbol.asyncIterator]
|
|
2246
|
+
? entries[Symbol.asyncIterator]()
|
|
2247
|
+
: entries[Symbol.iterator]();
|
|
2241
2248
|
let dbDone: boolean;
|
|
2242
2249
|
const dbOrderedAttribute = sort.dbOrderedAttribute;
|
|
2243
2250
|
let enqueuedEntryForNextGroup: any;
|
|
@@ -2351,7 +2358,10 @@ export function makeTable(options) {
|
|
|
2351
2358
|
};
|
|
2352
2359
|
applySortingOnSelect(sort);
|
|
2353
2360
|
} else {
|
|
2354
|
-
results.iterate = (
|
|
2361
|
+
results.iterate = (options: { async: boolean }) => {
|
|
2362
|
+
if (options?.async && entries[Symbol.asyncIterator]) return entries[Symbol.asyncIterator]();
|
|
2363
|
+
else return entries[Symbol.iterator]();
|
|
2364
|
+
};
|
|
2355
2365
|
results = results.map(function (entry) {
|
|
2356
2366
|
try {
|
|
2357
2367
|
// because this is a part of a stream of results, we will often be continuing to iterate over the results when there are errors,
|
|
@@ -2893,7 +2903,7 @@ export function makeTable(options) {
|
|
|
2893
2903
|
}
|
|
2894
2904
|
validate(record: any, patch?: boolean) {
|
|
2895
2905
|
let validationErrors;
|
|
2896
|
-
const validateValue = (value, attribute, name) => {
|
|
2906
|
+
const validateValue = (value, attribute: Attribute, name) => {
|
|
2897
2907
|
if (attribute.type && value != null) {
|
|
2898
2908
|
if (patch && value.__op__) value = value.value;
|
|
2899
2909
|
if (attribute.properties) {
|
|
@@ -3063,7 +3073,7 @@ export function makeTable(options) {
|
|
|
3063
3073
|
getUpdatedTime() {
|
|
3064
3074
|
return this.#version;
|
|
3065
3075
|
}
|
|
3066
|
-
static async addAttributes(attributesToAdd) {
|
|
3076
|
+
static async addAttributes(attributesToAdd: Attribute[]) {
|
|
3067
3077
|
const new_attributes = attributes.slice(0);
|
|
3068
3078
|
for (const attribute of attributesToAdd) {
|
|
3069
3079
|
if (!attribute.name) throw new ClientError('Attribute name is required');
|
|
@@ -3927,7 +3937,12 @@ export function makeTable(options) {
|
|
|
3927
3937
|
// it should be resolved now and we can use the value it saved.
|
|
3928
3938
|
clearTimeout(timer);
|
|
3929
3939
|
const entry = primaryStore.getEntry(id);
|
|
3930
|
-
if (
|
|
3940
|
+
if (
|
|
3941
|
+
!entry ||
|
|
3942
|
+
!entry.value ||
|
|
3943
|
+
entry.metadataFlags & (INVALIDATED | EVICTED) ||
|
|
3944
|
+
(entry.expiresAt != undefined && entry.expiresAt < Date.now())
|
|
3945
|
+
)
|
|
3931
3946
|
// try again
|
|
3932
3947
|
whenResolved(getFromSource(source, id, primaryStore.getEntry(id), context));
|
|
3933
3948
|
else whenResolved(entry);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Metric } from './write.ts';
|
|
2
2
|
import harperLogger from '../../utility/logging/harper_logger.js';
|
|
3
3
|
const { forComponent } = harperLogger;
|
|
4
|
-
import { getAnalyticsHostnameTable } from './hostnames.ts';
|
|
4
|
+
import { getAnalyticsHostnameTable, stableNodeId } from './hostnames.ts';
|
|
5
5
|
import type { Condition, Conditions } from '../ResourceInterface.ts';
|
|
6
6
|
import { METRIC, type BuiltInMetricName } from './metadata.ts';
|
|
7
7
|
import { CONFIG_PARAMS } from '../../utility/hdbTerms.ts';
|
|
@@ -12,9 +12,11 @@ const defaultCustomMetricWindow = 1000 * 60 * 60 * 24 * 7;
|
|
|
12
12
|
|
|
13
13
|
const log = forComponent('analytics').conditional;
|
|
14
14
|
|
|
15
|
-
async function lookupHostname(nodeId: number): Promise<string> {
|
|
15
|
+
async function lookupHostname(nodeId: number): Promise<string | undefined> {
|
|
16
16
|
const result = await getAnalyticsHostnameTable().get(nodeId);
|
|
17
|
-
return result.hostname;
|
|
17
|
+
if (result?.hostname) return result.hostname;
|
|
18
|
+
if (nodeId === stableNodeId(server.hostname)) return server.hostname;
|
|
19
|
+
return undefined;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
function isSelected(querySelect: string[], attr: string) {
|
|
@@ -57,25 +59,21 @@ function conformCondition(condition: Condition): Condition {
|
|
|
57
59
|
};
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
async function coalesceResults(results: Metric[], window: number):
|
|
61
|
-
|
|
62
|
-
let coalesceId;
|
|
63
|
-
let lastCoalescedId = new Map<string, number>();
|
|
62
|
+
async function* coalesceResults(results: Metric[], window: number): AsyncGenerator<Metric> {
|
|
63
|
+
let coalesceId: any;
|
|
64
64
|
for await (const result of results) {
|
|
65
65
|
const id = result.id;
|
|
66
66
|
if (!coalesceId) {
|
|
67
67
|
coalesceId = id;
|
|
68
68
|
}
|
|
69
69
|
const delta = Math.abs(id - coalesceId);
|
|
70
|
-
if (delta < window
|
|
71
|
-
|
|
72
|
-
lastCoalescedId[result.node] = id;
|
|
70
|
+
if (delta < window) {
|
|
71
|
+
yield { ...result, id: coalesceId };
|
|
73
72
|
} else {
|
|
74
|
-
|
|
73
|
+
yield result;
|
|
75
74
|
coalesceId = id;
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
return coalescedResults;
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
interface GetAnalyticsOpts {
|
|
@@ -99,19 +97,27 @@ export async function get(metric: string, opts?: GetAnalyticsOpts): Promise<Metr
|
|
|
99
97
|
select.push('id');
|
|
100
98
|
}
|
|
101
99
|
|
|
102
|
-
if (startTime) {
|
|
100
|
+
if (startTime && endTime) {
|
|
103
101
|
conditions.push({
|
|
104
102
|
attribute: 'id',
|
|
105
|
-
comparator: '
|
|
106
|
-
value: startTime,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
if (endTime) {
|
|
110
|
-
conditions.push({
|
|
111
|
-
attribute: 'id',
|
|
112
|
-
comparator: 'less_than',
|
|
113
|
-
value: endTime,
|
|
103
|
+
comparator: 'between',
|
|
104
|
+
value: [startTime, endTime],
|
|
114
105
|
});
|
|
106
|
+
} else {
|
|
107
|
+
if (startTime) {
|
|
108
|
+
conditions.push({
|
|
109
|
+
attribute: 'id',
|
|
110
|
+
comparator: 'greater_than_equal',
|
|
111
|
+
value: startTime,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (endTime) {
|
|
115
|
+
conditions.push({
|
|
116
|
+
attribute: 'id',
|
|
117
|
+
comparator: 'less_than',
|
|
118
|
+
value: endTime,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
const request = { conditions, allowConditionsOnDynamicAttributes: true };
|
|
@@ -128,16 +134,17 @@ export async function get(metric: string, opts?: GetAnalyticsOpts): Promise<Metr
|
|
|
128
134
|
result['id'] = result['id'][0];
|
|
129
135
|
if (isSelected(select, 'node')) {
|
|
130
136
|
log.trace?.(`get_analytics lookup hostname for nodeId: ${nodeId}`);
|
|
131
|
-
|
|
137
|
+
const hostname = await lookupHostname(nodeId);
|
|
138
|
+
result['node'] = hostname ?? nodeId;
|
|
132
139
|
}
|
|
133
140
|
log.trace?.(`get_analytics result:`, JSON.stringify(result));
|
|
134
141
|
return result;
|
|
135
142
|
});
|
|
136
143
|
|
|
137
144
|
if (opts?.coalesceTime) {
|
|
138
|
-
// coalescing window is the aggregate period
|
|
139
|
-
const window = envGet(CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) *
|
|
140
|
-
results =
|
|
145
|
+
// coalescing window is the aggregate period converted to milliseconds
|
|
146
|
+
const window = envGet(CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) * 1000;
|
|
147
|
+
results = coalesceResults(results, window);
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
return results;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parentPort, threadId } from 'worker_threads';
|
|
2
|
-
import {
|
|
2
|
+
import { onMessageByType } from '../../server/threads/manageThreads.js';
|
|
3
3
|
import { getDatabases, table } from '../databases.ts';
|
|
4
4
|
import type { Databases, Table, Tables } from '../databases.ts';
|
|
5
5
|
import harperLogger from '../../utility/logging/harper_logger.js';
|
|
@@ -14,10 +14,6 @@ import { server } from '../../server/Server.ts';
|
|
|
14
14
|
import * as fs from 'node:fs';
|
|
15
15
|
import { getAnalyticsHostnameTable, nodeIds, stableNodeId } from './hostnames.ts';
|
|
16
16
|
import { METRIC } from './metadata.ts';
|
|
17
|
-
setTimeout(() => {
|
|
18
|
-
// let everything load before we actually load and start the profiler
|
|
19
|
-
import('./profile.ts');
|
|
20
|
-
}, 1000);
|
|
21
17
|
import { RocksDatabase } from '@harperfast/rocksdb-js';
|
|
22
18
|
|
|
23
19
|
const log = forComponent('analytics').conditional;
|
|
@@ -230,7 +226,7 @@ export async function recordHostname() {
|
|
|
230
226
|
hostname,
|
|
231
227
|
};
|
|
232
228
|
log.trace?.(`recordHostname storing hostname: ${JSON.stringify(hostnameRecord)}`);
|
|
233
|
-
hostnamesTable.put(hostnameRecord.id, hostnameRecord);
|
|
229
|
+
await hostnamesTable.put(hostnameRecord.id, hostnameRecord);
|
|
234
230
|
}
|
|
235
231
|
}
|
|
236
232
|
|
|
@@ -654,7 +650,7 @@ function getAnalyticsTable() {
|
|
|
654
650
|
);
|
|
655
651
|
}
|
|
656
652
|
|
|
657
|
-
|
|
653
|
+
if (!parentPort) onMessageByType(ANALYTICS_REPORT_TYPE, recordAnalytics);
|
|
658
654
|
let scheduledTasksRunning;
|
|
659
655
|
function startScheduledTasks() {
|
|
660
656
|
scheduledTasksRunning = true;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
1
2
|
import { initSync, getHdbBasePath, get as envGet } from '../utility/environment/environmentManager.js';
|
|
2
3
|
import { INTERNAL_DBIS_NAME } from '../utility/lmdb/terms.js';
|
|
3
4
|
import { open, compareKeys, type Database, type RootDatabase } from 'lmdb';
|
|
@@ -97,6 +98,14 @@ interface RocksRootDatabase extends RocksDatabaseEx {
|
|
|
97
98
|
|
|
98
99
|
export type RootDatabaseKind = LMDBRootDatabase | RocksRootDatabase;
|
|
99
100
|
|
|
101
|
+
export type DatabaseWatcherEventMap = {
|
|
102
|
+
updateTable: [table: Table, originIsNotCluster?: boolean];
|
|
103
|
+
dropTable: [tableName: string, databaseName: string];
|
|
104
|
+
dropDatabase: [databaseName: string];
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const databaseEventsEmitter = new EventEmitter<DatabaseWatcherEventMap>();
|
|
108
|
+
|
|
100
109
|
export const tables: Tables = Object.create(null);
|
|
101
110
|
export const databases: Databases = Object.create(null);
|
|
102
111
|
|
|
@@ -127,8 +136,6 @@ _assignPackageExport('databases', databases);
|
|
|
127
136
|
_assignPackageExport('tables', tables);
|
|
128
137
|
|
|
129
138
|
const NEXT_TABLE_ID = Symbol.for('next-table-id');
|
|
130
|
-
const tableListeners = [];
|
|
131
|
-
const dbRemovalListeners = [];
|
|
132
139
|
let loadedDatabases; // indicates if we have loaded databases from the file system yet
|
|
133
140
|
|
|
134
141
|
// This is used to track all the databases that are found when iterating through the file system so that anything that is missing
|
|
@@ -577,9 +584,7 @@ function initStores(
|
|
|
577
584
|
})
|
|
578
585
|
);
|
|
579
586
|
table.schemaVersion = 1;
|
|
580
|
-
|
|
581
|
-
listener(table);
|
|
582
|
-
}
|
|
587
|
+
databaseEventsEmitter.emit('updateTable', table);
|
|
583
588
|
}
|
|
584
589
|
}
|
|
585
590
|
return rootStore;
|
|
@@ -600,7 +605,7 @@ export function resetDatabases() {
|
|
|
600
605
|
const table = db[tableName];
|
|
601
606
|
if (table.primaryStore.path === path) {
|
|
602
607
|
delete databases[store.databaseName];
|
|
603
|
-
|
|
608
|
+
databaseEventsEmitter.emit('dropDatabase', store.databaseName);
|
|
604
609
|
break;
|
|
605
610
|
}
|
|
606
611
|
}
|
|
@@ -745,6 +750,7 @@ export async function dropDatabase(databaseName) {
|
|
|
745
750
|
await unlink(rootStore.path);
|
|
746
751
|
}
|
|
747
752
|
}
|
|
753
|
+
databaseEventsEmitter.emit('dropTable', tableName, databaseName);
|
|
748
754
|
}
|
|
749
755
|
if (!rootStore) {
|
|
750
756
|
rootStore = database({ database: databaseName, table: null });
|
|
@@ -762,7 +768,7 @@ export async function dropDatabase(databaseName) {
|
|
|
762
768
|
delete tables[DEFINED_TABLES];
|
|
763
769
|
}
|
|
764
770
|
delete databases[databaseName];
|
|
765
|
-
|
|
771
|
+
databaseEventsEmitter.emit('dropDatabase', databaseName);
|
|
766
772
|
await deleteRootBlobPathsForDB(rootStore);
|
|
767
773
|
}
|
|
768
774
|
// opens an index, consulting with custom indexes that may use alternate store configuration
|
|
@@ -1090,9 +1096,7 @@ export function table<TableResourceType>(tableDefinition: TableDefinition): Tabl
|
|
|
1090
1096
|
|
|
1091
1097
|
Table.origin = origin;
|
|
1092
1098
|
if (hasChanges) {
|
|
1093
|
-
|
|
1094
|
-
listener(Table, origin !== 'cluster');
|
|
1095
|
-
}
|
|
1099
|
+
databaseEventsEmitter.emit('updateTable', Table, origin !== 'cluster');
|
|
1096
1100
|
}
|
|
1097
1101
|
if (expiration || eviction || scanInterval)
|
|
1098
1102
|
Table.setTTLExpiration({
|
|
@@ -1244,24 +1248,31 @@ export function dropTableMeta({ table: tableName, database: databaseName }) {
|
|
|
1244
1248
|
for (const key of dbisDb.getKeys({ start: tableName + '/', end: tableName + '0' })) {
|
|
1245
1249
|
removals.push(dbisDb.remove(key));
|
|
1246
1250
|
}
|
|
1251
|
+
databaseEventsEmitter.emit('dropTable', tableName, databaseName);
|
|
1247
1252
|
return Promise.all(removals);
|
|
1248
1253
|
}
|
|
1249
1254
|
|
|
1250
|
-
export function onUpdatedTable(listener) {
|
|
1251
|
-
|
|
1255
|
+
export function onUpdatedTable(listener: (table: Table) => void) {
|
|
1256
|
+
databaseEventsEmitter.on('updateTable', listener);
|
|
1257
|
+
return {
|
|
1258
|
+
remove() {
|
|
1259
|
+
databaseEventsEmitter.off('updateTable', listener);
|
|
1260
|
+
},
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
export function onRemovedTable(listener: (tableName: string, databaseName: string) => void) {
|
|
1264
|
+
databaseEventsEmitter.on('dropTable', listener);
|
|
1252
1265
|
return {
|
|
1253
1266
|
remove() {
|
|
1254
|
-
|
|
1255
|
-
if (index > -1) tableListeners.splice(index, 1);
|
|
1267
|
+
databaseEventsEmitter.off('dropTable', listener);
|
|
1256
1268
|
},
|
|
1257
1269
|
};
|
|
1258
1270
|
}
|
|
1259
|
-
export function onRemovedDB(listener) {
|
|
1260
|
-
|
|
1271
|
+
export function onRemovedDB(listener: (databaseName: string) => void) {
|
|
1272
|
+
databaseEventsEmitter.on('dropDatabase', listener);
|
|
1261
1273
|
return {
|
|
1262
1274
|
remove() {
|
|
1263
|
-
|
|
1264
|
-
if (index > -1) dbRemovalListeners.splice(index, 1);
|
|
1275
|
+
databaseEventsEmitter.off('dropDatabase', listener);
|
|
1265
1276
|
},
|
|
1266
1277
|
};
|
|
1267
1278
|
}
|
package/core/resources/search.ts
CHANGED
|
@@ -337,11 +337,16 @@ export function searchByIndex(
|
|
|
337
337
|
})
|
|
338
338
|
);
|
|
339
339
|
}
|
|
340
|
-
: (entry)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
340
|
+
: function (entry) {
|
|
341
|
+
let result: any;
|
|
342
|
+
if (entry.value == null && !(entry.metadataFlags & (INVALIDATED | EVICTED))) result = SKIP;
|
|
343
|
+
else {
|
|
344
|
+
Object.freeze(entry.value);
|
|
345
|
+
recordRead(entry);
|
|
346
|
+
result = entry;
|
|
347
|
+
}
|
|
348
|
+
if (this.isSync) return result;
|
|
349
|
+
return new Promise((resolve) => setImmediate(() => resolve(result)));
|
|
345
350
|
}
|
|
346
351
|
);
|
|
347
352
|
results.hasEntries = true;
|
package/core/security/auth.ts
CHANGED
|
@@ -102,7 +102,7 @@ export async function authentication(request, nextHandler) {
|
|
|
102
102
|
break;
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
-
request.session = session
|
|
105
|
+
request.session = session ? { ...session } : (session = {});
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
const authAuditLog = (username, status, strategy) => {
|