@fluidframework/container-loader 1.3.0 → 2.0.0-dev.1.4.5.105745
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 +8 -21
- package/.mocharc.js +12 -0
- package/dist/audience.d.ts +2 -2
- package/dist/audience.d.ts.map +1 -1
- 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 +13 -18
- 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 +30 -17
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +173 -165
- 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 +33 -6
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.js +3 -3
- 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 +22 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +53 -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 -2
- package/lib/audience.d.ts.map +1 -1
- 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 +14 -21
- 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 +30 -17
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +176 -168
- 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 +35 -8
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.js +3 -3
- 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 +22 -0
- package/lib/protocol.d.ts.map +1 -0
- package/lib/protocol.js +49 -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 +26 -20
- package/src/audience.ts +2 -2
- package/src/catchUpMonitor.ts +59 -0
- package/src/collabWindowTracker.ts +15 -6
- package/src/connectionManager.ts +23 -27
- package/src/connectionState.ts +0 -6
- package/src/connectionStateHandler.ts +235 -70
- package/src/container.ts +223 -209
- package/src/containerContext.ts +22 -8
- package/src/containerStorageAdapter.ts +71 -16
- package/src/contracts.ts +7 -7
- package/src/deltaManager.ts +42 -11
- package/src/deltaQueue.ts +3 -3
- package/src/index.ts +4 -0
- package/src/loader.ts +14 -3
- package/src/packageVersion.ts +1 -1
- package/src/protocol.ts +97 -0
- package/src/retriableDocumentStorageService.ts +8 -2
package/lib/container.js
CHANGED
|
@@ -7,9 +7,8 @@ import merge from "lodash/merge";
|
|
|
7
7
|
import { v4 as uuid } from "uuid";
|
|
8
8
|
import { assert, performance, unreachableCase } from "@fluidframework/common-utils";
|
|
9
9
|
import { AttachState, isFluidCodeDetails, } from "@fluidframework/container-definitions";
|
|
10
|
-
import {
|
|
11
|
-
import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl,
|
|
12
|
-
import { ProtocolOpHandlerWithClientValidation, } from "@fluidframework/protocol-base";
|
|
10
|
+
import { GenericError, UsageError, } from "@fluidframework/container-utils";
|
|
11
|
+
import { readAndParse, OnlineStatus, isOnline, ensureFluidResolvedUrl, combineAppAndProtocolSummary, runWithRetry, isFluidResolvedUrl, } from "@fluidframework/driver-utils";
|
|
13
12
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
14
13
|
import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, disconnectedEventName, normalizeError, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
15
14
|
import { Audience } from "./audience";
|
|
@@ -19,27 +18,31 @@ import { DeltaManager } from "./deltaManager";
|
|
|
19
18
|
import { DeltaManagerProxy } from "./deltaManagerProxy";
|
|
20
19
|
import { RelativeLoader } from "./loader";
|
|
21
20
|
import { pkgVersion } from "./packageVersion";
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { ProtocolTreeStorageService } from "./protocolTreeDocumentStorageService";
|
|
25
|
-
import { BlobOnlyStorage, ContainerStorageAdapter } from "./containerStorageAdapter";
|
|
21
|
+
import { ContainerStorageAdapter } from "./containerStorageAdapter";
|
|
22
|
+
import { createConnectionStateHandler, } from "./connectionStateHandler";
|
|
26
23
|
import { getProtocolSnapshotTree, getSnapshotTreeFromSerializedContainer } from "./utils";
|
|
27
24
|
import { initQuorumValuesFromCodeDetails, getCodeDetailsFromQuorumValues, QuorumProxy } from "./quorum";
|
|
28
25
|
import { CollabWindowTracker } from "./collabWindowTracker";
|
|
29
26
|
import { ConnectionManager } from "./connectionManager";
|
|
30
27
|
import { ConnectionState } from "./connectionState";
|
|
28
|
+
import { ProtocolHandler, } from "./protocol";
|
|
31
29
|
const detachedContainerRefSeqNumber = 0;
|
|
32
30
|
const dirtyContainerEvent = "dirty";
|
|
33
31
|
const savedContainerEvent = "saved";
|
|
34
32
|
/**
|
|
35
|
-
* Waits until container connects to delta storage and gets up-to-date
|
|
33
|
+
* Waits until container connects to delta storage and gets up-to-date.
|
|
34
|
+
*
|
|
36
35
|
* Useful when resolving URIs and hitting 404, due to container being loaded from (stale) snapshot and not being
|
|
37
36
|
* up to date. Host may chose to wait in such case and retry resolving URI.
|
|
37
|
+
*
|
|
38
38
|
* Warning: Will wait infinitely for connection to establish if there is no connection.
|
|
39
39
|
* May result in deadlock if Container.disconnect() is called and never followed by a call to Container.connect().
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
40
|
+
*
|
|
41
|
+
* @returns `true`: container is up to date, it processed all the ops that were know at the time of first connection.
|
|
42
|
+
*
|
|
43
|
+
* `false`: storage does not provide indication of how far the client is. Container processed all the ops known to it,
|
|
44
|
+
* but it maybe still behind.
|
|
45
|
+
*
|
|
43
46
|
* @throws an error beginning with `"Container closed"` if the container is closed before it catches up.
|
|
44
47
|
*/
|
|
45
48
|
export async function waitContainerToCatchUp(container) {
|
|
@@ -57,6 +60,10 @@ export async function waitContainerToCatchUp(container) {
|
|
|
57
60
|
: new GenericError(baseMessage));
|
|
58
61
|
};
|
|
59
62
|
container.on("closed", closedCallback);
|
|
63
|
+
// Depending on config, transition to "connected" state may include the guarantee
|
|
64
|
+
// that all known ops have been processed. If so, we may introduce additional wait here.
|
|
65
|
+
// Waiting for "connected" state in either case gets us at least to our own Join op
|
|
66
|
+
// which is a reasonable approximation of "caught up"
|
|
60
67
|
const waitForOps = () => {
|
|
61
68
|
assert(container.connectionState === ConnectionState.CatchingUp
|
|
62
69
|
|| container.connectionState === ConnectionState.Connected, 0x0cd /* "Container disconnected while waiting for ops!" */);
|
|
@@ -113,7 +120,7 @@ async function ReportIfTooLong(logger, eventName, action) {
|
|
|
113
120
|
}
|
|
114
121
|
const summarizerClientType = "summarizer";
|
|
115
122
|
export class Container extends EventEmitterWithErrorHandling {
|
|
116
|
-
constructor(loader, config) {
|
|
123
|
+
constructor(loader, config, protocolHandlerBuilder) {
|
|
117
124
|
var _a, _b;
|
|
118
125
|
super((name, error) => {
|
|
119
126
|
this.mc.logger.sendErrorEvent({
|
|
@@ -122,6 +129,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
122
129
|
}, error);
|
|
123
130
|
});
|
|
124
131
|
this.loader = loader;
|
|
132
|
+
this.protocolHandlerBuilder = protocolHandlerBuilder;
|
|
125
133
|
// Tells if container can reconnect on losing fist connection
|
|
126
134
|
// If false, container gets closed on loss of connection.
|
|
127
135
|
this._canReconnect = true;
|
|
@@ -135,7 +143,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
135
143
|
this.attachStarted = false;
|
|
136
144
|
this._dirtyContainer = false;
|
|
137
145
|
this.setAutoReconnectTime = performance.now();
|
|
138
|
-
this._audience = new Audience();
|
|
139
146
|
this.clientDetailsOverride = config.clientDetailsOverride;
|
|
140
147
|
this._resolvedUrl = config.resolvedUrl;
|
|
141
148
|
if (config.canReconnect !== undefined) {
|
|
@@ -179,9 +186,19 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
179
186
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
|
|
180
187
|
const summarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree")) !== null && _a !== void 0 ? _a : this.loader.services.options.summarizeProtocolTree;
|
|
181
188
|
this.options = Object.assign(Object.assign({}, this.loader.services.options), { summarizeProtocolTree });
|
|
182
|
-
this.
|
|
183
|
-
|
|
184
|
-
|
|
189
|
+
this._deltaManager = this.createDeltaManager();
|
|
190
|
+
this._clientId = (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId;
|
|
191
|
+
this.connectionStateHandler = createConnectionStateHandler({
|
|
192
|
+
logger: this.mc.logger,
|
|
193
|
+
connectionStateChanged: (value, oldState, reason) => {
|
|
194
|
+
if (value === ConnectionState.Connected) {
|
|
195
|
+
this._clientId = this.connectionStateHandler.pendingClientId;
|
|
196
|
+
}
|
|
197
|
+
this.logConnectionStateChangeTelemetry(value, oldState, reason);
|
|
198
|
+
if (this._lifecycleState === "loaded") {
|
|
199
|
+
this.propagateConnectionState(false /* initial transition */);
|
|
200
|
+
}
|
|
201
|
+
},
|
|
185
202
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
186
203
|
maxClientLeaveWaitTime: this.loader.services.options.maxClientLeaveWaitTime,
|
|
187
204
|
logConnectionIssue: (eventName, details) => {
|
|
@@ -189,29 +206,13 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
189
206
|
// its own join op. Attempt recovery option.
|
|
190
207
|
this._deltaManager.logConnectionIssue(Object.assign({ eventName, duration: performance.now() - this.connectionTransitionTimes[ConnectionState.CatchingUp] }, (details === undefined ? {} : { details: JSON.stringify(details) })));
|
|
191
208
|
},
|
|
192
|
-
|
|
193
|
-
// Fire events only if container is fully loaded and not closed
|
|
194
|
-
if (this._lifecycleState === "loaded") {
|
|
195
|
-
this.propagateConnectionState();
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
}, this.mc.logger, (_b = config.serializedContainerState) === null || _b === void 0 ? void 0 : _b.clientId);
|
|
209
|
+
}, this.deltaManager, this._clientId);
|
|
199
210
|
this.on(savedContainerEvent, () => {
|
|
200
211
|
this.connectionStateHandler.containerSaved();
|
|
201
212
|
});
|
|
202
|
-
this.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (this.loader.services.detachedBlobStorage !== undefined) {
|
|
206
|
-
return new BlobOnlyStorage(this.loader.services.detachedBlobStorage, this.mc.logger);
|
|
207
|
-
}
|
|
208
|
-
this.mc.logger.sendErrorEvent({
|
|
209
|
-
eventName: "NoRealStorageInDetachedContainer",
|
|
210
|
-
});
|
|
211
|
-
throw new Error("Real storage calls not allowed in Unattached container");
|
|
212
|
-
}
|
|
213
|
-
return this.storageService;
|
|
214
|
-
});
|
|
213
|
+
this.storageService = new ContainerStorageAdapter(this.loader.services.detachedBlobStorage, this.mc.logger, this.options.summarizeProtocolTree === true
|
|
214
|
+
? () => this.captureProtocolSummary()
|
|
215
|
+
: undefined);
|
|
215
216
|
const isDomAvailable = typeof document === "object" &&
|
|
216
217
|
document !== null &&
|
|
217
218
|
typeof document.addEventListener === "function" &&
|
|
@@ -268,13 +269,13 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
268
269
|
/**
|
|
269
270
|
* Load an existing container.
|
|
270
271
|
*/
|
|
271
|
-
static async load(loader, loadOptions, pendingLocalState) {
|
|
272
|
+
static async load(loader, loadOptions, pendingLocalState, protocolHandlerBuilder) {
|
|
272
273
|
const container = new Container(loader, {
|
|
273
274
|
clientDetailsOverride: loadOptions.clientDetailsOverride,
|
|
274
275
|
resolvedUrl: loadOptions.resolvedUrl,
|
|
275
276
|
canReconnect: loadOptions.canReconnect,
|
|
276
277
|
serializedContainerState: pendingLocalState,
|
|
277
|
-
});
|
|
278
|
+
}, protocolHandlerBuilder);
|
|
278
279
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
279
280
|
var _a, _b;
|
|
280
281
|
const version = loadOptions.version;
|
|
@@ -308,8 +309,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
308
309
|
/**
|
|
309
310
|
* Create a new container in a detached state.
|
|
310
311
|
*/
|
|
311
|
-
static async createDetached(loader, codeDetails) {
|
|
312
|
-
const container = new Container(loader, {});
|
|
312
|
+
static async createDetached(loader, codeDetails, protocolHandlerBuilder) {
|
|
313
|
+
const container = new Container(loader, {}, protocolHandlerBuilder);
|
|
313
314
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
314
315
|
await container.createDetached(codeDetails);
|
|
315
316
|
return container;
|
|
@@ -319,8 +320,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
319
320
|
* Create a new container in a detached state that is initialized with a
|
|
320
321
|
* snapshot from a previous detached container.
|
|
321
322
|
*/
|
|
322
|
-
static async rehydrateDetachedFromSnapshot(loader, snapshot) {
|
|
323
|
-
const container = new Container(loader, {});
|
|
323
|
+
static async rehydrateDetachedFromSnapshot(loader, snapshot, protocolHandlerBuilder) {
|
|
324
|
+
const container = new Container(loader, {}, protocolHandlerBuilder);
|
|
324
325
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
325
326
|
const deserializedSummary = JSON.parse(snapshot);
|
|
326
327
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
@@ -332,7 +333,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
332
333
|
// Only transition states if currently loading
|
|
333
334
|
if (this._lifecycleState === "loading") {
|
|
334
335
|
// Propagate current connection state through the system.
|
|
335
|
-
this.propagateConnectionState();
|
|
336
|
+
this.propagateConnectionState(true /* initial transition */);
|
|
336
337
|
this._lifecycleState = "loaded";
|
|
337
338
|
}
|
|
338
339
|
}
|
|
@@ -340,13 +341,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
340
341
|
return (this._lifecycleState === "closing" || this._lifecycleState === "closed");
|
|
341
342
|
}
|
|
342
343
|
get storage() {
|
|
343
|
-
return this.
|
|
344
|
-
}
|
|
345
|
-
get storageService() {
|
|
346
|
-
if (this._storageService === undefined) {
|
|
347
|
-
throw new Error("Attempted to access storageService before it was defined");
|
|
348
|
-
}
|
|
349
|
-
return this._storageService;
|
|
344
|
+
return this.storageService;
|
|
350
345
|
}
|
|
351
346
|
get context() {
|
|
352
347
|
if (this._context === undefined) {
|
|
@@ -387,7 +382,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
387
382
|
return this.connectionStateHandler.connectionState;
|
|
388
383
|
}
|
|
389
384
|
get connected() {
|
|
390
|
-
return this.connectionStateHandler.
|
|
385
|
+
return this.connectionStateHandler.connectionState === ConnectionState.Connected;
|
|
391
386
|
}
|
|
392
387
|
/**
|
|
393
388
|
* Service configuration details. If running in offline mode will be undefined otherwise will contain service
|
|
@@ -401,7 +396,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
401
396
|
* Set once this.connected is true, otherwise undefined
|
|
402
397
|
*/
|
|
403
398
|
get clientId() {
|
|
404
|
-
return this.
|
|
399
|
+
return this._clientId;
|
|
405
400
|
}
|
|
406
401
|
/**
|
|
407
402
|
* The server provided claims of the client.
|
|
@@ -433,12 +428,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
433
428
|
* Retrieves the audience associated with the document
|
|
434
429
|
*/
|
|
435
430
|
get audience() {
|
|
436
|
-
return this.
|
|
431
|
+
return this.protocolHandler.audience;
|
|
437
432
|
}
|
|
438
433
|
/**
|
|
439
434
|
* Returns true if container is dirty.
|
|
440
435
|
* Which means data loss if container is closed at that same moment
|
|
441
|
-
* Most likely that happens when there is no network connection to
|
|
436
|
+
* Most likely that happens when there is no network connection to Relay Service
|
|
442
437
|
*/
|
|
443
438
|
get isDirty() {
|
|
444
439
|
return this._dirtyContainer;
|
|
@@ -463,7 +458,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
463
458
|
assert(this._lifecycleState === "closed", 0x314 /* Container properly closed */);
|
|
464
459
|
}
|
|
465
460
|
closeCore(error) {
|
|
466
|
-
var _a, _b, _c
|
|
461
|
+
var _a, _b, _c;
|
|
467
462
|
assert(!this.closed, 0x315 /* re-entrancy */);
|
|
468
463
|
try {
|
|
469
464
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -478,11 +473,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
478
473
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
479
474
|
this.connectionStateHandler.dispose();
|
|
480
475
|
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
481
|
-
|
|
476
|
+
this.storageService.dispose();
|
|
482
477
|
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
483
478
|
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
484
479
|
// Driver need to ensure all caches are cleared on critical errors
|
|
485
|
-
(
|
|
480
|
+
(_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
|
|
486
481
|
}
|
|
487
482
|
catch (exception) {
|
|
488
483
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
@@ -504,7 +499,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
504
499
|
assert(this.attachState === AttachState.Attached, 0x0d1 /* "Container should be attached before close" */);
|
|
505
500
|
assert(this.resolvedUrl !== undefined && this.resolvedUrl.type === "fluid", 0x0d2 /* "resolved url should be valid Fluid url" */);
|
|
506
501
|
assert(!!this._protocolHandler, 0x2e3 /* "Must have a valid protocol handler instance" */);
|
|
507
|
-
assert(this._protocolHandler.attributes.term !== undefined,
|
|
502
|
+
assert(this._protocolHandler.attributes.term !== undefined, 0x37e /* Must have a valid protocol handler instance */);
|
|
508
503
|
const pendingState = {
|
|
509
504
|
pendingRuntimeState: this.context.getPendingLocalState(),
|
|
510
505
|
url: this.resolvedUrl.url,
|
|
@@ -512,6 +507,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
512
507
|
term: this._protocolHandler.attributes.term,
|
|
513
508
|
clientId: this.clientId,
|
|
514
509
|
};
|
|
510
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "CloseAndGetPendingLocalState" });
|
|
515
511
|
this.close();
|
|
516
512
|
return JSON.stringify(pendingState);
|
|
517
513
|
}
|
|
@@ -568,7 +564,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
568
564
|
const resolvedUrl = this.service.resolvedUrl;
|
|
569
565
|
ensureFluidResolvedUrl(resolvedUrl);
|
|
570
566
|
this._resolvedUrl = resolvedUrl;
|
|
571
|
-
await this.
|
|
567
|
+
await this.storageService.connectToService(this.service);
|
|
572
568
|
if (hasAttachmentBlobs) {
|
|
573
569
|
// upload blobs to storage
|
|
574
570
|
assert(!!this.loader.services.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
@@ -598,8 +594,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
598
594
|
}
|
|
599
595
|
this._attachState = AttachState.Attached;
|
|
600
596
|
this.emit("attached");
|
|
601
|
-
// Propagate current connection state through the system.
|
|
602
|
-
this.propagateConnectionState();
|
|
603
597
|
if (!this.closed) {
|
|
604
598
|
this.resumeInternal({ fetchOpsFromStorage: false, reason: "createDetached" });
|
|
605
599
|
}
|
|
@@ -738,9 +732,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
738
732
|
/**
|
|
739
733
|
* Load container.
|
|
740
734
|
*
|
|
741
|
-
* @param specifiedVersion -
|
|
742
|
-
* - undefined - fetch latest snapshot
|
|
743
|
-
* - otherwise, version sha to load snapshot
|
|
735
|
+
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
744
736
|
*/
|
|
745
737
|
async load(specifiedVersion, loadMode, pendingLocalState) {
|
|
746
738
|
if (this._resolvedUrl === undefined) {
|
|
@@ -763,11 +755,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
763
755
|
this.connectToDeltaStream(connectionArgs);
|
|
764
756
|
}
|
|
765
757
|
if (!pendingLocalState) {
|
|
766
|
-
await this.
|
|
758
|
+
await this.storageService.connectToService(this.service);
|
|
767
759
|
}
|
|
768
760
|
else {
|
|
769
761
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
770
|
-
this.
|
|
762
|
+
this.storageService.connectToService(this.service).catch((error) => this.close(error));
|
|
771
763
|
}
|
|
772
764
|
this._attachState = AttachState.Attached;
|
|
773
765
|
// Fetch specified snapshot.
|
|
@@ -802,9 +794,16 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
802
794
|
}
|
|
803
795
|
// ...load in the existing quorum
|
|
804
796
|
// Initialize the protocol handler
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
797
|
+
if (pendingLocalState === undefined) {
|
|
798
|
+
await this.initializeProtocolStateFromSnapshot(attributes, this.storageService, snapshot);
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
this.initializeProtocolState(attributes, {
|
|
802
|
+
members: pendingLocalState.protocol.members,
|
|
803
|
+
proposals: pendingLocalState.protocol.proposals,
|
|
804
|
+
values: pendingLocalState.protocol.values,
|
|
805
|
+
});
|
|
806
|
+
}
|
|
808
807
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
809
808
|
await this.instantiateContext(true, // existing
|
|
810
809
|
codeDetails, snapshot, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.pendingRuntimeState);
|
|
@@ -858,9 +857,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
858
857
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
859
858
|
// Need to just seed the source data in the code quorum. Quorum itself is empty
|
|
860
859
|
const qValues = initQuorumValuesFromCodeDetails(source);
|
|
861
|
-
this.
|
|
862
|
-
|
|
863
|
-
|
|
860
|
+
this.initializeProtocolState(attributes, {
|
|
861
|
+
members: [],
|
|
862
|
+
proposals: [],
|
|
863
|
+
values: qValues,
|
|
864
|
+
});
|
|
864
865
|
// The load context - given we seeded the quorum - will be great
|
|
865
866
|
await this.instantiateContextDetached(false);
|
|
866
867
|
this.setLoaded();
|
|
@@ -871,38 +872,22 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
871
872
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
872
873
|
}
|
|
873
874
|
const snapshotTree = getSnapshotTreeFromSerializedContainer(detachedContainerSnapshot);
|
|
874
|
-
this.
|
|
875
|
-
const attributes = await this.getDocumentAttributes(this.
|
|
875
|
+
this.storageService.loadSnapshotForRehydratingContainer(snapshotTree);
|
|
876
|
+
const attributes = await this.getDocumentAttributes(this.storageService, snapshotTree);
|
|
876
877
|
await this.attachDeltaManagerOpHandler(attributes);
|
|
877
878
|
// Initialize the protocol handler
|
|
878
879
|
const baseTree = getProtocolSnapshotTree(snapshotTree);
|
|
879
|
-
const qValues = await readAndParse(this.
|
|
880
|
+
const qValues = await readAndParse(this.storageService, baseTree.blobs.quorumValues);
|
|
880
881
|
const codeDetails = getCodeDetailsFromQuorumValues(qValues);
|
|
881
|
-
this.
|
|
882
|
-
|
|
883
|
-
[],
|
|
884
|
-
codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : []
|
|
882
|
+
this.initializeProtocolState(attributes, {
|
|
883
|
+
members: [],
|
|
884
|
+
proposals: [],
|
|
885
|
+
values: codeDetails !== undefined ? initQuorumValuesFromCodeDetails(codeDetails) : [],
|
|
886
|
+
});
|
|
885
887
|
await this.instantiateContextDetached(true, // existing
|
|
886
888
|
snapshotTree);
|
|
887
889
|
this.setLoaded();
|
|
888
890
|
}
|
|
889
|
-
async connectStorageService() {
|
|
890
|
-
var _a, _b;
|
|
891
|
-
if (this._storageService !== undefined) {
|
|
892
|
-
return;
|
|
893
|
-
}
|
|
894
|
-
assert(this.service !== undefined, 0x1ef /* "services must be defined" */);
|
|
895
|
-
const storageService = await this.service.connectToStorage();
|
|
896
|
-
this._storageService =
|
|
897
|
-
new RetriableDocumentStorageService(storageService, this.mc.logger);
|
|
898
|
-
if (this.options.summarizeProtocolTree === true) {
|
|
899
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "summarizeProtocolTreeEnabled" });
|
|
900
|
-
this._storageService =
|
|
901
|
-
new ProtocolTreeStorageService(this._storageService, () => this.captureProtocolSummary());
|
|
902
|
-
}
|
|
903
|
-
// ensure we did not lose that policy in the process of wrapping
|
|
904
|
-
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" */);
|
|
905
|
-
}
|
|
906
891
|
async getDocumentAttributes(storage, tree) {
|
|
907
892
|
if (tree === undefined) {
|
|
908
893
|
return {
|
|
@@ -923,22 +908,26 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
923
908
|
return attributes;
|
|
924
909
|
}
|
|
925
910
|
async initializeProtocolStateFromSnapshot(attributes, storage, snapshot) {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
911
|
+
const quorumSnapshot = {
|
|
912
|
+
members: [],
|
|
913
|
+
proposals: [],
|
|
914
|
+
values: [],
|
|
915
|
+
};
|
|
929
916
|
if (snapshot !== undefined) {
|
|
930
917
|
const baseTree = getProtocolSnapshotTree(snapshot);
|
|
931
|
-
[members, proposals, values] = await Promise.all([
|
|
918
|
+
[quorumSnapshot.members, quorumSnapshot.proposals, quorumSnapshot.values] = await Promise.all([
|
|
932
919
|
readAndParse(storage, baseTree.blobs.quorumMembers),
|
|
933
920
|
readAndParse(storage, baseTree.blobs.quorumProposals),
|
|
934
921
|
readAndParse(storage, baseTree.blobs.quorumValues),
|
|
935
922
|
]);
|
|
936
923
|
}
|
|
937
|
-
|
|
938
|
-
return protocolHandler;
|
|
924
|
+
this.initializeProtocolState(attributes, quorumSnapshot);
|
|
939
925
|
}
|
|
940
|
-
|
|
941
|
-
|
|
926
|
+
initializeProtocolState(attributes, quorumSnapshot) {
|
|
927
|
+
var _a, _b;
|
|
928
|
+
const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
929
|
+
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })), (_b = this._initialClients) !== null && _b !== void 0 ? _b : []);
|
|
930
|
+
this._initialClients = undefined;
|
|
942
931
|
const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
943
932
|
protocol.quorum.on("error", (error) => {
|
|
944
933
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -963,7 +952,11 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
963
952
|
});
|
|
964
953
|
}
|
|
965
954
|
});
|
|
966
|
-
|
|
955
|
+
// we need to make sure this member get set in a synchronous context,
|
|
956
|
+
// or other things can happen after the object that will be set is created, but not yet set
|
|
957
|
+
// this was breaking this._initialClients handling
|
|
958
|
+
//
|
|
959
|
+
this._protocolHandler = protocol;
|
|
967
960
|
}
|
|
968
961
|
captureProtocolSummary() {
|
|
969
962
|
const quorumSnapshot = this.protocolHandler.snapshot();
|
|
@@ -1032,12 +1025,20 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1032
1025
|
deltaManager.inbound.pause();
|
|
1033
1026
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1034
1027
|
deltaManager.inboundSignal.pause();
|
|
1035
|
-
deltaManager.on("connect", (details,
|
|
1028
|
+
deltaManager.on("connect", (details, _opsBehind) => {
|
|
1036
1029
|
var _a;
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
this.
|
|
1030
|
+
if (this._protocolHandler === undefined) {
|
|
1031
|
+
// Store the initial clients so that they can be submitted to the
|
|
1032
|
+
// protocol handler when it is created.
|
|
1033
|
+
this._initialClients = details.initialClients;
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
// When reconnecting, the protocol handler is already created,
|
|
1037
|
+
// so we can update the audience right now.
|
|
1038
|
+
this._protocolHandler.audience.clear();
|
|
1039
|
+
for (const priorClient of (_a = details.initialClients) !== null && _a !== void 0 ? _a : []) {
|
|
1040
|
+
this._protocolHandler.audience.addMember(priorClient.clientId, priorClient.client);
|
|
1041
|
+
}
|
|
1041
1042
|
}
|
|
1042
1043
|
this.connectionStateHandler.receivedConnectEvent(this.connectionMode, details);
|
|
1043
1044
|
});
|
|
@@ -1056,6 +1057,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1056
1057
|
this.emit("warning", warn);
|
|
1057
1058
|
});
|
|
1058
1059
|
deltaManager.on("readonly", (readonly) => {
|
|
1060
|
+
this.setContextConnectedState(this.connectionState === ConnectionState.Connected, readonly);
|
|
1059
1061
|
this.emit("readonly", readonly);
|
|
1060
1062
|
});
|
|
1061
1063
|
deltaManager.on("closed", (error) => {
|
|
@@ -1098,12 +1100,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1098
1100
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
1099
1101
|
}
|
|
1100
1102
|
}
|
|
1101
|
-
|
|
1102
|
-
connectionInitiationReason = "InitialConnect";
|
|
1103
|
-
}
|
|
1104
|
-
else {
|
|
1105
|
-
connectionInitiationReason = "AutoReconnect";
|
|
1106
|
-
}
|
|
1103
|
+
connectionInitiationReason = this.firstConnection ? "InitialConnect" : "AutoReconnect";
|
|
1107
1104
|
}
|
|
1108
1105
|
this.mc.logger.sendPerformanceEvent(Object.assign({ eventName: `ConnectionStateChange_${ConnectionState[value]}`, from: ConnectionState[oldState], duration,
|
|
1109
1106
|
durationFromDisconnected,
|
|
@@ -1114,49 +1111,64 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1114
1111
|
this.firstConnection = false;
|
|
1115
1112
|
}
|
|
1116
1113
|
}
|
|
1117
|
-
propagateConnectionState() {
|
|
1114
|
+
propagateConnectionState(initialTransition) {
|
|
1118
1115
|
var _a;
|
|
1116
|
+
// When container loaded, we want to propagate initial connection state.
|
|
1117
|
+
// After that, we communicate only transitions to Connected & Disconnected states, skipping all other states.
|
|
1118
|
+
// This can be changed in the future, for example we likely should add "CatchingUp" event on Container.
|
|
1119
|
+
if (!initialTransition &&
|
|
1120
|
+
this.connectionState !== ConnectionState.Connected &&
|
|
1121
|
+
this.connectionState !== ConnectionState.Disconnected) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
const state = this.connectionState === ConnectionState.Connected;
|
|
1119
1125
|
const logOpsOnReconnect = this.connectionState === ConnectionState.Connected &&
|
|
1120
1126
|
!this.firstConnection &&
|
|
1121
1127
|
this.connectionMode === "write";
|
|
1122
1128
|
if (logOpsOnReconnect) {
|
|
1123
1129
|
this.messageCountAfterDisconnection = 0;
|
|
1124
1130
|
}
|
|
1125
|
-
const state = this.connectionState === ConnectionState.Connected;
|
|
1126
1131
|
// Both protocol and context should not be undefined if we got so far.
|
|
1127
|
-
|
|
1128
|
-
this.context.setConnectionState(state, this.clientId);
|
|
1129
|
-
}
|
|
1132
|
+
this.setContextConnectedState(state, (_a = this._deltaManager.connectionManager.readOnlyInfo.readonly) !== null && _a !== void 0 ? _a : false);
|
|
1130
1133
|
this.protocolHandler.setConnectionState(state, this.clientId);
|
|
1131
1134
|
raiseConnectedEvent(this.mc.logger, this, state, this.clientId);
|
|
1132
1135
|
if (logOpsOnReconnect) {
|
|
1133
1136
|
this.mc.logger.sendTelemetryEvent({ eventName: "OpsSentOnReconnect", count: this.messageCountAfterDisconnection });
|
|
1134
1137
|
}
|
|
1135
1138
|
}
|
|
1139
|
+
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1136
1140
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
1137
|
-
|
|
1138
|
-
switch (outboundMessageType) {
|
|
1141
|
+
switch (type) {
|
|
1139
1142
|
case MessageType.Operation:
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
// github #6451: this is only needed for staging so the server
|
|
1144
|
-
// know when the protocol tree is included
|
|
1145
|
-
// this can be removed once all clients send
|
|
1146
|
-
// protocol tree by default
|
|
1147
|
-
const summary = contents;
|
|
1148
|
-
if (summary.details === undefined) {
|
|
1149
|
-
summary.details = {};
|
|
1150
|
-
}
|
|
1151
|
-
summary.details.includesProtocolTree =
|
|
1152
|
-
this.options.summarizeProtocolTree === true;
|
|
1153
|
-
break;
|
|
1154
|
-
}
|
|
1143
|
+
return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
|
|
1144
|
+
case MessageType.Summarize:
|
|
1145
|
+
return this.submitSummaryMessage(contents);
|
|
1155
1146
|
default:
|
|
1156
1147
|
this.close(new GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type }));
|
|
1157
1148
|
return -1;
|
|
1158
1149
|
}
|
|
1159
|
-
|
|
1150
|
+
}
|
|
1151
|
+
/** @returns clientSequenceNumber of last message in a batch */
|
|
1152
|
+
submitBatch(batch) {
|
|
1153
|
+
let clientSequenceNumber = -1;
|
|
1154
|
+
for (const message of batch) {
|
|
1155
|
+
clientSequenceNumber = this.submitMessage(MessageType.Operation, message.contents, true, // batch
|
|
1156
|
+
message.metadata);
|
|
1157
|
+
}
|
|
1158
|
+
this._deltaManager.flush();
|
|
1159
|
+
return clientSequenceNumber;
|
|
1160
|
+
}
|
|
1161
|
+
submitSummaryMessage(summary) {
|
|
1162
|
+
// github #6451: this is only needed for staging so the server
|
|
1163
|
+
// know when the protocol tree is included
|
|
1164
|
+
// this can be removed once all clients send
|
|
1165
|
+
// protocol tree by default
|
|
1166
|
+
if (summary.details === undefined) {
|
|
1167
|
+
summary.details = {};
|
|
1168
|
+
}
|
|
1169
|
+
summary.details.includesProtocolTree =
|
|
1170
|
+
this.options.summarizeProtocolTree === true;
|
|
1171
|
+
return this.submitMessage(MessageType.Summarize, JSON.stringify(summary), false /* batch */);
|
|
1160
1172
|
}
|
|
1161
1173
|
submitMessage(type, contents, batch, metadata) {
|
|
1162
1174
|
var _a;
|
|
@@ -1171,22 +1183,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1171
1183
|
processRemoteMessage(message) {
|
|
1172
1184
|
const local = this.clientId === message.clientId;
|
|
1173
1185
|
// Allow the protocol handler to process the message
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1178
|
-
catch (error) {
|
|
1179
|
-
this.close(wrapError(error, (errorMessage) => new DataCorruptionError(errorMessage, extractSafePropertiesFromMessage(message))));
|
|
1180
|
-
}
|
|
1181
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1182
|
-
if (isUnpackedRuntimeMessage(message) && !isRuntimeMessage(message)) {
|
|
1183
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "UnpackedRuntimeMessage", type: message.type });
|
|
1184
|
-
}
|
|
1185
|
-
// Forward non system messages to the loaded runtime for processing
|
|
1186
|
-
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
1187
|
-
if (isRuntimeMessage(message) || isUnpackedRuntimeMessage(message)) {
|
|
1188
|
-
this.context.process(message, local, undefined);
|
|
1189
|
-
}
|
|
1186
|
+
const result = this.protocolHandler.processMessage(message, local);
|
|
1187
|
+
// Forward messages to the loaded runtime for processing
|
|
1188
|
+
this.context.process(message, local, undefined);
|
|
1190
1189
|
// Inactive (not in quorum or not writers) clients don't take part in the minimum sequence number calculation.
|
|
1191
1190
|
if (this.activeConnection()) {
|
|
1192
1191
|
if (this.collabWindowTracker === undefined) {
|
|
@@ -1195,15 +1194,14 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1195
1194
|
// clients.
|
|
1196
1195
|
// All existing will continue to use settings they got earlier.
|
|
1197
1196
|
assert(this.serviceConfiguration !== undefined, 0x2e4 /* "there should be service config for active connection" */);
|
|
1198
|
-
this.collabWindowTracker = new CollabWindowTracker((type
|
|
1197
|
+
this.collabWindowTracker = new CollabWindowTracker((type) => {
|
|
1199
1198
|
assert(this.activeConnection(), 0x241 /* "disconnect should result in stopSequenceNumberUpdate() call" */);
|
|
1200
|
-
this.submitMessage(type
|
|
1199
|
+
this.submitMessage(type);
|
|
1201
1200
|
}, this.serviceConfiguration.noopTimeFrequency, this.serviceConfiguration.noopCountFrequency);
|
|
1202
1201
|
}
|
|
1203
1202
|
this.collabWindowTracker.scheduleSequenceNumberUpdate(message, result.immediateNoOp === true);
|
|
1204
1203
|
}
|
|
1205
1204
|
this.emit("op", message);
|
|
1206
|
-
return result;
|
|
1207
1205
|
}
|
|
1208
1206
|
submitSignal(message) {
|
|
1209
1207
|
this._deltaManager.submitSignal(JSON.stringify(message));
|
|
@@ -1211,15 +1209,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1211
1209
|
processSignal(message) {
|
|
1212
1210
|
// No clientId indicates a system signal message.
|
|
1213
1211
|
if (message.clientId === null) {
|
|
1214
|
-
|
|
1215
|
-
if (innerContent.type === MessageType.ClientJoin) {
|
|
1216
|
-
const newClient = innerContent.content;
|
|
1217
|
-
this._audience.addMember(newClient.clientId, newClient.client);
|
|
1218
|
-
}
|
|
1219
|
-
else if (innerContent.type === MessageType.ClientLeave) {
|
|
1220
|
-
const leftClientId = innerContent.content;
|
|
1221
|
-
this._audience.removeMember(leftClientId);
|
|
1222
|
-
}
|
|
1212
|
+
this.protocolHandler.processSignal(message);
|
|
1223
1213
|
}
|
|
1224
1214
|
else {
|
|
1225
1215
|
const local = this.clientId === message.clientId;
|
|
@@ -1258,7 +1248,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1258
1248
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1259
1249
|
// are set. Global requests will still go directly to the loader
|
|
1260
1250
|
const loader = new RelativeLoader(this, this.loader);
|
|
1261
|
-
this._context = await ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new DeltaManagerProxy(this._deltaManager), new 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);
|
|
1251
|
+
this._context = await ContainerContext.createOrLoad(this, this.scope, this.codeLoader, codeDetails, snapshot, new DeltaManagerProxy(this._deltaManager), new 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);
|
|
1262
1252
|
this.emit("contextChanged", codeDetails);
|
|
1263
1253
|
}
|
|
1264
1254
|
updateDirtyContainerState(dirty) {
|
|
@@ -1271,6 +1261,24 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1271
1261
|
logContainerError(warning) {
|
|
1272
1262
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerWarning" }, warning);
|
|
1273
1263
|
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Set the connected state of the ContainerContext
|
|
1266
|
+
* This controls the "connected" state of the ContainerRuntime as well
|
|
1267
|
+
* @param state - Is the container currently connected?
|
|
1268
|
+
* @param readonly - Is the container in readonly mode?
|
|
1269
|
+
*/
|
|
1270
|
+
setContextConnectedState(state, readonly) {
|
|
1271
|
+
var _a;
|
|
1272
|
+
if (((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) === false) {
|
|
1273
|
+
/**
|
|
1274
|
+
* We want to lie to the ContainerRuntime when we are in readonly mode to prevent issues with pending
|
|
1275
|
+
* ops getting through to the DeltaManager.
|
|
1276
|
+
* The ContainerRuntime's "connected" state simply means it is ok to send ops
|
|
1277
|
+
* See https://dev.azure.com/fluidframework/internal/_workitems/edit/1246
|
|
1278
|
+
*/
|
|
1279
|
+
this.context.setConnectionState(state && !readonly, this.clientId);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1274
1282
|
}
|
|
1275
1283
|
Container.version = "^0.1.0";
|
|
1276
1284
|
//# sourceMappingURL=container.js.map
|