@fluidframework/container-runtime 0.56.5 → 0.57.0
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 +67 -26
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +146 -87
- 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 +66 -27
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +272 -97
- 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.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 +67 -26
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +147 -88
- 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 +66 -27
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +274 -99
- 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.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 +244 -113
- package/src/dataStore.ts +187 -0
- package/src/dataStoreContext.ts +1 -1
- package/src/dataStores.ts +23 -38
- package/src/garbageCollection.ts +385 -150
- 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/dist/containerRuntime.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.ContainerMessageType = void 0;
|
|
7
|
+
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.ContainerMessageType = void 0;
|
|
8
8
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
9
9
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
10
10
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
@@ -32,6 +32,7 @@ const summarizerClientElection_1 = require("./summarizerClientElection");
|
|
|
32
32
|
const throttler_1 = require("./throttler");
|
|
33
33
|
const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator");
|
|
34
34
|
const garbageCollection_1 = require("./garbageCollection");
|
|
35
|
+
const dataStore_1 = require("./dataStore");
|
|
35
36
|
var ContainerMessageType;
|
|
36
37
|
(function (ContainerMessageType) {
|
|
37
38
|
// An op to be delivered to store
|
|
@@ -59,9 +60,24 @@ const DefaultSummaryConfiguration = {
|
|
|
59
60
|
// the min of the two will be chosen
|
|
60
61
|
maxAckWaitTime: 120000,
|
|
61
62
|
};
|
|
62
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Accepted header keys for requests coming to the runtime.
|
|
65
|
+
*/
|
|
66
|
+
var RuntimeHeaders;
|
|
67
|
+
(function (RuntimeHeaders) {
|
|
68
|
+
/** True to wait for a data store to be created and loaded before returning it. */
|
|
69
|
+
RuntimeHeaders["wait"] = "wait";
|
|
70
|
+
/**
|
|
71
|
+
* True if the request is from an external app. Used for GC to handle scenarios where a data store
|
|
72
|
+
* is deleted and requested via an external app.
|
|
73
|
+
*/
|
|
74
|
+
RuntimeHeaders["externalRequest"] = "externalRequest";
|
|
75
|
+
/** True if the request is coming from an IFluidHandle. */
|
|
76
|
+
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
77
|
+
})(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
|
|
63
78
|
// Local storage key to set the default flush mode to TurnBased
|
|
64
79
|
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
80
|
+
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
65
81
|
var RuntimeMessage;
|
|
66
82
|
(function (RuntimeMessage) {
|
|
67
83
|
RuntimeMessage["FluidDataStoreOp"] = "component";
|
|
@@ -339,7 +355,7 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
339
355
|
*/
|
|
340
356
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
341
357
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
342
|
-
var _a, _b, _c, _d, _e;
|
|
358
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
343
359
|
super();
|
|
344
360
|
this.context = context;
|
|
345
361
|
this.registry = registry;
|
|
@@ -353,7 +369,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
353
369
|
this.flushTrigger = false;
|
|
354
370
|
this.paused = false;
|
|
355
371
|
this._disposed = false;
|
|
356
|
-
this.dirtyContainer = false;
|
|
357
372
|
this.emitDirtyDocumentEvent = true;
|
|
358
373
|
this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
|
|
359
374
|
/**
|
|
@@ -405,7 +420,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
405
420
|
throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
406
421
|
}
|
|
407
422
|
};
|
|
408
|
-
this.
|
|
423
|
+
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
409
424
|
// If this is an existing container, we get values from metadata.
|
|
410
425
|
// otherwise, we initialize them.
|
|
411
426
|
if (existing) {
|
|
@@ -429,20 +444,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
429
444
|
this.mc = telemetry_utils_1.loggerToMonitoringContext(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
430
445
|
this._flushMode =
|
|
431
446
|
((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? runtime_definitions_1.FlushMode.TurnBased : runtime_definitions_1.FlushMode.Immediate;
|
|
447
|
+
this._aliasingEnabled =
|
|
448
|
+
((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
|
|
449
|
+
((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
|
|
450
|
+
this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
|
|
432
451
|
/**
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* We use the timestamp of the last op for current timestamp. However, there can be cases where
|
|
436
|
-
* we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
|
|
437
|
-
* of this client's connection.
|
|
452
|
+
* Returns the timestamp of the last message seen by this client. This is used by garbage collector as
|
|
453
|
+
* the current reference timestamp for tracking unreferenced objects.
|
|
438
454
|
*/
|
|
439
|
-
|
|
440
|
-
var _a, _b, _c;
|
|
441
|
-
const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
|
|
442
|
-
const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
|
|
443
|
-
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();
|
|
444
|
-
};
|
|
445
|
-
this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
455
|
+
() => { var _a, _b, _c; return (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : (_c = this.messageAtLastSummary) === null || _c === void 0 ? void 0 : _c.timestamp; }, () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, context.baseSnapshot, async (id) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
446
456
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
447
457
|
this.summarizerNode = runtime_utils_1.createRootSummarizerNodeWithGC(telemetry_utils_1.ChildLogger.create(this.logger, "SummarizerNode"),
|
|
448
458
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
@@ -463,7 +473,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
463
473
|
if (this.context.baseSnapshot) {
|
|
464
474
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
|
|
465
475
|
}
|
|
466
|
-
this.dataStores = new dataStores_1.DataStores(dataStores_1.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(), (
|
|
476
|
+
this.dataStores = new dataStores_1.DataStores(dataStores_1.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(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
|
|
467
477
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
|
|
468
478
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
469
479
|
this.deltaSender = this.deltaManager;
|
|
@@ -472,6 +482,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
472
482
|
this.clearPartialChunks(clientId);
|
|
473
483
|
});
|
|
474
484
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
485
|
+
const { attachState, pendingLocalState } = this.context;
|
|
486
|
+
this.dirtyContainer = attachState !== container_definitions_1.AttachState.Attached
|
|
487
|
+
|| ((_e = pendingLocalState) === null || _e === void 0 ? void 0 : _e.pendingStates.length) > 0;
|
|
488
|
+
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
475
489
|
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
476
490
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
477
491
|
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
@@ -483,8 +497,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
483
497
|
const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
|
|
484
498
|
const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
485
499
|
const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
|
|
486
|
-
const summarizerClientElectionEnabled = (
|
|
487
|
-
const maxOpsSinceLastSummary = (
|
|
500
|
+
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;
|
|
501
|
+
const maxOpsSinceLastSummary = (_h = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _h !== void 0 ? _h : 7000;
|
|
488
502
|
this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
489
503
|
if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
490
504
|
this._summarizer = new summarizer_1.Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => runWhileConnectedCoordinator_1.RunWhileConnectedCoordinator.create(runtime));
|
|
@@ -568,7 +582,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
568
582
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
569
583
|
},
|
|
570
584
|
});
|
|
571
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
|
|
585
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
|
|
572
586
|
// We pack at data store level only. If isolated channels are disabled,
|
|
573
587
|
// then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
|
|
574
588
|
const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
|
|
@@ -636,6 +650,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
636
650
|
summaryOptions,
|
|
637
651
|
gcOptions,
|
|
638
652
|
loadSequenceNumberVerification,
|
|
653
|
+
useDataStoreAliasing,
|
|
639
654
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
|
|
640
655
|
return runtime;
|
|
641
656
|
}
|
|
@@ -775,7 +790,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
775
790
|
* @param request - Request made to the handler.
|
|
776
791
|
*/
|
|
777
792
|
async resolveHandle(request) {
|
|
778
|
-
var _a, _b;
|
|
779
793
|
try {
|
|
780
794
|
const requestParser = runtime_utils_1.RequestParser.create(request);
|
|
781
795
|
const id = requestParser.pathParts[0];
|
|
@@ -796,18 +810,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
796
810
|
}
|
|
797
811
|
}
|
|
798
812
|
else if (requestParser.pathParts.length > 0) {
|
|
799
|
-
|
|
800
|
-
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
801
|
-
* an error if the data store being requested is marked as unreferenced as per the data store's initial
|
|
802
|
-
* summary.
|
|
803
|
-
*
|
|
804
|
-
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
805
|
-
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
806
|
-
*/
|
|
807
|
-
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a.wait) === "boolean" ? request.headers.wait : undefined;
|
|
808
|
-
const dataStore = ((_b = request.headers) === null || _b === void 0 ? void 0 : _b.externalRequest) && this.garbageCollector.shouldRunGC
|
|
809
|
-
? await this.getDataStoreIfInitiallyReferenced(id, wait)
|
|
810
|
-
: await this.getDataStore(id, wait);
|
|
813
|
+
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
811
814
|
const subRequest = requestParser.createSubRequest(1);
|
|
812
815
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
813
816
|
// unintentionally modifying the url if that changes.
|
|
@@ -820,46 +823,37 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
820
823
|
return runtime_utils_1.exceptionToResponse(error);
|
|
821
824
|
}
|
|
822
825
|
}
|
|
823
|
-
|
|
824
|
-
var _a;
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
// the base summary we loaded from. So, use the message from its metadata blob.
|
|
828
|
-
message: (_a = summaryFormat_1.extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
|
|
832
|
-
* This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
|
|
833
|
-
* as unreferenced by GC.
|
|
834
|
-
* @param id - Id supplied during creating the data store.
|
|
835
|
-
* @param wait - True if you want to wait for it.
|
|
836
|
-
* @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
|
|
837
|
-
*/
|
|
838
|
-
async getDataStoreIfInitiallyReferenced(id, wait = true) {
|
|
826
|
+
async getDataStoreFromRequest(id, request) {
|
|
827
|
+
var _a, _b, _c;
|
|
828
|
+
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
829
|
+
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
|
|
839
830
|
const dataStoreContext = await this.dataStores.getDataStore(id, wait);
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
831
|
+
/**
|
|
832
|
+
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
833
|
+
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
834
|
+
* GC data.
|
|
835
|
+
*
|
|
836
|
+
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
837
|
+
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
838
|
+
*/
|
|
839
|
+
if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
840
|
+
// The data store is referenced if used routes in the base summary has a route to self.
|
|
841
|
+
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
842
|
+
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
843
|
+
if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
|
|
844
|
+
throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
|
|
845
|
+
}
|
|
845
846
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
847
|
+
const dataStoreChannel = await dataStoreContext.realize();
|
|
848
|
+
this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
|
|
849
|
+
return dataStoreChannel;
|
|
849
850
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
summaryLogger: this.logger,
|
|
857
|
-
fullTree: true,
|
|
858
|
-
trackState: false,
|
|
859
|
-
runGC: this.garbageCollector.shouldRunGC,
|
|
860
|
-
fullGC: true,
|
|
861
|
-
});
|
|
862
|
-
return runtime_utils_1.convertSummaryTreeToITree(summaryResult.summary);
|
|
851
|
+
formMetadata() {
|
|
852
|
+
var _a;
|
|
853
|
+
return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
|
|
854
|
+
// The last message processed at the time of summary. If there are no new messages, use the message from the
|
|
855
|
+
// last summary.
|
|
856
|
+
message: (_a = summaryFormat_1.extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
|
|
863
857
|
}
|
|
864
858
|
addContainerStateToSummary(summaryTree) {
|
|
865
859
|
var _a;
|
|
@@ -1022,9 +1016,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1022
1016
|
common_utils_1.assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1023
1017
|
return context.realize();
|
|
1024
1018
|
}
|
|
1025
|
-
async getDataStore(id, wait = true) {
|
|
1026
|
-
return (await this.dataStores.getDataStore(id, wait)).realize();
|
|
1027
|
-
}
|
|
1028
1019
|
setFlushMode(mode) {
|
|
1029
1020
|
if (mode === this._flushMode) {
|
|
1030
1021
|
return;
|
|
@@ -1089,28 +1080,84 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1089
1080
|
}
|
|
1090
1081
|
}
|
|
1091
1082
|
async createDataStore(pkg) {
|
|
1092
|
-
|
|
1083
|
+
const internalId = uuid_1.v4();
|
|
1084
|
+
return dataStore_1.channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
|
|
1093
1085
|
}
|
|
1094
|
-
|
|
1086
|
+
/**
|
|
1087
|
+
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
1088
|
+
* It is vulnerable to name collisions and should not be used.
|
|
1089
|
+
*
|
|
1090
|
+
* This method will be removed. See #6465.
|
|
1091
|
+
*/
|
|
1092
|
+
async createRootDataStoreLegacy(pkg, rootDataStoreId) {
|
|
1095
1093
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1096
1094
|
fluidDataStore.bindToContext();
|
|
1097
1095
|
return fluidDataStore;
|
|
1098
1096
|
}
|
|
1097
|
+
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1098
|
+
return this._aliasingEnabled === true ?
|
|
1099
|
+
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1100
|
+
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Creates a data store then attempts to alias it.
|
|
1104
|
+
* If aliasing fails, it will raise an exception.
|
|
1105
|
+
*
|
|
1106
|
+
* This method will be removed. See #6465.
|
|
1107
|
+
*
|
|
1108
|
+
* @param pkg - Package name of the data store
|
|
1109
|
+
* @param alias - Alias to be assigned to the data store
|
|
1110
|
+
* @param props - Properties for the data store
|
|
1111
|
+
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
1112
|
+
*/
|
|
1113
|
+
async createAndAliasDataStore(pkg, alias, props) {
|
|
1114
|
+
const internalId = uuid_1.v4();
|
|
1115
|
+
const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
1116
|
+
const aliasedDataStore = dataStore_1.channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
|
|
1117
|
+
const result = await aliasedDataStore.trySetAlias(alias);
|
|
1118
|
+
if (result !== dataStore_1.AliasResult.Success) {
|
|
1119
|
+
throw new container_utils_1.GenericError("dataStoreAliasFailure", undefined /* error */, {
|
|
1120
|
+
alias: {
|
|
1121
|
+
value: alias,
|
|
1122
|
+
tag: telemetry_utils_1.TelemetryDataTag.UserData,
|
|
1123
|
+
},
|
|
1124
|
+
internalId: {
|
|
1125
|
+
value: internalId,
|
|
1126
|
+
tag: telemetry_utils_1.TelemetryDataTag.PackageData,
|
|
1127
|
+
},
|
|
1128
|
+
aliasResult: result,
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
return aliasedDataStore;
|
|
1132
|
+
}
|
|
1099
1133
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1100
1134
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1101
1135
|
}
|
|
1102
1136
|
createDetachedDataStore(pkg) {
|
|
1103
1137
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1104
1138
|
}
|
|
1105
|
-
|
|
1139
|
+
/**
|
|
1140
|
+
* Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
|
|
1141
|
+
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
1142
|
+
*
|
|
1143
|
+
* This method will be removed. See #6465.
|
|
1144
|
+
*/
|
|
1145
|
+
async _createDataStoreWithPropsLegacy(pkg, props, id = uuid_1.v4(), isRoot = false) {
|
|
1106
1146
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1107
1147
|
if (isRoot) {
|
|
1108
1148
|
fluidDataStore.bindToContext();
|
|
1109
1149
|
}
|
|
1110
1150
|
return fluidDataStore;
|
|
1111
1151
|
}
|
|
1112
|
-
async
|
|
1113
|
-
return this.
|
|
1152
|
+
async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
|
|
1153
|
+
return this._aliasingEnabled === true && isRoot ?
|
|
1154
|
+
this.createAndAliasDataStore(pkg, id, props) :
|
|
1155
|
+
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
1156
|
+
}
|
|
1157
|
+
async _createDataStore(pkg, isRoot, id = uuid_1.v4(), props) {
|
|
1158
|
+
return this.dataStores
|
|
1159
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
|
|
1160
|
+
.realize();
|
|
1114
1161
|
}
|
|
1115
1162
|
canSendOps() {
|
|
1116
1163
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
@@ -1168,6 +1215,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1168
1215
|
common_utils_1.assert(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
|
|
1169
1216
|
this.emit("attached");
|
|
1170
1217
|
}
|
|
1218
|
+
if (attachState === container_definitions_1.AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
|
|
1219
|
+
this.updateDocumentDirtyState(false);
|
|
1220
|
+
}
|
|
1171
1221
|
this.dataStores.setAttachState(attachState);
|
|
1172
1222
|
}
|
|
1173
1223
|
/**
|
|
@@ -1214,13 +1264,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1214
1264
|
*/
|
|
1215
1265
|
async summarize(options) {
|
|
1216
1266
|
this.verifyNotClosed();
|
|
1217
|
-
const {
|
|
1267
|
+
const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
|
|
1268
|
+
let gcStats;
|
|
1218
1269
|
if (runGC) {
|
|
1219
|
-
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1270
|
+
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1220
1271
|
}
|
|
1221
1272
|
const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
|
|
1222
1273
|
common_utils_1.assert(summarizeResult.summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1223
|
-
return summarizeResult;
|
|
1274
|
+
return Object.assign(Object.assign({}, summarizeResult), { gcStats });
|
|
1224
1275
|
}
|
|
1225
1276
|
/**
|
|
1226
1277
|
* Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
|
|
@@ -1245,7 +1296,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1245
1296
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1246
1297
|
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1247
1298
|
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1248
|
-
* @returns the statistics of the used state of the data stores.
|
|
1249
1299
|
*/
|
|
1250
1300
|
updateUsedRoutes(usedRoutes, gcTimestamp) {
|
|
1251
1301
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
@@ -1279,7 +1329,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1279
1329
|
* @param options - options controlling how the summary is generated or submitted
|
|
1280
1330
|
*/
|
|
1281
1331
|
async submitSummary(options) {
|
|
1282
|
-
var _a;
|
|
1332
|
+
var _a, _b;
|
|
1283
1333
|
const { fullTree, refreshLatestAck, summaryLogger } = options;
|
|
1284
1334
|
if (refreshLatestAck) {
|
|
1285
1335
|
const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1343,9 +1393,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1343
1393
|
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
1344
1394
|
try {
|
|
1345
1395
|
summarizeResult = await this.summarize({
|
|
1346
|
-
summaryLogger,
|
|
1347
1396
|
fullTree: fullTree || forcedFullTree,
|
|
1348
1397
|
trackState: true,
|
|
1398
|
+
summaryLogger,
|
|
1349
1399
|
runGC: this.garbageCollector.shouldRunGC,
|
|
1350
1400
|
});
|
|
1351
1401
|
}
|
|
@@ -1353,13 +1403,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1353
1403
|
return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
|
|
1354
1404
|
}
|
|
1355
1405
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
1406
|
+
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
1407
|
+
this.messageAtLastSummary = this.deltaManager.lastMessage;
|
|
1356
1408
|
// Counting dataStores and handles
|
|
1357
1409
|
// Because handles are unchanged dataStores in the current logic,
|
|
1358
1410
|
// summarized dataStore count is total dataStore count minus handle count
|
|
1359
1411
|
const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1360
1412
|
common_utils_1.assert(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1361
1413
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1362
|
-
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
|
|
1414
|
+
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);
|
|
1363
1415
|
const generateSummaryData = {
|
|
1364
1416
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1365
1417
|
summaryTree,
|
|
@@ -1375,7 +1427,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1375
1427
|
const summaryContext = lastAck === undefined
|
|
1376
1428
|
? {
|
|
1377
1429
|
proposalHandle: undefined,
|
|
1378
|
-
ackHandle: (
|
|
1430
|
+
ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
|
|
1379
1431
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1380
1432
|
}
|
|
1381
1433
|
: {
|
|
@@ -1470,6 +1522,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1470
1522
|
};
|
|
1471
1523
|
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
1472
1524
|
}
|
|
1525
|
+
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1526
|
+
const aliasMessage = contents;
|
|
1527
|
+
if (!dataStore_1.isDataStoreAliasMessage(aliasMessage)) {
|
|
1528
|
+
throw new container_utils_1.UsageError("malformedDataStoreAliasMessage");
|
|
1529
|
+
}
|
|
1530
|
+
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
1531
|
+
}
|
|
1473
1532
|
async uploadBlob(blob) {
|
|
1474
1533
|
this.verifyNotClosed();
|
|
1475
1534
|
return this.blobManager.createBlob(blob);
|