@fluidframework/container-runtime 0.56.3 → 0.57.0-51086
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/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +9 -1
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +6 -6
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +65 -24
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +139 -72
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +62 -0
- package/dist/dataStore.d.ts.map +1 -0
- package/dist/dataStore.js +135 -0
- package/dist/dataStore.js.map +1 -0
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +9 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +14 -19
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +47 -21
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +195 -61
- package/dist/garbageCollection.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.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 +1 -0
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +23 -15
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizerTypes.d.ts +4 -6
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryGenerator.d.ts +2 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +46 -29
- package/dist/summaryGenerator.js.map +1 -1
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +9 -1
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +6 -6
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +65 -24
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +140 -73
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +62 -0
- package/lib/dataStore.d.ts.map +1 -0
- package/lib/dataStore.js +130 -0
- package/lib/dataStore.js.map +1 -0
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +9 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +13 -18
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +47 -21
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +197 -63
- package/lib/garbageCollection.js.map +1 -1
- package/lib/index.d.ts +3 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.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 +1 -0
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +23 -15
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizerTypes.d.ts +4 -6
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryGenerator.d.ts +2 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +46 -29
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +13 -13
- package/src/blobManager.ts +12 -1
- package/src/connectionTelemetry.ts +7 -6
- package/src/containerRuntime.ts +220 -93
- package/src/dataStore.ts +187 -0
- package/src/dataStoreContext.ts +1 -1
- package/src/dataStores.ts +18 -38
- package/src/garbageCollection.ts +283 -105
- package/src/index.ts +3 -1
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +25 -16
- package/src/summarizerTypes.ts +4 -8
- package/src/summaryGenerator.ts +71 -23
package/lib/containerRuntime.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
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, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
7
|
+
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, TelemetryDataTag, } from "@fluidframework/telemetry-utils";
|
|
8
8
|
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
9
9
|
import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
|
|
10
10
|
import { DataCorruptionError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
|
|
11
11
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
12
12
|
import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
13
|
-
import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree,
|
|
13
|
+
import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, } from "@fluidframework/runtime-utils";
|
|
14
14
|
import { v4 as uuid } from "uuid";
|
|
15
15
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
16
16
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
@@ -29,6 +29,7 @@ import { SummarizerClientElection, summarizerClientType } from "./summarizerClie
|
|
|
29
29
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
30
30
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
31
31
|
import { GarbageCollector, gcTreeKey, } from "./garbageCollection";
|
|
32
|
+
import { AliasResult, channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
32
33
|
export var ContainerMessageType;
|
|
33
34
|
(function (ContainerMessageType) {
|
|
34
35
|
// An op to be delivered to store
|
|
@@ -56,9 +57,24 @@ const DefaultSummaryConfiguration = {
|
|
|
56
57
|
// the min of the two will be chosen
|
|
57
58
|
maxAckWaitTime: 120000,
|
|
58
59
|
};
|
|
59
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Accepted header keys for requests coming to the runtime.
|
|
62
|
+
*/
|
|
63
|
+
export var RuntimeHeaders;
|
|
64
|
+
(function (RuntimeHeaders) {
|
|
65
|
+
/** True to wait for a data store to be created and loaded before returning it. */
|
|
66
|
+
RuntimeHeaders["wait"] = "wait";
|
|
67
|
+
/**
|
|
68
|
+
* True if the request is from an external app. Used for GC to handle scenarios where a data store
|
|
69
|
+
* is deleted and requested via an external app.
|
|
70
|
+
*/
|
|
71
|
+
RuntimeHeaders["externalRequest"] = "externalRequest";
|
|
72
|
+
/** True if the request is coming from an IFluidHandle. */
|
|
73
|
+
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
74
|
+
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
60
75
|
// Local storage key to set the default flush mode to TurnBased
|
|
61
76
|
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
77
|
+
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
62
78
|
export var RuntimeMessage;
|
|
63
79
|
(function (RuntimeMessage) {
|
|
64
80
|
RuntimeMessage["FluidDataStoreOp"] = "component";
|
|
@@ -332,7 +348,7 @@ export function getDeviceSpec() {
|
|
|
332
348
|
*/
|
|
333
349
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
334
350
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
335
|
-
var _a, _b, _c, _d, _e;
|
|
351
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
336
352
|
super();
|
|
337
353
|
this.context = context;
|
|
338
354
|
this.registry = registry;
|
|
@@ -346,7 +362,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
346
362
|
this.flushTrigger = false;
|
|
347
363
|
this.paused = false;
|
|
348
364
|
this._disposed = false;
|
|
349
|
-
this.dirtyContainer = false;
|
|
350
365
|
this.emitDirtyDocumentEvent = true;
|
|
351
366
|
this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
|
|
352
367
|
/**
|
|
@@ -422,6 +437,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
422
437
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
423
438
|
this._flushMode =
|
|
424
439
|
((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? FlushMode.TurnBased : FlushMode.Immediate;
|
|
440
|
+
this._aliasingEnabled =
|
|
441
|
+
((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
|
|
442
|
+
((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
|
|
425
443
|
/**
|
|
426
444
|
* Function that return the current server timestamp. This is used by the garbage collector to set the
|
|
427
445
|
* time when a node becomes unreferenced.
|
|
@@ -435,7 +453,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
435
453
|
const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
|
|
436
454
|
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();
|
|
437
455
|
};
|
|
438
|
-
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
456
|
+
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
439
457
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
440
458
|
this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
|
|
441
459
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
@@ -456,7 +474,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
456
474
|
if (this.context.baseSnapshot) {
|
|
457
475
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
|
|
458
476
|
}
|
|
459
|
-
this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getDataStoreBaseGCDetails(), (
|
|
477
|
+
this.dataStores = new DataStores(getSummaryForDatastores(context.baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getDataStoreBaseGCDetails(), (dataStorePath, packagePath) => this.garbageCollector.nodeUpdated(dataStorePath, "Changed", packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
|
|
460
478
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
|
|
461
479
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
462
480
|
this.deltaSender = this.deltaManager;
|
|
@@ -465,6 +483,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
465
483
|
this.clearPartialChunks(clientId);
|
|
466
484
|
});
|
|
467
485
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
486
|
+
const { attachState, pendingLocalState } = this.context;
|
|
487
|
+
this.dirtyContainer = attachState !== AttachState.Attached
|
|
488
|
+
|| ((_e = pendingLocalState) === null || _e === void 0 ? void 0 : _e.pendingStates.length) > 0;
|
|
489
|
+
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
468
490
|
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
469
491
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
470
492
|
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
@@ -476,8 +498,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
476
498
|
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
477
499
|
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
478
500
|
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
479
|
-
const summarizerClientElectionEnabled = (
|
|
480
|
-
const maxOpsSinceLastSummary = (
|
|
501
|
+
const summarizerClientElectionEnabled = (_f = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _f !== void 0 ? _f : ((_g = this.runtimeOptions.summaryOptions) === null || _g === void 0 ? void 0 : _g.summarizerClientElection) === true;
|
|
502
|
+
const maxOpsSinceLastSummary = (_h = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _h !== void 0 ? _h : 7000;
|
|
481
503
|
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
482
504
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
483
505
|
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
@@ -561,7 +583,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
561
583
|
runtimeVersion: pkgVersion,
|
|
562
584
|
},
|
|
563
585
|
});
|
|
564
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
|
|
586
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
|
|
565
587
|
// We pack at data store level only. If isolated channels are disabled,
|
|
566
588
|
// then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
|
|
567
589
|
const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
|
|
@@ -629,6 +651,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
629
651
|
summaryOptions,
|
|
630
652
|
gcOptions,
|
|
631
653
|
loadSequenceNumberVerification,
|
|
654
|
+
useDataStoreAliasing,
|
|
632
655
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
|
|
633
656
|
return runtime;
|
|
634
657
|
}
|
|
@@ -768,7 +791,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
768
791
|
* @param request - Request made to the handler.
|
|
769
792
|
*/
|
|
770
793
|
async resolveHandle(request) {
|
|
771
|
-
var _a, _b;
|
|
772
794
|
try {
|
|
773
795
|
const requestParser = RequestParser.create(request);
|
|
774
796
|
const id = requestParser.pathParts[0];
|
|
@@ -789,18 +811,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
789
811
|
}
|
|
790
812
|
}
|
|
791
813
|
else if (requestParser.pathParts.length > 0) {
|
|
792
|
-
|
|
793
|
-
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
794
|
-
* an error if the data store being requested is marked as unreferenced as per the data store's initial
|
|
795
|
-
* summary.
|
|
796
|
-
*
|
|
797
|
-
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
798
|
-
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
799
|
-
*/
|
|
800
|
-
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a.wait) === "boolean" ? request.headers.wait : undefined;
|
|
801
|
-
const dataStore = ((_b = request.headers) === null || _b === void 0 ? void 0 : _b.externalRequest) && this.garbageCollector.shouldRunGC
|
|
802
|
-
? await this.getDataStoreIfInitiallyReferenced(id, wait)
|
|
803
|
-
: await this.getDataStore(id, wait);
|
|
814
|
+
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
804
815
|
const subRequest = requestParser.createSubRequest(1);
|
|
805
816
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
806
817
|
// unintentionally modifying the url if that changes.
|
|
@@ -813,6 +824,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
813
824
|
return exceptionToResponse(error);
|
|
814
825
|
}
|
|
815
826
|
}
|
|
827
|
+
async getDataStoreFromRequest(id, request) {
|
|
828
|
+
var _a, _b, _c;
|
|
829
|
+
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
830
|
+
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
|
|
831
|
+
const dataStoreContext = await this.dataStores.getDataStore(id, wait);
|
|
832
|
+
/**
|
|
833
|
+
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
834
|
+
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
835
|
+
* GC data.
|
|
836
|
+
*
|
|
837
|
+
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
838
|
+
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
839
|
+
*/
|
|
840
|
+
if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
841
|
+
// The data store is referenced if used routes in the base summary has a route to self.
|
|
842
|
+
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
843
|
+
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
844
|
+
if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
|
|
845
|
+
throw responseToException(create404Response(request), request);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
const dataStoreChannel = await dataStoreContext.realize();
|
|
849
|
+
// Let the garbage collector know that a data store was requested / loaded. Realize the data store first so
|
|
850
|
+
// that the package path is available.
|
|
851
|
+
this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
|
|
852
|
+
return dataStoreChannel;
|
|
853
|
+
}
|
|
816
854
|
formMetadata() {
|
|
817
855
|
var _a;
|
|
818
856
|
return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
|
|
@@ -820,40 +858,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
820
858
|
// the base summary we loaded from. So, use the message from its metadata blob.
|
|
821
859
|
message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
|
|
822
860
|
}
|
|
823
|
-
/**
|
|
824
|
-
* Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
|
|
825
|
-
* This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
|
|
826
|
-
* as unreferenced by GC.
|
|
827
|
-
* @param id - Id supplied during creating the data store.
|
|
828
|
-
* @param wait - True if you want to wait for it.
|
|
829
|
-
* @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
|
|
830
|
-
*/
|
|
831
|
-
async getDataStoreIfInitiallyReferenced(id, wait = true) {
|
|
832
|
-
const dataStoreContext = await this.dataStores.getDataStore(id, wait);
|
|
833
|
-
// The data store is referenced if used routes in the initial summary has a route to self.
|
|
834
|
-
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
835
|
-
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
836
|
-
if (usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/")) {
|
|
837
|
-
return dataStoreContext.realize();
|
|
838
|
-
}
|
|
839
|
-
// The data store is unreferenced. Throw a 404 response exception.
|
|
840
|
-
const request = { url: id };
|
|
841
|
-
throw responseToException(create404Response(request), request);
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Notifies this object to take the snapshot of the container.
|
|
845
|
-
* @deprecated - Use summarize to get summary of the container runtime.
|
|
846
|
-
*/
|
|
847
|
-
async snapshot() {
|
|
848
|
-
const summaryResult = await this.summarize({
|
|
849
|
-
summaryLogger: this.logger,
|
|
850
|
-
fullTree: true,
|
|
851
|
-
trackState: false,
|
|
852
|
-
runGC: this.garbageCollector.shouldRunGC,
|
|
853
|
-
fullGC: true,
|
|
854
|
-
});
|
|
855
|
-
return convertSummaryTreeToITree(summaryResult.summary);
|
|
856
|
-
}
|
|
857
861
|
addContainerStateToSummary(summaryTree) {
|
|
858
862
|
var _a;
|
|
859
863
|
addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(this.formMetadata()));
|
|
@@ -1015,9 +1019,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1015
1019
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1016
1020
|
return context.realize();
|
|
1017
1021
|
}
|
|
1018
|
-
async getDataStore(id, wait = true) {
|
|
1019
|
-
return (await this.dataStores.getDataStore(id, wait)).realize();
|
|
1020
|
-
}
|
|
1021
1022
|
setFlushMode(mode) {
|
|
1022
1023
|
if (mode === this._flushMode) {
|
|
1023
1024
|
return;
|
|
@@ -1082,28 +1083,84 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1082
1083
|
}
|
|
1083
1084
|
}
|
|
1084
1085
|
async createDataStore(pkg) {
|
|
1085
|
-
|
|
1086
|
+
const internalId = uuid();
|
|
1087
|
+
return channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
|
|
1086
1088
|
}
|
|
1087
|
-
|
|
1089
|
+
/**
|
|
1090
|
+
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
1091
|
+
* It is vulnerable to name collisions and should not be used.
|
|
1092
|
+
*
|
|
1093
|
+
* This method will be removed. See #6465.
|
|
1094
|
+
*/
|
|
1095
|
+
async createRootDataStoreLegacy(pkg, rootDataStoreId) {
|
|
1088
1096
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1089
1097
|
fluidDataStore.bindToContext();
|
|
1090
1098
|
return fluidDataStore;
|
|
1091
1099
|
}
|
|
1100
|
+
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1101
|
+
return this._aliasingEnabled === true ?
|
|
1102
|
+
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1103
|
+
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Creates a data store then attempts to alias it.
|
|
1107
|
+
* If aliasing fails, it will raise an exception.
|
|
1108
|
+
*
|
|
1109
|
+
* This method will be removed. See #6465.
|
|
1110
|
+
*
|
|
1111
|
+
* @param pkg - Package name of the data store
|
|
1112
|
+
* @param alias - Alias to be assigned to the data store
|
|
1113
|
+
* @param props - Properties for the data store
|
|
1114
|
+
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
1115
|
+
*/
|
|
1116
|
+
async createAndAliasDataStore(pkg, alias, props) {
|
|
1117
|
+
const internalId = uuid();
|
|
1118
|
+
const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
1119
|
+
const aliasedDataStore = channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
|
|
1120
|
+
const result = await aliasedDataStore.trySetAlias(alias);
|
|
1121
|
+
if (result !== AliasResult.Success) {
|
|
1122
|
+
throw new GenericError("dataStoreAliasFailure", undefined /* error */, {
|
|
1123
|
+
alias: {
|
|
1124
|
+
value: alias,
|
|
1125
|
+
tag: TelemetryDataTag.UserData,
|
|
1126
|
+
},
|
|
1127
|
+
internalId: {
|
|
1128
|
+
value: internalId,
|
|
1129
|
+
tag: TelemetryDataTag.PackageData,
|
|
1130
|
+
},
|
|
1131
|
+
aliasResult: result,
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
return aliasedDataStore;
|
|
1135
|
+
}
|
|
1092
1136
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1093
1137
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1094
1138
|
}
|
|
1095
1139
|
createDetachedDataStore(pkg) {
|
|
1096
1140
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1097
1141
|
}
|
|
1098
|
-
|
|
1142
|
+
/**
|
|
1143
|
+
* Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
|
|
1144
|
+
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
1145
|
+
*
|
|
1146
|
+
* This method will be removed. See #6465.
|
|
1147
|
+
*/
|
|
1148
|
+
async _createDataStoreWithPropsLegacy(pkg, props, id = uuid(), isRoot = false) {
|
|
1099
1149
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1100
1150
|
if (isRoot) {
|
|
1101
1151
|
fluidDataStore.bindToContext();
|
|
1102
1152
|
}
|
|
1103
1153
|
return fluidDataStore;
|
|
1104
1154
|
}
|
|
1105
|
-
async
|
|
1106
|
-
return this.
|
|
1155
|
+
async _createDataStoreWithProps(pkg, props, id = uuid(), isRoot = false) {
|
|
1156
|
+
return this._aliasingEnabled === true && isRoot ?
|
|
1157
|
+
this.createAndAliasDataStore(pkg, id, props) :
|
|
1158
|
+
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
1159
|
+
}
|
|
1160
|
+
async _createDataStore(pkg, isRoot, id = uuid(), props) {
|
|
1161
|
+
return this.dataStores
|
|
1162
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
|
|
1163
|
+
.realize();
|
|
1107
1164
|
}
|
|
1108
1165
|
canSendOps() {
|
|
1109
1166
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
@@ -1161,6 +1218,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1161
1218
|
assert(this.attachState === AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
|
|
1162
1219
|
this.emit("attached");
|
|
1163
1220
|
}
|
|
1221
|
+
if (attachState === AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
|
|
1222
|
+
this.updateDocumentDirtyState(false);
|
|
1223
|
+
}
|
|
1164
1224
|
this.dataStores.setAttachState(attachState);
|
|
1165
1225
|
}
|
|
1166
1226
|
/**
|
|
@@ -1207,13 +1267,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1207
1267
|
*/
|
|
1208
1268
|
async summarize(options) {
|
|
1209
1269
|
this.verifyNotClosed();
|
|
1210
|
-
const {
|
|
1270
|
+
const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
|
|
1271
|
+
let gcStats;
|
|
1211
1272
|
if (runGC) {
|
|
1212
|
-
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1273
|
+
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1213
1274
|
}
|
|
1214
1275
|
const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
|
|
1215
1276
|
assert(summarizeResult.summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1216
|
-
return summarizeResult;
|
|
1277
|
+
return Object.assign(Object.assign({}, summarizeResult), { gcStats });
|
|
1217
1278
|
}
|
|
1218
1279
|
/**
|
|
1219
1280
|
* Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
|
|
@@ -1238,7 +1299,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1238
1299
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1239
1300
|
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1240
1301
|
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1241
|
-
* @returns the statistics of the used state of the data stores.
|
|
1242
1302
|
*/
|
|
1243
1303
|
updateUsedRoutes(usedRoutes, gcTimestamp) {
|
|
1244
1304
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
@@ -1272,7 +1332,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1272
1332
|
* @param options - options controlling how the summary is generated or submitted
|
|
1273
1333
|
*/
|
|
1274
1334
|
async submitSummary(options) {
|
|
1275
|
-
var _a;
|
|
1335
|
+
var _a, _b;
|
|
1276
1336
|
const { fullTree, refreshLatestAck, summaryLogger } = options;
|
|
1277
1337
|
if (refreshLatestAck) {
|
|
1278
1338
|
const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1336,9 +1396,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1336
1396
|
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
1337
1397
|
try {
|
|
1338
1398
|
summarizeResult = await this.summarize({
|
|
1339
|
-
summaryLogger,
|
|
1340
1399
|
fullTree: fullTree || forcedFullTree,
|
|
1341
1400
|
trackState: true,
|
|
1401
|
+
summaryLogger,
|
|
1342
1402
|
runGC: this.garbageCollector.shouldRunGC,
|
|
1343
1403
|
});
|
|
1344
1404
|
}
|
|
@@ -1352,7 +1412,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1352
1412
|
const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
|
|
1353
1413
|
assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1354
1414
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
|
|
1355
|
-
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
|
|
1415
|
+
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_a = summarizeResult.gcStats) === null || _a === void 0 ? void 0 : _a.updatedDataStoreCount }, partialStats);
|
|
1356
1416
|
const generateSummaryData = {
|
|
1357
1417
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1358
1418
|
summaryTree,
|
|
@@ -1368,7 +1428,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1368
1428
|
const summaryContext = lastAck === undefined
|
|
1369
1429
|
? {
|
|
1370
1430
|
proposalHandle: undefined,
|
|
1371
|
-
ackHandle: (
|
|
1431
|
+
ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
|
|
1372
1432
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1373
1433
|
}
|
|
1374
1434
|
: {
|
|
@@ -1463,6 +1523,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1463
1523
|
};
|
|
1464
1524
|
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
1465
1525
|
}
|
|
1526
|
+
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1527
|
+
const aliasMessage = contents;
|
|
1528
|
+
if (!isDataStoreAliasMessage(aliasMessage)) {
|
|
1529
|
+
throw new UsageError("malformedDataStoreAliasMessage");
|
|
1530
|
+
}
|
|
1531
|
+
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
1532
|
+
}
|
|
1466
1533
|
async uploadBlob(blob) {
|
|
1467
1534
|
this.verifyNotClosed();
|
|
1468
1535
|
return this.blobManager.createBlob(blob);
|