@milaboratories/pl-tree 1.4.33 → 1.5.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/src/dump.ts ADDED
@@ -0,0 +1,115 @@
1
+ import { ExtendedResourceData } from "./state"
2
+
3
+ export type ResourceStats = {
4
+ /** Number of resources of this type */
5
+ count: number,
6
+ /** Total number of bytes in the field names of all resources of this type */
7
+ fieldNameBytes: number,
8
+ /** Total number of fields in all resources of this type */
9
+ fieldsCount: number,
10
+ /** Total number of bytes in the data of all resources of this type */
11
+ dataBytes: number,
12
+ /** Total number of key-value records in all resources of this type */
13
+ kvCount: number,
14
+ /** Total number of bytes in the key-value records of all resources of this type */
15
+ kvBytes: number,
16
+ }
17
+
18
+ /**
19
+ * A map of resource type statistics, keyed by the resource type name and version.
20
+ *
21
+ * @type {Record<string, ResourceStats>}
22
+ */
23
+ export type TreeDumpStats = {
24
+ total: ResourceStats,
25
+ byResourceType: Record<`${string}/${string}`, ResourceStats>
26
+ }
27
+
28
+ /**
29
+ * Analyzes a collection of resources and generates statistics grouped by resource type.
30
+ *
31
+ * This function processes an array of ExtendedResourceData and calculates various metrics
32
+ * for each unique resource type, including:
33
+ * - Count of resources
34
+ * - Total bytes in field names
35
+ * - Total number of fields
36
+ * - Total bytes in resource data
37
+ * - Total number of key-value records
38
+ * - Total bytes in key-value records
39
+ *
40
+ * The statistics are organized by resource type using a key in the format "typeName/version".
41
+ *
42
+ * @param dumpStats - Array of ExtendedResourceData objects to analyze
43
+ * @returns A DumpStats object containing statistics for each resource type
44
+ * @example
45
+ * ```typescript
46
+ * const resources = [...]; // Array of ExtendedResourceData
47
+ * const stats = treeDumpStats(resources);
48
+ * // stats = {
49
+ * // "MyResource/1": {
50
+ * // count: 5,
51
+ * // fieldNameBytes: 150,
52
+ * // fieldsCount: 10,
53
+ * // dataBytes: 1024,
54
+ * // kvCount: 3,
55
+ * // kvBytes: 256
56
+ * // },
57
+ * // ...
58
+ * // }
59
+ * ```
60
+ */
61
+ export function treeDumpStats(dumpStats: ExtendedResourceData[]): TreeDumpStats {
62
+ const stats: TreeDumpStats = {
63
+ total: {
64
+ count: 0,
65
+ fieldNameBytes: 0,
66
+ fieldsCount: 0,
67
+ dataBytes: 0,
68
+ kvCount: 0,
69
+ kvBytes: 0
70
+ },
71
+ byResourceType: {}
72
+ };
73
+
74
+ for (const resource of dumpStats) {
75
+ const typeKey = `${resource.type.name}/${resource.type.version}` as const;
76
+ if (!stats.byResourceType[typeKey]) {
77
+ stats.byResourceType[typeKey] = {
78
+ count: 0,
79
+ fieldNameBytes: 0,
80
+ fieldsCount: 0,
81
+ dataBytes: 0,
82
+ kvCount: 0,
83
+ kvBytes: 0
84
+ };
85
+ }
86
+
87
+ const typeStats = stats.byResourceType[typeKey];
88
+ typeStats.count++;
89
+ stats.total.count++;
90
+
91
+ for (const field of resource.fields) {
92
+ typeStats.fieldNameBytes += field.name.length;
93
+ typeStats.fieldsCount++;
94
+ stats.total.fieldNameBytes += field.name.length;
95
+ stats.total.fieldsCount++;
96
+ }
97
+
98
+ if (resource.data) {
99
+ const dataLength = resource.data?.length ?? 0;
100
+ typeStats.dataBytes += dataLength;
101
+ stats.total.dataBytes += dataLength;
102
+ }
103
+
104
+ typeStats.kvCount += resource.kv.length;
105
+ stats.total.kvCount += resource.kv.length;
106
+
107
+ for (const kv of resource.kv) {
108
+ const kvLength = kv.key.length + kv.value.length;
109
+ typeStats.kvBytes += kvLength;
110
+ stats.total.kvBytes += kvLength;
111
+ }
112
+ }
113
+
114
+ return stats;
115
+ }
package/src/index.ts CHANGED
@@ -6,3 +6,4 @@ export * from './snapshot';
6
6
  export * from './synchronized_tree';
7
7
  export * from './value_and_error';
8
8
  export * from './value_or_error';
9
+ export * from './dump';
package/src/state.ts CHANGED
@@ -45,6 +45,17 @@ class PlTreeField implements FieldData {
45
45
  /** Last version of resource this field was observed, used to garbage collect fields in tree patching procedure */
46
46
  public resourceVersion: number
47
47
  ) {}
