@milaboratories/pl-client 2.10.1 → 2.11.0
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/dist/core/final.d.ts.map +1 -1
- package/dist/core/ll_transaction.d.ts +1 -0
- package/dist/core/ll_transaction.d.ts.map +1 -1
- package/dist/core/stat.d.ts +3 -0
- package/dist/core/stat.d.ts.map +1 -1
- package/dist/core/transaction.d.ts +6 -1
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/helpers/pl.d.ts +1 -0
- package/dist/helpers/pl.d.ts.map +1 -1
- package/dist/helpers/poll.d.ts +1 -0
- package/dist/helpers/poll.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1084 -1047
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- package/src/core/final.ts +4 -0
- package/src/core/ll_transaction.ts +1 -0
- package/src/core/stat.ts +11 -1
- package/src/core/transaction.ts +55 -35
- package/src/helpers/pl.ts +1 -0
- package/src/helpers/poll.ts +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20.3.0"
|
|
6
6
|
},
|
|
@@ -20,26 +20,26 @@
|
|
|
20
20
|
"./src/**/*"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@grpc/grpc-js": "~1.13.
|
|
24
|
-
"@protobuf-ts/grpc-transport": "2.
|
|
25
|
-
"@protobuf-ts/runtime": "2.
|
|
26
|
-
"@protobuf-ts/runtime-rpc": "2.
|
|
23
|
+
"@grpc/grpc-js": "~1.13.4",
|
|
24
|
+
"@protobuf-ts/grpc-transport": "2.11.0",
|
|
25
|
+
"@protobuf-ts/runtime": "2.11.0",
|
|
26
|
+
"@protobuf-ts/runtime-rpc": "2.11.0",
|
|
27
27
|
"canonicalize": "~2.1.0",
|
|
28
28
|
"denque": "^2.1.0",
|
|
29
29
|
"lru-cache": "^11.1.0",
|
|
30
30
|
"https-proxy-agent": "^7.0.6",
|
|
31
|
-
"long": "^5.3.
|
|
32
|
-
"undici": "~7.
|
|
31
|
+
"long": "^5.3.2",
|
|
32
|
+
"undici": "~7.10.0",
|
|
33
33
|
"utility-types": "^3.11.0",
|
|
34
34
|
"yaml": "^2.7.0",
|
|
35
|
-
"@milaboratories/pl-http": "^1.1.
|
|
36
|
-
"@milaboratories/ts-helpers": "^1.
|
|
35
|
+
"@milaboratories/pl-http": "^1.1.3",
|
|
36
|
+
"@milaboratories/ts-helpers": "^1.4.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"typescript": "~5.5.4",
|
|
40
40
|
"vite": "^6.3.5",
|
|
41
41
|
"@types/node": "~20.16.15",
|
|
42
|
-
"@protobuf-ts/plugin": "2.
|
|
42
|
+
"@protobuf-ts/plugin": "2.11.0",
|
|
43
43
|
"@types/http-proxy": "^1.17.16",
|
|
44
44
|
"@types/jest": "^29.5.14",
|
|
45
45
|
"jest": "^29.7.0",
|
package/src/core/final.ts
CHANGED
|
@@ -71,6 +71,7 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
|
|
|
71
71
|
case 'json/resourceError':
|
|
72
72
|
return r.type.version === '1';
|
|
73
73
|
case 'json/object':
|
|
74
|
+
case 'json-gz/object':
|
|
74
75
|
case 'json/string':
|
|
75
76
|
case 'json/array':
|
|
76
77
|
case 'json/number':
|
|
@@ -79,6 +80,7 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
|
|
|
79
80
|
case 'Frontend/FromFolder':
|
|
80
81
|
case 'BObjectSpec':
|
|
81
82
|
case 'Blob':
|
|
83
|
+
case 'Null':
|
|
82
84
|
case 'binary':
|
|
83
85
|
case 'LSProvider':
|
|
84
86
|
return true;
|
|
@@ -92,6 +94,8 @@ export const DefaultFinalResourceDataPredicate: FinalResourceDataPredicate = (r)
|
|
|
92
94
|
return readyAndHasAllOutputsFilled(r);
|
|
93
95
|
} else if (r.type.name.startsWith('PColumnData/')) {
|
|
94
96
|
return readyOrDuplicateOrError(r);
|
|
97
|
+
} else if (r.type.name.startsWith('StreamWorkdir/')) {
|
|
98
|
+
return readyOrDuplicateOrError(r);
|
|
95
99
|
} else {
|
|
96
100
|
// Unknonw resource type detected
|
|
97
101
|
// Set used to log this message only once
|
package/src/core/stat.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type TxStat = {
|
|
2
2
|
txCount: number;
|
|
3
|
+
timeMs: number;
|
|
3
4
|
|
|
4
5
|
rootsCreated: number;
|
|
5
6
|
structsCreated: number;
|
|
@@ -33,7 +34,9 @@ export type TxStat = {
|
|
|
33
34
|
kvGetBytes: number;
|
|
34
35
|
};
|
|
35
36
|
|
|
36
|
-
export
|
|
37
|
+
export type TxStatWithoutTime = Omit<TxStat, 'timeMs'>;
|
|
38
|
+
|
|
39
|
+
export function initialTxStatWithoutTime(): TxStatWithoutTime {
|
|
37
40
|
return {
|
|
38
41
|
txCount: 0,
|
|
39
42
|
rootsCreated: 0,
|
|
@@ -63,10 +66,17 @@ export function initialTxStat(): TxStat {
|
|
|
63
66
|
kvGetBytes: 0,
|
|
64
67
|
};
|
|
65
68
|
}
|
|
69
|
+
export function initialTxStat(): TxStat {
|
|
70
|
+
return {
|
|
71
|
+
...initialTxStatWithoutTime(),
|
|
72
|
+
timeMs: 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
66
75
|
|
|
67
76
|
export function addStat(a: TxStat, b: TxStat): TxStat {
|
|
68
77
|
return {
|
|
69
78
|
txCount: a.txCount + b.txCount,
|
|
79
|
+
timeMs: a.timeMs + b.timeMs,
|
|
70
80
|
rootsCreated: a.rootsCreated + b.rootsCreated,
|
|
71
81
|
structsCreated: a.structsCreated + b.structsCreated,
|
|
72
82
|
structsCreatedDataBytes: a.structsCreatedDataBytes + b.structsCreatedDataBytes,
|
package/src/core/transaction.ts
CHANGED
|
@@ -29,15 +29,16 @@ import { TxAPI_Open_Request_WritableTx } from '../proto/github.com/milaboratory/
|
|
|
29
29
|
import type { NonUndefined } from 'utility-types';
|
|
30
30
|
import { toBytes } from '../util/util';
|
|
31
31
|
import { fieldTypeToProto, protoToField, protoToResource } from './type_conversion';
|
|
32
|
-
import { deepFreeze, notEmpty } from '@milaboratories/ts-helpers';
|
|
32
|
+
import { canonicalJsonBytes, canonicalJsonGzBytes, deepFreeze, notEmpty } from '@milaboratories/ts-helpers';
|
|
33
33
|
import { isNotFoundError } from './errors';
|
|
34
34
|
import type { FinalResourceDataPredicate } from './final';
|
|
35
35
|
import type { LRUCache } from 'lru-cache';
|
|
36
36
|
import type { ResourceDataCacheRecord } from './cache';
|
|
37
37
|
import type { TxStat } from './stat';
|
|
38
|
-
import {
|
|
38
|
+
import { initialTxStatWithoutTime } from './stat';
|
|
39
39
|
import type { ErrorResourceData } from './error_resource';
|
|
40
40
|
import { ErrorResourceType } from './error_resource';
|
|
41
|
+
import { JsonGzObject, JsonObject } from '../helpers/pl';
|
|
41
42
|
|
|
42
43
|
/** Reference to resource, used only within transaction */
|
|
43
44
|
export interface ResourceRef {
|
|
@@ -126,7 +127,9 @@ export function field(resourceId: AnyResourceRef, fieldName: string): AnyFieldRe
|
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
/** If transaction commit failed due to write conflicts */
|
|
129
|
-
export class TxCommitConflict extends Error {
|
|
130
|
+
export class TxCommitConflict extends Error {
|
|
131
|
+
name = 'TxCommitConflict';
|
|
132
|
+
}
|
|
130
133
|
|
|
131
134
|
async function notFoundToUndefined<T>(cb: () => Promise<T>): Promise<T | undefined> {
|
|
132
135
|
try {
|
|
@@ -166,7 +169,14 @@ export class PlTransaction {
|
|
|
166
169
|
|
|
167
170
|
private globalTxIdWasAwaited: boolean = false;
|
|
168
171
|
|
|
169
|
-
|
|
172
|
+
private readonly _startTime = Date.now();
|
|
173
|
+
private readonly _stat = initialTxStatWithoutTime();
|
|
174
|
+
public get stat(): TxStat {
|
|
175
|
+
return {
|
|
176
|
+
...this._stat,
|
|
177
|
+
timeMs: Date.now() - this._startTime,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
170
180
|
|
|
171
181
|
constructor(
|
|
172
182
|
private readonly ll: LLPlTransaction,
|
|
@@ -198,7 +208,7 @@ export class PlTransaction {
|
|
|
198
208
|
});
|
|
199
209
|
|
|
200
210
|
// Adding stats
|
|
201
|
-
this.
|
|
211
|
+
this._stat.txCount++;
|
|
202
212
|
}
|
|
203
213
|
|
|
204
214
|
private async drainAndAwaitPendingOps(): Promise<void> {
|
|
@@ -378,7 +388,7 @@ export class PlTransaction {
|
|
|
378
388
|
}
|
|
379
389
|
|
|
380
390
|
public createRoot(type: ResourceType): ResourceRef {
|
|
381
|
-
this.
|
|
391
|
+
this._stat.rootsCreated++;
|
|
382
392
|
return this.createResource(
|
|
383
393
|
true,
|
|
384
394
|
(localId) => ({ oneofKind: 'resourceCreateRoot', resourceCreateRoot: { type, id: localId } }),
|
|
@@ -387,8 +397,8 @@ export class PlTransaction {
|
|
|
387
397
|
}
|
|
388
398
|
|
|
389
399
|
public createStruct(type: ResourceType, data?: Uint8Array | string): ResourceRef {
|
|
390
|
-
this.
|
|
391
|
-
this.
|
|
400
|
+
this._stat.structsCreated++;
|
|
401
|
+
this._stat.structsCreatedDataBytes += data?.length ?? 0;
|
|
392
402
|
return this.createResource(
|
|
393
403
|
false,
|
|
394
404
|
(localId) => ({
|
|
@@ -404,8 +414,8 @@ export class PlTransaction {
|
|
|
404
414
|
}
|
|
405
415
|
|
|
406
416
|
public createEphemeral(type: ResourceType, data?: Uint8Array | string): ResourceRef {
|
|
407
|
-
this.
|
|
408
|
-
this.
|
|
417
|
+
this._stat.ephemeralsCreated++;
|
|
418
|
+
this._stat.ephemeralsCreatedDataBytes += data?.length ?? 0;
|
|
409
419
|
return this.createResource(
|
|
410
420
|
false,
|
|
411
421
|
(localId) => ({
|
|
@@ -425,8 +435,8 @@ export class PlTransaction {
|
|
|
425
435
|
data: Uint8Array | string,
|
|
426
436
|
errorIfExists: boolean = false,
|
|
427
437
|
): ResourceRef {
|
|
428
|
-
this.
|
|
429
|
-
this.
|
|
438
|
+
this._stat.valuesCreated++;
|
|
439
|
+
this._stat.valuesCreatedDataBytes += data?.length ?? 0;
|
|
430
440
|
return this.createResource(
|
|
431
441
|
false,
|
|
432
442
|
(localId) => ({
|
|
@@ -442,6 +452,16 @@ export class PlTransaction {
|
|
|
442
452
|
);
|
|
443
453
|
}
|
|
444
454
|
|
|
455
|
+
public createJsonValue(data: unknown): ResourceRef {
|
|
456
|
+
const jsonData = canonicalJsonBytes(data);
|
|
457
|
+
return this.createValue(JsonObject, jsonData, false);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
public createJsonGzValue(data: unknown, minSizeToGzip: number | undefined = 16_384): ResourceRef {
|
|
461
|
+
const { data: jsonData, isGzipped } = canonicalJsonGzBytes(data, minSizeToGzip);
|
|
462
|
+
return this.createValue(isGzipped ? JsonGzObject : JsonObject, jsonData, false);
|
|
463
|
+
}
|
|
464
|
+
|
|
445
465
|
public createError(message: string): ResourceRef {
|
|
446
466
|
return this.createValue(ErrorResourceType, JSON.stringify({ message } satisfies ErrorResourceData));
|
|
447
467
|
}
|
|
@@ -519,13 +539,13 @@ export class PlTransaction {
|
|
|
519
539
|
const fromCache = this.sharedResourceDataCache.get(rId);
|
|
520
540
|
if (fromCache && fromCache.cacheTxOpenTimestamp < this.txOpenTimestamp) {
|
|
521
541
|
if (!loadFields) {
|
|
522
|
-
this.
|
|
523
|
-
this.
|
|
542
|
+
this._stat.rGetDataCacheHits++;
|
|
543
|
+
this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
|
|
524
544
|
return fromCache.basicData;
|
|
525
545
|
} else if (fromCache.data) {
|
|
526
|
-
this.
|
|
527
|
-
this.
|
|
528
|
-
this.
|
|
546
|
+
this._stat.rGetDataCacheHits++;
|
|
547
|
+
this._stat.rGetDataCacheBytes += fromCache.basicData.data?.length ?? 0;
|
|
548
|
+
this._stat.rGetDataCacheFields += fromCache.data.fields.length;
|
|
529
549
|
return fromCache.data;
|
|
530
550
|
}
|
|
531
551
|
}
|
|
@@ -539,9 +559,9 @@ export class PlTransaction {
|
|
|
539
559
|
(r) => protoToResource(notEmpty(r.resourceGet.resource)),
|
|
540
560
|
);
|
|
541
561
|
|
|
542
|
-
this.
|
|
543
|
-
this.
|
|
544
|
-
this.
|
|
562
|
+
this._stat.rGetDataNetRequests++;
|
|
563
|
+
this._stat.rGetDataNetBytes += result.data?.length ?? 0;
|
|
564
|
+
this._stat.rGetDataNetFields += result.fields.length;
|
|
545
565
|
|
|
546
566
|
// we will cache only final resource data states
|
|
547
567
|
// caching result even if we were ignore the cache
|
|
@@ -612,7 +632,7 @@ export class PlTransaction {
|
|
|
612
632
|
* have their values, if inputs list is not locked.
|
|
613
633
|
*/
|
|
614
634
|
public lockInputs(rId: AnyResourceRef): void {
|
|
615
|
-
this.
|
|
635
|
+
this._stat.inputsLocked++;
|
|
616
636
|
this.sendVoidAsync({
|
|
617
637
|
oneofKind: 'resourceLockInputs',
|
|
618
638
|
resourceLockInputs: { resourceId: toResourceId(rId) },
|
|
@@ -624,7 +644,7 @@ export class PlTransaction {
|
|
|
624
644
|
* This is required for resource to pass deduplication.
|
|
625
645
|
*/
|
|
626
646
|
public lockOutputs(rId: AnyResourceRef): void {
|
|
627
|
-
this.
|
|
647
|
+
this._stat.outputsLocked++;
|
|
628
648
|
this.sendVoidAsync({
|
|
629
649
|
oneofKind: 'resourceLockOutputs',
|
|
630
650
|
resourceLockOutputs: { resourceId: toResourceId(rId) },
|
|
@@ -648,7 +668,7 @@ export class PlTransaction {
|
|
|
648
668
|
//
|
|
649
669
|
|
|
650
670
|
public createField(fId: AnyFieldRef, fieldType: FieldType, value?: AnyRef): void {
|
|
651
|
-
this.
|
|
671
|
+
this._stat.fieldsCreated++;
|
|
652
672
|
this.sendVoidAsync({
|
|
653
673
|
oneofKind: 'fieldCreate',
|
|
654
674
|
fieldCreate: { type: fieldTypeToProto(fieldType), id: toFieldId(fId) },
|
|
@@ -667,7 +687,7 @@ export class PlTransaction {
|
|
|
667
687
|
}
|
|
668
688
|
|
|
669
689
|
public setField(fId: AnyFieldRef, ref: AnyRef): void {
|
|
670
|
-
this.
|
|
690
|
+
this._stat.fieldsSet++;
|
|
671
691
|
if (isResource(ref))
|
|
672
692
|
this.sendVoidAsync({
|
|
673
693
|
oneofKind: 'fieldSet',
|
|
@@ -690,7 +710,7 @@ export class PlTransaction {
|
|
|
690
710
|
}
|
|
691
711
|
|
|
692
712
|
public setFieldError(fId: AnyFieldRef, ref: AnyResourceRef): void {
|
|
693
|
-
this.
|
|
713
|
+
this._stat.fieldsSet++;
|
|
694
714
|
this.sendVoidAsync({
|
|
695
715
|
oneofKind: 'fieldSetError',
|
|
696
716
|
fieldSetError: { field: toFieldId(fId), errResourceId: toResourceId(ref) },
|
|
@@ -698,7 +718,7 @@ export class PlTransaction {
|
|
|
698
718
|
}
|
|
699
719
|
|
|
700
720
|
public async getField(fId: AnyFieldRef): Promise<FieldData> {
|
|
701
|
-
this.
|
|
721
|
+
this._stat.fieldsGet++;
|
|
702
722
|
return await this.sendSingleAndParse(
|
|
703
723
|
{ oneofKind: 'fieldGet', fieldGet: { field: toFieldId(fId) } },
|
|
704
724
|
(r) => protoToField(notEmpty(r.fieldGet.field)),
|
|
@@ -730,9 +750,9 @@ export class PlTransaction {
|
|
|
730
750
|
(r) => r.map((e) => e.resourceKeyValueList.record!),
|
|
731
751
|
);
|
|
732
752
|
|
|
733
|
-
this.
|
|
734
|
-
this.
|
|
735
|
-
for (const kv of result) this.
|
|
753
|
+
this._stat.kvListRequests++;
|
|
754
|
+
this._stat.kvListEntries += result.length;
|
|
755
|
+
for (const kv of result) this._stat.kvListBytes += kv.key.length + kv.value.length;
|
|
736
756
|
|
|
737
757
|
return result;
|
|
738
758
|
}
|
|
@@ -755,8 +775,8 @@ export class PlTransaction {
|
|
|
755
775
|
}
|
|
756
776
|
|
|
757
777
|
public setKValue(rId: AnyResourceRef, key: string, value: Uint8Array | string): void {
|
|
758
|
-
this.
|
|
759
|
-
this.
|
|
778
|
+
this._stat.kvSetRequests++;
|
|
779
|
+
this._stat.kvSetBytes++;
|
|
760
780
|
this.sendVoidAsync({
|
|
761
781
|
oneofKind: 'resourceKeyValueSet',
|
|
762
782
|
resourceKeyValueSet: {
|
|
@@ -786,8 +806,8 @@ export class PlTransaction {
|
|
|
786
806
|
(r) => r.resourceKeyValueGet.value,
|
|
787
807
|
);
|
|
788
808
|
|
|
789
|
-
this.
|
|
790
|
-
this.
|
|
809
|
+
this._stat.kvGetRequests++;
|
|
810
|
+
this._stat.kvGetBytes += result.length;
|
|
791
811
|
|
|
792
812
|
return result;
|
|
793
813
|
}
|
|
@@ -813,8 +833,8 @@ export class PlTransaction {
|
|
|
813
833
|
r.resourceKeyValueGetIfExists.exists ? r.resourceKeyValueGetIfExists.value : undefined,
|
|
814
834
|
);
|
|
815
835
|
|
|
816
|
-
this.
|
|
817
|
-
this.
|
|
836
|
+
this._stat.kvGetRequests++;
|
|
837
|
+
this._stat.kvGetBytes += result?.length ?? 0;
|
|
818
838
|
|
|
819
839
|
return result;
|
|
820
840
|
}
|
package/src/helpers/pl.ts
CHANGED
|
@@ -23,6 +23,7 @@ export const ValueTestResource = rt('ValueTest', '1');
|
|
|
23
23
|
export const JsonString = rt('json/string', '1');
|
|
24
24
|
export const JsonBool = rt('json/bool', '1');
|
|
25
25
|
export const JsonObject = rt('json/object', '1');
|
|
26
|
+
export const JsonGzObject = rt('json-gz/object', '1');
|
|
26
27
|
export const JsonArray = rt('json/array', '1');
|
|
27
28
|
export const JsonNumber = rt('json/number', '1');
|
|
28
29
|
export const JsonNull = rt('json/null', '1');
|
package/src/helpers/poll.ts
CHANGED
|
@@ -21,7 +21,9 @@ import type { PlTransaction } from '../core/transaction';
|
|
|
21
21
|
import * as tp from 'node:timers/promises';
|
|
22
22
|
|
|
23
23
|
/** This error tells state assertion mechanism that required state is not yet ready */
|
|
24
|
-
export class ContinuePolling extends Error {
|
|
24
|
+
export class ContinuePolling extends Error {
|
|
25
|
+
name = 'ContinuePolling';
|
|
26
|
+
}
|
|
25
27
|
|
|
26
28
|
export type PollFieldTraverseOps = {
|
|
27
29
|
expectedType?: FieldType;
|