@fluidframework/container-loader 2.0.0-dev.4.4.0.162574 → 2.0.0-dev.5.2.0.169897
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/README.md +27 -3
- package/dist/connectionManager.d.ts +3 -2
- package/dist/connectionManager.d.ts.map +1 -1
- package/dist/connectionManager.js +32 -13
- package/dist/connectionManager.js.map +1 -1
- package/dist/connectionStateHandler.d.ts +15 -3
- package/dist/connectionStateHandler.d.ts.map +1 -1
- package/dist/connectionStateHandler.js +24 -1
- package/dist/connectionStateHandler.js.map +1 -1
- package/dist/container.d.ts +74 -44
- package/dist/container.d.ts.map +1 -1
- package/dist/container.js +81 -111
- package/dist/container.js.map +1 -1
- package/dist/containerContext.d.ts +2 -2
- package/dist/containerContext.d.ts.map +1 -1
- package/dist/containerContext.js +3 -7
- package/dist/containerContext.js.map +1 -1
- package/dist/containerStorageAdapter.d.ts +3 -3
- package/dist/containerStorageAdapter.d.ts.map +1 -1
- package/dist/containerStorageAdapter.js +6 -15
- package/dist/containerStorageAdapter.js.map +1 -1
- package/dist/contracts.d.ts +8 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/deltaManager.d.ts +21 -9
- package/dist/deltaManager.d.ts.map +1 -1
- package/dist/deltaManager.js +42 -31
- package/dist/deltaManager.js.map +1 -1
- package/dist/deltaQueue.d.ts +2 -3
- package/dist/deltaQueue.d.ts.map +1 -1
- package/dist/deltaQueue.js +2 -3
- package/dist/deltaQueue.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loader.d.ts +9 -7
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +47 -61
- package/dist/loader.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/retriableDocumentStorageService.d.ts +3 -2
- package/dist/retriableDocumentStorageService.d.ts.map +1 -1
- package/dist/retriableDocumentStorageService.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -1
- package/dist/utils.js.map +1 -1
- package/lib/connectionManager.d.ts +3 -2
- package/lib/connectionManager.d.ts.map +1 -1
- package/lib/connectionManager.js +33 -14
- package/lib/connectionManager.js.map +1 -1
- package/lib/connectionStateHandler.d.ts +15 -3
- package/lib/connectionStateHandler.d.ts.map +1 -1
- package/lib/connectionStateHandler.js +25 -2
- package/lib/connectionStateHandler.js.map +1 -1
- package/lib/container.d.ts +74 -44
- package/lib/container.d.ts.map +1 -1
- package/lib/container.js +82 -112
- package/lib/container.js.map +1 -1
- package/lib/containerContext.d.ts +2 -2
- package/lib/containerContext.d.ts.map +1 -1
- package/lib/containerContext.js +3 -7
- package/lib/containerContext.js.map +1 -1
- package/lib/containerStorageAdapter.d.ts +3 -3
- package/lib/containerStorageAdapter.d.ts.map +1 -1
- package/lib/containerStorageAdapter.js +6 -15
- package/lib/containerStorageAdapter.js.map +1 -1
- package/lib/contracts.d.ts +8 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/deltaManager.d.ts +21 -9
- package/lib/deltaManager.d.ts.map +1 -1
- package/lib/deltaManager.js +44 -33
- package/lib/deltaManager.js.map +1 -1
- package/lib/deltaQueue.d.ts +2 -3
- package/lib/deltaQueue.d.ts.map +1 -1
- package/lib/deltaQueue.js +2 -3
- package/lib/deltaQueue.js.map +1 -1
- package/lib/index.d.ts +1 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/loader.d.ts +9 -7
- package/lib/loader.d.ts.map +1 -1
- package/lib/loader.js +47 -61
- package/lib/loader.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/retriableDocumentStorageService.d.ts +3 -2
- package/lib/retriableDocumentStorageService.d.ts.map +1 -1
- package/lib/retriableDocumentStorageService.js.map +1 -1
- package/lib/utils.d.ts +2 -0
- package/lib/utils.d.ts.map +1 -1
- package/lib/utils.js +7 -1
- package/lib/utils.js.map +1 -1
- package/package.json +16 -18
- package/src/connectionManager.ts +40 -22
- package/src/connectionStateHandler.ts +52 -8
- package/src/container.ts +191 -159
- package/src/containerContext.ts +3 -9
- package/src/containerStorageAdapter.ts +8 -20
- package/src/contracts.ts +10 -0
- package/src/deltaManager.ts +59 -37
- package/src/deltaQueue.ts +2 -3
- package/src/index.ts +1 -8
- package/src/loader.ts +85 -83
- package/src/packageVersion.ts +1 -1
- package/src/retriableDocumentStorageService.ts +3 -2
- package/src/utils.ts +15 -1
package/lib/container.js
CHANGED
|
@@ -8,7 +8,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
10
|
import { GenericError, UsageError } from "@fluidframework/container-utils";
|
|
11
|
-
import { readAndParse, OnlineStatus, isOnline,
|
|
11
|
+
import { readAndParse, OnlineStatus, isOnline, combineAppAndProtocolSummary, runWithRetry, isCombinedAppAndProtocolSummary, } from "@fluidframework/driver-utils";
|
|
12
12
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
13
13
|
import { ChildLogger, EventEmitterWithErrorHandling, PerformanceEvent, raiseConnectedEvent, TelemetryLogger, connectedEventName, normalizeError, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
14
14
|
import { Audience } from "./audience";
|
|
@@ -119,26 +119,18 @@ export async function ReportIfTooLong(logger, eventName, action) {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
const summarizerClientType = "summarizer";
|
|
122
|
-
/**
|
|
123
|
-
* @deprecated - In the next release Container will no longer be exported, IContainer should be used in its place.
|
|
124
|
-
*/
|
|
125
122
|
export class Container extends EventEmitterWithErrorHandling {
|
|
126
123
|
/**
|
|
127
124
|
* @internal
|
|
128
125
|
*/
|
|
129
|
-
constructor(
|
|
130
|
-
var _a
|
|
126
|
+
constructor(createProps, loadProps) {
|
|
127
|
+
var _a;
|
|
131
128
|
super((name, error) => {
|
|
132
129
|
this.mc.logger.sendErrorEvent({
|
|
133
130
|
eventName: "ContainerEventHandlerException",
|
|
134
131
|
name: typeof name === "string" ? name : undefined,
|
|
135
132
|
}, error);
|
|
136
133
|
});
|
|
137
|
-
this.loader = loader;
|
|
138
|
-
this.protocolHandlerBuilder = protocolHandlerBuilder;
|
|
139
|
-
// Tells if container can reconnect on losing fist connection
|
|
140
|
-
// If false, container gets closed on loss of connection.
|
|
141
|
-
this._canReconnect = true;
|
|
142
134
|
/**
|
|
143
135
|
* Lifecycle state of the container, used mainly to prevent re-entrancy and telemetry
|
|
144
136
|
*
|
|
@@ -166,26 +158,41 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
166
158
|
this.savedOps = [];
|
|
167
159
|
this.setAutoReconnectTime = performance.now();
|
|
168
160
|
this._disposed = false;
|
|
169
|
-
|
|
170
|
-
this.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
161
|
+
const { canReconnect, clientDetailsOverride, urlResolver, documentServiceFactory, codeLoader, options, scope, subLogger, detachedBlobStorage, protocolHandlerBuilder, } = createProps;
|
|
162
|
+
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
163
|
+
const pendingLocalState = loadProps === null || loadProps === void 0 ? void 0 : loadProps.pendingLocalState;
|
|
164
|
+
this._canReconnect = canReconnect !== null && canReconnect !== void 0 ? canReconnect : true;
|
|
165
|
+
this.clientDetailsOverride = clientDetailsOverride;
|
|
166
|
+
this.urlResolver = urlResolver;
|
|
167
|
+
this.serviceFactory = documentServiceFactory;
|
|
168
|
+
this.codeLoader = codeLoader;
|
|
169
|
+
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
170
|
+
// all clients that were loaded from the same loader (including summarizer clients).
|
|
171
|
+
// Tracking alternative ways to handle this in AB#4129.
|
|
172
|
+
this.options = Object.assign({}, options);
|
|
173
|
+
this.scope = scope;
|
|
174
|
+
this.detachedBlobStorage = detachedBlobStorage;
|
|
175
|
+
this.protocolHandlerBuilder =
|
|
176
|
+
protocolHandlerBuilder !== null && protocolHandlerBuilder !== void 0 ? protocolHandlerBuilder : ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
177
|
+
// Note that we capture the createProps here so we can replicate the creation call when we want to clone.
|
|
178
|
+
this.clone = async (_loadProps, createParamOverrides) => {
|
|
179
|
+
return Container.load(_loadProps, Object.assign(Object.assign({}, createProps), createParamOverrides));
|
|
180
|
+
};
|
|
174
181
|
// Create logger for data stores to use
|
|
175
182
|
const type = this.client.details.type;
|
|
176
183
|
const interactive = this.client.details.capabilities.interactive;
|
|
177
184
|
const clientType = `${interactive ? "interactive" : "noninteractive"}${type !== undefined && type !== "" ? `/${type}` : ""}`;
|
|
178
185
|
// Need to use the property getter for docId because for detached flow we don't have the docId initially.
|
|
179
186
|
// We assign the id later so property getter is used.
|
|
180
|
-
this.subLogger = ChildLogger.create(
|
|
187
|
+
this.subLogger = ChildLogger.create(subLogger, undefined, {
|
|
181
188
|
all: {
|
|
182
189
|
clientType,
|
|
183
190
|
containerId: uuid(),
|
|
184
|
-
docId: () => { var _a
|
|
191
|
+
docId: () => { var _a; return (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.id; },
|
|
185
192
|
containerAttachState: () => this._attachState,
|
|
186
193
|
containerLifecycleState: () => this._lifecycleState,
|
|
187
194
|
containerConnectionState: () => ConnectionState[this.connectionState],
|
|
188
|
-
serializedContainer:
|
|
195
|
+
serializedContainer: pendingLocalState !== undefined,
|
|
189
196
|
},
|
|
190
197
|
// we need to be judicious with our logging here to avoid generating too much data
|
|
191
198
|
// all data logged here should be broadly applicable, and not specific to a
|
|
@@ -208,10 +215,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
208
215
|
});
|
|
209
216
|
// Prefix all events in this file with container-loader
|
|
210
217
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.subLogger, "Container"));
|
|
211
|
-
// Warning: this is only a shallow clone. Mutation of any individual loader option will mutate it for
|
|
212
|
-
// all clients that were loaded from the same loader (including summarizer clients).
|
|
213
|
-
// Tracking alternative ways to handle this in AB#4129.
|
|
214
|
-
this.options = Object.assign({}, this.loader.services.options);
|
|
215
218
|
this._deltaManager = this.createDeltaManager();
|
|
216
219
|
this.connectionStateHandler = createConnectionStateHandler({
|
|
217
220
|
logger: this.mc.logger,
|
|
@@ -227,7 +230,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
227
230
|
}
|
|
228
231
|
},
|
|
229
232
|
shouldClientJoinWrite: () => this._deltaManager.connectionManager.shouldJoinWrite(),
|
|
230
|
-
maxClientLeaveWaitTime:
|
|
233
|
+
maxClientLeaveWaitTime: options.maxClientLeaveWaitTime,
|
|
231
234
|
logConnectionIssue: (eventName, category, details) => {
|
|
232
235
|
const mode = this.connectionMode;
|
|
233
236
|
// We get here when socket does not receive any ops on "write" connection, including
|
|
@@ -251,7 +254,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
251
254
|
this.connect();
|
|
252
255
|
}
|
|
253
256
|
},
|
|
254
|
-
}, this.deltaManager,
|
|
257
|
+
}, this.deltaManager, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.clientId);
|
|
255
258
|
this.on(savedContainerEvent, () => {
|
|
256
259
|
this.connectionStateHandler.containerSaved();
|
|
257
260
|
});
|
|
@@ -263,8 +266,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
263
266
|
: combineAppAndProtocolSummary(summaryTree, this.captureProtocolSummary());
|
|
264
267
|
// Whether the combined summary tree has been forced on by either the loader option or the monitoring context.
|
|
265
268
|
// Even if not forced on via this flag, combined summaries may still be enabled by service policy.
|
|
266
|
-
const forceEnableSummarizeProtocolTree = (
|
|
267
|
-
this.storageAdapter = new ContainerStorageAdapter(
|
|
269
|
+
const forceEnableSummarizeProtocolTree = (_a = this.mc.config.getBoolean("Fluid.Container.summarizeProtocolTree2")) !== null && _a !== void 0 ? _a : options.summarizeProtocolTree;
|
|
270
|
+
this.storageAdapter = new ContainerStorageAdapter(detachedBlobStorage, this.mc.logger, pendingLocalState === null || pendingLocalState === void 0 ? void 0 : pendingLocalState.snapshotBlobs, addProtocolSummaryIfMissing, forceEnableSummarizeProtocolTree);
|
|
268
271
|
const isDomAvailable = typeof document === "object" &&
|
|
269
272
|
document !== null &&
|
|
270
273
|
typeof document.addEventListener === "function" &&
|
|
@@ -290,33 +293,28 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
290
293
|
* Load an existing container.
|
|
291
294
|
* @internal
|
|
292
295
|
*/
|
|
293
|
-
static async load(
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
canReconnect: loadOptions.canReconnect,
|
|
298
|
-
serializedContainerState: pendingLocalState,
|
|
299
|
-
}, protocolHandlerBuilder);
|
|
296
|
+
static async load(loadProps, createProps) {
|
|
297
|
+
const { version, pendingLocalState, loadMode, resolvedUrl } = loadProps;
|
|
298
|
+
const container = new Container(createProps, loadProps);
|
|
299
|
+
const disableRecordHeapSize = container.mc.config.getBoolean("Fluid.Loader.DisableRecordHeapSize");
|
|
300
300
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "Load" }, async (event) => new Promise((resolve, reject) => {
|
|
301
|
-
var _a, _b;
|
|
302
|
-
const version = loadOptions.version;
|
|
303
301
|
const defaultMode = { opsBeforeReturn: "cached" };
|
|
304
302
|
// if we have pendingLocalState, anything we cached is not useful and we shouldn't wait for connection
|
|
305
303
|
// to return container, so ignore this value and use undefined for opsBeforeReturn
|
|
306
304
|
const mode = pendingLocalState
|
|
307
|
-
? Object.assign(Object.assign({}, (
|
|
305
|
+
? Object.assign(Object.assign({}, (loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode)), { opsBeforeReturn: undefined }) : loadMode !== null && loadMode !== void 0 ? loadMode : defaultMode;
|
|
308
306
|
const onClosed = (err) => {
|
|
309
307
|
// pre-0.58 error message: containerClosedWithoutErrorDuringLoad
|
|
310
308
|
reject(err !== null && err !== void 0 ? err : new GenericError("Container closed without error during load"));
|
|
311
309
|
};
|
|
312
310
|
container.on("closed", onClosed);
|
|
313
311
|
container
|
|
314
|
-
.load(version, mode, pendingLocalState)
|
|
312
|
+
.load(version, mode, resolvedUrl, pendingLocalState)
|
|
315
313
|
.finally(() => {
|
|
316
314
|
container.removeListener("closed", onClosed);
|
|
317
315
|
})
|
|
318
316
|
.then((props) => {
|
|
319
|
-
event.end(Object.assign(Object.assign({}, props),
|
|
317
|
+
event.end(Object.assign(Object.assign({}, props), loadMode));
|
|
320
318
|
resolve(container);
|
|
321
319
|
}, (error) => {
|
|
322
320
|
const err = normalizeError(error);
|
|
@@ -326,13 +324,13 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
326
324
|
container.close(err);
|
|
327
325
|
onClosed(err);
|
|
328
326
|
});
|
|
329
|
-
}), { start: true, end: true, cancel: "generic" });
|
|
327
|
+
}), { start: true, end: true, cancel: "generic" }, disableRecordHeapSize !== true /* recordHeapSize */);
|
|
330
328
|
}
|
|
331
329
|
/**
|
|
332
330
|
* Create a new container in a detached state.
|
|
333
331
|
*/
|
|
334
|
-
static async createDetached(
|
|
335
|
-
const container = new Container(
|
|
332
|
+
static async createDetached(createProps, codeDetails) {
|
|
333
|
+
const container = new Container(createProps);
|
|
336
334
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "CreateDetached" }, async (_event) => {
|
|
337
335
|
await container.createDetached(codeDetails);
|
|
338
336
|
return container;
|
|
@@ -342,8 +340,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
342
340
|
* Create a new container in a detached state that is initialized with a
|
|
343
341
|
* snapshot from a previous detached container.
|
|
344
342
|
*/
|
|
345
|
-
static async rehydrateDetachedFromSnapshot(
|
|
346
|
-
const container = new Container(
|
|
343
|
+
static async rehydrateDetachedFromSnapshot(createProps, snapshot) {
|
|
344
|
+
const container = new Container(createProps);
|
|
347
345
|
return PerformanceEvent.timedExecAsync(container.mc.logger, { eventName: "RehydrateDetachedFromSnapshot" }, async (_event) => {
|
|
348
346
|
const deserializedSummary = JSON.parse(snapshot);
|
|
349
347
|
await container.rehydrateDetachedFromSnapshot(deserializedSummary);
|
|
@@ -387,7 +385,19 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
387
385
|
return this;
|
|
388
386
|
}
|
|
389
387
|
get resolvedUrl() {
|
|
390
|
-
|
|
388
|
+
var _a;
|
|
389
|
+
/**
|
|
390
|
+
* All attached containers will have a document service,
|
|
391
|
+
* this is required, as attached containers are attached to
|
|
392
|
+
* a service. Detached containers will neither have a document
|
|
393
|
+
* service or a resolved url as they only exist locally.
|
|
394
|
+
* in order to create a document service a resolved url must
|
|
395
|
+
* first be obtained, this is how the container is identified.
|
|
396
|
+
* Because of this, the document service's resolved url
|
|
397
|
+
* is always the same as the containers, as we had to
|
|
398
|
+
* obtain the resolved url, and then create the service from it.
|
|
399
|
+
*/
|
|
400
|
+
return (_a = this.service) === null || _a === void 0 ? void 0 : _a.resolvedUrl;
|
|
391
401
|
}
|
|
392
402
|
get loadedFromVersion() {
|
|
393
403
|
return this._loadedFromVersion;
|
|
@@ -473,18 +483,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
473
483
|
get isDirty() {
|
|
474
484
|
return this._dirtyContainer;
|
|
475
485
|
}
|
|
476
|
-
get serviceFactory() {
|
|
477
|
-
return this.loader.services.documentServiceFactory;
|
|
478
|
-
}
|
|
479
|
-
get urlResolver() {
|
|
480
|
-
return this.loader.services.urlResolver;
|
|
481
|
-
}
|
|
482
|
-
get scope() {
|
|
483
|
-
return this.loader.services.scope;
|
|
484
|
-
}
|
|
485
|
-
get codeLoader() {
|
|
486
|
-
return this.loader.services.codeLoader;
|
|
487
|
-
}
|
|
488
486
|
/**
|
|
489
487
|
* {@inheritDoc @fluidframework/container-definitions#IContainer.entryPoint}
|
|
490
488
|
*/
|
|
@@ -524,7 +522,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
524
522
|
return this.protocolHandler.quorum;
|
|
525
523
|
}
|
|
526
524
|
dispose(error) {
|
|
527
|
-
this._deltaManager.
|
|
525
|
+
this._deltaManager.dispose(error);
|
|
528
526
|
this.verifyClosed();
|
|
529
527
|
}
|
|
530
528
|
close(error) {
|
|
@@ -540,7 +538,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
540
538
|
assert(this._lifecycleState === "closed" || this._lifecycleState === "disposed", 0x314 /* Container properly closed */);
|
|
541
539
|
}
|
|
542
540
|
closeCore(error) {
|
|
543
|
-
var _a
|
|
541
|
+
var _a;
|
|
544
542
|
assert(!this.closed, 0x315 /* re-entrancy */);
|
|
545
543
|
try {
|
|
546
544
|
// Ensure that we raise all key events even if one of these throws
|
|
@@ -558,12 +556,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
558
556
|
this._lifecycleState = "closing";
|
|
559
557
|
(_a = this._protocolHandler) === null || _a === void 0 ? void 0 : _a.close();
|
|
560
558
|
this.connectionStateHandler.dispose();
|
|
561
|
-
(_b = this._context) === null || _b === void 0 ? void 0 : _b.dispose(error !== undefined ? new Error(error.message) : undefined);
|
|
562
|
-
this.storageAdapter.dispose();
|
|
563
|
-
// Notify storage about critical errors. They may be due to disconnect between client & server knowledge
|
|
564
|
-
// about file, like file being overwritten in storage, but client having stale local cache.
|
|
565
|
-
// Driver need to ensure all caches are cleared on critical errors
|
|
566
|
-
(_c = this.service) === null || _c === void 0 ? void 0 : _c.dispose(error);
|
|
567
559
|
}
|
|
568
560
|
catch (exception) {
|
|
569
561
|
this.mc.logger.sendErrorEvent({ eventName: "ContainerCloseException" }, exception);
|
|
@@ -652,8 +644,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
652
644
|
const appSummary = this.context.createSummary();
|
|
653
645
|
const protocolSummary = this.captureProtocolSummary();
|
|
654
646
|
const combinedSummary = combineAppAndProtocolSummary(appSummary, protocolSummary);
|
|
655
|
-
if (this.
|
|
656
|
-
this.loader.services.detachedBlobStorage.size > 0) {
|
|
647
|
+
if (this.detachedBlobStorage && this.detachedBlobStorage.size > 0) {
|
|
657
648
|
combinedSummary.tree[".hasAttachmentBlobs"] = {
|
|
658
649
|
type: SummaryType.Blob,
|
|
659
650
|
content: "true",
|
|
@@ -672,8 +663,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
672
663
|
assert(this._attachState === AttachState.Detached && !this.attachStarted, 0x205 /* "attach() called more than once" */);
|
|
673
664
|
this.attachStarted = true;
|
|
674
665
|
// If attachment blobs were uploaded in detached state we will go through a different attach flow
|
|
675
|
-
const hasAttachmentBlobs = this.
|
|
676
|
-
this.loader.services.detachedBlobStorage.size > 0;
|
|
666
|
+
const hasAttachmentBlobs = this.detachedBlobStorage !== undefined && this.detachedBlobStorage.size > 0;
|
|
677
667
|
try {
|
|
678
668
|
assert(this.deltaManager.inbound.length === 0, 0x0d6 /* "Inbound queue should be empty when attaching" */);
|
|
679
669
|
let summary;
|
|
@@ -697,31 +687,28 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
697
687
|
}
|
|
698
688
|
}
|
|
699
689
|
// Actually go and create the resolved document
|
|
700
|
-
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
701
|
-
ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
702
690
|
if (this.service === undefined) {
|
|
703
|
-
|
|
691
|
+
const createNewResolvedUrl = await this.urlResolver.resolve(request);
|
|
692
|
+
assert(this.client.details.type !== summarizerClientType &&
|
|
693
|
+
createNewResolvedUrl !== undefined, 0x2c4 /* "client should not be summarizer before container is created" */);
|
|
704
694
|
this.service = await runWithRetry(async () => this.serviceFactory.createContainer(summary, createNewResolvedUrl, this.subLogger, false), "containerAttach", this.mc.logger, {
|
|
705
695
|
cancel: this.closeSignal,
|
|
706
696
|
});
|
|
707
697
|
}
|
|
708
|
-
const resolvedUrl = this.service.resolvedUrl;
|
|
709
|
-
ensureFluidResolvedUrl(resolvedUrl);
|
|
710
|
-
this._resolvedUrl = resolvedUrl;
|
|
711
698
|
await this.storageAdapter.connectToService(this.service);
|
|
712
699
|
if (hasAttachmentBlobs) {
|
|
713
700
|
// upload blobs to storage
|
|
714
|
-
assert(!!this.
|
|
701
|
+
assert(!!this.detachedBlobStorage, 0x24e /* "assertion for type narrowing" */);
|
|
715
702
|
// build a table mapping IDs assigned locally to IDs assigned by storage and pass it to runtime to
|
|
716
703
|
// support blob handles that only know about the local IDs
|
|
717
704
|
const redirectTable = new Map();
|
|
718
705
|
// if new blobs are added while uploading, upload them too
|
|
719
|
-
while (redirectTable.size < this.
|
|
720
|
-
const newIds = this.
|
|
706
|
+
while (redirectTable.size < this.detachedBlobStorage.size) {
|
|
707
|
+
const newIds = this.detachedBlobStorage
|
|
721
708
|
.getBlobIds()
|
|
722
709
|
.filter((id) => !redirectTable.has(id));
|
|
723
710
|
for (const id of newIds) {
|
|
724
|
-
const blob = await this.
|
|
711
|
+
const blob = await this.detachedBlobStorage.readBlob(id);
|
|
725
712
|
const response = await this.storageAdapter.createBlob(blob);
|
|
726
713
|
redirectTable.set(id, response.id);
|
|
727
714
|
}
|
|
@@ -756,12 +743,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
756
743
|
catch (error) {
|
|
757
744
|
// add resolved URL on error object so that host has the ability to find this document and delete it
|
|
758
745
|
const newError = normalizeError(error);
|
|
759
|
-
|
|
760
|
-
if (isFluidResolvedUrl(resolvedUrl)) {
|
|
761
|
-
newError.addTelemetryProperties({ resolvedUrl: resolvedUrl.url });
|
|
762
|
-
}
|
|
746
|
+
newError.addTelemetryProperties({ resolvedUrl: (_a = this.resolvedUrl) === null || _a === void 0 ? void 0 : _a.url });
|
|
763
747
|
this.close(newError);
|
|
764
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
765
748
|
throw newError;
|
|
766
749
|
}
|
|
767
750
|
}, { start: true, end: true, cancel: "generic" });
|
|
@@ -856,7 +839,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
856
839
|
.catch(() => false);
|
|
857
840
|
}
|
|
858
841
|
async processCodeProposal() {
|
|
859
|
-
var _a;
|
|
860
842
|
const codeDetails = this.getCodeDetailsFromQuorum();
|
|
861
843
|
await Promise.all([
|
|
862
844
|
this.deltaManager.inbound.pause(),
|
|
@@ -870,19 +852,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
870
852
|
// pre-0.58 error message: existingContextDoesNotSatisfyIncomingProposal
|
|
871
853
|
const error = new GenericError("Existing context does not satisfy incoming proposal");
|
|
872
854
|
this.close(error);
|
|
873
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
874
855
|
}
|
|
875
856
|
async getVersion(version) {
|
|
876
857
|
const versions = await this.storageAdapter.getVersions(version, 1);
|
|
877
858
|
return versions[0];
|
|
878
859
|
}
|
|
879
|
-
recordConnectStartTime() {
|
|
880
|
-
if (this.connectionTransitionTimes[ConnectionState.Disconnected] === undefined) {
|
|
881
|
-
this.connectionTransitionTimes[ConnectionState.Disconnected] = performance.now();
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
860
|
connectToDeltaStream(args) {
|
|
885
|
-
this.recordConnectStartTime();
|
|
886
861
|
// All agents need "write" access, including summarizer.
|
|
887
862
|
if (!this._canReconnect || !this.client.details.capabilities.interactive) {
|
|
888
863
|
args.mode = "write";
|
|
@@ -894,12 +869,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
894
869
|
*
|
|
895
870
|
* @param specifiedVersion - Version SHA to load snapshot. If not specified, will fetch the latest snapshot.
|
|
896
871
|
*/
|
|
897
|
-
async load(specifiedVersion, loadMode, pendingLocalState) {
|
|
872
|
+
async load(specifiedVersion, loadMode, resolvedUrl, pendingLocalState) {
|
|
898
873
|
var _a;
|
|
899
|
-
|
|
900
|
-
throw new Error("Attempting to load without a resolved url");
|
|
901
|
-
}
|
|
902
|
-
this.service = await this.serviceFactory.createDocumentService(this._resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
874
|
+
this.service = await this.serviceFactory.createDocumentService(resolvedUrl, this.subLogger, this.client.details.type === summarizerClientType);
|
|
903
875
|
// Ideally we always connect as "read" by default.
|
|
904
876
|
// Currently that works with SPO & r11s, because we get "write" connection when connecting to non-existing file.
|
|
905
877
|
// We should not rely on it by (one of them will address the issue, but we need to address both)
|
|
@@ -925,9 +897,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
925
897
|
else {
|
|
926
898
|
// if we have pendingLocalState we can load without storage; don't wait for connection
|
|
927
899
|
this.storageAdapter.connectToService(this.service).catch((error) => {
|
|
928
|
-
var _a;
|
|
929
900
|
this.close(error);
|
|
930
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
931
901
|
});
|
|
932
902
|
}
|
|
933
903
|
this._attachState = AttachState.Attached;
|
|
@@ -1053,8 +1023,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1053
1023
|
}
|
|
1054
1024
|
async rehydrateDetachedFromSnapshot(detachedContainerSnapshot) {
|
|
1055
1025
|
if (detachedContainerSnapshot.tree[".hasAttachmentBlobs"] !== undefined) {
|
|
1056
|
-
assert(!!this.
|
|
1057
|
-
this.loader.services.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1026
|
+
assert(!!this.detachedBlobStorage && this.detachedBlobStorage.size > 0, 0x250 /* "serialized container with attachment blobs must be rehydrated with detached blob storage" */);
|
|
1058
1027
|
delete detachedContainerSnapshot.tree[".hasAttachmentBlobs"];
|
|
1059
1028
|
}
|
|
1060
1029
|
const snapshotTree = getSnapshotTreeFromSerializedContainer(detachedContainerSnapshot);
|
|
@@ -1107,9 +1076,7 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1107
1076
|
this.initializeProtocolState(attributes, quorumSnapshot);
|
|
1108
1077
|
}
|
|
1109
1078
|
initializeProtocolState(attributes, quorumSnapshot) {
|
|
1110
|
-
|
|
1111
|
-
const protocolHandlerBuilder = (_a = this.protocolHandlerBuilder) !== null && _a !== void 0 ? _a : ((...args) => new ProtocolHandler(...args, new Audience()));
|
|
1112
|
-
const protocol = protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })));
|
|
1079
|
+
const protocol = this.protocolHandlerBuilder(attributes, quorumSnapshot, (key, value) => this.submitMessage(MessageType.Propose, JSON.stringify({ key, value })));
|
|
1113
1080
|
const protocolLogger = ChildLogger.create(this.subLogger, "ProtocolHandler");
|
|
1114
1081
|
protocol.quorum.on("error", (error) => {
|
|
1115
1082
|
protocolLogger.sendErrorEvent(error);
|
|
@@ -1129,10 +1096,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1129
1096
|
});
|
|
1130
1097
|
}
|
|
1131
1098
|
this.processCodeProposal().catch((error) => {
|
|
1132
|
-
var _a;
|
|
1133
1099
|
const normalizedError = normalizeError(error);
|
|
1134
1100
|
this.close(normalizedError);
|
|
1135
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, normalizedError);
|
|
1136
1101
|
throw error;
|
|
1137
1102
|
});
|
|
1138
1103
|
}
|
|
@@ -1216,6 +1181,12 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1216
1181
|
assert(this.connectionMode === details.mode, 0x4b7 /* mismatch */);
|
|
1217
1182
|
this.connectionStateHandler.receivedConnectEvent(details);
|
|
1218
1183
|
});
|
|
1184
|
+
deltaManager.on("establishingConnection", (reason) => {
|
|
1185
|
+
this.connectionStateHandler.establishingConnection(reason);
|
|
1186
|
+
});
|
|
1187
|
+
deltaManager.on("cancelEstablishingConnection", (reason) => {
|
|
1188
|
+
this.connectionStateHandler.cancelEstablishingConnection(reason);
|
|
1189
|
+
});
|
|
1219
1190
|
deltaManager.on("disconnect", (reason, error) => {
|
|
1220
1191
|
var _a;
|
|
1221
1192
|
(_a = this.collabWindowTracker) === null || _a === void 0 ? void 0 : _a.stopSequenceNumberUpdate();
|
|
@@ -1272,8 +1243,8 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1272
1243
|
time - this.connectionTransitionTimes[ConnectionState.Disconnected];
|
|
1273
1244
|
durationFromDisconnected = TelemetryLogger.formatTick(durationFromDisconnected);
|
|
1274
1245
|
}
|
|
1275
|
-
else {
|
|
1276
|
-
// This info is of most
|
|
1246
|
+
else if (value === ConnectionState.CatchingUp) {
|
|
1247
|
+
// This info is of most interesting while Catching Up.
|
|
1277
1248
|
checkpointSequenceNumber = this.deltaManager.lastKnownSeqNumber;
|
|
1278
1249
|
if (this.deltaManager.hasCheckpointSequenceNumber) {
|
|
1279
1250
|
opsBehind = checkpointSequenceNumber - this.deltaManager.lastSequenceNumber;
|
|
@@ -1322,7 +1293,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1322
1293
|
}
|
|
1323
1294
|
// back-compat: ADO #1385: Remove in the future, summary op should come through submitSummaryMessage()
|
|
1324
1295
|
submitContainerMessage(type, contents, batch, metadata) {
|
|
1325
|
-
var _a;
|
|
1326
1296
|
switch (type) {
|
|
1327
1297
|
case MessageType.Operation:
|
|
1328
1298
|
return this.submitMessage(type, JSON.stringify(contents), batch, metadata);
|
|
@@ -1331,7 +1301,6 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1331
1301
|
default: {
|
|
1332
1302
|
const newError = new GenericError("invalidContainerSubmitOpType", undefined /* error */, { messageType: type });
|
|
1333
1303
|
this.close(newError);
|
|
1334
|
-
(_a = this.dispose) === null || _a === void 0 ? void 0 : _a.call(this, newError);
|
|
1335
1304
|
return -1;
|
|
1336
1305
|
}
|
|
1337
1306
|
}
|
|
@@ -1440,8 +1409,9 @@ export class Container extends EventEmitterWithErrorHandling {
|
|
|
1440
1409
|
assert(((_a = this._context) === null || _a === void 0 ? void 0 : _a.disposed) !== false, 0x0dd /* "Existing context not disposed" */);
|
|
1441
1410
|
// The relative loader will proxy requests to '/' to the loader itself assuming no non-cache flags
|
|
1442
1411
|
// are set. Global requests will still go directly to the loader
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1412
|
+
const maybeLoader = this.scope;
|
|
1413
|
+
const loader = new RelativeLoader(this, maybeLoader.ILoader);
|
|
1414
|
+
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, referenceSequenceNumber) => this.submitSummaryMessage(summaryOp, referenceSequenceNumber), (batch, referenceSequenceNumber) => this.submitBatch(batch, referenceSequenceNumber), (message) => this.submitSignal(message), (error) => this.dispose(error), (error) => this.close(error), Container.version, (dirty) => this.updateDirtyContainerState(dirty), existing, pendingLocalState);
|
|
1445
1415
|
this.emit("contextChanged", codeDetails);
|
|
1446
1416
|
}
|
|
1447
1417
|
updateDirtyContainerState(dirty) {
|