@fluidframework/container-loader 1.2.6 → 2.0.0-dev.1.3.0.96595
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/.mocharc.js +12 -0
- package/dist/audience.d.ts +2 -6
- package/dist/audience.d.ts.map +1 -1
- package/dist/audience.js +6 -11
- package/dist/audience.js.map +1 -1
- package/dist/catchUpMonitor.d.ts +29 -0
- package/dist/catchUpMonitor.d.ts.map +1 -0
- package/dist/catchUpMonitor.js +43 -0
- package/dist/catchUpMonitor.js.map +1 -0
- package/dist/collabWindowTracker.d.ts +1 -1
- package/dist/collabWindowTracker.d.ts.map +1 -1
- package/dist/collabWindowTracker.js +12 -4
- package/dist/collabWindowTracker.js.map +1 -1
- package/dist/connectionManager.d.ts +5 -5
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +43 -22
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionState.d.ts +0 -5
- package/dist/connectionState.d.ts.map +1 -1
- package/dist/connectionState.js +0 -5
- package/dist/connectionState.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +84 -22
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +172 -59
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +29 -17
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +181 -171
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +18 -7
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +18 -8
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +11 -25
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +51 -17
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +5 -5
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +4 -1
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +39 -12
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaManagerProxy.d.ts +4 -1
- package/dist/deltaManagerProxy.d.ts.map +1 -1
- package/dist/deltaQueue.d.ts +9 -2
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +31 -26
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +8 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +4 -3
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/protocol.d.ts +27 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +79 -0
- package/dist/protocol.js.map +1 -0
- package/dist/protocolTreeDocumentStorageService.d.ts +1 -1
- package/dist/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +2 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js +2 -2
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/lib/audience.d.ts +2 -6
- package/lib/audience.d.ts.map +1 -1
- package/lib/audience.js +6 -11
- package/lib/audience.js.map +1 -1
- package/lib/catchUpMonitor.d.ts +29 -0
- package/lib/catchUpMonitor.d.ts.map +1 -0
- package/lib/catchUpMonitor.js +39 -0
- package/lib/catchUpMonitor.js.map +1 -0
- package/lib/collabWindowTracker.d.ts +1 -1
- package/lib/collabWindowTracker.d.ts.map +1 -1
- package/lib/collabWindowTracker.js +13 -5
- package/lib/collabWindowTracker.js.map +1 -1
- package/lib/connectionManager.d.ts +5 -5
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +44 -25
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionState.d.ts +0 -5
- package/lib/connectionState.d.ts.map +1 -1
- package/lib/connectionState.js +0 -5
- package/lib/connectionState.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +84 -22
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +171 -59
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +29 -17
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +184 -174
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +18 -7
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +19 -9
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +11 -25
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +51 -16
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +5 -5
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +4 -1
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +41 -14
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaManagerProxy.d.ts +4 -1
- package/lib/deltaManagerProxy.d.ts.map +1 -1
- package/lib/deltaQueue.d.ts +9 -2
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +32 -27
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +8 -1
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +4 -3
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/protocol.d.ts +27 -0
- package/lib/protocol.d.ts.map +1 -0
- package/lib/protocol.js +75 -0
- package/lib/protocol.js.map +1 -0
- package/lib/protocolTreeDocumentStorageService.d.ts +1 -1
- package/lib/protocolTreeDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +2 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js +2 -2
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/package.json +27 -19
- package/src/audience.ts +8 -14
- package/src/catchUpMonitor.ts +59 -0
- package/src/collabWindowTracker.ts +15 -6
- package/src/connectionManager.ts +56 -33
- package/src/connectionState.ts +0 -6
- package/src/connectionStateHandler.ts +235 -70
- package/src/container.ts +241 -218
- package/src/containerContext.ts +22 -8
- package/src/containerStorageAdapter.ts +71 -16
- package/src/contracts.ts +7 -7
- package/src/deltaManager.ts +48 -15
- package/src/deltaQueue.ts +34 -28
- package/src/index.ts +4 -0
- package/src/loader.ts +14 -3
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +120 -0
- package/src/retriableDocumentStorageService.ts +8 -2
package/dist/container.js
CHANGED
|
@@ -15,7 +15,6 @@ const common_utils_1 = require("@fluidframework/common-utils");
|
|
|
15
15
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
16
16
|
const container_utils_1 = require("@fluidframework/container-utils");
|
|
17
17
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
18
|
-
const protocol_base_1 = require("@fluidframework/protocol-base");
|
|
19
18
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
20
19
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
21
20
|
const audience_1 = require("./audience");
|
|
@@ -25,27 +24,31 @@ const deltaManager_1 = require("./deltaManager");
|
|
|
25
24
|
const deltaManagerProxy_1 = require("./deltaManagerProxy");
|
|
26
25
|
const loader_1 = require("./loader");
|
|
27
26
|
const packageVersion_1 = require("./packageVersion");
|
|
28
|
-
const connectionStateHandler_1 = require("./connectionStateHandler");
|
|
29
|
-
const retriableDocumentStorageService_1 = require("./retriableDocumentStorageService");
|
|
30
|
-
const protocolTreeDocumentStorageService_1 = require("./protocolTreeDocumentStorageService");
|
|
31
27
|
const containerStorageAdapter_1 = require("./containerStorageAdapter");
|
|
28
|
+
const connectionStateHandler_1 = require("./connectionStateHandler");
|
|
32
29
|
const utils_1 = require("./utils");
|
|
33
30
|
const quorum_1 = require("./quorum");
|
|
34
31
|
const collabWindowTracker_1 = require("./collabWindowTracker");
|
|
35
32
|
const connectionManager_1 = require("./connectionManager");
|
|
36
33
|
const connectionState_1 = require("./connectionState");
|
|
34
|
+
const protocol_1 = require("./protocol");
|
|
37
35
|
const detachedContainerRefSeqNumber = 0;
|
|
38
36
|
const dirtyContainerEvent = "dirty";
|
|
39
37
|
const savedContainerEvent = "saved";
|
|
40
38
|
/**
|
|
41
|
-
* Waits until container connects to delta storage and gets up-to-date
|
|
39
|
+
* Waits until container connects to delta storage and gets up-to-date.
|
|
40
|
+
*
|
|
42
41
|
* Useful when resolving URIs and hitting 404, due to container being loaded from (stale) snapshot and not being
|
|
43
42
|
* up to date. Host may chose to wait in such case and retry resolving URI.
|
|
43
|
+
*
|
|
44
44
|
* Warning: Will wait infinitely for connection to establish if there is no connection.
|
|
45
45
|
* May result in deadlock if Container.disconnect() is called and never followed by a call to Container.connect().
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
46
|
+
*
|
|
47
|
+
* @returns `true`: container is up to date, it processed all the ops that were know at the time of first connection.
|
|
48
|
+
*
|
|
49
|
+
* `false`: storage does not provide indication of how far the client is. Container processed all the ops known to it,
|
|
50
|
+
* but it maybe still behind.
|
|
51
|
+
*
|
|
49
52
|
* @throws an error beginning with `"Container closed"` if the container is closed before it catches up.
|
|
50
53
|
*/
|
|
51
54
|
async function waitContainerToCatchUp(container) {
|
|
@@ -63,6 +66,10 @@ async function waitContainerToCatchUp(container) {
|
|
|
63
66
|
: new container_utils_1.GenericError(baseMessage));
|
|
64
67
|
};
|
|
65
68
|
container.on("closed", closedCallback);
|
|
69
|
+
// Depending on config, transition to "connected" state may include the guarantee
|
|
70
|
+
// that all known ops have been processed. If so, we may introduce additional wait here.
|
|
71
|
+
// Waiting for "connected" state in either case gets us at least to our own Join op
|
|
72
|
+
// which is a reasonable approximation of "caught up"
|
|
66
73
|
const waitForOps = () => {
|
|
67
74
|
(0, common_utils_1.assert)(container.connectionState === connectionState_1.ConnectionState.CatchingUp
|
|
68
75
|
|| container.connectionState === connectionState_1.ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
|
|
@@ -105,9 +112,22 @@ exports.waitContainerToCatchUp = waitContainerToCatchUp;
|
|
|
105
112
|
const getCodeProposal =
|
|
106
113
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
107
114
|
(quorum) => { var _a; return (_a = quorum.get("code")) !== null && _a !== void 0 ? _a : quorum.get("code2"); };
|
|
115
|
+
/**
|
|
116
|
+
* Helper function to report to telemetry cases where operation takes longer than expected (1s)
|
|
117
|
+
* @param logger - logger to use
|
|
118
|
+
* @param eventName - event name
|
|
119
|
+
* @param action - functor to call and measure
|
|
120
|
+
*/
|
|
121
|
+
async function ReportIfTooLong(logger, eventName, action) {
|
|
122
|
+
const event = telemetry_utils_1.PerformanceEvent.start(logger, { eventName });
|
|
123
|
+
const props = await action();
|
|
124
|
+
if (event.duration > 1000) {
|
|
125
|
+
event.end(props);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
108
128
|
const summarizerClientType = "summarizer";
|
|
109
129
|
class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
110
|
-
constructor(loader, config) {
|
|
130
|
+
constructor(loader, config, protocolHandlerBuilder) {
|
|
111
131
|
var _a, _b;
|
|
112
132
|
super((name, error) => {
|
|
113
133
|
this.mc.logger.sendErrorEvent({
|
|
@@ -116,6 +136,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
116
136
|
}, error);
|
|
117
137
|
});
|
|
118
138
|
this.loader = loader;
|
|
139
|
+
this.protocolHandlerBuilder = protocolHandlerBuilder;
|
|
119
140
|
// Tells if container can reconnect on losing fist connection
|
|
120
141
|
// If false, container gets closed on loss of connection.
|
|
121
142
|
this._canReconnect = true;
|
|
@@ -129,7 +150,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
129
150
|
this.attachStarted = false;
|
|
130
151
|
this._dirtyContainer = false;
|
|
131
152
|
this.setAutoReconnectTime = common_utils_1.performance.now();
|
|
132
|
-
this._audience = new audience_1.Audience();
|
|
133
153
|
this.clientDetailsOverride = config.clientDetailsOverride;
|
|
134
154
|
this._resolvedUrl = config.resolvedUrl;
|
|
135
155
|
if (config.canReconnect !== undefined) {
|
|
@@ -149,6 +169,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
149
169
|
containerAttachState: () => this._attachState,
|
|
150
170
|
containerLifecycleState: () => this._lifecycleState,
|
|
151
171
|
containerConnectionState: () => connectionState_1.ConnectionState[this.connectionState],
|
|
172
|
+
serializedContainer: config.serializedContainerState !== undefined,
|
|
152
173
|
},
|
|
153
174
|
// we need to be judicious with our logging here to avoid generating too much data
|
|
154
175
|
// all data logged here should be broadly applicable, and not specific to a
|
|
@@ -161,6 +182,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
161
182
|
containerLoadedFromVersionId: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.id; },
|
|
162
183
|
containerLoadedFromVersionDate: () => { var _a; return (_a = this.loadedFromVersion) === null || _a === void 0 ? void 0 : _a.date; },
|
|
163
184
|
// message information to associate errors with the specific execution state
|
|
185
|
+
// dmLastMsqSeqNumber: if present, same as dmLastProcessedSeqNumber
|
|
164
186
|
dmLastMsqSeqNumber: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.sequenceNumber; },
|
|
165
187
|
dmLastMsqSeqTimestamp: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.timestamp; },
|
|
166
188
|
dmLastMsqSeqClientId: () => { var _a, _b; return (_b = (_a = this.deltaManager) === null || _a === void 0 ? void 0 : _a.lastMessage) === null || _b === void 0 ? void 0 : _b.clientId; },
|
|
@@ -171,9 +193,19 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
171
193
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.subLogger, "Container"));
|
|
172
194
|
const summarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree")) !== null && _a !== void 0 ? _a : this.loader.services.options.summarizeProtocolTree;
|
|
173
195
|
this.options = Object.assign(Object.assign({}, this.loader.services.options), { summarizeProtocolTree });
|
|
174
|
-
this.
|
|
175
|
-
|
|
176
|
-
|
|
196
|
+
this._deltaManager = this.createDeltaManager();
|
|
197
|
+
this._clientId = (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId;
|
|
198
|
+
this.connectionStateHandler = (0, connectionStateHandler_1.createConnectionStateHandler)({
|
|
199
|
+
logger: this.mc.logger,
|
|
200
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
201
|
+
if (value === connectionState_1.ConnectionState.Connected) {
|
|
202
|
+
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
203
|
+
}
|
|
204
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
205
|
+
if (this._lifecycleState === "loaded") {
|
|
206
|
+
this.propagateConnectionState(false /* initial transition */);
|
|
207
|
+
}
|
|
208
|
+
},
|
|
177
209
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
178
210
|
maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
|
|
179
211
|
logConnectionIssue: (eventName, details) => {
|
|
@@ -181,29 +213,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
181
213
|
// its own join op. Attempt recovery option.
|
|
182
214
|
this._deltaManager.logConnectionIssue(Object.assign({ eventName, duration: common_utils_1.performance.now() - this.connectionTransitionTimes[connectionState_1.ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
|
|
183
215
|
},
|
|
184
|
-
|
|
185
|
-
// Fire events only if container is fully loaded and not closed
|
|
186
|
-
if (this._lifecycleState === "loaded") {
|
|
187
|
-
this.propagateConnectionState();
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
}, this.mc.logger, (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId);
|
|
216
|
+
}, this.deltaManager, this._clientId);
|
|
191
217
|
this.on(savedContainerEvent, () => {
|
|
192
218
|
this.connectionStateHandler.containerSaved();
|
|
193
219
|
});
|
|
194
|
-
this.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (this.loader.services.detachedBlobStorage !== undefined) {
|
|
198
|
-
return new containerStorageAdapter_1.BlobOnlyStorage(this.loader.services.detachedBlobStorage, this.mc.logger);
|
|
199
|
-
}
|
|
200
|
-
this.mc.logger.sendErrorEvent({
|
|
201
|
-
eventName: "NoRealStorageInDetachedContainer",
|
|
202
|
-
});
|
|
203
|
-
throw new Error("Real storage calls not allowed in Unattached container");
|
|
204
|
-
}
|
|
205
|
-
return this.storageService;
|
|
206
|
-
});
|
|
220
|
+
this.storageService = new containerStorageAdapter_1.ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, this.options.summarizeProtocolTree === true
|
|
221
|
+
? () => this.captureProtocolSummary()
|
|
222
|
+
: undefined);
|
|
207
223
|
const isDomAvailable = typeof document === "object" &&
|
|
208
224
|
document !== null &&
|
|
209
225
|
typeof document.addEventListener === "function" &&
|
|
@@ -260,13 +276,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
260
276
|
/**
|
|
261
277
|
* Load an existing container.
|
|
262
278
|
*/
|
|
263
|
-
static async load(loader, loadOptions, pendingLocalState) {
|
|
279
|
+
static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
|
|
264
280
|
const container = new Container(loader, {
|
|
265
281
|
clientDetailsOverride: loadOptions.clientDetailsOverride,
|
|
266
282
|
resolvedUrl: loadOptions.resolvedUrl,
|
|
267
283
|
canReconnect: loadOptions.canReconnect,
|
|
268
284
|
serializedContainerState: pendingLocalState,
|
|
269
|
-
});
|
|
285
|
+
}, protocolHandlerBuilder);
|
|
270
286
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
271
287
|
var _a, _b;
|
|
272
288
|
const version = loadOptions.version;
|
|
@@ -300,8 +316,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
300
316
|
/**
|
|
301
317
|
* Create a new container in a detached state.
|
|
302
318
|
*/
|
|
303
|
-
static async createDetached(loader, codeDetails) {
|
|
304
|
-
const container = new Container(loader, {});
|
|
319
|
+
static async createDetached(loader, codeDetails, protocolHandlerBuilder) {
|
|
320
|
+
const container = new Container(loader, {}, protocolHandlerBuilder);
|
|
305
321
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
306
322
|
await container.createDetached(codeDetails);
|
|
307
323
|
return container;
|
|
@@ -311,8 +327,8 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
311
327
|
* Create a new container in a detached state that is initialized with a
|
|
312
328
|
* snapshot from a previous detached container.
|
|
313
329
|
*/
|
|
314
|
-
static async rehydrateDetachedFromSnapshot(loader, snapshot) {
|
|
315
|
-
const container = new Container(loader, {});
|
|
330
|
+
static async rehydrateDetachedFromSnapshot(loader, snapshot, protocolHandlerBuilder) {
|
|
331
|
+
const container = new Container(loader, {}, protocolHandlerBuilder);
|
|
316
332
|
return telemetry_utils_1.PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
317
333
|
const deserializedSummary = JSON.parse(snapshot);
|
|
318
334
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
@@ -324,7 +340,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
324
340
|
// Only transition states if currently loading
|
|
325
341
|
if (this._lifecycleState === "loading") {
|
|
326
342
|
// Propagate current connection state through the system.
|
|
327
|
-
this.propagateConnectionState();
|
|
343
|
+
this.propagateConnectionState(true /* initial transition */);
|
|
328
344
|
this._lifecycleState = "loaded";
|
|
329
345
|
}
|
|
330
346
|
}
|
|
@@ -332,13 +348,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
332
348
|
return (this._lifecycleState === "closing" || this._lifecycleState === "closed");
|
|
333
349
|
}
|
|
334
350
|
get storage() {
|
|
335
|
-
return this.
|
|
336
|
-
}
|
|
337
|
-
get storageService() {
|
|
338
|
-
if (this._storageService === undefined) {
|
|
339
|
-
throw new Error("Attempted to access storageService before it was defined");
|
|
340
|
-
}
|
|
341
|
-
return this._storageService;
|
|
351
|
+
return this.storageService;
|
|
342
352
|
}
|
|
343
353
|
get context() {
|
|
344
354
|
if (this._context === undefined) {
|
|
@@ -379,7 +389,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
379
389
|
return this.connectionStateHandler.connectionState;
|
|
380
390
|
}
|
|
381
391
|
get connected() {
|
|
382
|
-
return this.connectionStateHandler.
|
|
392
|
+
return this.connectionStateHandler.connectionState === connectionState_1.ConnectionState.Connected;
|
|
383
393
|
}
|
|
384
394
|
/**
|
|
385
395
|
* Service configuration details. If running in offline mode will be undefined otherwise will contain service
|
|
@@ -393,7 +403,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
393
403
|
* Set once this.connected is true, otherwise undefined
|
|
394
404
|
*/
|
|
395
405
|
get clientId() {
|
|
396
|
-
return this.
|
|
406
|
+
return this._clientId;
|
|
397
407
|
}
|
|
398
408
|
/**
|
|
399
409
|
* The server provided claims of the client.
|
|
@@ -425,12 +435,12 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
425
435
|
* Retrieves the audience associated with the document
|
|
426
436
|
*/
|
|
427
437
|
get audience() {
|
|
428
|
-
return this.
|
|
438
|
+
return this.protocolHandler.audience;
|
|
429
439
|
}
|
|
430
440
|
/**
|
|
431
441
|
* Returns true if container is dirty.
|
|
432
442
|
* Which means data loss if container is closed at that same moment
|
|
433
|
-
* Most likely that happens when there is no network connection to
|
|
443
|
+
* Most likely that happens when there is no network connection to Relay Service
|
|
434
444
|
*/
|
|
435
445
|
get isDirty() {
|
|
436
446
|
return this._dirtyContainer;
|
|
@@ -455,7 +465,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
455
465
|
(0, common_utils_1.assert)(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
|
|
456
466
|
}
|
|
457
467
|
closeCore(error) {
|
|
458
|
-
var _a, _b, _c
|
|
468
|
+
var _a, _b, _c;
|
|
459
469
|
(0, common_utils_1.assert)(!this.closed, 0x315 /* re-entrancy */);
|
|
460
470
|
try {
|
|
461
471
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -470,11 +480,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
470
480
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
471
481
|
this.connectionStateHandler.dispose();
|
|
472
482
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
473
|
-
|
|
483
|
+
this.storageService.dispose();
|
|
474
484
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
475
485
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
476
486
|
// Driver need to ensure all caches are cleared on critical errors
|
|
477
|
-
(
|
|
487
|
+
(_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
|
|
478
488
|
}
|
|
479
489
|
catch (exception) {
|
|
480
490
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
@@ -496,7 +506,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
496
506
|
(0, common_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
497
507
|
(0, common_utils_1.assert)(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
498
508
|
(0, common_utils_1.assert)(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
499
|
-
(0, common_utils_1.assert)(this._protocolHandler.attributes.term !== undefined,
|
|
509
|
+
(0, common_utils_1.assert)(this._protocolHandler.attributes.term !== undefined, 0x37e /* Must have a valid protocol handler instance */);
|
|
500
510
|
const pendingState = {
|
|
501
511
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
502
512
|
url: this.resolvedUrl.url,
|
|
@@ -504,6 +514,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
504
514
|
term: this._protocolHandler.attributes.term,
|
|
505
515
|
clientId: this.clientId,
|
|
506
516
|
};
|
|
517
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "CloseAndGetPendingLocalState" });
|
|
507
518
|
this.close();
|
|
508
519
|
return JSON.stringify(pendingState);
|
|
509
520
|
}
|
|
@@ -560,7 +571,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
560
571
|
const resolvedUrl = this.service.resolvedUrl;
|
|
561
572
|
(0, driver_utils_1.ensureFluidResolvedUrl)(resolvedUrl);
|
|
562
573
|
this._resolvedUrl = resolvedUrl;
|
|
563
|
-
await this.
|
|
574
|
+
await this.storageService.connectToService(this.service);
|
|
564
575
|
if (hasAttachmentBlobs) {
|
|
565
576
|
// upload blobs to storage
|
|
566
577
|
(0, common_utils_1.assert)(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
@@ -590,8 +601,6 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
590
601
|
}
|
|
591
602
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
592
603
|
this.emit("attached");
|
|
593
|
-
// Propagate current connection state through the system.
|
|
594
|
-
this.propagateConnectionState();
|
|
595
604
|
if (!this.closed) {
|
|
596
605
|
this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
|
|
597
606
|
}
|
|
@@ -730,9 +739,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
730
739
|
/**
|
|
731
740
|
* Load container.
|
|
732
741
|
*
|
|
733
|
-
* @param specifiedVersion -
|
|
734
|
-
* - undefined - fetch latest snapshot
|
|
735
|
-
* - otherwise, version sha to load snapshot
|
|
742
|
+
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
736
743
|
*/
|
|
737
744
|
async load(specifiedVersion, loadMode, pendingLocalState) {
|
|
738
745
|
if (this._resolvedUrl === undefined) {
|
|
@@ -755,11 +762,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
755
762
|
this.connectToDeltaStream(connectionArgs);
|
|
756
763
|
}
|
|
757
764
|
if (!pendingLocalState) {
|
|
758
|
-
await this.
|
|
765
|
+
await this.storageService.connectToService(this.service);
|
|
759
766
|
}
|
|
760
767
|
else {
|
|
761
768
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
762
|
-
this.
|
|
769
|
+
this.storageService.connectToService(this.service).catch((error) => this.close(error));
|
|
763
770
|
}
|
|
764
771
|
this._attachState = container_definitions_1.AttachState.Attached;
|
|
765
772
|
// Fetch specified snapshot.
|
|
@@ -794,21 +801,26 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
794
801
|
}
|
|
795
802
|
// ...load in the existing quorum
|
|
796
803
|
// Initialize the protocol handler
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
804
|
+
if (pendingLocalState === undefined) {
|
|
805
|
+
await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot);
|
|
806
|
+
}
|
|
807
|
+
else {
|
|
808
|
+
this.initializeProtocolState(attributes, {
|
|
809
|
+
members: pendingLocalState.protocol.members,
|
|
810
|
+
proposals: pendingLocalState.protocol.proposals,
|
|
811
|
+
values: pendingLocalState.protocol.values,
|
|
812
|
+
});
|
|
813
|
+
}
|
|
800
814
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
801
815
|
await this.instantiateContext(true, // existing
|
|
802
816
|
codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
|
|
803
|
-
// Internal context is fully loaded at this point
|
|
804
|
-
this.setLoaded();
|
|
805
817
|
// We might have hit some failure that did not manifest itself in exception in this flow,
|
|
806
818
|
// do not start op processing in such case - static version of Container.load() will handle it correctly.
|
|
807
819
|
if (!this.closed) {
|
|
808
820
|
if (opsBeforeReturnP !== undefined) {
|
|
809
821
|
this._deltaManager.inbound.resume();
|
|
810
|
-
await opsBeforeReturnP;
|
|
811
|
-
await this._deltaManager.inbound.waitTillProcessingDone();
|
|
822
|
+
await ReportIfTooLong(this.mc.logger, "WaitOps", async () => { await opsBeforeReturnP; return {}; });
|
|
823
|
+
await ReportIfTooLong(this.mc.logger, "WaitOpProcessing", async () => this._deltaManager.inbound.waitTillProcessingDone());
|
|
812
824
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
813
825
|
this._deltaManager.inbound.pause();
|
|
814
826
|
}
|
|
@@ -834,9 +846,13 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
834
846
|
if (this.closed) {
|
|
835
847
|
throw new Error("Container was closed while load()");
|
|
836
848
|
}
|
|
849
|
+
// Internal context is fully loaded at this point
|
|
850
|
+
this.setLoaded();
|
|
837
851
|
return {
|
|
838
852
|
sequenceNumber: attributes.sequenceNumber,
|
|
839
853
|
version: versionId,
|
|
854
|
+
dmLastProcessedSeqNumber: this._deltaManager.lastSequenceNumber,
|
|
855
|
+
dmLastKnownSeqNumber: this._deltaManager.lastKnownSeqNumber,
|
|
840
856
|
};
|
|
841
857
|
}
|
|
842
858
|
async createDetached(source) {
|
|
@@ -848,9 +864,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
848
864
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
849
865
|
// Need to just seed the source data in the code quorum. Quorum itself is empty
|
|
850
866
|
const qValues = (0, quorum_1.initQuorumValuesFromCodeDetails)(source);
|
|
851
|
-
this.
|
|
852
|
-
|
|
853
|
-
|
|
867
|
+
this.initializeProtocolState(attributes, {
|
|
868
|
+
members: [],
|
|
869
|
+
proposals: [],
|
|
870
|
+
values: qValues,
|
|
871
|
+
});
|
|
854
872
|
// The load context - given we seeded the quorum - will be great
|
|
855
873
|
await this.instantiateContextDetached(false);
|
|
856
874
|
this.setLoaded();
|
|
@@ -861,38 +879,22 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
861
879
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
862
880
|
}
|
|
863
881
|
const snapshotTree = (0, utils_1.getSnapshotTreeFromSerializedContainer)(detachedContainerSnapshot);
|
|
864
|
-
this.
|
|
865
|
-
const attributes = await this.getDocumentAttributes(this.
|
|
882
|
+
this.storageService.loadSnapshotForRehydratingContainer(snapshotTree);
|
|
883
|
+
const attributes = await this.getDocumentAttributes(this.storageService, snapshotTree);
|
|
866
884
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
867
885
|
// Initialize the protocol handler
|
|
868
886
|
const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshotTree);
|
|
869
|
-
const qValues = await (0, driver_utils_1.readAndParse)(this.
|
|
887
|
+
const qValues = await (0, driver_utils_1.readAndParse)(this.storageService, baseTree.blobs.quorumValues);
|
|
870
888
|
const codeDetails = (0, quorum_1.getCodeDetailsFromQuorumValues)(qValues);
|
|
871
|
-
this.
|
|
872
|
-
|
|
873
|
-
[],
|
|
874
|
-
codeDetails !== undefined ? (0, quorum_1.initQuorumValuesFromCodeDetails)(codeDetails) : []
|
|
889
|
+
this.initializeProtocolState(attributes, {
|
|
890
|
+
members: [],
|
|
891
|
+
proposals: [],
|
|
892
|
+
values: codeDetails !== undefined ? (0, quorum_1.initQuorumValuesFromCodeDetails)(codeDetails) : [],
|
|
893
|
+
});
|
|
875
894
|
await this.instantiateContextDetached(true, // existing
|
|
876
895
|
snapshotTree);
|
|
877
896
|
this.setLoaded();
|
|
878
897
|
}
|
|
879
|
-
async connectStorageService() {
|
|
880
|
-
var _a, _b;
|
|
881
|
-
if (this._storageService !== undefined) {
|
|
882
|
-
return;
|
|
883
|
-
}
|
|
884
|
-
(0, common_utils_1.assert)(this.service !== undefined, 0x1ef /* "services must be defined" */);
|
|
885
|
-
const storageService = await this.service.connectToStorage();
|
|
886
|
-
this._storageService =
|
|
887
|
-
new retriableDocumentStorageService_1.RetriableDocumentStorageService(storageService, this.mc.logger);
|
|
888
|
-
if (this.options.summarizeProtocolTree === true) {
|
|
889
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
|
|
890
|
-
this._storageService =
|
|
891
|
-
new protocolTreeDocumentStorageService_1.ProtocolTreeStorageService(this._storageService, () => this.captureProtocolSummary());
|
|
892
|
-
}
|
|
893
|
-
// ensure we did not lose that policy in the process of wrapping
|
|
894
|
-
(0, common_utils_1.assert)(((_a = storageService.policies) === null || _a === void 0 ? void 0 : _a.minBlobSize) === ((_b = this.storageService.policies) === null || _b === void 0 ? void 0 : _b.minBlobSize), 0x0e0 /* "lost minBlobSize policy" */);
|
|
895
|
-
}
|
|
896
898
|
async getDocumentAttributes(storage, tree) {
|
|
897
899
|
if (tree === undefined) {
|
|
898
900
|
return {
|
|
@@ -913,22 +915,25 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
913
915
|
return attributes;
|
|
914
916
|
}
|
|
915
917
|
async initializeProtocolStateFromSnapshot(attributes, storage, snapshot) {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
918
|
+
const quorumSnapshot = {
|
|
919
|
+
members: [],
|
|
920
|
+
proposals: [],
|
|
921
|
+
values: [],
|
|
922
|
+
};
|
|
919
923
|
if (snapshot !== undefined) {
|
|
920
924
|
const baseTree = (0, utils_1.getProtocolSnapshotTree)(snapshot);
|
|
921
|
-
[members, proposals, values] = await Promise.all([
|
|
925
|
+
[quorumSnapshot.members, quorumSnapshot.proposals, quorumSnapshot.values] = await Promise.all([
|
|
922
926
|
(0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumMembers),
|
|
923
927
|
(0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumProposals),
|
|
924
928
|
(0, driver_utils_1.readAndParse)(storage, baseTree.blobs.quorumValues),
|
|
925
929
|
]);
|
|
926
930
|
}
|
|
927
|
-
|
|
928
|
-
return protocolHandler;
|
|
931
|
+
this.initializeProtocolState(attributes, quorumSnapshot);
|
|
929
932
|
}
|
|
930
|
-
|
|
931
|
-
|
|
933
|
+
initializeProtocolState(attributes, quorumSnapshot) {
|
|
934
|
+
var _a;
|
|
935
|
+
const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new protocol_1.ProtocolHandler(...args, new audience_1.Audience()));
|
|
936
|
+
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(protocol_definitions_1.MessageType.Propose, JSON.stringify({ key, value })));
|
|
932
937
|
const protocolLogger = telemetry_utils_1.ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
933
938
|
protocol.quorum.on("error", (error) => {
|
|
934
939
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -953,7 +958,11 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
953
958
|
});
|
|
954
959
|
}
|
|
955
960
|
});
|
|
956
|
-
|
|
961
|
+
// we need to make sure this member get set in a synchronous context,
|
|
962
|
+
// or other things can happen after the object that will be set is created, but not yet set
|
|
963
|
+
// this was breaking this._initialClients handling
|
|
964
|
+
//
|
|
965
|
+
this._protocolHandler = protocol;
|
|
957
966
|
}
|
|
958
967
|
captureProtocolSummary() {
|
|
959
968
|
const quorumSnapshot = this.protocolHandler.snapshot();
|
|
@@ -1022,13 +1031,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1022
1031
|
deltaManager.inbound.pause();
|
|
1023
1032
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1024
1033
|
deltaManager.inboundSignal.pause();
|
|
1025
|
-
deltaManager.on("connect", (details,
|
|
1026
|
-
var _a;
|
|
1027
|
-
// Back-compat for new client and old server.
|
|
1028
|
-
this._audience.clear();
|
|
1029
|
-
for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
|
|
1030
|
-
this._audience.addMember(priorClient.clientId, priorClient.client);
|
|
1031
|
-
}
|
|
1034
|
+
deltaManager.on("connect", (details, _opsBehind) => {
|
|
1032
1035
|
this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
|
|
1033
1036
|
});
|
|
1034
1037
|
deltaManager.on("disconnect", (reason) => {
|
|
@@ -1046,6 +1049,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1046
1049
|
this.emit("warning", warn);
|
|
1047
1050
|
});
|
|
1048
1051
|
deltaManager.on("readonly", (readonly) => {
|
|
1052
|
+
this.setContextConnectedState(this.connectionState === connectionState_1.ConnectionState.Connected, readonly);
|
|
1049
1053
|
this.emit("readonly", readonly);
|
|
1050
1054
|
});
|
|
1051
1055
|
deltaManager.on("closed", (error) => {
|
|
@@ -1088,12 +1092,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1088
1092
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
1089
1093
|
}
|
|
1090
1094
|
}
|
|
1091
|
-
|
|
1092
|
-
connectionInitiationReason = "InitialConnect";
|
|
1093
|
-
}
|
|
1094
|
-
else {
|
|
1095
|
-
connectionInitiationReason = "AutoReconnect";
|
|
1096
|
-
}
|
|
1095
|
+
connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
|
|
1097
1096
|
}
|
|
1098
1097
|
this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${connectionState_1.ConnectionState[value]}`, from: connectionState_1.ConnectionState[oldState], duration,
|
|
1099
1098
|
durationFromDisconnected,
|
|
@@ -1104,49 +1103,64 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1104
1103
|
this.firstConnection = false;
|
|
1105
1104
|
}
|
|
1106
1105
|
}
|
|
1107
|
-
propagateConnectionState() {
|
|
1106
|
+
propagateConnectionState(initialTransition) {
|
|
1108
1107
|
var _a;
|
|
1108
|
+
// When container loaded, we want to propagate initial connection state.
|
|
1109
|
+
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
1110
|
+
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
1111
|
+
if (!initialTransition &&
|
|
1112
|
+
this.connectionState !== connectionState_1.ConnectionState.Connected &&
|
|
1113
|
+
this.connectionState !== connectionState_1.ConnectionState.Disconnected) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
const state = this.connectionState === connectionState_1.ConnectionState.Connected;
|
|
1109
1117
|
const logOpsOnReconnect = this.connectionState === connectionState_1.ConnectionState.Connected &&
|
|
1110
1118
|
!this.firstConnection &&
|
|
1111
1119
|
this.connectionMode === "write";
|
|
1112
1120
|
if (logOpsOnReconnect) {
|
|
1113
1121
|
this.messageCountAfterDisconnection = 0;
|
|
1114
1122
|
}
|
|
1115
|
-
const state = this.connectionState === connectionState_1.ConnectionState.Connected;
|
|
1116
1123
|
// Both protocol and context should not be undefined if we got so far.
|
|
1117
|
-
|
|
1118
|
-
this.context.setConnectionState(state, this.clientId);
|
|
1119
|
-
}
|
|
1124
|
+
this.setContextConnectedState(state, (_a = this._deltaManager.connectionManager.readOnlyInfo.readonly) !== null && _a !== void 0 ? _a : false);
|
|
1120
1125
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1121
1126
|
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, state, this.clientId);
|
|
1122
1127
|
if (logOpsOnReconnect) {
|
|
1123
1128
|
this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
|
|
1124
1129
|
}
|
|
1125
1130
|
}
|
|
1131
|
+
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1126
1132
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
1127
|
-
|
|
1128
|
-
switch (outboundMessageType) {
|
|
1133
|
+
switch (type) {
|
|
1129
1134
|
case protocol_definitions_1.MessageType.Operation:
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
// github #6451: this is only needed for staging so the server
|
|
1134
|
-
// know when the protocol tree is included
|
|
1135
|
-
// this can be removed once all clients send
|
|
1136
|
-
// protocol tree by default
|
|
1137
|
-
const summary = contents;
|
|
1138
|
-
if (summary.details === undefined) {
|
|
1139
|
-
summary.details = {};
|
|
1140
|
-
}
|
|
1141
|
-
summary.details.includesProtocolTree =
|
|
1142
|
-
this.options.summarizeProtocolTree === true;
|
|
1143
|
-
break;
|
|
1144
|
-
}
|
|
1135
|
+
return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
|
|
1136
|
+
case protocol_definitions_1.MessageType.Summarize:
|
|
1137
|
+
return this.submitSummaryMessage(contents);
|
|
1145
1138
|
default:
|
|
1146
1139
|
this.close(new container_utils_1.GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type }));
|
|
1147
1140
|
return -1;
|
|
1148
1141
|
}
|
|
1149
|
-
|
|
1142
|
+
}
|
|
1143
|
+
/** @returns clientSequenceNumber of last message in a batch */
|
|
1144
|
+
submitBatch(batch) {
|
|
1145
|
+
let clientSequenceNumber = -1;
|
|
1146
|
+
for (const message of batch) {
|
|
1147
|
+
clientSequenceNumber = this.submitMessage(protocol_definitions_1.MessageType.Operation, message.contents, true, // batch
|
|
1148
|
+
message.metadata);
|
|
1149
|
+
}
|
|
1150
|
+
this._deltaManager.flush();
|
|
1151
|
+
return clientSequenceNumber;
|
|
1152
|
+
}
|
|
1153
|
+
submitSummaryMessage(summary) {
|
|
1154
|
+
// github #6451: this is only needed for staging so the server
|
|
1155
|
+
// know when the protocol tree is included
|
|
1156
|
+
// this can be removed once all clients send
|
|
1157
|
+
// protocol tree by default
|
|
1158
|
+
if (summary.details === undefined) {
|
|
1159
|
+
summary.details = {};
|
|
1160
|
+
}
|
|
1161
|
+
summary.details.includesProtocolTree =
|
|
1162
|
+
this.options.summarizeProtocolTree === true;
|
|
1163
|
+
return this.submitMessage(protocol_definitions_1.MessageType.Summarize, JSON.stringify(summary), false /* batch */);
|
|
1150
1164
|
}
|
|
1151
1165
|
submitMessage(type, contents, batch, metadata) {
|
|
1152
1166
|
var _a;
|
|
@@ -1161,22 +1175,9 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1161
1175
|
processRemoteMessage(message) {
|
|
1162
1176
|
const local = this.clientId === message.clientId;
|
|
1163
1177
|
// Allow the protocol handler to process the message
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
}
|
|
1168
|
-
catch (error) {
|
|
1169
|
-
this.close((0, telemetry_utils_1.wrapError)(error, (errorMessage) => new container_utils_1.DataCorruptionError(errorMessage, (0, container_utils_1.extractSafePropertiesFromMessage)(message))));
|
|
1170
|
-
}
|
|
1171
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1172
|
-
if ((0, driver_utils_1.isUnpackedRuntimeMessage)(message) && !(0, driver_utils_1.isRuntimeMessage)(message)) {
|
|
1173
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "UnpackedRuntimeMessage", type: message.type });
|
|
1174
|
-
}
|
|
1175
|
-
// Forward non system messages to the loaded runtime for processing
|
|
1176
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1177
|
-
if ((0, driver_utils_1.isRuntimeMessage)(message) || (0, driver_utils_1.isUnpackedRuntimeMessage)(message)) {
|
|
1178
|
-
this.context.process(message, local, undefined);
|
|
1179
|
-
}
|
|
1178
|
+
const result = this.protocolHandler.processMessage(message, local);
|
|
1179
|
+
// Forward messages to the loaded runtime for processing
|
|
1180
|
+
this.context.process(message, local, undefined);
|
|
1180
1181
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1181
1182
|
if (this.activeConnection()) {
|
|
1182
1183
|
if (this.collabWindowTracker === undefined) {
|
|
@@ -1185,15 +1186,14 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1185
1186
|
// clients.
|
|
1186
1187
|
// All existing will continue to use settings they got earlier.
|
|
1187
1188
|
(0, common_utils_1.assert)(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1188
|
-
this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type
|
|
1189
|
+
this.collabWindowTracker = new collabWindowTracker_1.CollabWindowTracker((type) => {
|
|
1189
1190
|
(0, common_utils_1.assert)(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
|
|
1190
|
-
this.submitMessage(type
|
|
1191
|
+
this.submitMessage(type);
|
|
1191
1192
|
}, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
|
|
1192
1193
|
}
|
|
1193
1194
|
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1194
1195
|
}
|
|
1195
1196
|
this.emit("op", message);
|
|
1196
|
-
return result;
|
|
1197
1197
|
}
|
|
1198
1198
|
submitSignal(message) {
|
|
1199
1199
|
this._deltaManager.submitSignal(JSON.stringify(message));
|
|
@@ -1201,15 +1201,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1201
1201
|
processSignal(message) {
|
|
1202
1202
|
// No clientId indicates a system signal message.
|
|
1203
1203
|
if (message.clientId === null) {
|
|
1204
|
-
|
|
1205
|
-
if (innerContent.type === protocol_definitions_1.MessageType.ClientJoin) {
|
|
1206
|
-
const newClient = innerContent.content;
|
|
1207
|
-
this._audience.addMember(newClient.clientId, newClient.client);
|
|
1208
|
-
}
|
|
1209
|
-
else if (innerContent.type === protocol_definitions_1.MessageType.ClientLeave) {
|
|
1210
|
-
const leftClientId = innerContent.content;
|
|
1211
|
-
this._audience.removeMember(leftClientId);
|
|
1212
|
-
}
|
|
1204
|
+
this.protocolHandler.processSignal(message);
|
|
1213
1205
|
}
|
|
1214
1206
|
else {
|
|
1215
1207
|
const local = this.clientId === message.clientId;
|
|
@@ -1248,7 +1240,7 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1248
1240
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1249
1241
|
// are set. Global requests will still go directly to the loader
|
|
1250
1242
|
const loader = new loader_1.RelativeLoader(this, this.loader);
|
|
1251
|
-
this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (message) => this.submitSignal(message), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
|
|
1243
|
+
this._context = await containerContext_1.ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new deltaManagerProxy_1.DeltaManagerProxy(this._deltaManager), new quorum_1.QuorumProxy(this.protocolHandler.quorum), loader, (type, contents, batch, metadata) => this.submitContainerMessage(type, contents, batch, metadata), (summaryOp) => this.submitSummaryMessage(summaryOp), (batch) => this.submitBatch(batch), (message) => this.submitSignal(message), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
|
|
1252
1244
|
this.emit("contextChanged", codeDetails);
|
|
1253
1245
|
}
|
|
1254
1246
|
updateDirtyContainerState(dirty) {
|
|
@@ -1261,6 +1253,24 @@ class Container extends telemetry_utils_1.EventEmitterWithErrorHandling {
|
|
|
1261
1253
|
logContainerError(warning) {
|
|
1262
1254
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
|
|
1263
1255
|
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Set the connected state of the ContainerContext
|
|
1258
|
+
* This controls the "connected" state of the ContainerRuntime as well
|
|
1259
|
+
* @param state - Is the container currently connected?
|
|
1260
|
+
* @param readonly - Is the container in readonly mode?
|
|
1261
|
+
*/
|
|
1262
|
+
setContextConnectedState(state, readonly) {
|
|
1263
|
+
var _a;
|
|
1264
|
+
if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1265
|
+
/**
|
|
1266
|
+
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1267
|
+
* ops getting through to the DeltaManager.
|
|
1268
|
+
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1269
|
+
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1270
|
+
*/
|
|
1271
|
+
this.context.setConnectionState(state && !readonly, this.clientId);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1264
1274
|
}
|
|
1265
1275
|
exports.Container = Container;
|
|
1266
1276
|
Container.version = "^0.1.0";
|