@fluidframework/container-runtime 0.53.0 → 0.54.0-47413
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/dist/containerRuntime.d.ts +25 -16
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +125 -77
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +29 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +29 -4
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +7 -3
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +54 -5
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +22 -2
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +112 -34
- package/dist/garbageCollection.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/runningSummarizer.d.ts +3 -2
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +6 -6
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts +22 -0
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +135 -33
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerTypes.d.ts +1 -8
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts +1 -0
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +2 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryManager.d.ts +0 -15
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +1 -35
- package/dist/summaryManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +25 -16
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +131 -83
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +29 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +29 -4
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +7 -3
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +54 -5
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +22 -2
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +114 -36
- package/lib/garbageCollection.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/runningSummarizer.d.ts +3 -2
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +6 -6
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts +22 -0
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +135 -33
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerTypes.d.ts +1 -8
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts +1 -0
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +1 -0
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryManager.d.ts +0 -15
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +1 -34
- package/lib/summaryManager.js.map +1 -1
- package/package.json +13 -13
- package/src/containerRuntime.ts +176 -93
- package/src/dataStoreContext.ts +44 -6
- package/src/dataStores.ts +84 -4
- package/src/garbageCollection.ts +137 -46
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +12 -10
- package/src/summarizer.ts +154 -38
- package/src/summarizerTypes.ts +2 -9
- package/src/summaryFormat.ts +1 -0
- package/src/summaryManager.ts +2 -49
- package/dist/localStorageFeatureGates.d.ts +0 -13
- package/dist/localStorageFeatureGates.d.ts.map +0 -1
- package/dist/localStorageFeatureGates.js +0 -31
- package/dist/localStorageFeatureGates.js.map +0 -1
- package/lib/localStorageFeatureGates.d.ts +0 -13
- package/lib/localStorageFeatureGates.d.ts.map +0 -1
- package/lib/localStorageFeatureGates.js +0 -27
- package/lib/localStorageFeatureGates.js.map +0 -1
- package/src/localStorageFeatureGates.ts +0 -27
package/lib/containerRuntime.js
CHANGED
|
@@ -2,28 +2,28 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { AttachState, } from "@fluidframework/container-definitions";
|
|
5
|
+
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
6
6
|
import { assert, Trace, TypedEventEmitter, unreachableCase, performance, } from "@fluidframework/common-utils";
|
|
7
|
-
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, } from "@fluidframework/telemetry-utils";
|
|
7
|
+
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
8
|
+
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
8
9
|
import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
|
|
9
|
-
import { DataCorruptionError, GenericError, extractSafePropertiesFromMessage } from "@fluidframework/container-utils";
|
|
10
|
+
import { CreateProcessingError, DataCorruptionError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
|
|
10
11
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
11
12
|
import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
12
|
-
import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, responseToException, seqFromTree, convertSummaryTreeToITree, } from "@fluidframework/runtime-utils";
|
|
13
|
+
import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, convertSummaryTreeToITree, } from "@fluidframework/runtime-utils";
|
|
13
14
|
import { v4 as uuid } from "uuid";
|
|
14
15
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
15
16
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
16
17
|
import { Summarizer } from "./summarizer";
|
|
17
|
-
import {
|
|
18
|
+
import { SummaryManager } from "./summaryManager";
|
|
18
19
|
import { DeltaScheduler } from "./deltaScheduler";
|
|
19
20
|
import { ReportOpPerfTelemetry, latencyThreshold } from "./connectionTelemetry";
|
|
20
21
|
import { PendingStateManager } from "./pendingStateManager";
|
|
21
22
|
import { pkgVersion } from "./packageVersion";
|
|
22
23
|
import { BlobManager } from "./blobManager";
|
|
23
24
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
24
|
-
import { blobsTreeName, chunksBlobName, electedSummarizerBlobName, extractSummaryMetadataMessage, metadataBlobName, wrapSummaryInChannelsTree, } from "./summaryFormat";
|
|
25
|
+
import { aliasBlobName, blobsTreeName, chunksBlobName, electedSummarizerBlobName, extractSummaryMetadataMessage, metadataBlobName, wrapSummaryInChannelsTree, } from "./summaryFormat";
|
|
25
26
|
import { SummaryCollection } from "./summaryCollection";
|
|
26
|
-
import { getLocalStorageFeatureGate } from "./localStorageFeatureGates";
|
|
27
27
|
import { OrderedClientCollection, OrderedClientElection } from "./orderedClientElection";
|
|
28
28
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
29
29
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
@@ -41,6 +41,8 @@ export var ContainerMessageType;
|
|
|
41
41
|
ContainerMessageType["BlobAttach"] = "blobAttach";
|
|
42
42
|
// Ties our new clientId to our old one on reconnect
|
|
43
43
|
ContainerMessageType["Rejoin"] = "rejoin";
|
|
44
|
+
// Sets the alias of a root data store
|
|
45
|
+
ContainerMessageType["Alias"] = "alias";
|
|
44
46
|
})(ContainerMessageType || (ContainerMessageType = {}));
|
|
45
47
|
// Consider idle 5s of no activity. And snapshot if a minute has gone by with no snapshot.
|
|
46
48
|
const IdleDetectionTime = 5000;
|
|
@@ -55,12 +57,13 @@ const DefaultSummaryConfiguration = {
|
|
|
55
57
|
maxAckWaitTime: 120000,
|
|
56
58
|
};
|
|
57
59
|
// Local storage key to set the default flush mode to TurnBased
|
|
58
|
-
const turnBasedFlushModeKey = "
|
|
60
|
+
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
59
61
|
export function isRuntimeMessage(message) {
|
|
60
62
|
switch (message.type) {
|
|
61
63
|
case ContainerMessageType.FluidDataStoreOp:
|
|
62
64
|
case ContainerMessageType.ChunkedOp:
|
|
63
65
|
case ContainerMessageType.Attach:
|
|
66
|
+
case ContainerMessageType.Alias:
|
|
64
67
|
case ContainerMessageType.BlobAttach:
|
|
65
68
|
case ContainerMessageType.Rejoin:
|
|
66
69
|
case MessageType.Operation:
|
|
@@ -324,8 +327,8 @@ export function getDeviceSpec() {
|
|
|
324
327
|
* It will define the store level mappings.
|
|
325
328
|
*/
|
|
326
329
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
327
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
328
|
-
var _a, _b, _c, _d;
|
|
330
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
331
|
+
var _a, _b, _c, _d, _e;
|
|
329
332
|
super();
|
|
330
333
|
this.context = context;
|
|
331
334
|
this.registry = registry;
|
|
@@ -334,14 +337,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
334
337
|
this.logger = logger;
|
|
335
338
|
this.requestHandler = requestHandler;
|
|
336
339
|
this._storage = _storage;
|
|
337
|
-
// back-compat: Used by loader in <= 0.35
|
|
338
|
-
/**
|
|
339
|
-
* @internal
|
|
340
|
-
* @deprecated Back-compat only. Used by the loader in versions earlier than 0.35.
|
|
341
|
-
*/
|
|
342
|
-
this.runtimeVersion = pkgVersion;
|
|
343
340
|
this._orderSequentiallyCalls = 0;
|
|
344
|
-
this._flushMode = ContainerRuntime.defaultFlushMode;
|
|
345
341
|
this.needsFlush = false;
|
|
346
342
|
this.flushTrigger = false;
|
|
347
343
|
this.paused = false;
|
|
@@ -384,7 +380,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
384
380
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
385
381
|
// disableSummaries is turned on. We are throwing instead of returning a failure here,
|
|
386
382
|
// because it is a misuse of the API rather than an expected failure.
|
|
387
|
-
throw new
|
|
383
|
+
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
388
384
|
}
|
|
389
385
|
};
|
|
390
386
|
this.enqueueSummarize = (...args) => {
|
|
@@ -398,7 +394,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
398
394
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
399
395
|
// generateSummaries is turned off. We are throwing instead of returning a failure here,
|
|
400
396
|
// because it is a misuse of the API rather than an expected failure.
|
|
401
|
-
throw new
|
|
397
|
+
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
402
398
|
}
|
|
403
399
|
};
|
|
404
400
|
this.baseSummaryMessage = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
@@ -421,20 +417,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
421
417
|
this.disableIsolatedChannels = (_a = this.runtimeOptions.summaryOptions.disableIsolatedChannels) !== null && _a !== void 0 ? _a : false;
|
|
422
418
|
this._connected = this.context.connected;
|
|
423
419
|
this.chunkMap = new Map(chunks);
|
|
424
|
-
this.
|
|
425
|
-
this.
|
|
420
|
+
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
421
|
+
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
422
|
+
this._flushMode =
|
|
423
|
+
((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? FlushMode.TurnBased : FlushMode.Immediate;
|
|
426
424
|
/**
|
|
427
425
|
* Function that return the current server timestamp. This is used by the garbage collector to set the
|
|
428
426
|
* time when a node becomes unreferenced.
|
|
429
|
-
*
|
|
427
|
+
* We use the timestamp of the last op for current timestamp. However, there can be cases where
|
|
430
428
|
* we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
|
|
431
|
-
* of this client's connection
|
|
429
|
+
* of this client's connection.
|
|
432
430
|
*/
|
|
433
431
|
const getCurrentTimestamp = () => {
|
|
434
|
-
var _a, _b;
|
|
435
|
-
|
|
432
|
+
var _a, _b, _c;
|
|
433
|
+
const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
|
|
434
|
+
const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
|
|
435
|
+
return (_c = (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : timestamp) !== null && _c !== void 0 ? _c : Date.now();
|
|
436
436
|
};
|
|
437
|
-
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.
|
|
437
|
+
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
438
438
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
439
439
|
this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
|
|
440
440
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
@@ -455,8 +455,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
455
455
|
if (this.context.baseSnapshot) {
|
|
456
456
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
|
|
457
457
|
}
|
|
458
|
-
this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getInitialGCSummaryDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getInitialGCSummaryDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.
|
|
459
|
-
this.blobManager = new BlobManager(this.
|
|
458
|
+
this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getInitialGCSummaryDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getInitialGCSummaryDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getDataStoreBaseGCDetails(), (id) => this.garbageCollector.nodeChanged(id), new Map(dataStoreAliasMap));
|
|
459
|
+
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
|
|
460
460
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
461
461
|
this.deltaSender = this.deltaManager;
|
|
462
462
|
this.pendingStateManager = new PendingStateManager(this, async (type, content) => this.applyStashedOp(type, content), context.pendingLocalState);
|
|
@@ -464,49 +464,45 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
464
464
|
this.clearPartialChunks(clientId);
|
|
465
465
|
});
|
|
466
466
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
467
|
-
// Only create a SummaryManager if summaries are enabled and we are not the summarizer client
|
|
468
467
|
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
469
468
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
470
469
|
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
471
470
|
}
|
|
472
|
-
if (this.summariesDisabled
|
|
473
|
-
this.
|
|
471
|
+
if (this.summariesDisabled) {
|
|
472
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
474
473
|
}
|
|
475
474
|
else {
|
|
476
|
-
const maxOpsSinceLastSummary = (_b = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _b !== void 0 ? _b : 7000;
|
|
477
|
-
const defaultAction = () => {
|
|
478
|
-
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
479
|
-
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
480
|
-
// unregister default to no log on every op after falling behind
|
|
481
|
-
// and register summary ack handler to re-register this handler
|
|
482
|
-
// after successful summary
|
|
483
|
-
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
484
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
|
|
485
|
-
// we've caught up, so re-register the default action to monitor for
|
|
486
|
-
// falling behind, and unregister ourself
|
|
487
|
-
this.summaryCollection.on("default", defaultAction);
|
|
488
|
-
});
|
|
489
|
-
this.summaryCollection.off("default", defaultAction);
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
this.summaryCollection.on("default", defaultAction);
|
|
493
475
|
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
494
476
|
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
495
477
|
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
496
|
-
const summarizerClientElectionEnabled = (_c =
|
|
478
|
+
const summarizerClientElectionEnabled = (_c = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _c !== void 0 ? _c : ((_d = this.runtimeOptions.summaryOptions) === null || _d === void 0 ? void 0 : _d.summarizerClientElection) === true;
|
|
479
|
+
const maxOpsSinceLastSummary = (_e = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _e !== void 0 ? _e : 7000;
|
|
497
480
|
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
498
481
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
499
|
-
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.
|
|
482
|
+
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
500
483
|
}
|
|
501
484
|
else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
485
|
+
// Only create a SummaryManager and SummarizerClientElection
|
|
486
|
+
// if summaries are enabled and we are not the summarizer client.
|
|
487
|
+
const defaultAction = () => {
|
|
488
|
+
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
489
|
+
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
490
|
+
// unregister default to no log on every op after falling behind
|
|
491
|
+
// and register summary ack handler to re-register this handler
|
|
492
|
+
// after successful summary
|
|
493
|
+
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
494
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
|
|
495
|
+
// we've caught up, so re-register the default action to monitor for
|
|
496
|
+
// falling behind, and unregister ourself
|
|
497
|
+
this.summaryCollection.on("default", defaultAction);
|
|
498
|
+
});
|
|
499
|
+
this.summaryCollection.off("default", defaultAction);
|
|
500
|
+
}
|
|
507
501
|
};
|
|
502
|
+
this.summaryCollection.on("default", defaultAction);
|
|
503
|
+
// Create the SummaryManager and mark the initial state
|
|
508
504
|
this.summaryManager = new SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
509
|
-
this.summaryCollection, this.logger, formRequestSummarizerFn(this.context.loader
|
|
505
|
+
this.summaryCollection, this.logger, this.formRequestSummarizerFn(this.context.loader), new Throttler(60 * 1000, // 60 sec delay window
|
|
510
506
|
30 * 1000, // 30 sec max delay
|
|
511
507
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
512
508
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -519,7 +515,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
519
515
|
this.deltaManager.on("readonly", (readonly) => {
|
|
520
516
|
// we accumulate ops while being in read-only state.
|
|
521
517
|
// once user gets write permissions and we have active connection, flush all pending ops.
|
|
522
|
-
// eslint-disable-next-line max-len
|
|
523
518
|
assert(readonly === this.deltaManager.readOnlyInfo.readonly, 0x124 /* "inconsistent readonly property/event state" */);
|
|
524
519
|
// We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
|
|
525
520
|
// when we either never send an op, or attempted to send it but we know for sure it was not
|
|
@@ -556,7 +551,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
556
551
|
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
557
552
|
*/
|
|
558
553
|
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing) {
|
|
559
|
-
var _a, _b, _c
|
|
554
|
+
var _a, _b, _c;
|
|
560
555
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
561
556
|
const passLogger = (_a = context.taggedLogger) !== null && _a !== void 0 ? _a : new TaggedLoggerAdapter(context.logger);
|
|
562
557
|
const logger = ChildLogger.create(passLogger, undefined, {
|
|
@@ -598,19 +593,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
598
593
|
return readAndParse(storage, blobId);
|
|
599
594
|
}
|
|
600
595
|
};
|
|
601
|
-
const chunks
|
|
602
|
-
|
|
603
|
-
|
|
596
|
+
const [chunks, metadata, electedSummarizerData, aliases] = await Promise.all([
|
|
597
|
+
tryFetchBlob(chunksBlobName),
|
|
598
|
+
tryFetchBlob(metadataBlobName),
|
|
599
|
+
tryFetchBlob(electedSummarizerBlobName),
|
|
600
|
+
tryFetchBlob(aliasBlobName),
|
|
601
|
+
]);
|
|
604
602
|
const loadExisting = existing === true || context.existing === true;
|
|
605
603
|
// read snapshot blobs needed for BlobManager to load
|
|
606
|
-
const blobManagerSnapshot = await BlobManager.load((
|
|
604
|
+
const blobManagerSnapshot = await BlobManager.load((_b = context.baseSnapshot) === null || _b === void 0 ? void 0 : _b.trees[blobsTreeName], async (id) => {
|
|
607
605
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
608
606
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
609
607
|
assert(storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
610
608
|
return readAndParse(storage, id);
|
|
611
609
|
});
|
|
612
610
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
613
|
-
const runtimeSequenceNumber = (
|
|
611
|
+
const runtimeSequenceNumber = (_c = metadata === null || metadata === void 0 ? void 0 : metadata.message) === null || _c === void 0 ? void 0 : _c.sequenceNumber;
|
|
614
612
|
if (runtimeSequenceNumber !== undefined) {
|
|
615
613
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
616
614
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
@@ -625,13 +623,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
625
623
|
}
|
|
626
624
|
}
|
|
627
625
|
}
|
|
628
|
-
const runtime = new ContainerRuntime(context, registry, metadata, electedSummarizerData, chunks, {
|
|
626
|
+
const runtime = new ContainerRuntime(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
629
627
|
summaryOptions,
|
|
630
628
|
gcOptions,
|
|
631
629
|
loadSequenceNumberVerification,
|
|
632
630
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
|
|
633
631
|
return runtime;
|
|
634
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* @deprecated This will be removed in a later release. Deprecated in 0.53
|
|
635
|
+
*/
|
|
635
636
|
get id() {
|
|
636
637
|
return this.context.id;
|
|
637
638
|
}
|
|
@@ -682,6 +683,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
682
683
|
get attachState() {
|
|
683
684
|
return this.context.attachState;
|
|
684
685
|
}
|
|
686
|
+
get IFluidHandleContext() {
|
|
687
|
+
return this.handleContext;
|
|
688
|
+
}
|
|
685
689
|
get connected() {
|
|
686
690
|
return this._connected;
|
|
687
691
|
}
|
|
@@ -702,13 +706,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
702
706
|
get writeGCDataAtRoot() {
|
|
703
707
|
return this.garbageCollector.writeDataAtRoot;
|
|
704
708
|
}
|
|
705
|
-
static get defaultFlushMode() {
|
|
706
|
-
return getLocalStorageFeatureGate(turnBasedFlushModeKey) ? FlushMode.TurnBased : FlushMode.Immediate;
|
|
707
|
-
}
|
|
708
709
|
get summarizer() {
|
|
709
710
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
710
711
|
return this._summarizer;
|
|
711
712
|
}
|
|
713
|
+
get summariesDisabled() {
|
|
714
|
+
var _a;
|
|
715
|
+
return this.runtimeOptions.summaryOptions.disableSummaries === true ||
|
|
716
|
+
((_a = this.runtimeOptions.summaryOptions.summaryConfigOverrides) === null || _a === void 0 ? void 0 : _a.disableSummaries) === true;
|
|
717
|
+
}
|
|
712
718
|
dispose(error) {
|
|
713
719
|
var _a;
|
|
714
720
|
if (this._disposed) {
|
|
@@ -868,6 +874,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
868
874
|
const content = JSON.stringify([...this.chunkMap]);
|
|
869
875
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
870
876
|
}
|
|
877
|
+
const dataStoreAliases = this.dataStores.aliases();
|
|
878
|
+
if (dataStoreAliases.size > 0) {
|
|
879
|
+
addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
880
|
+
}
|
|
871
881
|
if (this.summarizerClientElection) {
|
|
872
882
|
const electedSummarizerContent = JSON.stringify((_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.serialize());
|
|
873
883
|
addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
|
|
@@ -919,6 +929,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
919
929
|
return this.dataStores.applyStashedOp(op);
|
|
920
930
|
case ContainerMessageType.Attach:
|
|
921
931
|
return this.dataStores.applyStashedAttachOp(op);
|
|
932
|
+
case ContainerMessageType.Alias:
|
|
922
933
|
case ContainerMessageType.BlobAttach:
|
|
923
934
|
return;
|
|
924
935
|
case ContainerMessageType.ChunkedOp:
|
|
@@ -940,7 +951,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
940
951
|
this.replayPendingStates();
|
|
941
952
|
}
|
|
942
953
|
this.dataStores.setConnectionState(connected, clientId);
|
|
943
|
-
raiseConnectedEvent(this.
|
|
954
|
+
raiseConnectedEvent(this.mc.logger, this, connected, clientId);
|
|
944
955
|
}
|
|
945
956
|
process(messageArg, local) {
|
|
946
957
|
var _a;
|
|
@@ -974,6 +985,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
974
985
|
case ContainerMessageType.Attach:
|
|
975
986
|
this.dataStores.processAttachMessage(message, local || localAck);
|
|
976
987
|
break;
|
|
988
|
+
case ContainerMessageType.Alias:
|
|
989
|
+
this.processAliasMessage(message, localOpMetadata, local);
|
|
990
|
+
break;
|
|
977
991
|
case ContainerMessageType.FluidDataStoreOp:
|
|
978
992
|
// if localAck === true, treat this as a local op because it's one we sent on a previous container
|
|
979
993
|
this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
|
|
@@ -992,6 +1006,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
992
1006
|
throw e;
|
|
993
1007
|
}
|
|
994
1008
|
}
|
|
1009
|
+
processAliasMessage(message, localOpMetadata, local) {
|
|
1010
|
+
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1011
|
+
}
|
|
995
1012
|
processSignal(message, local) {
|
|
996
1013
|
const envelope = message.content;
|
|
997
1014
|
const transformed = {
|
|
@@ -1041,6 +1058,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1041
1058
|
return;
|
|
1042
1059
|
}
|
|
1043
1060
|
this.needsFlush = false;
|
|
1061
|
+
// Did we disconnect in the middle of turn-based batch?
|
|
1062
|
+
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1063
|
+
if (!this.canSendOps()) {
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1044
1066
|
return this.deltaSender.flush();
|
|
1045
1067
|
}
|
|
1046
1068
|
orderSequentially(callback) {
|
|
@@ -1057,11 +1079,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1057
1079
|
this.setFlushMode(FlushMode.TurnBased);
|
|
1058
1080
|
try {
|
|
1059
1081
|
this.trackOrderSequentiallyCalls(callback);
|
|
1060
|
-
}
|
|
1061
|
-
finally {
|
|
1062
1082
|
this.flush();
|
|
1063
1083
|
this.setFlushMode(savedFlushMode);
|
|
1064
1084
|
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
this.closeFn(CreateProcessingError(error, "orderSequentially"));
|
|
1087
|
+
}
|
|
1065
1088
|
}
|
|
1066
1089
|
trackOrderSequentiallyCalls(callback) {
|
|
1067
1090
|
try {
|
|
@@ -1229,12 +1252,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1229
1252
|
return this.dataStores.updateUsedRoutes(usedRoutes, gcTimestamp);
|
|
1230
1253
|
}
|
|
1231
1254
|
/**
|
|
1232
|
-
* Runs garbage collection and
|
|
1255
|
+
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
1233
1256
|
* @returns the statistics of the garbage collection run.
|
|
1234
1257
|
*/
|
|
1235
1258
|
async collectGarbage(options) {
|
|
1236
1259
|
return this.garbageCollector.collectGarbage(options);
|
|
1237
1260
|
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Called when a new outbound reference is added to another node. This is used by garbage collection to identify
|
|
1263
|
+
* all references added in the system.
|
|
1264
|
+
* @param srcHandle - The handle of the node that added the reference.
|
|
1265
|
+
* @param outboundHandle - The handle of the outbound node that is referenced.
|
|
1266
|
+
*/
|
|
1267
|
+
addedGCOutboundReference(srcHandle, outboundHandle) {
|
|
1268
|
+
this.garbageCollector.addedOutboundReference(srcHandle.absolutePath, outboundHandle.absolutePath);
|
|
1269
|
+
}
|
|
1238
1270
|
/**
|
|
1239
1271
|
* Generates the summary tree, uploads it to storage, and then submits the summarize op.
|
|
1240
1272
|
* This is intended to be called by the summarizer, since it is the implementation of
|
|
@@ -1424,10 +1456,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1424
1456
|
this.dirtyContainer = dirty;
|
|
1425
1457
|
if (this.emitDirtyDocumentEvent) {
|
|
1426
1458
|
this.emit(dirty ? "dirty" : "saved");
|
|
1427
|
-
|
|
1428
|
-
if (this.context.updateDirtyContainerState !== undefined) {
|
|
1429
|
-
this.context.updateDirtyContainerState(dirty);
|
|
1430
|
-
}
|
|
1459
|
+
this.context.updateDirtyContainerState(dirty);
|
|
1431
1460
|
}
|
|
1432
1461
|
}
|
|
1433
1462
|
submitDataStoreOp(id, contents, localOpMetadata = undefined) {
|
|
@@ -1508,7 +1537,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1508
1537
|
// That might be not what caller hopes to get, but we can look deeper if telemetry tells us it's a problem.
|
|
1509
1538
|
const middleOfBatch = this.flushMode === FlushMode.TurnBased && this.needsFlush;
|
|
1510
1539
|
if (middleOfBatch) {
|
|
1511
|
-
this.
|
|
1540
|
+
this.mc.logger.sendErrorEvent({ eventName: "submitSystemMessageError", type });
|
|
1512
1541
|
}
|
|
1513
1542
|
return this.context.submitFn(type, contents, middleOfBatch);
|
|
1514
1543
|
}
|
|
@@ -1541,6 +1570,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1541
1570
|
this.dataStores.resubmitDataStoreOp(content, localOpMetadata);
|
|
1542
1571
|
break;
|
|
1543
1572
|
case ContainerMessageType.Attach:
|
|
1573
|
+
case ContainerMessageType.Alias:
|
|
1544
1574
|
this.submit(type, content, localOpMetadata);
|
|
1545
1575
|
break;
|
|
1546
1576
|
case ContainerMessageType.ChunkedOp:
|
|
@@ -1572,7 +1602,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1572
1602
|
* @returns downloaded snapshot's reference sequence number
|
|
1573
1603
|
*/
|
|
1574
1604
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
1575
|
-
const snapshot = await this.fetchSnapshotFromStorage(
|
|
1605
|
+
const snapshot = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
1576
1606
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1577
1607
|
fetchLatest: true,
|
|
1578
1608
|
});
|
|
@@ -1601,12 +1631,30 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1601
1631
|
return this.pendingStateManager.getLocalState();
|
|
1602
1632
|
}
|
|
1603
1633
|
/**
|
|
1604
|
-
*
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
return
|
|
1609
|
-
|
|
1634
|
+
* * Forms a function that will request a Summarizer.
|
|
1635
|
+
* @param loaderRouter - the loader acting as an IFluidRouter
|
|
1636
|
+
* */
|
|
1637
|
+
formRequestSummarizerFn(loaderRouter) {
|
|
1638
|
+
return async () => {
|
|
1639
|
+
const request = {
|
|
1640
|
+
headers: {
|
|
1641
|
+
[LoaderHeader.cache]: false,
|
|
1642
|
+
[LoaderHeader.clientDetails]: {
|
|
1643
|
+
capabilities: { interactive: false },
|
|
1644
|
+
type: summarizerClientType,
|
|
1645
|
+
},
|
|
1646
|
+
[DriverHeader.summarizingClient]: true,
|
|
1647
|
+
[LoaderHeader.reconnect]: false,
|
|
1648
|
+
},
|
|
1649
|
+
url: "/_summarizer",
|
|
1650
|
+
};
|
|
1651
|
+
const fluidObject = await requestFluidObject(loaderRouter, request);
|
|
1652
|
+
const summarizer = fluidObject.ISummarizer;
|
|
1653
|
+
if (!summarizer) {
|
|
1654
|
+
throw new UsageError("Fluid object does not implement ISummarizer");
|
|
1655
|
+
}
|
|
1656
|
+
return summarizer;
|
|
1657
|
+
};
|
|
1610
1658
|
}
|
|
1611
1659
|
}
|
|
1612
1660
|
/**
|