@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.
- package/.eslintrc.js +1 -1
- package/dist/compactSnapshotParser.d.ts +1 -2
- package/dist/compactSnapshotParser.d.ts.map +1 -1
- package/dist/compactSnapshotParser.js +6 -2
- package/dist/compactSnapshotParser.js.map +1 -1
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +13 -18
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/localOdspDriver/localOdspDeltaStorageService.d.ts +17 -0
- package/dist/localOdspDriver/localOdspDeltaStorageService.d.ts.map +1 -0
- package/dist/localOdspDriver/localOdspDeltaStorageService.js +35 -0
- package/dist/localOdspDriver/localOdspDeltaStorageService.js.map +1 -0
- package/dist/localOdspDriver/localOdspDocumentService.d.ts +1 -0
- package/dist/localOdspDriver/localOdspDocumentService.d.ts.map +1 -1
- package/dist/localOdspDriver/localOdspDocumentService.js +5 -2
- package/dist/localOdspDriver/localOdspDocumentService.js.map +1 -1
- package/dist/odspDeltaStorageService.d.ts +0 -1
- package/dist/odspDeltaStorageService.d.ts.map +1 -1
- package/dist/odspDeltaStorageService.js +4 -20
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +1 -1
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +9 -2
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspUtils.d.ts +4 -0
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +32 -1
- package/dist/odspUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/zipItDataRepresentationUtils.d.ts +17 -2
- package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
- package/dist/zipItDataRepresentationUtils.js +15 -2
- package/dist/zipItDataRepresentationUtils.js.map +1 -1
- package/lib/compactSnapshotParser.d.ts +1 -2
- package/lib/compactSnapshotParser.d.ts.map +1 -1
- package/lib/compactSnapshotParser.js +6 -2
- package/lib/compactSnapshotParser.js.map +1 -1
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +14 -19
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/localOdspDriver/localOdspDeltaStorageService.d.ts +17 -0
- package/lib/localOdspDriver/localOdspDeltaStorageService.d.ts.map +1 -0
- package/lib/localOdspDriver/localOdspDeltaStorageService.js +31 -0
- package/lib/localOdspDriver/localOdspDeltaStorageService.js.map +1 -0
- package/lib/localOdspDriver/localOdspDocumentService.d.ts +1 -0
- package/lib/localOdspDriver/localOdspDocumentService.d.ts.map +1 -1
- package/lib/localOdspDriver/localOdspDocumentService.js +6 -3
- package/lib/localOdspDriver/localOdspDocumentService.js.map +1 -1
- package/lib/odspDeltaStorageService.d.ts +0 -1
- package/lib/odspDeltaStorageService.d.ts.map +1 -1
- package/lib/odspDeltaStorageService.js +5 -21
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +1 -1
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +10 -3
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspUtils.d.ts +4 -0
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +28 -0
- package/lib/odspUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/zipItDataRepresentationUtils.d.ts +17 -2
- package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
- package/lib/zipItDataRepresentationUtils.js +15 -2
- package/lib/zipItDataRepresentationUtils.js.map +1 -1
- package/package.json +19 -14
- package/prettier.config.cjs +8 -0
- package/src/compactSnapshotParser.ts +16 -12
- package/src/fetchSnapshot.ts +29 -24
- package/src/localOdspDriver/localOdspDeltaStorageService.ts +46 -0
- package/src/localOdspDriver/localOdspDocumentService.ts +9 -3
- package/src/odspDeltaStorageService.ts +5 -22
- package/src/odspDocumentDeltaConnection.ts +1 -1
- package/src/odspDocumentService.ts +9 -2
- package/src/odspUtils.ts +37 -0
- package/src/packageVersion.ts +1 -1
- package/src/zipItDataRepresentationUtils.ts +18 -3
package/src/fetchSnapshot.ts
CHANGED
|
@@ -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
|
|
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
|
|
273
|
+
[text, receiveContentTime] = await measureP(async () => odspResponse.content.text());
|
|
279
274
|
propsToLog.bodySize = text.length;
|
|
280
275
|
let content: IOdspSnapshot;
|
|
281
|
-
[content, parseTime] =
|
|
276
|
+
[content, parseTime] = measure( () => JSON.parse(text) as IOdspSnapshot);
|
|
282
277
|
validateBlobsAndTrees(content);
|
|
283
278
|
const snapshotContents: ISnapshotContents =
|
|
284
279
|
convertOdspSnapshotToSnapshotTreeAndBlobs(content);
|
|
285
|
-
parsedSnapshotContents = {
|
|
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
|
|
292
|
+
await measureP(async () => odspResponse.content.arrayBuffer());
|
|
292
293
|
propsToLog.bodySize = content.byteLength;
|
|
293
294
|
let snapshotContents: ISnapshotContentsWithProps;
|
|
294
|
-
[snapshotContents, parseTime] =
|
|
295
|
+
[snapshotContents, parseTime] = measure(() => parseCompactSnapshotResponse(
|
|
295
296
|
new Uint8Array(content),
|
|
296
297
|
logger));
|
|
297
298
|
if (snapshotContents.snapshotTree.trees === undefined ||
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
+
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -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)
|
|
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
|
|