@fluidframework/container-loader 2.0.0-internal.6.1.1 → 2.0.0-internal.6.3.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/CHANGELOG.md +42 -0
- package/README.md +4 -3
- package/dist/audience.js +2 -2
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.js +2 -2
- package/dist/catchUpMonitor.js.map +1 -1
- package/dist/connectionManager.d.ts +1 -1
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +38 -37
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.js +24 -24
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +8 -1
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +117 -98
- package/dist/container.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +10 -9
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +2 -2
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/debugLogger.js +2 -2
- package/dist/debugLogger.js.map +1 -1
- package/dist/deltaManager.d.ts +3 -4
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +39 -38
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +1 -1
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +9 -8
- package/dist/deltaQueue.js.map +1 -1
- package/dist/disposal.d.ts +2 -2
- package/dist/disposal.d.ts.map +1 -1
- package/dist/disposal.js +1 -1
- package/dist/disposal.js.map +1 -1
- package/dist/error.d.ts +23 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +32 -0
- package/dist/error.js.map +1 -0
- package/dist/loader.d.ts +9 -2
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +12 -7
- package/dist/loader.js.map +1 -1
- package/dist/noopHeuristic.d.ts +2 -2
- package/dist/noopHeuristic.d.ts.map +1 -1
- package/dist/noopHeuristic.js +6 -5
- package/dist/noopHeuristic.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/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +4 -4
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +9 -8
- package/dist/utils.js.map +1 -1
- package/lib/audience.js +1 -1
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.js +1 -1
- package/lib/catchUpMonitor.js.map +1 -1
- package/lib/connectionManager.d.ts +1 -1
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +10 -9
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.js +1 -1
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +8 -1
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +73 -54
- package/lib/container.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +2 -1
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +2 -2
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/debugLogger.js +1 -1
- package/lib/debugLogger.js.map +1 -1
- package/lib/deltaManager.d.ts +3 -4
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +6 -5
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +1 -1
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -1
- package/lib/deltaQueue.js.map +1 -1
- package/lib/disposal.d.ts +2 -2
- package/lib/disposal.d.ts.map +1 -1
- package/lib/disposal.js +1 -1
- package/lib/disposal.js.map +1 -1
- package/lib/error.d.ts +23 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +28 -0
- package/lib/error.js.map +1 -0
- package/lib/loader.d.ts +9 -2
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +11 -6
- package/lib/loader.js.map +1 -1
- package/lib/noopHeuristic.d.ts +2 -2
- package/lib/noopHeuristic.d.ts.map +1 -1
- package/lib/noopHeuristic.js +2 -1
- package/lib/noopHeuristic.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/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +2 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +4 -3
- package/lib/utils.js.map +1 -1
- package/package.json +21 -27
- package/src/audience.ts +1 -1
- package/src/catchUpMonitor.ts +1 -1
- package/src/connectionManager.ts +20 -10
- package/src/connectionStateHandler.ts +1 -1
- package/src/container.ts +120 -79
- package/src/containerStorageAdapter.ts +2 -1
- package/src/contracts.ts +1 -2
- package/src/debugLogger.ts +1 -1
- package/src/deltaManager.ts +16 -13
- package/src/deltaQueue.ts +2 -1
- package/src/disposal.ts +2 -2
- package/src/error.ts +44 -0
- package/src/loader.ts +30 -5
- package/src/noopHeuristic.ts +3 -2
- package/src/packageVersion.ts +1 -1
- package/src/retriableDocumentStorageService.ts +2 -4
- package/src/utils.ts +4 -8
package/src/connectionManager.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IDisposable, ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
7
|
-
import { assert
|
|
7
|
+
import { assert } from "@fluidframework/core-utils";
|
|
8
|
+
import { performance, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
8
9
|
import {
|
|
9
10
|
ICriticalContainerError,
|
|
10
11
|
IDeltaQueue,
|
|
11
12
|
ReadOnlyInfo,
|
|
12
13
|
} from "@fluidframework/container-definitions";
|
|
13
|
-
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
14
14
|
import {
|
|
15
15
|
IAnyDriverError,
|
|
16
16
|
IDocumentService,
|
|
@@ -42,7 +42,13 @@ import {
|
|
|
42
42
|
ScopeType,
|
|
43
43
|
ISequencedDocumentSystemMessage,
|
|
44
44
|
} from "@fluidframework/protocol-definitions";
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
formatTick,
|
|
47
|
+
GenericError,
|
|
48
|
+
ITelemetryLoggerExt,
|
|
49
|
+
normalizeError,
|
|
50
|
+
UsageError,
|
|
51
|
+
} from "@fluidframework/telemetry-utils";
|
|
46
52
|
import {
|
|
47
53
|
ReconnectMode,
|
|
48
54
|
IConnectionManager,
|
|
@@ -356,7 +362,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
356
362
|
constructor(
|
|
357
363
|
private readonly serviceProvider: () => IDocumentService | undefined,
|
|
358
364
|
public readonly containerDirty: () => boolean,
|
|
359
|
-
private client: IClient,
|
|
365
|
+
private readonly client: IClient,
|
|
360
366
|
reconnectAllowed: boolean,
|
|
361
367
|
private readonly logger: ITelemetryLoggerExt,
|
|
362
368
|
private readonly props: IConnectionManagerFactoryArgs,
|
|
@@ -395,6 +401,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
395
401
|
error,
|
|
396
402
|
};
|
|
397
403
|
|
|
404
|
+
const oldReadonlyValue = this.readonly;
|
|
398
405
|
// This raises "disconnect" event if we have active connection.
|
|
399
406
|
this.disconnectFromDeltaStream(disconnectReason);
|
|
400
407
|
|
|
@@ -402,7 +409,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
402
409
|
// Notify everyone we are in read-only state.
|
|
403
410
|
// Useful for data stores in case we hit some critical error,
|
|
404
411
|
// to switch to a mode where user edits are not accepted
|
|
405
|
-
this.set_readonlyPermissions(true);
|
|
412
|
+
this.set_readonlyPermissions(true, oldReadonlyValue);
|
|
406
413
|
}
|
|
407
414
|
}
|
|
408
415
|
|
|
@@ -477,10 +484,12 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
477
484
|
}
|
|
478
485
|
}
|
|
479
486
|
|
|
480
|
-
private set_readonlyPermissions(
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
487
|
+
private set_readonlyPermissions(
|
|
488
|
+
newReadonlyValue: boolean,
|
|
489
|
+
oldReadonlyValue: boolean | undefined,
|
|
490
|
+
) {
|
|
491
|
+
this._readonlyPermissions = newReadonlyValue;
|
|
492
|
+
if (oldReadonlyValue !== this.readonly) {
|
|
484
493
|
this.props.readonlyChangeHandler(this.readonly);
|
|
485
494
|
}
|
|
486
495
|
}
|
|
@@ -770,6 +779,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
770
779
|
|
|
771
780
|
this.pendingConnection = undefined;
|
|
772
781
|
|
|
782
|
+
const oldReadonlyValue = this.readonly;
|
|
773
783
|
this.connection = connection;
|
|
774
784
|
|
|
775
785
|
// Does information in scopes & mode matches?
|
|
@@ -795,7 +805,7 @@ export class ConnectionManager implements IConnectionManager {
|
|
|
795
805
|
0x0e8 /* "readonly perf with write connection" */,
|
|
796
806
|
);
|
|
797
807
|
|
|
798
|
-
this.set_readonlyPermissions(readonly);
|
|
808
|
+
this.set_readonlyPermissions(readonly, oldReadonlyValue);
|
|
799
809
|
|
|
800
810
|
if (this._disposed) {
|
|
801
811
|
// Raise proper events, Log telemetry event and close connection.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryProperties, TelemetryEventCategory } from "@fluidframework/core-interfaces";
|
|
7
|
-
import { assert, Timer } from "@fluidframework/
|
|
7
|
+
import { assert, Timer } from "@fluidframework/core-utils";
|
|
8
8
|
import { IDeltaManager } from "@fluidframework/container-definitions";
|
|
9
9
|
import { ISequencedClient, IClient } from "@fluidframework/protocol-definitions";
|
|
10
10
|
import {
|
package/src/container.ts
CHANGED
|
@@ -7,20 +7,17 @@
|
|
|
7
7
|
import merge from "lodash/merge";
|
|
8
8
|
|
|
9
9
|
import { v4 as uuid } from "uuid";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
TypedEventEmitter,
|
|
13
|
-
assert,
|
|
14
|
-
performance,
|
|
15
|
-
unreachableCase,
|
|
16
|
-
} from "@fluidframework/common-utils";
|
|
10
|
+
import { assert, unreachableCase } from "@fluidframework/core-utils";
|
|
11
|
+
import { TypedEventEmitter, performance } from "@fluid-internal/client-utils";
|
|
17
12
|
import {
|
|
13
|
+
IEvent,
|
|
18
14
|
ITelemetryProperties,
|
|
19
15
|
TelemetryEventCategory,
|
|
20
16
|
IRequest,
|
|
21
17
|
IResponse,
|
|
22
18
|
IFluidRouter,
|
|
23
19
|
FluidObject,
|
|
20
|
+
LogLevel,
|
|
24
21
|
} from "@fluidframework/core-interfaces";
|
|
25
22
|
import {
|
|
26
23
|
AttachState,
|
|
@@ -43,7 +40,6 @@ import {
|
|
|
43
40
|
ReadOnlyInfo,
|
|
44
41
|
isFluidCodeDetails,
|
|
45
42
|
} from "@fluidframework/container-definitions";
|
|
46
|
-
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
47
43
|
import {
|
|
48
44
|
IDocumentService,
|
|
49
45
|
IDocumentServiceFactory,
|
|
@@ -92,6 +88,8 @@ import {
|
|
|
92
88
|
wrapError,
|
|
93
89
|
ITelemetryLoggerExt,
|
|
94
90
|
formatTick,
|
|
91
|
+
GenericError,
|
|
92
|
+
UsageError,
|
|
95
93
|
} from "@fluidframework/telemetry-utils";
|
|
96
94
|
import { Audience } from "./audience";
|
|
97
95
|
import { ContainerContext } from "./containerContext";
|
|
@@ -217,6 +215,10 @@ export interface IContainerCreateProps {
|
|
|
217
215
|
*/
|
|
218
216
|
readonly detachedBlobStorage?: IDetachedBlobStorage;
|
|
219
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Optional property for allowing the container to use a custom
|
|
220
|
+
* protocol implementation for handling the quorum and/or the audience.
|
|
221
|
+
*/
|
|
220
222
|
readonly protocolHandlerBuilder?: ProtocolHandlerBuilder;
|
|
221
223
|
}
|
|
222
224
|
|
|
@@ -424,7 +426,11 @@ export class Container
|
|
|
424
426
|
// Depending where error happens, we can be attempting to connect to web socket
|
|
425
427
|
// and continuously retrying (consider offline mode)
|
|
426
428
|
// Host has no container to close, so it's prudent to do it here
|
|
429
|
+
// Note: We could only dispose the container instead of just close but that would
|
|
430
|
+
// the telemetry where users sometimes search for ContainerClose event to look
|
|
431
|
+
// for load failures. So not removing this at this time.
|
|
427
432
|
container.close(err);
|
|
433
|
+
container.dispose(err);
|
|
428
434
|
onClosed(err);
|
|
429
435
|
},
|
|
430
436
|
);
|
|
@@ -747,6 +753,7 @@ export class Container
|
|
|
747
753
|
|
|
748
754
|
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
749
755
|
const pendingLocalState = loadProps?.pendingLocalState;
|
|
756
|
+
this._clientId = pendingLocalState?.clientId;
|
|
750
757
|
|
|
751
758
|
this._canReconnect = canReconnect ?? true;
|
|
752
759
|
this.clientDetailsOverride = clientDetailsOverride;
|
|
@@ -1094,38 +1101,50 @@ export class Container
|
|
|
1094
1101
|
}
|
|
1095
1102
|
|
|
1096
1103
|
private async getPendingLocalStateCore(props: { notifyImminentClosure: boolean }) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1104
|
+
return PerformanceEvent.timedExecAsync(
|
|
1105
|
+
this.mc.logger,
|
|
1106
|
+
{
|
|
1107
|
+
eventName: "getPendingLocalState",
|
|
1108
|
+
notifyImminentClosure: props.notifyImminentClosure,
|
|
1109
|
+
savedOpsSize: this.savedOps.length,
|
|
1110
|
+
clientId: this.clientId,
|
|
1111
|
+
},
|
|
1112
|
+
async () => {
|
|
1113
|
+
if (!this.offlineLoadEnabled) {
|
|
1114
|
+
throw new UsageError(
|
|
1115
|
+
"Can't get pending local state unless offline load is enabled",
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
if (this.closed || this._disposed) {
|
|
1119
|
+
throw new UsageError(
|
|
1120
|
+
"Pending state cannot be retried if the container is closed or disposed",
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
assert(
|
|
1124
|
+
this.attachState === AttachState.Attached,
|
|
1125
|
+
0x0d1 /* "Container should be attached before close" */,
|
|
1126
|
+
);
|
|
1127
|
+
assert(
|
|
1128
|
+
this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid",
|
|
1129
|
+
0x0d2 /* "resolved url should be valid Fluid url" */,
|
|
1130
|
+
);
|
|
1131
|
+
assert(!!this.baseSnapshot, 0x5d4 /* no base snapshot */);
|
|
1132
|
+
assert(!!this.baseSnapshotBlobs, 0x5d5 /* no snapshot blobs */);
|
|
1133
|
+
const pendingRuntimeState = await this.runtime.getPendingLocalState(props);
|
|
1134
|
+
const pendingState: IPendingContainerState = {
|
|
1135
|
+
pendingRuntimeState,
|
|
1136
|
+
baseSnapshot: this.baseSnapshot,
|
|
1137
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
1138
|
+
savedOps: this.savedOps,
|
|
1139
|
+
url: this.resolvedUrl.url,
|
|
1140
|
+
term: OnlyValidTermValue,
|
|
1141
|
+
// no need to save this if there is no pending runtime state
|
|
1142
|
+
clientId: pendingRuntimeState !== undefined ? this.clientId : undefined,
|
|
1143
|
+
};
|
|
1127
1144
|
|
|
1128
|
-
|
|
1145
|
+
return JSON.stringify(pendingState);
|
|
1146
|
+
},
|
|
1147
|
+
);
|
|
1129
1148
|
}
|
|
1130
1149
|
|
|
1131
1150
|
public get attachState(): AttachState {
|
|
@@ -1151,7 +1170,10 @@ export class Container
|
|
|
1151
1170
|
return JSON.stringify(combinedSummary);
|
|
1152
1171
|
}
|
|
1153
1172
|
|
|
1154
|
-
public async attach(
|
|
1173
|
+
public async attach(
|
|
1174
|
+
request: IRequest,
|
|
1175
|
+
attachProps?: { deltaConnection?: "none" | "delayed" },
|
|
1176
|
+
): Promise<void> {
|
|
1155
1177
|
await PerformanceEvent.timedExecAsync(
|
|
1156
1178
|
this.mc.logger,
|
|
1157
1179
|
{ eventName: "Attach" },
|
|
@@ -1277,10 +1299,13 @@ export class Container
|
|
|
1277
1299
|
this.emit("attached");
|
|
1278
1300
|
|
|
1279
1301
|
if (!this.closed) {
|
|
1280
|
-
this.
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1302
|
+
this.handleDeltaConnectionArg(
|
|
1303
|
+
{
|
|
1304
|
+
fetchOpsFromStorage: false,
|
|
1305
|
+
reason: { text: "createDetached" },
|
|
1306
|
+
},
|
|
1307
|
+
attachProps?.deltaConnection,
|
|
1308
|
+
);
|
|
1284
1309
|
}
|
|
1285
1310
|
} catch (error) {
|
|
1286
1311
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
@@ -1505,6 +1530,7 @@ export class Container
|
|
|
1505
1530
|
pendingLocalState: IPendingContainerState | undefined,
|
|
1506
1531
|
loadToSequenceNumber: number | undefined,
|
|
1507
1532
|
) {
|
|
1533
|
+
const timings: Record<string, number> = { phase1: performance.now() };
|
|
1508
1534
|
this.service = await this.serviceFactory.createDocumentService(
|
|
1509
1535
|
resolvedUrl,
|
|
1510
1536
|
this.subLogger,
|
|
@@ -1543,6 +1569,7 @@ export class Container
|
|
|
1543
1569
|
|
|
1544
1570
|
this._attachState = AttachState.Attached;
|
|
1545
1571
|
|
|
1572
|
+
timings.phase2 = performance.now();
|
|
1546
1573
|
// Fetch specified snapshot.
|
|
1547
1574
|
const { snapshot, versionId } =
|
|
1548
1575
|
pendingLocalState === undefined
|
|
@@ -1640,13 +1667,12 @@ export class Container
|
|
|
1640
1667
|
);
|
|
1641
1668
|
break;
|
|
1642
1669
|
case "sequenceNumber":
|
|
1643
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "sequenceNumber");
|
|
1644
|
-
break;
|
|
1645
1670
|
case "cached":
|
|
1646
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(dmAttributes, "cached");
|
|
1647
|
-
break;
|
|
1648
1671
|
case "all":
|
|
1649
|
-
opsBeforeReturnP = this.attachDeltaManagerOpHandler(
|
|
1672
|
+
opsBeforeReturnP = this.attachDeltaManagerOpHandler(
|
|
1673
|
+
dmAttributes,
|
|
1674
|
+
loadMode.opsBeforeReturn,
|
|
1675
|
+
);
|
|
1650
1676
|
break;
|
|
1651
1677
|
default:
|
|
1652
1678
|
unreachableCase(loadMode.opsBeforeReturn);
|
|
@@ -1656,11 +1682,13 @@ export class Container
|
|
|
1656
1682
|
// Initialize the protocol handler
|
|
1657
1683
|
await this.initializeProtocolStateFromSnapshot(attributes, this.storageAdapter, snapshot);
|
|
1658
1684
|
|
|
1685
|
+
timings.phase3 = performance.now();
|
|
1659
1686
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
1660
1687
|
await this.instantiateRuntime(
|
|
1661
1688
|
codeDetails,
|
|
1662
1689
|
snapshot,
|
|
1663
|
-
|
|
1690
|
+
// give runtime a dummy value so it knows we're loading from a stash blob
|
|
1691
|
+
pendingLocalState ? pendingLocalState?.pendingRuntimeState ?? {} : undefined,
|
|
1664
1692
|
);
|
|
1665
1693
|
|
|
1666
1694
|
// replay saved ops
|
|
@@ -1672,13 +1700,6 @@ export class Container
|
|
|
1672
1700
|
await this.runtime.notifyOpReplay?.(message);
|
|
1673
1701
|
}
|
|
1674
1702
|
pendingLocalState.savedOps = [];
|
|
1675
|
-
|
|
1676
|
-
// now set clientId to stashed clientId so live ops are correctly processed as local
|
|
1677
|
-
assert(
|
|
1678
|
-
this.clientId === undefined,
|
|
1679
|
-
0x5d6 /* Unexpected clientId when setting stashed clientId */,
|
|
1680
|
-
);
|
|
1681
|
-
this._clientId = pendingLocalState?.clientId;
|
|
1682
1703
|
}
|
|
1683
1704
|
|
|
1684
1705
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
@@ -1702,27 +1723,11 @@ export class Container
|
|
|
1702
1723
|
this._deltaManager.inbound.pause();
|
|
1703
1724
|
}
|
|
1704
1725
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
}
|
|
1711
|
-
// intentional fallthrough
|
|
1712
|
-
case "delayed":
|
|
1713
|
-
assert(
|
|
1714
|
-
this.inboundQueuePausedFromInit,
|
|
1715
|
-
0x346 /* inboundQueuePausedFromInit should be true */,
|
|
1716
|
-
);
|
|
1717
|
-
this.inboundQueuePausedFromInit = false;
|
|
1718
|
-
this._deltaManager.inbound.resume();
|
|
1719
|
-
this._deltaManager.inboundSignal.resume();
|
|
1720
|
-
break;
|
|
1721
|
-
case "none":
|
|
1722
|
-
break;
|
|
1723
|
-
default:
|
|
1724
|
-
unreachableCase(loadMode.deltaConnection);
|
|
1725
|
-
}
|
|
1726
|
+
this.handleDeltaConnectionArg(
|
|
1727
|
+
connectionArgs,
|
|
1728
|
+
loadMode.deltaConnection,
|
|
1729
|
+
pendingLocalState !== undefined,
|
|
1730
|
+
);
|
|
1726
1731
|
}
|
|
1727
1732
|
|
|
1728
1733
|
// If we have not yet reached `loadToSequenceNumber`, we will wait for ops to arrive until we reach it
|
|
@@ -1752,7 +1757,15 @@ export class Container
|
|
|
1752
1757
|
|
|
1753
1758
|
// Internal context is fully loaded at this point
|
|
1754
1759
|
this.setLoaded();
|
|
1755
|
-
|
|
1760
|
+
timings.end = performance.now();
|
|
1761
|
+
this.subLogger.sendTelemetryEvent(
|
|
1762
|
+
{
|
|
1763
|
+
eventName: "LoadStagesTimings",
|
|
1764
|
+
details: JSON.stringify(timings),
|
|
1765
|
+
},
|
|
1766
|
+
undefined,
|
|
1767
|
+
LogLevel.verbose,
|
|
1768
|
+
);
|
|
1756
1769
|
return {
|
|
1757
1770
|
sequenceNumber: attributes.sequenceNumber,
|
|
1758
1771
|
version: versionId,
|
|
@@ -2454,6 +2467,34 @@ export class Container
|
|
|
2454
2467
|
this.runtime.setConnectionState(state && !readonly, this.clientId);
|
|
2455
2468
|
}
|
|
2456
2469
|
}
|
|
2470
|
+
|
|
2471
|
+
private handleDeltaConnectionArg(
|
|
2472
|
+
connectionArgs: IConnectionArgs,
|
|
2473
|
+
deltaConnectionArg?: "none" | "delayed",
|
|
2474
|
+
canConnect: boolean = true,
|
|
2475
|
+
) {
|
|
2476
|
+
switch (deltaConnectionArg) {
|
|
2477
|
+
case undefined:
|
|
2478
|
+
if (canConnect) {
|
|
2479
|
+
// connect to delta stream now since we did not before
|
|
2480
|
+
this.connectToDeltaStream(connectionArgs);
|
|
2481
|
+
}
|
|
2482
|
+
// intentional fallthrough
|
|
2483
|
+
case "delayed":
|
|
2484
|
+
assert(
|
|
2485
|
+
this.inboundQueuePausedFromInit,
|
|
2486
|
+
0x346 /* inboundQueuePausedFromInit should be true */,
|
|
2487
|
+
);
|
|
2488
|
+
this.inboundQueuePausedFromInit = false;
|
|
2489
|
+
this._deltaManager.inbound.resume();
|
|
2490
|
+
this._deltaManager.inboundSignal.resume();
|
|
2491
|
+
break;
|
|
2492
|
+
case "none":
|
|
2493
|
+
break;
|
|
2494
|
+
default:
|
|
2495
|
+
unreachableCase(deltaConnectionArg);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2457
2498
|
}
|
|
2458
2499
|
|
|
2459
2500
|
/**
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils";
|
|
8
|
-
import {
|
|
8
|
+
import { bufferToString, stringToBuffer } from "@fluid-internal/client-utils";
|
|
9
|
+
import { assert } from "@fluidframework/core-utils";
|
|
9
10
|
import { ISnapshotTreeWithBlobContents } from "@fluidframework/container-definitions";
|
|
10
11
|
import {
|
|
11
12
|
FetchSource,
|
package/src/contracts.ts
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
6
|
+
import { IErrorBase, ITelemetryProperties } from "@fluidframework/core-interfaces";
|
|
7
7
|
import {
|
|
8
8
|
IConnectionDetails,
|
|
9
9
|
ICriticalContainerError,
|
|
10
10
|
IDeltaQueue,
|
|
11
|
-
IErrorBase,
|
|
12
11
|
IFluidCodeDetails,
|
|
13
12
|
isFluidPackage,
|
|
14
13
|
ReadOnlyInfo,
|
package/src/debugLogger.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
ITelemetryBaseLogger,
|
|
9
9
|
ITelemetryProperties,
|
|
10
10
|
} from "@fluidframework/core-interfaces";
|
|
11
|
-
import { performance } from "@
|
|
11
|
+
import { performance } from "@fluid-internal/client-utils";
|
|
12
12
|
import { debug as registerDebug, IDebugger } from "debug";
|
|
13
13
|
import {
|
|
14
14
|
ITelemetryLoggerExt,
|
package/src/deltaManager.ts
CHANGED
|
@@ -4,27 +4,35 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { v4 as uuid } from "uuid";
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
IThrottlingWarning,
|
|
9
|
+
IEventProvider,
|
|
10
|
+
ITelemetryProperties,
|
|
11
|
+
ITelemetryErrorEvent,
|
|
12
|
+
} from "@fluidframework/core-interfaces";
|
|
9
13
|
import {
|
|
10
14
|
ICriticalContainerError,
|
|
11
15
|
IDeltaManager,
|
|
12
16
|
IDeltaManagerEvents,
|
|
13
17
|
IDeltaQueue,
|
|
14
|
-
IThrottlingWarning,
|
|
15
18
|
} from "@fluidframework/container-definitions";
|
|
16
|
-
import {
|
|
19
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
20
|
+
import { assert } from "@fluidframework/core-utils";
|
|
17
21
|
import {
|
|
22
|
+
DataProcessingError,
|
|
23
|
+
extractSafePropertiesFromMessage,
|
|
18
24
|
normalizeError,
|
|
19
25
|
logIfFalse,
|
|
20
26
|
safeRaiseEvent,
|
|
21
27
|
isFluidError,
|
|
22
28
|
ITelemetryLoggerExt,
|
|
29
|
+
DataCorruptionError,
|
|
30
|
+
UsageError,
|
|
23
31
|
} from "@fluidframework/telemetry-utils";
|
|
24
32
|
import {
|
|
25
33
|
IDocumentDeltaStorageService,
|
|
26
34
|
IDocumentService,
|
|
27
|
-
|
|
35
|
+
DriverErrorTypes,
|
|
28
36
|
} from "@fluidframework/driver-definitions";
|
|
29
37
|
import {
|
|
30
38
|
IDocumentMessage,
|
|
@@ -34,13 +42,7 @@ import {
|
|
|
34
42
|
ConnectionMode,
|
|
35
43
|
} from "@fluidframework/protocol-definitions";
|
|
36
44
|
import { NonRetryableError, isRuntimeMessage, MessageType2 } from "@fluidframework/driver-utils";
|
|
37
|
-
|
|
38
|
-
ThrottlingWarning,
|
|
39
|
-
DataCorruptionError,
|
|
40
|
-
extractSafePropertiesFromMessage,
|
|
41
|
-
DataProcessingError,
|
|
42
|
-
UsageError,
|
|
43
|
-
} from "@fluidframework/container-utils";
|
|
45
|
+
|
|
44
46
|
import {
|
|
45
47
|
IConnectionDetailsInternal,
|
|
46
48
|
IConnectionManager,
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
} from "./contracts";
|
|
50
52
|
import { DeltaQueue } from "./deltaQueue";
|
|
51
53
|
import { OnlyValidTermValue } from "./protocol";
|
|
54
|
+
import { ThrottlingWarning } from "./error";
|
|
52
55
|
|
|
53
56
|
export interface IConnectionArgs {
|
|
54
57
|
mode?: ConnectionMode;
|
|
@@ -940,7 +943,7 @@ export class DeltaManager<TConnectionManager extends IConnectionManager>
|
|
|
940
943
|
// pre-0.58 error message: twoMessagesWithSameSeqNumAndDifferentPayload
|
|
941
944
|
"Found two messages with the same sequenceNumber but different payloads. Likely to be a " +
|
|
942
945
|
"service issue",
|
|
943
|
-
|
|
946
|
+
DriverErrorTypes.fileOverwrittenInStorage,
|
|
944
947
|
{
|
|
945
948
|
clientId: this.connectionManager.clientId,
|
|
946
949
|
sequenceNumber: message.sequenceNumber,
|
package/src/deltaQueue.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IDeltaQueue, IDeltaQueueEvents } from "@fluidframework/container-definitions";
|
|
7
|
-
import { assert
|
|
7
|
+
import { assert } from "@fluidframework/core-utils";
|
|
8
|
+
import { performance, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
8
9
|
import Deque from "double-ended-queue";
|
|
9
10
|
|
|
10
11
|
export interface IDeltaQueueWriter<T> {
|
package/src/disposal.ts
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { IDisposable } from "@fluidframework/
|
|
6
|
+
import { IDisposable } from "@fluidframework/core-interfaces";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Returns a wrapper around the provided function, which will only invoke the inner function if the provided
|
|
10
|
-
* {@link @fluidframework/
|
|
10
|
+
* {@link @fluidframework/core-interfaces#IDisposable | disposable} object has not yet been disposed.
|
|
11
11
|
*
|
|
12
12
|
* @throws Will throw an error if the item has already been disposed.
|
|
13
13
|
*/
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ITelemetryProperties, IThrottlingWarning } from "@fluidframework/core-interfaces";
|
|
7
|
+
import { ContainerErrorTypes } from "@fluidframework/container-definitions";
|
|
8
|
+
import {
|
|
9
|
+
IFluidErrorBase,
|
|
10
|
+
ITelemetryLoggerExt,
|
|
11
|
+
LoggingError,
|
|
12
|
+
wrapErrorAndLog,
|
|
13
|
+
} from "@fluidframework/telemetry-utils";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Warning emitted when requests to storage are being throttled.
|
|
17
|
+
*/
|
|
18
|
+
export class ThrottlingWarning extends LoggingError implements IThrottlingWarning, IFluidErrorBase {
|
|
19
|
+
/**
|
|
20
|
+
* {@inheritDoc @fluidframework/telemetry-utils#IFluidErrorBase.errorType}
|
|
21
|
+
*/
|
|
22
|
+
public readonly errorType = ContainerErrorTypes.throttlingError;
|
|
23
|
+
|
|
24
|
+
private constructor(
|
|
25
|
+
message: string,
|
|
26
|
+
readonly retryAfterSeconds: number,
|
|
27
|
+
props?: ITelemetryProperties,
|
|
28
|
+
) {
|
|
29
|
+
super(message, props);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Wrap the given error as a ThrottlingWarning
|
|
34
|
+
* Only preserves the error message, and applies the given retry after to the new warning object
|
|
35
|
+
*/
|
|
36
|
+
public static wrap(
|
|
37
|
+
error: unknown,
|
|
38
|
+
retryAfterSeconds: number,
|
|
39
|
+
logger: ITelemetryLoggerExt,
|
|
40
|
+
): IThrottlingWarning {
|
|
41
|
+
const newErrorFn = (errMsg: string) => new ThrottlingWarning(errMsg, retryAfterSeconds);
|
|
42
|
+
return wrapErrorAndLog(error, newErrorFn, logger);
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/loader.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
PerformanceEvent,
|
|
13
13
|
sessionStorageConfigProvider,
|
|
14
14
|
createChildMonitoringContext,
|
|
15
|
+
UsageError,
|
|
15
16
|
} from "@fluidframework/telemetry-utils";
|
|
16
17
|
import {
|
|
17
18
|
ITelemetryBaseLogger,
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
IResolvedUrl,
|
|
38
39
|
IUrlResolver,
|
|
39
40
|
} from "@fluidframework/driver-definitions";
|
|
40
|
-
import {
|
|
41
|
+
import { IClientDetails } from "@fluidframework/protocol-definitions";
|
|
41
42
|
import { Container, IPendingContainerState } from "./container";
|
|
42
43
|
import { IParsedUrl, parseUrl } from "./utils";
|
|
43
44
|
import { pkgVersion } from "./packageVersion";
|
|
@@ -357,8 +358,20 @@ export class Loader implements IHostLoader {
|
|
|
357
358
|
return this;
|
|
358
359
|
}
|
|
359
360
|
|
|
360
|
-
public async createDetachedContainer(
|
|
361
|
-
|
|
361
|
+
public async createDetachedContainer(
|
|
362
|
+
codeDetails: IFluidCodeDetails,
|
|
363
|
+
createDetachedProps?: {
|
|
364
|
+
canReconnect?: boolean;
|
|
365
|
+
clientDetailsOverride?: IClientDetails;
|
|
366
|
+
},
|
|
367
|
+
): Promise<IContainer> {
|
|
368
|
+
const container = await Container.createDetached(
|
|
369
|
+
{
|
|
370
|
+
...createDetachedProps,
|
|
371
|
+
...this.services,
|
|
372
|
+
},
|
|
373
|
+
codeDetails,
|
|
374
|
+
);
|
|
362
375
|
|
|
363
376
|
if (this.cachingEnabled) {
|
|
364
377
|
container.once("attached", () => {
|
|
@@ -373,8 +386,20 @@ export class Loader implements IHostLoader {
|
|
|
373
386
|
return container;
|
|
374
387
|
}
|
|
375
388
|
|
|
376
|
-
public async rehydrateDetachedContainerFromSnapshot(
|
|
377
|
-
|
|
389
|
+
public async rehydrateDetachedContainerFromSnapshot(
|
|
390
|
+
snapshot: string,
|
|
391
|
+
createDetachedProps?: {
|
|
392
|
+
canReconnect?: boolean;
|
|
393
|
+
clientDetailsOverride?: IClientDetails;
|
|
394
|
+
},
|
|
395
|
+
): Promise<IContainer> {
|
|
396
|
+
return Container.rehydrateDetachedFromSnapshot(
|
|
397
|
+
{
|
|
398
|
+
...createDetachedProps,
|
|
399
|
+
...this.services,
|
|
400
|
+
},
|
|
401
|
+
snapshot,
|
|
402
|
+
);
|
|
378
403
|
}
|
|
379
404
|
|
|
380
405
|
public async resolve(request: IRequest, pendingLocalState?: string): Promise<IContainer> {
|
package/src/noopHeuristic.ts
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
|
+
import { assert, Timer } from "@fluidframework/core-utils";
|
|
7
8
|
import { ISequencedDocumentMessage } from "@fluidframework/protocol-definitions";
|
|
8
9
|
import { isRuntimeMessage } from "@fluidframework/driver-utils";
|
|
9
|
-
import { IEvent } from "@fluidframework/
|
|
10
|
+
import { IEvent } from "@fluidframework/core-interfaces";
|
|
10
11
|
|
|
11
12
|
const defaultNoopTimeFrequency = 2000;
|
|
12
13
|
const defaultNoopCountFrequency = 50;
|