48
+
49
+ get state(): FieldData {
50
+ return {
51
+ name: this.name,
52
+ type: this.type,
53
+ status: this.status,
54
+ value: this.value,
55
+ error: this.error,
56
+ valueIsFinal: this.valueIsFinal
57
+ };
58
+ }
48
59
  }
49
60
 
50
61
  const InitialResourceVersion = 0;
@@ -306,10 +317,10 @@ export class PlTreeResource implements ResourceDataWithFinalState {
306
317
 
307
318
  verifyReadyState() {
308
319
  if (this.resourceReady && !this.inputsLocked)
309
- throw new Error(`ready without input or output lock: ${stringifyWithResourceId(this.state)}`);
320
+ throw new Error(`ready without input or output lock: ${stringifyWithResourceId(this.basicState)}`);
310
321
  }
311
322
 
312
- get state(): BasicResourceData {
323
+ get basicState(): BasicResourceData {
313
324
  return {
314
325
  id: this.id,
315
326
  kind: this.kind,
@@ -324,6 +335,14 @@ export class PlTreeResource implements ResourceDataWithFinalState {
324
335
  };
325
336
  }
326
337
 
338
+ get extendedState(): ExtendedResourceData {
339
+ return {
340
+ ...this.basicState,
341
+ fields: this.fields,
342
+ kv: Array.from(this.kv.entries()).map(([key, value]) => ({ key, value }))
343
+ };
344
+ }
345
+
327
346
  /** Called when {@link FinalResourceDataPredicate} returns true for the state. */
328
347
  markFinal() {
329
348
  if (this._finalState) return;
@@ -399,7 +418,7 @@ export class PlTreeState {
399
418
  for (const rd of resourceData) {
400
419
  let resource = this.resources.get(rd.id);
401
420
 
402
- const statBeforeMutation = resource?.state;
421
+ const statBeforeMutation = resource?.basicState;
403
422
  const unexpectedTransitionError = (reason: string): never => {
404
423
  const { fields, ...rdWithoutFields } = rd;
405
424
  this.invalidateTree();
@@ -718,4 +737,8 @@ export class PlTreeState {
718
737
  res.markAllChanged();
719
738
  });
720
739
  }
740
+
741
+ public dumpState(): ExtendedResourceData[] {
742
+ return Array.from(this.resources.values()).map((res) => res.extendedState);
743
+ }
721
744
  }
@@ -4,9 +4,10 @@ import {
4
4
  FinalResourceDataPredicate,
5
5
  isTimeoutOrCancelError,
6
6
  PlClient,
7
- ResourceId
7
+ ResourceId,
8
+ TxOps
8
9
  } from '@milaboratories/pl-client';
9
- import { PlTreeState, TreeStateUpdateError } from './state';
10
+ import { ExtendedResourceData, PlTreeState, TreeStateUpdateError } from './state';
10
11
  import {
11
12
  constructTreeLoadingRequest,
12
13
  initialTreeLoadingStat,
@@ -34,6 +35,9 @@ export type SynchronizedTreeOps = {
34
35
 
35
36
  /** If one of the values, tree will log stats of each polling request */
36
37
  logStat?: StatLoggingMode;
38
+
39
+ /** Timeout for initial tree loading. If not specified, will use default for RO tx from pl-client. */
40
+ initialTreeLoadingTimeout?: number;
37
41
  };
38
42
 
39
43
  type ScheduledRefresh = {
@@ -113,12 +117,12 @@ export class SynchronizedTreeState {
113
117
  private currentLoop: Promise<void> | undefined = undefined;
114
118
 
115
119
  /** Executed from the main loop, and initialization procedure. */
116
- private async refresh(stats?: TreeLoadingStat): Promise<void> {
120
+ private async refresh(stats?: TreeLoadingStat, txOps?: TxOps): Promise<void> {
117
121
  if (this.terminated) throw new Error('tree synchronization is terminated');
118
122
  const request = constructTreeLoadingRequest(this.state, this.pruning);
119
123
  const data = await this.pl.withReadTx('ReadingTree', async (tx) => {
120
124
  return await loadTreeState(tx, request, stats);
121
- });
125
+ }, txOps);
122
126
  this.state.updateFromResourceData(data, true);
123
127
  }
124
128
 
@@ -194,6 +198,14 @@ export class SynchronizedTreeState {
194
198
  this.currentLoop = undefined;
195
199
  }
196
200
 
201
+ /**
202
+ * Dumps the current state of the tree.
203
+ * @returns An array of ExtendedResourceData objects representing the current state of the tree.
204
+ */
205
+ public dumpState(): ExtendedResourceData[] {
206
+ return this.state.dumpState();
207
+ }
208
+
197
209
  /**
198
210
  * Terminates the internal loop, and permanently destoys all internal state, so
199
211
  * all computables using this state will resolve to errors.
@@ -228,7 +240,9 @@ export class SynchronizedTreeState {
228
240
  let ok = false;
229
241
 
230
242
  try {
231
- await tree.refresh(stat);
243
+ await tree.refresh(stat, {
244
+ timeout: ops.initialTreeLoadingTimeout
245
+ });
232
246
  ok = true;
233
247
  } finally {
234
248
  // logging stats if we were asked to (even if error occured)