@fluidframework/odsp-driver 2.0.0-internal.2.1.2 → 2.0.0-internal.2.2.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.
Files changed (83) hide show
  1. package/.eslintrc.js +1 -1
  2. package/dist/compactSnapshotParser.d.ts +1 -2
  3. package/dist/compactSnapshotParser.d.ts.map +1 -1
  4. package/dist/compactSnapshotParser.js +6 -2
  5. package/dist/compactSnapshotParser.js.map +1 -1
  6. package/dist/fetchSnapshot.d.ts.map +1 -1
  7. package/dist/fetchSnapshot.js +13 -18
  8. package/dist/fetchSnapshot.js.map +1 -1
  9. package/dist/localOdspDriver/localOdspDeltaStorageService.d.ts +17 -0
  10. package/dist/localOdspDriver/localOdspDeltaStorageService.d.ts.map +1 -0
  11. package/dist/localOdspDriver/localOdspDeltaStorageService.js +35 -0
  12. package/dist/localOdspDriver/localOdspDeltaStorageService.js.map +1 -0
  13. package/dist/localOdspDriver/localOdspDocumentService.d.ts +1 -0
  14. package/dist/localOdspDriver/localOdspDocumentService.d.ts.map +1 -1
  15. package/dist/localOdspDriver/localOdspDocumentService.js +5 -2
  16. package/dist/localOdspDriver/localOdspDocumentService.js.map +1 -1
  17. package/dist/odspDeltaStorageService.d.ts +0 -1
  18. package/dist/odspDeltaStorageService.d.ts.map +1 -1
  19. package/dist/odspDeltaStorageService.js +4 -20
  20. package/dist/odspDeltaStorageService.js.map +1 -1
  21. package/dist/odspDocumentDeltaConnection.js +1 -1
  22. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  23. package/dist/odspDocumentService.d.ts.map +1 -1
  24. package/dist/odspDocumentService.js +9 -2
  25. package/dist/odspDocumentService.js.map +1 -1
  26. package/dist/odspUtils.d.ts +4 -0
  27. package/dist/odspUtils.d.ts.map +1 -1
  28. package/dist/odspUtils.js +32 -1
  29. package/dist/odspUtils.js.map +1 -1
  30. package/dist/packageVersion.d.ts +1 -1
  31. package/dist/packageVersion.js +1 -1
  32. package/dist/packageVersion.js.map +1 -1
  33. package/dist/zipItDataRepresentationUtils.d.ts +17 -2
  34. package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
  35. package/dist/zipItDataRepresentationUtils.js +15 -2
  36. package/dist/zipItDataRepresentationUtils.js.map +1 -1
  37. package/lib/compactSnapshotParser.d.ts +1 -2
  38. package/lib/compactSnapshotParser.d.ts.map +1 -1
  39. package/lib/compactSnapshotParser.js +6 -2
  40. package/lib/compactSnapshotParser.js.map +1 -1
  41. package/lib/fetchSnapshot.d.ts.map +1 -1
  42. package/lib/fetchSnapshot.js +14 -19
  43. package/lib/fetchSnapshot.js.map +1 -1
  44. package/lib/localOdspDriver/localOdspDeltaStorageService.d.ts +17 -0
  45. package/lib/localOdspDriver/localOdspDeltaStorageService.d.ts.map +1 -0
  46. package/lib/localOdspDriver/localOdspDeltaStorageService.js +31 -0
  47. package/lib/localOdspDriver/localOdspDeltaStorageService.js.map +1 -0
  48. package/lib/localOdspDriver/localOdspDocumentService.d.ts +1 -0
  49. package/lib/localOdspDriver/localOdspDocumentService.d.ts.map +1 -1
  50. package/lib/localOdspDriver/localOdspDocumentService.js +6 -3
  51. package/lib/localOdspDriver/localOdspDocumentService.js.map +1 -1
  52. package/lib/odspDeltaStorageService.d.ts +0 -1
  53. package/lib/odspDeltaStorageService.d.ts.map +1 -1
  54. package/lib/odspDeltaStorageService.js +5 -21
  55. package/lib/odspDeltaStorageService.js.map +1 -1
  56. package/lib/odspDocumentDeltaConnection.js +1 -1
  57. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  58. package/lib/odspDocumentService.d.ts.map +1 -1
  59. package/lib/odspDocumentService.js +10 -3
  60. package/lib/odspDocumentService.js.map +1 -1
  61. package/lib/odspUtils.d.ts +4 -0
  62. package/lib/odspUtils.d.ts.map +1 -1
  63. package/lib/odspUtils.js +28 -0
  64. package/lib/odspUtils.js.map +1 -1
  65. package/lib/packageVersion.d.ts +1 -1
  66. package/lib/packageVersion.js +1 -1
  67. package/lib/packageVersion.js.map +1 -1
  68. package/lib/zipItDataRepresentationUtils.d.ts +17 -2
  69. package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
  70. package/lib/zipItDataRepresentationUtils.js +15 -2
  71. package/lib/zipItDataRepresentationUtils.js.map +1 -1
  72. package/package.json +19 -14
  73. package/prettier.config.cjs +8 -0
  74. package/src/compactSnapshotParser.ts +16 -12
  75. package/src/fetchSnapshot.ts +29 -24
  76. package/src/localOdspDriver/localOdspDeltaStorageService.ts +46 -0
  77. package/src/localOdspDriver/localOdspDocumentService.ts +9 -3
  78. package/src/odspDeltaStorageService.ts +5 -22
  79. package/src/odspDocumentDeltaConnection.ts +1 -1
  80. package/src/odspDocumentService.ts +9 -2
  81. package/src/odspUtils.ts +37 -0
  82. package/src/packageVersion.ts +1 -1
  83. package/src/zipItDataRepresentationUtils.ts +18 -3
