@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
package/studio/web/index.html
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<link rel="icon" type="dynamic-favicon" href="/favicon_purple.png" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Harper Fabric</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-DMDhGP7N.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="/assets/index-Y2g_iFpU.css">
|
|
11
11
|
</head>
|
|
12
12
|
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { User } from '../security/user.js';
|
|
2
|
-
import type { RecordObject } from './RecordEncoder.ts';
|
|
3
|
-
import { RequestTarget } from './RequestTarget.ts';
|
|
4
|
-
import { RequestTargetOrId, ResourceInterface, SubscriptionRequest, UpdatableRecord } from './ResourceInterface.ts';
|
|
5
|
-
|
|
6
|
-
// @ts-expect-error We changed the interface in v2 with breaking changes (by flipping target and newRecord/record)
|
|
7
|
-
export interface ResourceInterfaceV2<Record extends object = any> extends ResourceInterface<Record> {
|
|
8
|
-
get?(
|
|
9
|
-
target?: RequestTargetOrId
|
|
10
|
-
):
|
|
11
|
-
| (Record & Partial<RecordObject>)
|
|
12
|
-
| Promise<Record & Partial<RecordObject>>
|
|
13
|
-
| AsyncIterable<Record & Partial<RecordObject>>
|
|
14
|
-
| Promise<AsyncIterable<Record & Partial<RecordObject>>>;
|
|
15
|
-
search?(target: RequestTarget): AsyncIterable<Record & Partial<RecordObject>>;
|
|
16
|
-
|
|
17
|
-
create?(
|
|
18
|
-
target: RequestTargetOrId,
|
|
19
|
-
newRecord: Partial<Record & RecordObject>
|
|
20
|
-
): void | (Record & Partial<RecordObject>) | Promise<Record & Partial<RecordObject>>;
|
|
21
|
-
post?(
|
|
22
|
-
target: RequestTargetOrId,
|
|
23
|
-
newRecord: Partial<Record & RecordObject>
|
|
24
|
-
): void | (Record & Partial<RecordObject>) | Promise<Record & Partial<RecordObject>>;
|
|
25
|
-
|
|
26
|
-
put?(
|
|
27
|
-
target: RequestTargetOrId,
|
|
28
|
-
record: Record & RecordObject
|
|
29
|
-
): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
|
|
30
|
-
patch?(
|
|
31
|
-
target: RequestTargetOrId,
|
|
32
|
-
record: Partial<Record & RecordObject>
|
|
33
|
-
): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
|
|
34
|
-
update?(updates: Record & RecordObject, fullUpdate: true): ResourceInterface<Record & Partial<RecordObject>>;
|
|
35
|
-
update?(
|
|
36
|
-
updates: Partial<Record & RecordObject>,
|
|
37
|
-
fullUpdate?: boolean
|
|
38
|
-
):
|
|
39
|
-
| ResourceInterface<Record & Partial<RecordObject>>
|
|
40
|
-
| Promise<ResourceInterface<Record & Partial<RecordObject>> | UpdatableRecord<Record & Partial<RecordObject>>>;
|
|
41
|
-
|
|
42
|
-
delete?(target: RequestTargetOrId): boolean | Promise<boolean>;
|
|
43
|
-
|
|
44
|
-
invalidate(target: RequestTargetOrId): void | Promise<void>;
|
|
45
|
-
|
|
46
|
-
publish?(target: RequestTargetOrId, record: Record): void;
|
|
47
|
-
subscribe?(request: SubscriptionRequest): AsyncIterable<Record> | Promise<AsyncIterable<Record>>;
|
|
48
|
-
|
|
49
|
-
doesExist(): boolean;
|
|
50
|
-
wasLoadedFromSource(): boolean | void;
|
|
51
|
-
|
|
52
|
-
getCurrentUser(): User | undefined;
|
|
53
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { _assignPackageExport } from '../globals.js';
|
|
2
|
-
import { IterableEventQueue } from './IterableEventQueue.js';
|
|
3
|
-
import type { RecordObject } from './RecordEncoder.js';
|
|
4
|
-
import { RequestTarget } from './RequestTarget.js';
|
|
5
|
-
import { Resource } from './Resource.ts';
|
|
6
|
-
import { RequestTargetOrId } from './ResourceInterface.ts';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* This is the main class that can be extended for any resource in HarperDB and provides the essential reusable
|
|
10
|
-
* uniform interface for interacting with data, defining the API for providing data (data sources) and for consuming
|
|
11
|
-
* data. This interface is used pervasively in HarperDB and is implemented by database tables and can be used to define
|
|
12
|
-
* sources for caching, real-data sources for messaging protocols, and RESTful endpoints, as well as any other types of
|
|
13
|
-
* data aggregation, processing, or monitoring.
|
|
14
|
-
*
|
|
15
|
-
* This base Resource class provides a set of static methods that are main entry points for querying and updating data
|
|
16
|
-
* in resources/tables. The static methods provide the default handling of arguments, context, and ensuring that
|
|
17
|
-
* internal actions are wrapped in a transaction. The base Resource class intended to be extended, and the instance
|
|
18
|
-
* methods can be overridden to provide specific implementations of actions like get, put, post, delete, and subscribe.
|
|
19
|
-
*/
|
|
20
|
-
export class ResourceV2<Record extends object = any> extends Resource<Record> {
|
|
21
|
-
static loadAsInstance: boolean = false;
|
|
22
|
-
|
|
23
|
-
get?(
|
|
24
|
-
target?: RequestTargetOrId
|
|
25
|
-
):
|
|
26
|
-
| (Record & Partial<RecordObject>)
|
|
27
|
-
| Promise<Record & Partial<RecordObject>>
|
|
28
|
-
| AsyncIterable<Record & Partial<RecordObject>>
|
|
29
|
-
| Promise<AsyncIterable<Record & Partial<RecordObject>>>;
|
|
30
|
-
|
|
31
|
-
search?(target: RequestTargetOrId): AsyncIterable<Record & Partial<RecordObject>>;
|
|
32
|
-
|
|
33
|
-
// @ts-expect-error We swapped the order of target and newRecord.
|
|
34
|
-
create?(
|
|
35
|
-
target: RequestTargetOrId,
|
|
36
|
-
newRecord: Partial<Record & RecordObject>
|
|
37
|
-
): void | (Record & Partial<RecordObject>) | Promise<Record & Partial<RecordObject>>;
|
|
38
|
-
|
|
39
|
-
// @ts-expect-error In v2, we're adjusting the types.
|
|
40
|
-
post(
|
|
41
|
-
target: RequestTargetOrId,
|
|
42
|
-
newRecord: Partial<Record & RecordObject>
|
|
43
|
-
): void | (Record & Partial<RecordObject>) | Promise<Record & Partial<RecordObject>> {
|
|
44
|
-
return super.post(target, newRecord);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// @ts-expect-error We swapped the order of target and record.
|
|
48
|
-
put?(
|
|
49
|
-
target: RequestTargetOrId,
|
|
50
|
-
record: Record & RecordObject
|
|
51
|
-
): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
|
|
52
|
-
|
|
53
|
-
// @ts-expect-error We swapped the order of target and record.
|
|
54
|
-
patch?(
|
|
55
|
-
target: RequestTargetOrId,
|
|
56
|
-
record: Partial<Record & RecordObject>
|
|
57
|
-
): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
|
|
58
|
-
|
|
59
|
-
delete?(target: RequestTargetOrId): boolean | Promise<boolean>;
|
|
60
|
-
invalidate?(target: RequestTargetOrId): void | Promise<void>;
|
|
61
|
-
|
|
62
|
-
// @ts-expect-error We swapped the order of target and record.
|
|
63
|
-
connect?(incomingMessages: IterableEventQueue<Record>, target: RequestTarget): AsyncIterable<Record>;
|
|
64
|
-
publish?(target: RequestTargetOrId, record: Record): void;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
_assignPackageExport('ResourceV2', ResourceV2);
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This module is responsible for profiling threads so we can determine how much CPU usage can be attributed
|
|
3
|
-
* to user code, harper code, and individual "hot" functions
|
|
4
|
-
*/
|
|
5
|
-
import { recordAction } from './write.ts';
|
|
6
|
-
import { get as envGet, getHdbBasePath } from '../../utility/environment/environmentManager.js';
|
|
7
|
-
import { CONFIG_PARAMS } from '../../utility/hdbTerms.js';
|
|
8
|
-
import { PACKAGE_ROOT } from '../../utility/packageUtils.js';
|
|
9
|
-
import { realpathSync } from 'node:fs';
|
|
10
|
-
import { time as timeProfiler } from '@datadog/pprof';
|
|
11
|
-
import * as log from '../../utility/logging/harper_logger.js';
|
|
12
|
-
|
|
13
|
-
type Profile = ReturnType<typeof timeProfiler.stop>;
|
|
14
|
-
type Sample = Profile['sample'][0];
|
|
15
|
-
const basePath = getHdbBasePath();
|
|
16
|
-
export const userCodeFolders = basePath ? [basePath] : [];
|
|
17
|
-
if (process.env.RUN_HDB_APP) userCodeFolders.push(realpathSync(process.env.RUN_HDB_APP));
|
|
18
|
-
|
|
19
|
-
let profilerTimer: NodeJS.Timeout | undefined;
|
|
20
|
-
const SAMPLING_INTERVAL_IN_MICROSECONDS = 50000;
|
|
21
|
-
// TODO: Running this on the thread itself can be a problematic because the profiler snapshots are somewhat expensive
|
|
22
|
-
// (calling timeProfiler.stop and getting the large block of JSON and parsing it). This can take a 5ms or more
|
|
23
|
-
// which can have some impact on latency for users. However, the datadog profiler is much better than the node
|
|
24
|
-
// profiler, so we'll keep this for now.
|
|
25
|
-
(async () => {
|
|
26
|
-
if (userCodeFolders.length === 0) return;
|
|
27
|
-
// start the profiler
|
|
28
|
-
timeProfiler.start({ intervalMicros: SAMPLING_INTERVAL_IN_MICROSECONDS });
|
|
29
|
-
const PROFILE_PERIOD = (envGet(CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) ?? 60) * 1000;
|
|
30
|
-
if (PROFILE_PERIOD > 0) {
|
|
31
|
-
profilerTimer = setTimeout(() => {
|
|
32
|
-
captureProfile(PROFILE_PERIOD);
|
|
33
|
-
}, PROFILE_PERIOD).unref();
|
|
34
|
-
}
|
|
35
|
-
})();
|
|
36
|
-
|
|
37
|
-
export async function captureProfile(
|
|
38
|
-
delayToNextCapture = (envGet(CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) ?? 60) * 1000
|
|
39
|
-
): Promise<void> {
|
|
40
|
-
clearTimeout(profilerTimer);
|
|
41
|
-
const hitCountThreshold = 100;
|
|
42
|
-
const secondsPerHit = SAMPLING_INTERVAL_IN_MICROSECONDS / 1_000_000;
|
|
43
|
-
const locationById = new Map<number, any>();
|
|
44
|
-
const fileNameById = new Map<number, any>();
|
|
45
|
-
const samplesByLocationId = new Map<number, number>();
|
|
46
|
-
let totalUserCount = 0;
|
|
47
|
-
let totalHarperCount = 0;
|
|
48
|
-
try {
|
|
49
|
-
const profile = timeProfiler.stop(true);
|
|
50
|
-
const strings = profile.stringTable.strings;
|
|
51
|
-
for (let func of profile.function) {
|
|
52
|
-
fileNameById.set(func.id as number, strings[func.filename as number]);
|
|
53
|
-
}
|
|
54
|
-
for (let location of profile.location) {
|
|
55
|
-
locationById.set(location.id as number, location.line[0]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
for (const sample of profile.sample) {
|
|
59
|
-
getUserHitCount(sample);
|
|
60
|
-
}
|
|
61
|
-
recordAction(totalHarperCount * secondsPerHit, 'cpu-usage', 'harper');
|
|
62
|
-
recordAction(totalUserCount * secondsPerHit, 'cpu-usage', 'user');
|
|
63
|
-
for (let [locationId, sampleCount] of samplesByLocationId) {
|
|
64
|
-
if (sampleCount > hitCountThreshold) {
|
|
65
|
-
const location = locationById.get(locationId);
|
|
66
|
-
const locationName = fileNameById.get(location.functionId) + ':' + location.line;
|
|
67
|
-
recordAction(sampleCount * secondsPerHit, 'cpu-usage', locationName);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
} catch (error) {
|
|
71
|
-
log.error?.('analytics profiler error:', error);
|
|
72
|
-
} finally {
|
|
73
|
-
// and start the profiler again
|
|
74
|
-
if (delayToNextCapture > 0) {
|
|
75
|
-
profilerTimer = setTimeout(() => {
|
|
76
|
-
captureProfile();
|
|
77
|
-
}, delayToNextCapture).unref();
|
|
78
|
-
} else {
|
|
79
|
-
// somehow this can later get set to a negative number which causes big problems (high-frequency restarts of the profiler)
|
|
80
|
-
log.info?.('Profiling disabled');
|
|
81
|
-
timeProfiler.stop();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// this traverses the nodes and returns the number of sampling hits for the sample and attributes it
|
|
85
|
-
// to harper or user code (as opposed to execution of things like node internal modules or native code)
|
|
86
|
-
function getUserHitCount(sample: Sample) {
|
|
87
|
-
// if we can assign to user code or harper code, do so
|
|
88
|
-
let recordedTopSample = false;
|
|
89
|
-
for (let locationId of sample.locationId) {
|
|
90
|
-
let fileName = fileNameById.get(locationById.get(locationId).functionId);
|
|
91
|
-
if (userCodeFolders.some((userCodeFolder) => fileName.startsWith(userCodeFolder))) {
|
|
92
|
-
// the call frame location is in user code
|
|
93
|
-
const sampleCount = sample.value[0];
|
|
94
|
-
totalUserCount += sampleCount;
|
|
95
|
-
if (!recordedTopSample)
|
|
96
|
-
samplesByLocationId.set(locationId, (samplesByLocationId.get(locationId) ?? 0) + sampleCount);
|
|
97
|
-
return; // if the highest point in the call stack is in user code, we don't need to check the rest of the call stack, this "counts" as user execution
|
|
98
|
-
}
|
|
99
|
-
if (fileName.startsWith(PACKAGE_ROOT)) {
|
|
100
|
-
const sampleCount = sample.value[0];
|
|
101
|
-
totalHarperCount += sampleCount;
|
|
102
|
-
if (!recordedTopSample) {
|
|
103
|
-
samplesByLocationId.set(locationId, (samplesByLocationId.get(locationId) ?? 0) + sampleCount);
|
|
104
|
-
recordedTopSample = true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { assert } from 'chai';
|
|
2
|
-
import axios from 'axios';
|
|
3
|
-
import { setupTestApp } from './setupTestApp.mjs';
|
|
4
|
-
import { captureProfile, userCodeFolders } from '#src/resources/analytics/profile';
|
|
5
|
-
import analytics from '#src/resources/analytics/write';
|
|
6
|
-
|
|
7
|
-
describe('Analytics profiling user code', () => {
|
|
8
|
-
before(async () => {
|
|
9
|
-
await setupTestApp();
|
|
10
|
-
analytics.setAnalyticsEnabled(true);
|
|
11
|
-
analytics.analyticsDelay = 50; // let's make this fast
|
|
12
|
-
userCodeFolders.push(new URL('../testApp/', import.meta.url).toString());
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('can sample user code and record it', async () => {
|
|
16
|
-
await captureProfile(10000); // restart the profile
|
|
17
|
-
const start = Date.now();
|
|
18
|
-
let response = await axios.post('http://localhost:9926/SimpleCache/3', {
|
|
19
|
-
doExpensiveComputation: true,
|
|
20
|
-
});
|
|
21
|
-
assert.equal(response.status, 204);
|
|
22
|
-
await captureProfile();
|
|
23
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
24
|
-
const analyticsResults = await databases.system.hdb_raw_analytics.search({
|
|
25
|
-
conditions: [{ attribute: 'id', comparator: 'greater_than_equal', value: start }],
|
|
26
|
-
});
|
|
27
|
-
let userUsageRecorded, harperUsageRecorded;
|
|
28
|
-
for await (let { metrics } of analyticsResults) {
|
|
29
|
-
userUsageRecorded ??= metrics.find(({ metric, path }) => metric === 'cpu-usage' && path === 'user');
|
|
30
|
-
harperUsageRecorded ??= metrics.find(({ metric, path }) => metric === 'cpu-usage' && path === 'harper');
|
|
31
|
-
}
|
|
32
|
-
assert(userUsageRecorded, 'user cpu-usage was recorded in analytics');
|
|
33
|
-
assert(harperUsageRecorded, 'harper cpu-usage was recorded in analytics');
|
|
34
|
-
});
|
|
35
|
-
after(() => {
|
|
36
|
-
analytics.setAnalyticsEnabled(false);
|
|
37
|
-
});
|
|
38
|
-
});
|
package/core/v1.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export { Resource } from './resources/Resource.ts';
|
|
2
|
-
import { Resource as ResourceImport } from './resources/Resource.ts';
|
|
3
|
-
export type {
|
|
4
|
-
Context,
|
|
5
|
-
Query,
|
|
6
|
-
RequestTargetOrId,
|
|
7
|
-
Session,
|
|
8
|
-
SourceContext,
|
|
9
|
-
SubscriptionRequest,
|
|
10
|
-
} from './resources/ResourceInterface.ts';
|
|
11
|
-
export { ResourceInterface } from './resources/ResourceInterface.ts';
|
|
12
|
-
export type { User } from './security/user.ts';
|
|
13
|
-
export type { RecordObject } from './resources/RecordEncoder.ts';
|
|
14
|
-
export type { IterableEventQueue } from './resources/IterableEventQueue.ts';
|
|
15
|
-
export { RequestTarget } from './resources/RequestTarget.ts';
|
|
16
|
-
export { server } from './server/Server';
|
|
17
|
-
import { server as serverImport } from './server/Server.ts';
|
|
18
|
-
export { tables, databases } from './resources/databases.ts';
|
|
19
|
-
import { tables as dbTables, databases as dbDatabases } from './resources/databases.ts';
|
|
20
|
-
import { BlobCreationOptions } from './resources/blob.ts';
|
|
21
|
-
export { Scope } from './components/Scope.ts';
|
|
22
|
-
export type { FilesOption, FilesOptionObject } from './components/deriveGlobOptions.ts';
|
|
23
|
-
export type { FileAndURLPathConfig } from './components/Component.ts';
|
|
24
|
-
export { OptionsWatcher, type Config, type ConfigValue } from './components/OptionsWatcher.ts';
|
|
25
|
-
export {
|
|
26
|
-
EntryHandler,
|
|
27
|
-
type BaseEntry,
|
|
28
|
-
type FileEntry,
|
|
29
|
-
type EntryEvent,
|
|
30
|
-
type AddFileEvent,
|
|
31
|
-
type ChangeFileEvent,
|
|
32
|
-
type UnlinkFileEvent,
|
|
33
|
-
type FileEntryEvent,
|
|
34
|
-
type AddDirectoryEvent,
|
|
35
|
-
type UnlinkDirectoryEvent,
|
|
36
|
-
type DirectoryEntryEvent,
|
|
37
|
-
} from './components/EntryHandler.ts';
|
|
38
|
-
declare global {
|
|
39
|
-
const tables: typeof dbTables;
|
|
40
|
-
const databases: typeof dbDatabases;
|
|
41
|
-
const server: typeof serverImport;
|
|
42
|
-
const Resource: typeof ResourceImport;
|
|
43
|
-
const createBlob: (
|
|
44
|
-
source: Uint8Array | NodeJS.ReadableStream | string | Iterable<Uint8Array> | AsyncIterator<Uint8Array>,
|
|
45
|
-
options?: BlobCreationOptions
|
|
46
|
-
) => Blob;
|
|
47
|
-
}
|
package/core/v1.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const workerThreads = require('node:worker_threads');
|
|
2
|
-
if (!workerThreads.isMainThread) {
|
|
3
|
-
// Prevents server from starting in worker threads if this was directly imported from a non-server user thread
|
|
4
|
-
if (!workerThreads.workerData) workerThreads.workerData = {};
|
|
5
|
-
workerThreads.workerData.noServerStart = true;
|
|
6
|
-
}
|
|
7
|
-
const { globals } = require('./server/threads/threadServer.js');
|
|
8
|
-
|
|
9
|
-
// exported types are needed for parsing as well
|
|
10
|
-
exports.Config = undefined;
|
|
11
|
-
exports.ConfigValue = undefined;
|
|
12
|
-
exports.Context = undefined;
|
|
13
|
-
exports.FileAndURLPathConfig = undefined;
|
|
14
|
-
exports.FilesOption = undefined;
|
|
15
|
-
exports.FilesOptionObject = undefined;
|
|
16
|
-
exports.IterableEventQueue = undefined;
|
|
17
|
-
exports.Query = undefined;
|
|
18
|
-
exports.RecordObject = undefined;
|
|
19
|
-
exports.RequestTarget = undefined;
|
|
20
|
-
exports.RequestTargetOrId = undefined;
|
|
21
|
-
exports.Resource = undefined;
|
|
22
|
-
exports.ResourceInterface = undefined;
|
|
23
|
-
exports.Scope = undefined;
|
|
24
|
-
exports.Session = undefined;
|
|
25
|
-
exports.SourceContext = undefined;
|
|
26
|
-
exports.SubscriptionRequest = undefined;
|
|
27
|
-
exports.User = undefined;
|
|
28
|
-
|
|
29
|
-
// these are all overwritten by the globals, but need to be here so that Node's static
|
|
30
|
-
// exports parser can analyze them
|
|
31
|
-
exports.tables = {};
|
|
32
|
-
exports.databases = {};
|
|
33
|
-
exports.getUser = undefined;
|
|
34
|
-
exports.server = {};
|
|
35
|
-
exports.contentTypes = null;
|
|
36
|
-
exports.threads = [];
|
|
37
|
-
exports.logger = {};
|
|
38
|
-
Object.assign(exports, globals);
|
package/core/v2.d.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export { ResourceV2 as Resource } from './resources/ResourceV2.ts';
|
|
2
|
-
import { ResourceV2 as ResourceImport } from './resources/ResourceV2.ts';
|
|
3
|
-
export type {
|
|
4
|
-
Context,
|
|
5
|
-
Query,
|
|
6
|
-
RequestTargetOrId,
|
|
7
|
-
Session,
|
|
8
|
-
SourceContext,
|
|
9
|
-
SubscriptionRequest,
|
|
10
|
-
} from './resources/ResourceInterface.ts';
|
|
11
|
-
export { ResourceInterfaceV2 as ResourceInterface } from './resources/ResourceInterfaceV2.ts';
|
|
12
|
-
export type { User } from './security/user.ts';
|
|
13
|
-
export type { RecordObject } from './resources/RecordEncoder.ts';
|
|
14
|
-
export type { IterableEventQueue } from './resources/IterableEventQueue.ts';
|
|
15
|
-
export { RequestTarget } from './resources/RequestTarget.ts';
|
|
16
|
-
export { server } from './server/Server';
|
|
17
|
-
import { server as serverImport } from './server/Server.ts';
|
|
18
|
-
export { tables, databases } from './resources/databases.ts';
|
|
19
|
-
import { tables as dbTables, databases as dbDatabases } from './resources/databases.ts';
|
|
20
|
-
import { BlobCreationOptions } from './resources/blob.ts';
|
|
21
|
-
export { Scope } from './components/Scope.ts';
|
|
22
|
-
export type { FilesOption, FilesOptionObject } from './components/deriveGlobOptions.ts';
|
|
23
|
-
export type { FileAndURLPathConfig } from './components/Component.ts';
|
|
24
|
-
export { OptionsWatcher, type Config, type ConfigValue } from './components/OptionsWatcher.ts';
|
|
25
|
-
export {
|
|
26
|
-
EntryHandler,
|
|
27
|
-
type BaseEntry,
|
|
28
|
-
type FileEntry,
|
|
29
|
-
type EntryEvent,
|
|
30
|
-
type AddFileEvent,
|
|
31
|
-
type ChangeFileEvent,
|
|
32
|
-
type UnlinkFileEvent,
|
|
33
|
-
type FileEntryEvent,
|
|
34
|
-
type AddDirectoryEvent,
|
|
35
|
-
type UnlinkDirectoryEvent,
|
|
36
|
-
type DirectoryEntryEvent,
|
|
37
|
-
} from './components/EntryHandler.ts';
|
|
38
|
-
declare global {
|
|
39
|
-
const tables: typeof dbTables;
|
|
40
|
-
const databases: typeof dbDatabases;
|
|
41
|
-
const server: typeof serverImport;
|
|
42
|
-
const Resource: typeof ResourceImport;
|
|
43
|
-
const createBlob: (
|
|
44
|
-
source: Uint8Array | NodeJS.ReadableStream | string | Iterable<Uint8Array> | AsyncIterator<Uint8Array>,
|
|
45
|
-
options?: BlobCreationOptions
|
|
46
|
-
) => Blob;
|
|
47
|
-
}
|
package/core/v2.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const workerThreads = require('node:worker_threads');
|
|
2
|
-
if (!workerThreads.isMainThread) {
|
|
3
|
-
// Prevents server from starting in worker threads if this was directly imported from a non-server user thread
|
|
4
|
-
if (!workerThreads.workerData) workerThreads.workerData = {};
|
|
5
|
-
workerThreads.workerData.noServerStart = true;
|
|
6
|
-
}
|
|
7
|
-
const { globals } = require('./server/threads/threadServer.js');
|
|
8
|
-
|
|
9
|
-
// exported types are needed for parsing as well
|
|
10
|
-
exports.Config = undefined;
|
|
11
|
-
exports.ConfigValue = undefined;
|
|
12
|
-
exports.Context = undefined;
|
|
13
|
-
exports.FileAndURLPathConfig = undefined;
|
|
14
|
-
exports.FilesOption = undefined;
|
|
15
|
-
exports.FilesOptionObject = undefined;
|
|
16
|
-
exports.IterableEventQueue = undefined;
|
|
17
|
-
exports.Query = undefined;
|
|
18
|
-
exports.RecordObject = undefined;
|
|
19
|
-
exports.RequestTarget = undefined;
|
|
20
|
-
exports.RequestTargetOrId = undefined;
|
|
21
|
-
exports.Resource = undefined;
|
|
22
|
-
exports.ResourceInterface = undefined;
|
|
23
|
-
exports.Scope = undefined;
|
|
24
|
-
exports.Session = undefined;
|
|
25
|
-
exports.SourceContext = undefined;
|
|
26
|
-
exports.SubscriptionRequest = undefined;
|
|
27
|
-
exports.User = undefined;
|
|
28
|
-
|
|
29
|
-
// these are all overwritten by the globals, but need to be here so that Node's static
|
|
30
|
-
// exports parser can analyze them
|
|
31
|
-
exports.tables = {};
|
|
32
|
-
exports.databases = {};
|
|
33
|
-
exports.getUser = undefined;
|
|
34
|
-
exports.server = {};
|
|
35
|
-
exports.contentTypes = null;
|
|
36
|
-
exports.threads = [];
|
|
37
|
-
exports.logger = {};
|
|
38
|
-
Object.assign(exports, globals);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceInterfaceV2.js","sourceRoot":"","sources":["../../../core/resources/ResourceInterfaceV2.ts"],"names":[],"mappings":""}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ResourceV2 = void 0;
|
|
4
|
-
const globals_js_1 = require("../globals.js");
|
|
5
|
-
const Resource_ts_1 = require("./Resource.js");
|
|
6
|
-
/**
|
|
7
|
-
* This is the main class that can be extended for any resource in HarperDB and provides the essential reusable
|
|
8
|
-
* uniform interface for interacting with data, defining the API for providing data (data sources) and for consuming
|
|
9
|
-
* data. This interface is used pervasively in HarperDB and is implemented by database tables and can be used to define
|
|
10
|
-
* sources for caching, real-data sources for messaging protocols, and RESTful endpoints, as well as any other types of
|
|
11
|
-
* data aggregation, processing, or monitoring.
|
|
12
|
-
*
|
|
13
|
-
* This base Resource class provides a set of static methods that are main entry points for querying and updating data
|
|
14
|
-
* in resources/tables. The static methods provide the default handling of arguments, context, and ensuring that
|
|
15
|
-
* internal actions are wrapped in a transaction. The base Resource class intended to be extended, and the instance
|
|
16
|
-
* methods can be overridden to provide specific implementations of actions like get, put, post, delete, and subscribe.
|
|
17
|
-
*/
|
|
18
|
-
class ResourceV2 extends Resource_ts_1.Resource {
|
|
19
|
-
static loadAsInstance = false;
|
|
20
|
-
// @ts-expect-error In v2, we're adjusting the types.
|
|
21
|
-
post(target, newRecord) {
|
|
22
|
-
return super.post(target, newRecord);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
exports.ResourceV2 = ResourceV2;
|
|
26
|
-
(0, globals_js_1._assignPackageExport)('ResourceV2', ResourceV2);
|
|
27
|
-
//# sourceMappingURL=ResourceV2.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ResourceV2.js","sourceRoot":"","sources":["../../../core/resources/ResourceV2.ts"],"names":[],"mappings":";;;AAAA,8CAAqD;AAIrD,+CAAyC;AAGzC;;;;;;;;;;;GAWG;AACH,MAAa,UAAwC,SAAQ,sBAAgB;IAC5E,MAAM,CAAC,cAAc,GAAY,KAAK,CAAC;IAkBvC,qDAAqD;IACrD,IAAI,CACH,MAAyB,EACzB,SAAyC;QAEzC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACtC,CAAC;;AAzBF,gCA6CC;AAED,IAAA,iCAAoB,EAAC,YAAY,EAAE,UAAU,CAAC,CAAC"}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.userCodeFolders = void 0;
|
|
37
|
-
exports.captureProfile = captureProfile;
|
|
38
|
-
/**
|
|
39
|
-
* This module is responsible for profiling threads so we can determine how much CPU usage can be attributed
|
|
40
|
-
* to user code, harper code, and individual "hot" functions
|
|
41
|
-
*/
|
|
42
|
-
const write_ts_1 = require("./write.js");
|
|
43
|
-
const environmentManager_js_1 = require("../../utility/environment/environmentManager.js");
|
|
44
|
-
const hdbTerms_js_1 = require("../../utility/hdbTerms.js");
|
|
45
|
-
const packageUtils_js_1 = require("../../utility/packageUtils.js");
|
|
46
|
-
const node_fs_1 = require("node:fs");
|
|
47
|
-
const pprof_1 = require("@datadog/pprof");
|
|
48
|
-
const log = __importStar(require("../../utility/logging/harper_logger.js"));
|
|
49
|
-
const basePath = (0, environmentManager_js_1.getHdbBasePath)();
|
|
50
|
-
exports.userCodeFolders = basePath ? [basePath] : [];
|
|
51
|
-
if (process.env.RUN_HDB_APP)
|
|
52
|
-
exports.userCodeFolders.push((0, node_fs_1.realpathSync)(process.env.RUN_HDB_APP));
|
|
53
|
-
let profilerTimer;
|
|
54
|
-
const SAMPLING_INTERVAL_IN_MICROSECONDS = 50000;
|
|
55
|
-
// TODO: Running this on the thread itself can be a problematic because the profiler snapshots are somewhat expensive
|
|
56
|
-
// (calling timeProfiler.stop and getting the large block of JSON and parsing it). This can take a 5ms or more
|
|
57
|
-
// which can have some impact on latency for users. However, the datadog profiler is much better than the node
|
|
58
|
-
// profiler, so we'll keep this for now.
|
|
59
|
-
(async () => {
|
|
60
|
-
if (exports.userCodeFolders.length === 0)
|
|
61
|
-
return;
|
|
62
|
-
// start the profiler
|
|
63
|
-
pprof_1.time.start({ intervalMicros: SAMPLING_INTERVAL_IN_MICROSECONDS });
|
|
64
|
-
const PROFILE_PERIOD = ((0, environmentManager_js_1.get)(hdbTerms_js_1.CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) ?? 60) * 1000;
|
|
65
|
-
if (PROFILE_PERIOD > 0) {
|
|
66
|
-
profilerTimer = setTimeout(() => {
|
|
67
|
-
captureProfile(PROFILE_PERIOD);
|
|
68
|
-
}, PROFILE_PERIOD).unref();
|
|
69
|
-
}
|
|
70
|
-
})();
|
|
71
|
-
async function captureProfile(delayToNextCapture = ((0, environmentManager_js_1.get)(hdbTerms_js_1.CONFIG_PARAMS.ANALYTICS_AGGREGATEPERIOD) ?? 60) * 1000) {
|
|
72
|
-
clearTimeout(profilerTimer);
|
|
73
|
-
const hitCountThreshold = 100;
|
|
74
|
-
const secondsPerHit = SAMPLING_INTERVAL_IN_MICROSECONDS / 1_000_000;
|
|
75
|
-
const locationById = new Map();
|
|
76
|
-
const fileNameById = new Map();
|
|
77
|
-
const samplesByLocationId = new Map();
|
|
78
|
-
let totalUserCount = 0;
|
|
79
|
-
let totalHarperCount = 0;
|
|
80
|
-
try {
|
|
81
|
-
const profile = pprof_1.time.stop(true);
|
|
82
|
-
const strings = profile.stringTable.strings;
|
|
83
|
-
for (let func of profile.function) {
|
|
84
|
-
fileNameById.set(func.id, strings[func.filename]);
|
|
85
|
-
}
|
|
86
|
-
for (let location of profile.location) {
|
|
87
|
-
locationById.set(location.id, location.line[0]);
|
|
88
|
-
}
|
|
89
|
-
for (const sample of profile.sample) {
|
|
90
|
-
getUserHitCount(sample);
|
|
91
|
-
}
|
|
92
|
-
(0, write_ts_1.recordAction)(totalHarperCount * secondsPerHit, 'cpu-usage', 'harper');
|
|
93
|
-
(0, write_ts_1.recordAction)(totalUserCount * secondsPerHit, 'cpu-usage', 'user');
|
|
94
|
-
for (let [locationId, sampleCount] of samplesByLocationId) {
|
|
95
|
-
if (sampleCount > hitCountThreshold) {
|
|
96
|
-
const location = locationById.get(locationId);
|
|
97
|
-
const locationName = fileNameById.get(location.functionId) + ':' + location.line;
|
|
98
|
-
(0, write_ts_1.recordAction)(sampleCount * secondsPerHit, 'cpu-usage', locationName);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch (error) {
|
|
103
|
-
log.error?.('analytics profiler error:', error);
|
|
104
|
-
}
|
|
105
|
-
finally {
|
|
106
|
-
// and start the profiler again
|
|
107
|
-
if (delayToNextCapture > 0) {
|
|
108
|
-
profilerTimer = setTimeout(() => {
|
|
109
|
-
captureProfile();
|
|
110
|
-
}, delayToNextCapture).unref();
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
// somehow this can later get set to a negative number which causes big problems (high-frequency restarts of the profiler)
|
|
114
|
-
log.info?.('Profiling disabled');
|
|
115
|
-
pprof_1.time.stop();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
// this traverses the nodes and returns the number of sampling hits for the sample and attributes it
|
|
119
|
-
// to harper or user code (as opposed to execution of things like node internal modules or native code)
|
|
120
|
-
function getUserHitCount(sample) {
|
|
121
|
-
// if we can assign to user code or harper code, do so
|
|
122
|
-
let recordedTopSample = false;
|
|
123
|
-
for (let locationId of sample.locationId) {
|
|
124
|
-
let fileName = fileNameById.get(locationById.get(locationId).functionId);
|
|
125
|
-
if (exports.userCodeFolders.some((userCodeFolder) => fileName.startsWith(userCodeFolder))) {
|
|
126
|
-
// the call frame location is in user code
|
|
127
|
-
const sampleCount = sample.value[0];
|
|
128
|
-
totalUserCount += sampleCount;
|
|
129
|
-
if (!recordedTopSample)
|
|
130
|
-
samplesByLocationId.set(locationId, (samplesByLocationId.get(locationId) ?? 0) + sampleCount);
|
|
131
|
-
return; // if the highest point in the call stack is in user code, we don't need to check the rest of the call stack, this "counts" as user execution
|
|
132
|
-
}
|
|
133
|
-
if (fileName.startsWith(packageUtils_js_1.PACKAGE_ROOT)) {
|
|
134
|
-
const sampleCount = sample.value[0];
|
|
135
|
-
totalHarperCount += sampleCount;
|
|
136
|
-
if (!recordedTopSample) {
|
|
137
|
-
samplesByLocationId.set(locationId, (samplesByLocationId.get(locationId) ?? 0) + sampleCount);
|
|
138
|
-
recordedTopSample = true;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
//# sourceMappingURL=profile.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../../../core/resources/analytics/profile.ts"],"names":[],"mappings":"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"}
|