@fluidframework/container-runtime 0.56.0 → 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 -25
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +149 -79
- 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 +6 -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 -28
- 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 -25
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +150 -80
- 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 +6 -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 -28
- 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 +231 -103
- 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 +6 -8
- package/src/summaryGenerator.ts +72 -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";
|
|
@@ -141,6 +157,10 @@ class ScheduleManagerCore {
|
|
|
141
157
|
for (const pending of allPending) {
|
|
142
158
|
this.trackPending(pending);
|
|
143
159
|
}
|
|
160
|
+
// We are intentionally directly listening to the "op" to inspect system ops as well.
|
|
161
|
+
// If we do not observe system ops, we are likely to hit 0x296 assert when system ops
|
|
162
|
+
// precedes start of incomplete batch.
|
|
163
|
+
this.deltaManager.on("op", (message) => this.afterOpProcessing(message.sequenceNumber));
|
|
144
164
|
}
|
|
145
165
|
/**
|
|
146
166
|
* The only public function in this class - called when we processed an op,
|
|
@@ -265,7 +285,7 @@ class ScheduleManager {
|
|
|
265
285
|
this.logger = logger;
|
|
266
286
|
this.hitError = false;
|
|
267
287
|
this.deltaScheduler = new deltaScheduler_1.DeltaScheduler(this.deltaManager, telemetry_utils_1.ChildLogger.create(this.logger, "DeltaScheduler"));
|
|
268
|
-
|
|
288
|
+
void new ScheduleManagerCore(deltaManager, logger);
|
|
269
289
|
}
|
|
270
290
|
beforeOpProcessing(message) {
|
|
271
291
|
var _a;
|
|
@@ -287,9 +307,6 @@ class ScheduleManager {
|
|
|
287
307
|
var _a;
|
|
288
308
|
// If this is no longer true, we need to revisit what we do where we set this.hitError.
|
|
289
309
|
common_utils_1.assert(!this.hitError, 0x2a3 /* "container should be closed on any error" */);
|
|
290
|
-
// Let the scheduler know how far we progressed, to decide if op processing
|
|
291
|
-
// should be paused or not.
|
|
292
|
-
this.scheduler.afterOpProcessing(message.sequenceNumber);
|
|
293
310
|
if (error) {
|
|
294
311
|
// We assume here that loader will close container and stop processing all future ops.
|
|
295
312
|
// This is implicit dependency. If this flow changes, this code might no longer be correct.
|
|
@@ -338,7 +355,7 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
338
355
|
*/
|
|
339
356
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
340
357
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
341
|
-
var _a, _b, _c, _d, _e;
|
|
358
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
342
359
|
super();
|
|
343
360
|
this.context = context;
|
|
344
361
|
this.registry = registry;
|
|
@@ -352,7 +369,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
352
369
|
this.flushTrigger = false;
|
|
353
370
|
this.paused = false;
|
|
354
371
|
this._disposed = false;
|
|
355
|
-
this.dirtyContainer = false;
|
|
356
372
|
this.emitDirtyDocumentEvent = true;
|
|
357
373
|
this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
|
|
358
374
|
/**
|
|
@@ -428,6 +444,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
428
444
|
this.mc = telemetry_utils_1.loggerToMonitoringContext(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
429
445
|
this._flushMode =
|
|
430
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);
|
|
431
450
|
/**
|
|
432
451
|
* Function that return the current server timestamp. This is used by the garbage collector to set the
|
|
433
452
|
* time when a node becomes unreferenced.
|
|
@@ -441,7 +460,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
441
460
|
const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
|
|
442
461
|
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();
|
|
443
462
|
};
|
|
444
|
-
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);
|
|
463
|
+
this.garbageCollector = garbageCollection_1.GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => driver_utils_1.readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
445
464
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
446
465
|
this.summarizerNode = runtime_utils_1.createRootSummarizerNodeWithGC(telemetry_utils_1.ChildLogger.create(this.logger, "SummarizerNode"),
|
|
447
466
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
@@ -462,7 +481,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
462
481
|
if (this.context.baseSnapshot) {
|
|
463
482
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
|
|
464
483
|
}
|
|
465
|
-
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(), (
|
|
484
|
+
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(), (dataStorePath, packagePath) => this.garbageCollector.nodeUpdated(dataStorePath, "Changed", packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
|
|
466
485
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
|
|
467
486
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
468
487
|
this.deltaSender = this.deltaManager;
|
|
@@ -471,6 +490,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
471
490
|
this.clearPartialChunks(clientId);
|
|
472
491
|
});
|
|
473
492
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
493
|
+
const { attachState, pendingLocalState } = this.context;
|
|
494
|
+
this.dirtyContainer = attachState !== container_definitions_1.AttachState.Attached
|
|
495
|
+
|| ((_e = pendingLocalState) === null || _e === void 0 ? void 0 : _e.pendingStates.length) > 0;
|
|
496
|
+
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
474
497
|
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
475
498
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
476
499
|
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
@@ -482,8 +505,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
482
505
|
const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
|
|
483
506
|
const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
484
507
|
const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
|
|
485
|
-
const summarizerClientElectionEnabled = (
|
|
486
|
-
const maxOpsSinceLastSummary = (
|
|
508
|
+
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;
|
|
509
|
+
const maxOpsSinceLastSummary = (_h = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _h !== void 0 ? _h : 7000;
|
|
487
510
|
this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
488
511
|
if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
489
512
|
this._summarizer = new summarizer_1.Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => runWhileConnectedCoordinator_1.RunWhileConnectedCoordinator.create(runtime));
|
|
@@ -567,7 +590,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
567
590
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
568
591
|
},
|
|
569
592
|
});
|
|
570
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
|
|
593
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
|
|
571
594
|
// We pack at data store level only. If isolated channels are disabled,
|
|
572
595
|
// then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
|
|
573
596
|
const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
|
|
@@ -635,6 +658,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
635
658
|
summaryOptions,
|
|
636
659
|
gcOptions,
|
|
637
660
|
loadSequenceNumberVerification,
|
|
661
|
+
useDataStoreAliasing,
|
|
638
662
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
|
|
639
663
|
return runtime;
|
|
640
664
|
}
|
|
@@ -774,7 +798,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
774
798
|
* @param request - Request made to the handler.
|
|
775
799
|
*/
|
|
776
800
|
async resolveHandle(request) {
|
|
777
|
-
var _a, _b;
|
|
778
801
|
try {
|
|
779
802
|
const requestParser = runtime_utils_1.RequestParser.create(request);
|
|
780
803
|
const id = requestParser.pathParts[0];
|
|
@@ -795,18 +818,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
795
818
|
}
|
|
796
819
|
}
|
|
797
820
|
else if (requestParser.pathParts.length > 0) {
|
|
798
|
-
|
|
799
|
-
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
800
|
-
* an error if the data store being requested is marked as unreferenced as per the data store's initial
|
|
801
|
-
* summary.
|
|
802
|
-
*
|
|
803
|
-
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
804
|
-
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
805
|
-
*/
|
|
806
|
-
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a.wait) === "boolean" ? request.headers.wait : undefined;
|
|
807
|
-
const dataStore = ((_b = request.headers) === null || _b === void 0 ? void 0 : _b.externalRequest) && this.garbageCollector.shouldRunGC
|
|
808
|
-
? await this.getDataStoreIfInitiallyReferenced(id, wait)
|
|
809
|
-
: await this.getDataStore(id, wait);
|
|
821
|
+
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
810
822
|
const subRequest = requestParser.createSubRequest(1);
|
|
811
823
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
812
824
|
// unintentionally modifying the url if that changes.
|
|
@@ -819,6 +831,33 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
819
831
|
return runtime_utils_1.exceptionToResponse(error);
|
|
820
832
|
}
|
|
821
833
|
}
|
|
834
|
+
async getDataStoreFromRequest(id, request) {
|
|
835
|
+
var _a, _b, _c;
|
|
836
|
+
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
837
|
+
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
|
|
838
|
+
const dataStoreContext = await this.dataStores.getDataStore(id, wait);
|
|
839
|
+
/**
|
|
840
|
+
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
841
|
+
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
842
|
+
* GC data.
|
|
843
|
+
*
|
|
844
|
+
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
845
|
+
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
846
|
+
*/
|
|
847
|
+
if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
848
|
+
// The data store is referenced if used routes in the base summary has a route to self.
|
|
849
|
+
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
850
|
+
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
851
|
+
if (!(usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/"))) {
|
|
852
|
+
throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
const dataStoreChannel = await dataStoreContext.realize();
|
|
856
|
+
// Let the garbage collector know that a data store was requested / loaded. Realize the data store first so
|
|
857
|
+
// that the package path is available.
|
|
858
|
+
this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
|
|
859
|
+
return dataStoreChannel;
|
|
860
|
+
}
|
|
822
861
|
formMetadata() {
|
|
823
862
|
var _a;
|
|
824
863
|
return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
|
|
@@ -826,40 +865,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
826
865
|
// the base summary we loaded from. So, use the message from its metadata blob.
|
|
827
866
|
message: (_a = summaryFormat_1.extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
|
|
828
867
|
}
|
|
829
|
-
/**
|
|
830
|
-
* Retrieves the runtime for a data store if it's referenced as per the initially summary that it is loaded with.
|
|
831
|
-
* This is a workaround to handle scenarios where a data store shared with an external app is deleted and marked
|
|
832
|
-
* as unreferenced by GC.
|
|
833
|
-
* @param id - Id supplied during creating the data store.
|
|
834
|
-
* @param wait - True if you want to wait for it.
|
|
835
|
-
* @returns the data store runtime if the data store exists and is initially referenced; undefined otherwise.
|
|
836
|
-
*/
|
|
837
|
-
async getDataStoreIfInitiallyReferenced(id, wait = true) {
|
|
838
|
-
const dataStoreContext = await this.dataStores.getDataStore(id, wait);
|
|
839
|
-
// The data store is referenced if used routes in the initial summary has a route to self.
|
|
840
|
-
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
841
|
-
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
842
|
-
if (usedRoutes === undefined || usedRoutes.includes("") || usedRoutes.includes("/")) {
|
|
843
|
-
return dataStoreContext.realize();
|
|
844
|
-
}
|
|
845
|
-
// The data store is unreferenced. Throw a 404 response exception.
|
|
846
|
-
const request = { url: id };
|
|
847
|
-
throw runtime_utils_1.responseToException(runtime_utils_1.create404Response(request), request);
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Notifies this object to take the snapshot of the container.
|
|
851
|
-
* @deprecated - Use summarize to get summary of the container runtime.
|
|
852
|
-
*/
|
|
853
|
-
async snapshot() {
|
|
854
|
-
const summaryResult = await this.summarize({
|
|
855
|
-
summaryLogger: this.logger,
|
|
856
|
-
fullTree: true,
|
|
857
|
-
trackState: false,
|
|
858
|
-
runGC: this.garbageCollector.shouldRunGC,
|
|
859
|
-
fullGC: true,
|
|
860
|
-
});
|
|
861
|
-
return runtime_utils_1.convertSummaryTreeToITree(summaryResult.summary);
|
|
862
|
-
}
|
|
863
868
|
addContainerStateToSummary(summaryTree) {
|
|
864
869
|
var _a;
|
|
865
870
|
runtime_utils_1.addBlobToSummary(summaryTree, summaryFormat_1.metadataBlobName, JSON.stringify(this.formMetadata()));
|
|
@@ -1021,9 +1026,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1021
1026
|
common_utils_1.assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1022
1027
|
return context.realize();
|
|
1023
1028
|
}
|
|
1024
|
-
async getDataStore(id, wait = true) {
|
|
1025
|
-
return (await this.dataStores.getDataStore(id, wait)).realize();
|
|
1026
|
-
}
|
|
1027
1029
|
setFlushMode(mode) {
|
|
1028
1030
|
if (mode === this._flushMode) {
|
|
1029
1031
|
return;
|
|
@@ -1088,28 +1090,84 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1088
1090
|
}
|
|
1089
1091
|
}
|
|
1090
1092
|
async createDataStore(pkg) {
|
|
1091
|
-
|
|
1093
|
+
const internalId = uuid_1.v4();
|
|
1094
|
+
return dataStore_1.channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
|
|
1092
1095
|
}
|
|
1093
|
-
|
|
1096
|
+
/**
|
|
1097
|
+
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
1098
|
+
* It is vulnerable to name collisions and should not be used.
|
|
1099
|
+
*
|
|
1100
|
+
* This method will be removed. See #6465.
|
|
1101
|
+
*/
|
|
1102
|
+
async createRootDataStoreLegacy(pkg, rootDataStoreId) {
|
|
1094
1103
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1095
1104
|
fluidDataStore.bindToContext();
|
|
1096
1105
|
return fluidDataStore;
|
|
1097
1106
|
}
|
|
1107
|
+
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1108
|
+
return this._aliasingEnabled === true ?
|
|
1109
|
+
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1110
|
+
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Creates a data store then attempts to alias it.
|
|
1114
|
+
* If aliasing fails, it will raise an exception.
|
|
1115
|
+
*
|
|
1116
|
+
* This method will be removed. See #6465.
|
|
1117
|
+
*
|
|
1118
|
+
* @param pkg - Package name of the data store
|
|
1119
|
+
* @param alias - Alias to be assigned to the data store
|
|
1120
|
+
* @param props - Properties for the data store
|
|
1121
|
+
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
1122
|
+
*/
|
|
1123
|
+
async createAndAliasDataStore(pkg, alias, props) {
|
|
1124
|
+
const internalId = uuid_1.v4();
|
|
1125
|
+
const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
1126
|
+
const aliasedDataStore = dataStore_1.channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
|
|
1127
|
+
const result = await aliasedDataStore.trySetAlias(alias);
|
|
1128
|
+
if (result !== dataStore_1.AliasResult.Success) {
|
|
1129
|
+
throw new container_utils_1.GenericError("dataStoreAliasFailure", undefined /* error */, {
|
|
1130
|
+
alias: {
|
|
1131
|
+
value: alias,
|
|
1132
|
+
tag: telemetry_utils_1.TelemetryDataTag.UserData,
|
|
1133
|
+
},
|
|
1134
|
+
internalId: {
|
|
1135
|
+
value: internalId,
|
|
1136
|
+
tag: telemetry_utils_1.TelemetryDataTag.PackageData,
|
|
1137
|
+
},
|
|
1138
|
+
aliasResult: result,
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
return aliasedDataStore;
|
|
1142
|
+
}
|
|
1098
1143
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1099
1144
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1100
1145
|
}
|
|
1101
1146
|
createDetachedDataStore(pkg) {
|
|
1102
1147
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1103
1148
|
}
|
|
1104
|
-
|
|
1149
|
+
/**
|
|
1150
|
+
* Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
|
|
1151
|
+
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
1152
|
+
*
|
|
1153
|
+
* This method will be removed. See #6465.
|
|
1154
|
+
*/
|
|
1155
|
+
async _createDataStoreWithPropsLegacy(pkg, props, id = uuid_1.v4(), isRoot = false) {
|
|
1105
1156
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1106
1157
|
if (isRoot) {
|
|
1107
1158
|
fluidDataStore.bindToContext();
|
|
1108
1159
|
}
|
|
1109
1160
|
return fluidDataStore;
|
|
1110
1161
|
}
|
|
1111
|
-
async
|
|
1112
|
-
return this.
|
|
1162
|
+
async _createDataStoreWithProps(pkg, props, id = uuid_1.v4(), isRoot = false) {
|
|
1163
|
+
return this._aliasingEnabled === true && isRoot ?
|
|
1164
|
+
this.createAndAliasDataStore(pkg, id, props) :
|
|
1165
|
+
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
1166
|
+
}
|
|
1167
|
+
async _createDataStore(pkg, isRoot, id = uuid_1.v4(), props) {
|
|
1168
|
+
return this.dataStores
|
|
1169
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
|
|
1170
|
+
.realize();
|
|
1113
1171
|
}
|
|
1114
1172
|
canSendOps() {
|
|
1115
1173
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
@@ -1167,6 +1225,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1167
1225
|
common_utils_1.assert(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
|
|
1168
1226
|
this.emit("attached");
|
|
1169
1227
|
}
|
|
1228
|
+
if (attachState === container_definitions_1.AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
|
|
1229
|
+
this.updateDocumentDirtyState(false);
|
|
1230
|
+
}
|
|
1170
1231
|
this.dataStores.setAttachState(attachState);
|
|
1171
1232
|
}
|
|
1172
1233
|
/**
|
|
@@ -1213,13 +1274,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1213
1274
|
*/
|
|
1214
1275
|
async summarize(options) {
|
|
1215
1276
|
this.verifyNotClosed();
|
|
1216
|
-
const {
|
|
1277
|
+
const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
|
|
1278
|
+
let gcStats;
|
|
1217
1279
|
if (runGC) {
|
|
1218
|
-
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1280
|
+
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1219
1281
|
}
|
|
1220
1282
|
const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
|
|
1221
1283
|
common_utils_1.assert(summarizeResult.summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1222
|
-
return summarizeResult;
|
|
1284
|
+
return Object.assign(Object.assign({}, summarizeResult), { gcStats });
|
|
1223
1285
|
}
|
|
1224
1286
|
/**
|
|
1225
1287
|
* Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
|
|
@@ -1244,7 +1306,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1244
1306
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1245
1307
|
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1246
1308
|
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1247
|
-
* @returns the statistics of the used state of the data stores.
|
|
1248
1309
|
*/
|
|
1249
1310
|
updateUsedRoutes(usedRoutes, gcTimestamp) {
|
|
1250
1311
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
@@ -1278,7 +1339,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1278
1339
|
* @param options - options controlling how the summary is generated or submitted
|
|
1279
1340
|
*/
|
|
1280
1341
|
async submitSummary(options) {
|
|
1281
|
-
var _a;
|
|
1342
|
+
var _a, _b;
|
|
1282
1343
|
const { fullTree, refreshLatestAck, summaryLogger } = options;
|
|
1283
1344
|
if (refreshLatestAck) {
|
|
1284
1345
|
const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1337,13 +1398,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1337
1398
|
}
|
|
1338
1399
|
const trace = common_utils_1.Trace.start();
|
|
1339
1400
|
let summarizeResult;
|
|
1401
|
+
// If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
|
|
1402
|
+
// state of all the nodes.
|
|
1403
|
+
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
1340
1404
|
try {
|
|
1341
1405
|
summarizeResult = await this.summarize({
|
|
1342
|
-
|
|
1343
|
-
// If the GC state needs to be reset, we need to regenerate the summary and update the unreferenced
|
|
1344
|
-
// state of all the nodes.
|
|
1345
|
-
fullTree: fullTree || this.garbageCollector.summaryStateNeedsReset,
|
|
1406
|
+
fullTree: fullTree || forcedFullTree,
|
|
1346
1407
|
trackState: true,
|
|
1408
|
+
summaryLogger,
|
|
1347
1409
|
runGC: this.garbageCollector.shouldRunGC,
|
|
1348
1410
|
});
|
|
1349
1411
|
}
|
|
@@ -1357,12 +1419,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1357
1419
|
const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1358
1420
|
common_utils_1.assert(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1359
1421
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1360
|
-
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
|
|
1422
|
+
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);
|
|
1361
1423
|
const generateSummaryData = {
|
|
1362
1424
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1363
1425
|
summaryTree,
|
|
1364
1426
|
summaryStats,
|
|
1365
1427
|
generateDuration: trace.trace().duration,
|
|
1428
|
+
forcedFullTree,
|
|
1366
1429
|
};
|
|
1367
1430
|
continueResult = checkContinue();
|
|
1368
1431
|
if (!continueResult.continue) {
|
|
@@ -1372,7 +1435,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1372
1435
|
const summaryContext = lastAck === undefined
|
|
1373
1436
|
? {
|
|
1374
1437
|
proposalHandle: undefined,
|
|
1375
|
-
ackHandle: (
|
|
1438
|
+
ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
|
|
1376
1439
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1377
1440
|
}
|
|
1378
1441
|
: {
|
|
@@ -1467,6 +1530,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1467
1530
|
};
|
|
1468
1531
|
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
1469
1532
|
}
|
|
1533
|
+
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1534
|
+
const aliasMessage = contents;
|
|
1535
|
+
if (!dataStore_1.isDataStoreAliasMessage(aliasMessage)) {
|
|
1536
|
+
throw new container_utils_1.UsageError("malformedDataStoreAliasMessage");
|
|
1537
|
+
}
|
|
1538
|
+
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
1539
|
+
}
|
|
1470
1540
|
async uploadBlob(blob) {
|
|
1471
1541
|
this.verifyNotClosed();
|
|
1472
1542
|
return this.blobManager.createBlob(blob);
|