@@ -26,6 +26,8 @@ import {
26
26
  getWithRetryForTokenRefresh,
27
27
  getWithRetryForTokenRefreshRepeat,
28
28
  IOdspResponse,
29
+ measure,
30
+ measureP,
29
31
  } from "./odspUtils";
30
32
  import { ISnapshotContents } from "./odspPublicUtils";
31
33
  import { convertOdspSnapshotToSnapshotTreeAndBlobs } from "./odspSnapshotParser";
@@ -33,13 +35,6 @@ import { currentReadVersion, ISnapshotContentsWithProps, parseCompactSnapshotRes
33
35
  import { EpochTracker } from "./epochTracker";
34
36
  import { pkgVersion } from "./packageVersion";
35
37
 
36
- async function measure<T>(callback: () => T | Promise<T>): Promise<[T, number]> {
37
- const start = performance.now();
38
- const result = await callback();
39
- const time = performance.now() - start;
40
- return [result, time];
41
- }
42
-
43
38
  /**
44
39
  * Enum to support different types of snapshot formats.
45
40
  */
@@ -238,7 +233,7 @@ async function fetchLatestSnapshotCore(
238
233
  );
239
234
  }
240
235
 
241
- const [response, fetchTime] = await measure(async () => snapshotDownloader(
236
+ const [response, fetchTime] = await measureP(async () => snapshotDownloader(
242
237
  odspResolvedUrl,
243
238
  storageToken,
244
239
  snapshotOptions,
@@ -275,35 +270,43 @@ async function fetchLatestSnapshotCore(
275
270
  switch (contentTypeToRead) {
276
271
  case "application/json": {
277
272
  let text: string;
278
- [text, receiveContentTime] = await measure(async () => odspResponse.content.text());
273
+ [text, receiveContentTime] = await measureP(async () => odspResponse.content.text());
279
274
  propsToLog.bodySize = text.length;
280
275
  let content: IOdspSnapshot;
281
- [content, parseTime] = await measure( () => JSON.parse(text) as IOdspSnapshot);
276
+ [content, parseTime] = measure( () => JSON.parse(text) as IOdspSnapshot);
282
277
  validateBlobsAndTrees(content);
283
278
  const snapshotContents: ISnapshotContents =
284
279
  convertOdspSnapshotToSnapshotTreeAndBlobs(content);
285
- parsedSnapshotContents = { ...odspResponse, content: snapshotContents };
280
+ parsedSnapshotContents = {
281
+ ...odspResponse,
282
+ content: {
283
+ ...snapshotContents,
284
+ telemetryProps: {},
285
+ },
286
+ };
286
287
  break;
287
288
  }
288
289
  case "application/ms-fluid": {
289
290
  let content: ArrayBuffer;
290
291
  [content, receiveContentTime] =
291
- await measure(async () => odspResponse.content.arrayBuffer());
292
+ await measureP(async () => odspResponse.content.arrayBuffer());
292
293
  propsToLog.bodySize = content.byteLength;
293
294
  let snapshotContents: ISnapshotContentsWithProps;
294
- [snapshotContents, parseTime] = await measure(() => parseCompactSnapshotResponse(
295
+ [snapshotContents, parseTime] = measure(() => parseCompactSnapshotResponse(
295
296
  new Uint8Array(content),
296
297
  logger));
297
298
  if (snapshotContents.snapshotTree.trees === undefined ||
298
- snapshotContents.snapshotTree.blobs === undefined) {
299
- throw new NonRetryableError(
300
- "Returned odsp snapshot is malformed. No trees or blobs!",
301
- DriverErrorType.incorrectServerResponse,
302
- propsToLog,
303
- );
304
- }
305
- const slowTreeParseCodePaths = snapshotContents.slowTreeStructureCount ?? 0;
306
- const slowBlobParseCodePaths = snapshotContents.slowBlobStructureCount ?? 0;
299
+ snapshotContents.snapshotTree.blobs === undefined) {
300
+ throw new NonRetryableError(
301
+ "Returned odsp snapshot is malformed. No trees or blobs!",
302
+ DriverErrorType.incorrectServerResponse,
303
+ propsToLog,
304
+ );
305
+ }
306
+
307
+ const props = snapshotContents.telemetryProps;
308
+ const slowTreeParseCodePaths = props.slowTreeStructureCount ?? 0;
309
+ const slowBlobParseCodePaths = props.slowBlobStructureCount ?? 0;
307
310
  if (slowTreeParseCodePaths > 10 || slowBlobParseCodePaths > 10) {
308
311
  logger.sendErrorEvent({
309
312
  eventName: "SlowSnapshotParseCodePaths",
@@ -426,8 +429,6 @@ async function fetchLatestSnapshotCore(
426
429
  encodedBlobsSize,
427
430
  sequenceNumber,
428
431
  ops: snapshot.ops?.length ?? 0,
429
- slowTreeStructureCount: snapshot.slowTreeStructureCount ?? 0,
430
- slowBlobStructureCount: snapshot.slowBlobStructureCount ?? 0,
431
432
  userOps: snapshot.ops?.filter((op) => isRuntimeMessage(op)).length ?? 0,
432
433
  headers: Object.keys(response.requestHeaders).length !== 0 ? true : undefined,
433
434
  // Interval between the first fetch until the last byte of the last redirect.
@@ -468,6 +469,10 @@ async function fetchLatestSnapshotCore(
468
469
  sltelemetry: odspResponse.headers.get("x-fluid-sltelemetry"),
469
470
  // All other props
470
471
  ...propsToLog,
472
+ // Various perf counters and measures collected by binary parsing code:
473
+ // slowTreeStructureCount, slowBlobStructureCount, durationStructure, durationStrings,
474
+ // durationSnapshotTree, durationBlobs, etc.
475
+ ...parsedSnapshotContents.content.telemetryProps,
471
476
  });
472
477
  return snapshot;
473
478
  },
@@ -0,0 +1,46 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
+ import { IDocumentDeltaStorageService, IStream } from "@fluidframework/driver-definitions";
8
+ import { Queue, emptyMessageStream } from "@fluidframework/driver-utils";
9
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
10
+ import { validateMessages } from "../odspUtils";
11
+
12
+ /**
13
+ * Implementation of IDocumentDeltaStorageService that will return snapshot ops when fetching messages
14
+ */
15
+ export class LocalOdspDeltaStorageService implements IDocumentDeltaStorageService {
16
+ constructor(
17
+ private readonly logger: ITelemetryLogger,
18
+ private snapshotOps: ISequencedDocumentMessage[],
19
+ ) { }
20
+
21
+ public fetchMessages(
22
+ from: number,
23
+ to: number | undefined,
24
+ _abortSignal?: AbortSignal,
25
+ _cachedOnly?: boolean,
26
+ _fetchReason?: string,
27
+ ): IStream<ISequencedDocumentMessage[]> {
28
+ if (this.snapshotOps.length === 0) {
29
+ return emptyMessageStream;
30
+ }
31
+
32
+ const queue = new Queue<ISequencedDocumentMessage[]>();
33
+ const messages = this.snapshotOps.filter((op) =>
34
+ op.sequenceNumber >= from && (to === undefined || op.sequenceNumber < to));
35
+ validateMessages("cached", messages, from, this.logger);
36
+
37
+ if (messages.length === 0 || messages[0].sequenceNumber !== from) {
38
+ this.snapshotOps = [];
39
+ }
40
+ this.snapshotOps = this.snapshotOps.filter((op) => to !== undefined && op.sequenceNumber >= to);
41
+
42
+ queue.pushValue(messages);
43
+ queue.pushDone();
44
+ return queue;
45
+ }
46
+ }
@@ -10,9 +10,10 @@ import {
10
10
  IDocumentStorageService,
11
11
  IResolvedUrl,
12
12
  } from "@fluidframework/driver-definitions";
13
- import { UsageError, EmptyDocumentDeltaStorageService } from "@fluidframework/driver-utils";
13
+ import { UsageError } from "@fluidframework/driver-utils";
14
14
  import { IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions";
15
15
  import { IClient } from "@fluidframework/protocol-definitions";
16
+ import { LocalOdspDeltaStorageService } from "./localOdspDeltaStorageService";
16
17
  import { LocalOdspDocumentStorageService } from "./localOdspDocumentStorageManager";
17
18
 
18
19
  /**
@@ -20,6 +21,7 @@ import { LocalOdspDocumentStorageService } from "./localOdspDocumentStorageManag
20
21
  */
21
22
  export class LocalOdspDocumentService implements IDocumentService {
22
23
  public policies = { storageOnly: true };
24
+ private storageManager?: LocalOdspDocumentStorageService;
23
25
 
24
26
  constructor(
25
27
  private readonly odspResolvedUrl: IOdspResolvedUrl,
@@ -32,14 +34,18 @@ export class LocalOdspDocumentService implements IDocumentService {
32
34
  }
33
35
 
34
36
  public async connectToStorage(): Promise<IDocumentStorageService> {
35
- return new LocalOdspDocumentStorageService(
37
+ this.storageManager = new LocalOdspDocumentStorageService(
36
38
  this.logger,
37
39
  this.localSnapshot,
38
40
  );
41
+ return this.storageManager;
39
42
  }
40
43
 
41
44
  public async connectToDeltaStorage(): Promise<IDocumentDeltaStorageService> {
42
- return new EmptyDocumentDeltaStorageService();
45
+ return new LocalOdspDeltaStorageService(
46
+ this.logger,
47
+ this.storageManager?.ops ?? [],
48
+ );
43
49
  }
44
50
 
45
51
  public connectToDeltaStream(_client: IClient): never {
@@ -16,7 +16,7 @@ import {
16
16
  } from "@fluidframework/driver-utils";
17
17
  import { IDeltaStorageGetResponse, ISequencedDeltaOpMessage } from "./contracts";
18
18
  import { EpochTracker } from "./epochTracker";
19
- import { getWithRetryForTokenRefresh } from "./odspUtils";
19
+ import { getWithRetryForTokenRefresh, validateMessages } from "./odspUtils";
20
20
 
21
21
  /**
22
22
  * Provides access to the underlying delta storage on the server for sharepoint driver.
@@ -132,23 +132,6 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
132
132
  ) {
133
133
  }
134
134
 
135
- protected validateMessages(reason: string, messages: ISequencedDocumentMessage[], from: number) {
136
- if (messages.length !== 0) {
137
- const start = messages[0].sequenceNumber;
138
- const length = messages.length;
139
- const last = messages[length - 1].sequenceNumber;
140
- if (start !== from) {
141
- this.logger.sendErrorEvent({ eventName: "OpsFetchViolation", reason, from, start, last, length });
142
- messages.length = 0;
143
- }
144
- if (last + 1 !== from + length) {
145
- this.logger.sendErrorEvent({ eventName: "OpsFetchViolation", reason, from, start, last, length });
146
- // we can do better here by finding consecutive sub-block and return it
147
- messages.length = 0;
148
- }
149
- }
150
- }
151
-
152
135
  public fetchMessages(
153
136
  fromTotal: number,
154
137
  toTotal: number | undefined,
@@ -169,7 +152,7 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
169
152
  if (this.snapshotOps !== undefined && this.snapshotOps.length !== 0) {
170
153
  const messages = this.snapshotOps.filter((op) =>
171
154
  op.sequenceNumber >= from && op.sequenceNumber < to);
172
- this.validateMessages("cached", messages, from);
155
+ validateMessages("cached", messages, from, this.logger);
173
156
  if (messages.length > 0 && messages[0].sequenceNumber === from) {
174
157
  this.snapshotOps = this.snapshotOps.filter((op) => op.sequenceNumber >= to);
175
158
  opsFromSnapshot = messages.length;
@@ -185,7 +168,7 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
185
168
  // This saves a bit of processing time
186
169
  if (from < this.firstCacheMiss) {
187
170
  const messagesFromCache = await this.getCached(from, to);
188
- this.validateMessages("cached", messagesFromCache, from);
171
+ validateMessages("cached", messagesFromCache, from, this.logger);
189
172
  if (messagesFromCache.length !== 0) {
190
173
  opsFromCache += messagesFromCache.length;
191
174
  return {
@@ -201,7 +184,7 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
201
184
  }
202
185
 
203
186
  const ops = await this.getFromStorage(from, to, telemetryProps, fetchReason);
204
- this.validateMessages("storage", ops.messages, from);
187
+ validateMessages("storage", ops.messages, from, this.logger);
205
188
  opsFromStorage += ops.messages.length;
206
189
  this.opsReceived(ops.messages);
207
190
  return ops;
@@ -211,7 +194,7 @@ export class OdspDeltaStorageWithCache implements IDocumentDeltaStorageService {
211
194
  async (from: number, to: number, telemetryProps: ITelemetryProperties) => {
212
195
  const result = await requestCallback(from, to, telemetryProps);
213
196
  // Catch all case, just in case
214
- this.validateMessages("catch all", result.messages, from);
197
+ validateMessages("catch all", result.messages, from, this.logger);
215
198
  return result;
216
199
  },
217
200
  // Staging: starting with no concurrency, listening for feedback first.
@@ -454,8 +454,8 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
454
454
 
455
455
  protected disconnectHandler = (error: IFluidErrorBase & OdspError, clientId?: string) => {
456
456
  if (clientId === undefined || clientId === this.clientId) {
457
+ this.logger.sendTelemetryEvent({ eventName: "ServerDisconnect", clientId: this.hasDetails ? this.clientId : undefined }, error);
457
458
  this.disconnect(error);
458
- this.logger.sendTelemetryEvent({ eventName: "ServerDisconnect", clientId: this.clientId }, error);
459
459
  }
460
460
  };
461
461
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { performance } from "@fluidframework/common-utils";
7
+ import { assert, performance } from "@fluidframework/common-utils";
8
8
  import {
9
9
  ChildLogger,
10
10
  IFluidErrorBase,
@@ -251,6 +251,7 @@ export class OdspDocumentService implements IDocumentService {
251
251
  * @returns returns the document delta stream service for onedrive/sharepoint driver.
252
252
  */
253
253
  public async connectToDeltaStream(client: IClient): Promise<IDocumentDeltaConnection> {
254
+ assert(this.currentConnection === undefined, 0x4ad /* Should not be called when connection is already present! */);
254
255
  // Attempt to connect twice, in case we used expired token.
255
256
  return getWithRetryForTokenRefresh<IDocumentDeltaConnection>(async (options) => {
256
257
  // Presence of getWebsocketToken callback dictates whether callback is used for fetching
@@ -296,13 +297,17 @@ export class OdspDocumentService implements IDocumentService {
296
297
  });
297
298
  // On disconnect with 401/403 error code, we can just clear the joinSession cache as we will again
298
299
  // get the auth error on reconnecting and face latency.
299
- connection.on("disconnect", (error: any) => {
300
+ connection.once("disconnect", (error: any) => {
300
301
  // Clear the join session refresh timer so that it can be restarted on reconnection.
301
302
  this.clearJoinSessionTimer();
302
303
  if (typeof error === "object" && error !== null
303
304
  && error.errorType === DriverErrorType.authorizationError) {
304
305
  this.cache.sessionJoinCache.remove(this.joinSessionKey);
305
306
  }
307
+ // If we hit this assert, it means that "disconnect" event is emitted before the connection went through
308
+ // dispose flow which is not correct and could lead to a bunch of erros.
309
+ assert(connection.disposed, 0x4ae /* Connection should be disposed by now */);
310
+ this.currentConnection = undefined;
306
311
  });
307
312
  this.currentConnection = connection;
308
313
  return connection;
@@ -504,6 +509,8 @@ export class OdspDocumentService implements IDocumentService {
504
509
  }
505
510
  this._opsCache?.dispose();
506
511
  this.clearJoinSessionTimer();
512
+ this.currentConnection?.dispose();
513
+ this.currentConnection = undefined;
507
514
  }
508
515
 
509
516
  protected get opsCache() {
package/src/odspUtils.ts CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  InstrumentedStorageTokenFetcher,
35
35
  IOdspUrlParts,
36
36
  } from "@fluidframework/odsp-driver-definitions";
37
+ import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
37
38
  import { fetch } from "./fetch";
38
39
  import { pkgVersion as driverVersion } from "./packageVersion";
39
40
  import { IOdspSnapshot } from "./contracts";
@@ -370,3 +371,39 @@ export function buildOdspShareLinkReqParams(shareLinkType: ShareLinkTypes | ISha
370
371
  shareLinkRequestParams = role ? `${shareLinkRequestParams}&createLinkRole=${role}` : shareLinkRequestParams;
371
372
  return shareLinkRequestParams;
372
373
  }
374
+
375
+ export function measure<T>(callback: () => T): [T, number] {
376
+ const start = performance.now();
377
+ const result = callback();
378
+ const time = performance.now() - start;
379
+ return [result, time];
380
+ }
381
+
382
+ export async function measureP<T>(callback: () => Promise<T>): Promise<[T, number]> {
383
+ const start = performance.now();
384
+ const result = await callback();
385
+ const time = performance.now() - start;
386
+ return [result, time];
387
+ }
388
+
389
+ export function validateMessages(
390
+ reason: string,
391
+ messages: ISequencedDocumentMessage[],
392
+ from: number,
393
+ logger: ITelemetryLogger,
394
+ ) {
395
+ if (messages.length !== 0) {
396
+ const start = messages[0].sequenceNumber;
397
+ const length = messages.length;
398
+ const last = messages[length - 1].sequenceNumber;
399
+ if (start !== from) {
400
+ logger.sendErrorEvent({ eventName: "OpsFetchViolation", reason, from, start, last, length });
401
+ messages.length = 0;
402
+ }
403
+ if (last + 1 !== from + length) {
404
+ logger.sendErrorEvent({ eventName: "OpsFetchViolation", reason, from, start, last, length });
405
+ // we can do better here by finding consecutive sub-block and return it
406
+ messages.length = 0;
407
+ }
408
+ }
409
+ }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "2.0.0-internal.2.1.2";
9
+ export const pkgVersion = "2.0.0-internal.2.2.0";
@@ -14,6 +14,7 @@ import { NonRetryableError } from "@fluidframework/driver-utils";
14
14
  import { DriverErrorType } from "@fluidframework/driver-definitions";
15
15
  import { ReadBuffer } from "./ReadBufferUtils";
16
16
  import { pkgVersion as driverVersion } from "./packageVersion";
17
+ import { measure } from "./odspUtils";
17
18
 
18
19
  // eslint-disable-next-line max-len
19
20
  // https://onedrive.visualstudio.com/SharePoint%20Online/_git/SPO?path=/cobalt/Base/Property/BinaryEncodedPropertyReader.cs&version=GBmaster&_a=contents
@@ -388,6 +389,16 @@ export class NodeCore {
388
389
  * @param buffer - buffer to read from.
389
390
  */
390
391
  protected load(buffer: ReadBuffer, logger: ITelemetryLogger) {
392
+ const [stringsToResolve, durationStructure] = measure(() => this.loadStructure(buffer, logger));
393
+ const [, durationStrings] = measure(() => this.loadStrings(buffer, stringsToResolve, logger));
394
+ return { durationStructure, durationStrings };
395
+ }
396
+
397
+ /**
398
+ * Load and parse the buffer into a tree.
399
+ * @param buffer - buffer to read from.
400
+ */
401
+ protected loadStructure(buffer: ReadBuffer, logger: ITelemetryLogger) {
391
402
  const stack: NodeTypes[][] = [];
392
403
  const stringsToResolve: IStringElementInternal[] = [];
393
404
  const dictionary: IStringElement[] = [];
@@ -486,6 +497,10 @@ export class NodeCore {
486
497
  // This also ensures that stack.length === 0.
487
498
  assert(children === this.children, 0x3e7 /* Unpaired start/end list/set markers! */);
488
499
 
500
+ return stringsToResolve;
501
+ }
502
+
503
+ private loadStrings(buffer: ReadBuffer, stringsToResolve: IStringElementInternal[], logger: ITelemetryLogger) {
489
504
  /**
490
505
  * Process all the strings at once!
491
506
  */
@@ -532,11 +547,11 @@ export class NodeCore {
532
547
  * Provides loading and serialization capabilities.
533
548
  */
534
549
  export class TreeBuilder extends NodeCore {
535
- static load(buffer: ReadBuffer, logger: ITelemetryLogger): TreeBuilder {
550
+ static load(buffer: ReadBuffer, logger: ITelemetryLogger) {
536
551
  const builder = new TreeBuilder();
537
- builder.load(buffer, logger);
552
+ const telemetryProps = builder.load(buffer, logger);
538
553
  assert(buffer.eof, 0x233 /* "Unexpected data at the end of buffer" */);
539
- return builder;
554
+ return { builder, telemetryProps };
540
555
  }
541
556
  }
542
557