@fluidframework/container-runtime 0.56.7 → 0.57.1
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 +68 -28
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +148 -89
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +27 -0
- package/dist/dataStore.d.ts.map +1 -0
- package/dist/dataStore.js +113 -0
- package/dist/dataStore.js.map +1 -0
- package/dist/dataStoreContext.d.ts +1 -7
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +10 -6
- 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 +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -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 +68 -28
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +149 -90
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +27 -0
- package/lib/dataStore.d.ts.map +1 -0
- package/lib/dataStore.js +108 -0
- package/lib/dataStore.js.map +1 -0
- package/lib/dataStoreContext.d.ts +1 -7
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +10 -6
- 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 +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -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 -115
- package/src/dataStore.ts +151 -0
- package/src/dataStoreContext.ts +11 -14
- package/src/dataStores.ts +23 -38
- package/src/garbageCollection.ts +385 -150
- package/src/index.ts +2 -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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
2
2
|
import { assert, Trace, TypedEventEmitter, unreachableCase, performance, } from "@fluidframework/common-utils";
|
|
3
|
-
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
3
|
+
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, TelemetryDataTag, } from "@fluidframework/telemetry-utils";
|
|
4
4
|
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
5
5
|
import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-utils";
|
|
6
6
|
import { DataCorruptionError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
|
|
7
7
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
8
8
|
import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
9
|
-
import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree,
|
|
9
|
+
import { addBlobToSummary, addTreeToSummary, convertToSummaryTree, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, } from "@fluidframework/runtime-utils";
|
|
10
10
|
import { v4 as uuid } from "uuid";
|
|
11
11
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
12
12
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
@@ -25,6 +25,7 @@ import { SummarizerClientElection, summarizerClientType } from "./summarizerClie
|
|
|
25
25
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
26
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
27
27
|
import { GarbageCollector, gcTreeKey, } from "./garbageCollection";
|
|
28
|
+
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
28
29
|
export var ContainerMessageType;
|
|
29
30
|
(function (ContainerMessageType) {
|
|
30
31
|
// An op to be delivered to store
|
|
@@ -52,9 +53,24 @@ const DefaultSummaryConfiguration = {
|
|
|
52
53
|
// the min of the two will be chosen
|
|
53
54
|
maxAckWaitTime: 120000,
|
|
54
55
|
};
|
|
55
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Accepted header keys for requests coming to the runtime.
|
|
58
|
+
*/
|
|
59
|
+
export var RuntimeHeaders;
|
|
60
|
+
(function (RuntimeHeaders) {
|
|
61
|
+
/** True to wait for a data store to be created and loaded before returning it. */
|
|
62
|
+
RuntimeHeaders["wait"] = "wait";
|
|
63
|
+
/**
|
|
64
|
+
* True if the request is from an external app. Used for GC to handle scenarios where a data store
|
|
65
|
+
* is deleted and requested via an external app.
|
|
66
|
+
*/
|
|
67
|
+
RuntimeHeaders["externalRequest"] = "externalRequest";
|
|
68
|
+
/** True if the request is coming from an IFluidHandle. */
|
|
69
|
+
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
70
|
+
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
56
71
|
// Local storage key to set the default flush mode to TurnBased
|
|
57
72
|
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
73
|
+
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
58
74
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
59
75
|
export var RuntimeMessage;
|
|
60
76
|
(function (RuntimeMessage) {
|
|
@@ -329,7 +345,7 @@ export function getDeviceSpec() {
|
|
|
329
345
|
*/
|
|
330
346
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
331
347
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
332
|
-
var _a, _b, _c, _d, _e, _f;
|
|
348
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
333
349
|
super();
|
|
334
350
|
this.context = context;
|
|
335
351
|
this.registry = registry;
|
|
@@ -345,7 +361,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
345
361
|
this.paused = false;
|
|
346
362
|
this.consecutiveReconnects = 0;
|
|
347
363
|
this._disposed = false;
|
|
348
|
-
this.dirtyContainer = false;
|
|
349
364
|
this.emitDirtyDocumentEvent = true;
|
|
350
365
|
this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
|
|
351
366
|
/**
|
|
@@ -397,7 +412,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
397
412
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
398
413
|
}
|
|
399
414
|
};
|
|
400
|
-
this.
|
|
415
|
+
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
401
416
|
// If this is an existing container, we get values from metadata.
|
|
402
417
|
// otherwise, we initialize them.
|
|
403
418
|
if (existing) {
|
|
@@ -421,21 +436,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
421
436
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
422
437
|
this._flushMode =
|
|
423
438
|
((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? FlushMode.TurnBased : FlushMode.Immediate;
|
|
439
|
+
this._aliasingEnabled =
|
|
440
|
+
((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
|
|
441
|
+
((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
|
|
442
|
+
this.maxConsecutiveReconnects = (_e = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _e !== void 0 ? _e : this.defaultMaxConsecutiveReconnects;
|
|
443
|
+
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
|
|
424
444
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
* We use the timestamp of the last op for current timestamp. However, there can be cases where
|
|
428
|
-
* we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
|
|
429
|
-
* of this client's connection.
|
|
445
|
+
* Returns the timestamp of the last message seen by this client. This is used by garbage collector as
|
|
446
|
+
* the current reference timestamp for tracking unreferenced objects.
|
|
430
447
|
*/
|
|
431
|
-
|
|
432
|
-
var _a, _b, _c;
|
|
433
|
-
const client = this.clientId !== undefined ? this.getAudience().getMember(this.clientId) : undefined;
|
|
434
|
-
const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
|
|
435
|
-
return (_c = (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : timestamp) !== null && _c !== void 0 ? _c : Date.now();
|
|
436
|
-
};
|
|
437
|
-
this.maxConsecutiveReconnects = (_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
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);
|
|
448
|
+
() => { 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) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
439
449
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
440
450
|
this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
|
|
441
451
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
@@ -456,7 +466,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
456
466
|
if (this.context.baseSnapshot) {
|
|
457
467
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
|
|
458
468
|
}
|
|
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(), (
|
|
469
|
+
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(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
|
|
460
470
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
|
|
461
471
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
462
472
|
this.deltaSender = this.deltaManager;
|
|
@@ -465,6 +475,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
465
475
|
this.clearPartialChunks(clientId);
|
|
466
476
|
});
|
|
467
477
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
478
|
+
const { attachState, pendingLocalState } = this.context;
|
|
479
|
+
this.dirtyContainer = attachState !== AttachState.Attached
|
|
480
|
+
|| ((_f = pendingLocalState) === null || _f === void 0 ? void 0 : _f.pendingStates.length) > 0;
|
|
481
|
+
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
468
482
|
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
469
483
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
470
484
|
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
@@ -476,8 +490,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
476
490
|
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
477
491
|
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
478
492
|
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
479
|
-
const summarizerClientElectionEnabled = (
|
|
480
|
-
const maxOpsSinceLastSummary = (
|
|
493
|
+
const summarizerClientElectionEnabled = (_g = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _g !== void 0 ? _g : ((_h = this.runtimeOptions.summaryOptions) === null || _h === void 0 ? void 0 : _h.summarizerClientElection) === true;
|
|
494
|
+
const maxOpsSinceLastSummary = (_j = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _j !== void 0 ? _j : 7000;
|
|
481
495
|
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
482
496
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
483
497
|
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
@@ -561,7 +575,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
561
575
|
runtimeVersion: pkgVersion,
|
|
562
576
|
},
|
|
563
577
|
});
|
|
564
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", } = runtimeOptions;
|
|
578
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
|
|
565
579
|
// We pack at data store level only. If isolated channels are disabled,
|
|
566
580
|
// then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
|
|
567
581
|
const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
|
|
@@ -629,6 +643,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
629
643
|
summaryOptions,
|
|
630
644
|
gcOptions,
|
|
631
645
|
loadSequenceNumberVerification,
|
|
646
|
+
useDataStoreAliasing,
|
|
632
647
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
|
|
633
648
|
return runtime;
|
|
634
649
|
}
|
|
@@ -768,7 +783,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
768
783
|
* @param request - Request made to the handler.
|
|
769
784
|
*/
|
|
770
785
|
async resolveHandle(request) {
|
|
771
|
-
var _a, _b;
|
|
772
786
|
try {
|
|
773
787
|
const requestParser = RequestParser.create(request);
|
|
774
788
|
const id = requestParser.pathParts[0];
|
|
@@ -789,18 +803,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
789
803
|
}
|
|
790
804
|
}
|
|
791
805
|
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);
|
|
806
|
+
const dataStore = await this.getDataStoreFromRequest(id, request);
|
|
804
807
|
const subRequest = requestParser.createSubRequest(1);
|
|
805
808
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
806
809
|
// unintentionally modifying the url if that changes.
|
|
@@ -813,46 +816,37 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
813
816
|
return exceptionToResponse(error);
|
|
814
817
|
}
|
|
815
818
|
}
|
|
816
|
-
|
|
817
|
-
var _a;
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
// the base summary we loaded from. So, use the message from its metadata blob.
|
|
821
|
-
message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.baseSummaryMessage, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
|
|
822
|
-
}
|
|
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) {
|
|
819
|
+
async getDataStoreFromRequest(id, request) {
|
|
820
|
+
var _a, _b, _c;
|
|
821
|
+
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
822
|
+
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait] : true;
|
|
832
823
|
const dataStoreContext = await this.dataStores.getDataStore(id, wait);
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
824
|
+
/**
|
|
825
|
+
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
826
|
+
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
827
|
+
* GC data.
|
|
828
|
+
*
|
|
829
|
+
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
830
|
+
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
831
|
+
*/
|
|
832
|
+
if (((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
833
|
+
// The data store is referenced if used routes in the base 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
|
+
throw responseToException(create404Response(request), request);
|
|
838
|
+
}
|
|
838
839
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
840
|
+
const dataStoreChannel = await dataStoreContext.realize();
|
|
841
|
+
this.garbageCollector.nodeUpdated(`/${id}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request === null || request === void 0 ? void 0 : request.headers);
|
|
842
|
+
return dataStoreChannel;
|
|
842
843
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
summaryLogger: this.logger,
|
|
850
|
-
fullTree: true,
|
|
851
|
-
trackState: false,
|
|
852
|
-
runGC: this.garbageCollector.shouldRunGC,
|
|
853
|
-
fullGC: true,
|
|
854
|
-
});
|
|
855
|
-
return convertSummaryTreeToITree(summaryResult.summary);
|
|
844
|
+
formMetadata() {
|
|
845
|
+
var _a;
|
|
846
|
+
return Object.assign(Object.assign({}, this.createContainerMetadata), { summaryCount: this.summaryCount, summaryFormatVersion: 1, disableIsolatedChannels: this.disableIsolatedChannels || undefined, gcFeature: this.garbageCollector.gcSummaryFeatureVersion,
|
|
847
|
+
// The last message processed at the time of summary. If there are no new messages, use the message from the
|
|
848
|
+
// last summary.
|
|
849
|
+
message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary, sessionExpiryTimeoutMs: this.garbageCollector.sessionExpiryTimeoutMs });
|
|
856
850
|
}
|
|
857
851
|
addContainerStateToSummary(summaryTree) {
|
|
858
852
|
var _a;
|
|
@@ -1057,9 +1051,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1057
1051
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1058
1052
|
return context.realize();
|
|
1059
1053
|
}
|
|
1060
|
-
async getDataStore(id, wait = true) {
|
|
1061
|
-
return (await this.dataStores.getDataStore(id, wait)).realize();
|
|
1062
|
-
}
|
|
1063
1054
|
setFlushMode(mode) {
|
|
1064
1055
|
if (mode === this._flushMode) {
|
|
1065
1056
|
return;
|
|
@@ -1124,28 +1115,84 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1124
1115
|
}
|
|
1125
1116
|
}
|
|
1126
1117
|
async createDataStore(pkg) {
|
|
1127
|
-
|
|
1118
|
+
const internalId = uuid();
|
|
1119
|
+
return channelToDataStore(await this._createDataStore(pkg, false /* isRoot */, internalId), internalId, this, this.dataStores, this.mc.logger);
|
|
1128
1120
|
}
|
|
1129
|
-
|
|
1121
|
+
/**
|
|
1122
|
+
* Creates a root datastore directly with a user generated id and attaches it to storage.
|
|
1123
|
+
* It is vulnerable to name collisions and should not be used.
|
|
1124
|
+
*
|
|
1125
|
+
* This method will be removed. See #6465.
|
|
1126
|
+
*/
|
|
1127
|
+
async createRootDataStoreLegacy(pkg, rootDataStoreId) {
|
|
1130
1128
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1131
1129
|
fluidDataStore.bindToContext();
|
|
1132
1130
|
return fluidDataStore;
|
|
1133
1131
|
}
|
|
1132
|
+
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1133
|
+
return this._aliasingEnabled === true ?
|
|
1134
|
+
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1135
|
+
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Creates a data store then attempts to alias it.
|
|
1139
|
+
* If aliasing fails, it will raise an exception.
|
|
1140
|
+
*
|
|
1141
|
+
* This method will be removed. See #6465.
|
|
1142
|
+
*
|
|
1143
|
+
* @param pkg - Package name of the data store
|
|
1144
|
+
* @param alias - Alias to be assigned to the data store
|
|
1145
|
+
* @param props - Properties for the data store
|
|
1146
|
+
* @returns - An aliased data store which can can be found / loaded by alias.
|
|
1147
|
+
*/
|
|
1148
|
+
async createAndAliasDataStore(pkg, alias, props) {
|
|
1149
|
+
const internalId = uuid();
|
|
1150
|
+
const dataStore = await this._createDataStore(pkg, false /* isRoot */, internalId, props);
|
|
1151
|
+
const aliasedDataStore = channelToDataStore(dataStore, internalId, this, this.dataStores, this.mc.logger);
|
|
1152
|
+
const result = await aliasedDataStore.trySetAlias(alias);
|
|
1153
|
+
if (result !== "Success") {
|
|
1154
|
+
throw new GenericError("dataStoreAliasFailure", undefined /* error */, {
|
|
1155
|
+
alias: {
|
|
1156
|
+
value: alias,
|
|
1157
|
+
tag: TelemetryDataTag.UserData,
|
|
1158
|
+
},
|
|
1159
|
+
internalId: {
|
|
1160
|
+
value: internalId,
|
|
1161
|
+
tag: TelemetryDataTag.PackageData,
|
|
1162
|
+
},
|
|
1163
|
+
aliasResult: result,
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
return aliasedDataStore;
|
|
1167
|
+
}
|
|
1134
1168
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1135
1169
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1136
1170
|
}
|
|
1137
1171
|
createDetachedDataStore(pkg) {
|
|
1138
1172
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1139
1173
|
}
|
|
1140
|
-
|
|
1174
|
+
/**
|
|
1175
|
+
* Creates a possibly root datastore directly with a possibly user generated id and attaches it to storage.
|
|
1176
|
+
* It is vulnerable to name collisions if both aforementioned conditions are true, and should not be used.
|
|
1177
|
+
*
|
|
1178
|
+
* This method will be removed. See #6465.
|
|
1179
|
+
*/
|
|
1180
|
+
async _createDataStoreWithPropsLegacy(pkg, props, id = uuid(), isRoot = false) {
|
|
1141
1181
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1142
1182
|
if (isRoot) {
|
|
1143
1183
|
fluidDataStore.bindToContext();
|
|
1144
1184
|
}
|
|
1145
|
-
return fluidDataStore;
|
|
1185
|
+
return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
1146
1186
|
}
|
|
1147
|
-
async
|
|
1148
|
-
return this.
|
|
1187
|
+
async _createDataStoreWithProps(pkg, props, id = uuid(), isRoot = false) {
|
|
1188
|
+
return this._aliasingEnabled === true && isRoot ?
|
|
1189
|
+
this.createAndAliasDataStore(pkg, id, props) :
|
|
1190
|
+
this._createDataStoreWithPropsLegacy(pkg, props, id, isRoot);
|
|
1191
|
+
}
|
|
1192
|
+
async _createDataStore(pkg, isRoot, id = uuid(), props) {
|
|
1193
|
+
return this.dataStores
|
|
1194
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props)
|
|
1195
|
+
.realize();
|
|
1149
1196
|
}
|
|
1150
1197
|
canSendOps() {
|
|
1151
1198
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
@@ -1203,6 +1250,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1203
1250
|
assert(this.attachState === AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
|
|
1204
1251
|
this.emit("attached");
|
|
1205
1252
|
}
|
|
1253
|
+
if (attachState === AttachState.Attached && !this.pendingStateManager.hasPendingMessages()) {
|
|
1254
|
+
this.updateDocumentDirtyState(false);
|
|
1255
|
+
}
|
|
1206
1256
|
this.dataStores.setAttachState(attachState);
|
|
1207
1257
|
}
|
|
1208
1258
|
/**
|
|
@@ -1249,13 +1299,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1249
1299
|
*/
|
|
1250
1300
|
async summarize(options) {
|
|
1251
1301
|
this.verifyNotClosed();
|
|
1252
|
-
const {
|
|
1302
|
+
const { fullTree = false, trackState = true, summaryLogger = this.logger, runGC = this.garbageCollector.shouldRunGC, runSweep, fullGC, } = options;
|
|
1303
|
+
let gcStats;
|
|
1253
1304
|
if (runGC) {
|
|
1254
|
-
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1305
|
+
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1255
1306
|
}
|
|
1256
1307
|
const summarizeResult = await this.summarizerNode.summarize(fullTree, trackState);
|
|
1257
1308
|
assert(summarizeResult.summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1258
|
-
return summarizeResult;
|
|
1309
|
+
return Object.assign(Object.assign({}, summarizeResult), { gcStats });
|
|
1259
1310
|
}
|
|
1260
1311
|
/**
|
|
1261
1312
|
* Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
|
|
@@ -1280,7 +1331,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1280
1331
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1281
1332
|
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1282
1333
|
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1283
|
-
* @returns the statistics of the used state of the data stores.
|
|
1284
1334
|
*/
|
|
1285
1335
|
updateUsedRoutes(usedRoutes, gcTimestamp) {
|
|
1286
1336
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
@@ -1314,7 +1364,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1314
1364
|
* @param options - options controlling how the summary is generated or submitted
|
|
1315
1365
|
*/
|
|
1316
1366
|
async submitSummary(options) {
|
|
1317
|
-
var _a;
|
|
1367
|
+
var _a, _b;
|
|
1318
1368
|
const { fullTree, refreshLatestAck, summaryLogger } = options;
|
|
1319
1369
|
if (refreshLatestAck) {
|
|
1320
1370
|
const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1378,9 +1428,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1378
1428
|
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
1379
1429
|
try {
|
|
1380
1430
|
summarizeResult = await this.summarize({
|
|
1381
|
-
summaryLogger,
|
|
1382
1431
|
fullTree: fullTree || forcedFullTree,
|
|
1383
1432
|
trackState: true,
|
|
1433
|
+
summaryLogger,
|
|
1384
1434
|
runGC: this.garbageCollector.shouldRunGC,
|
|
1385
1435
|
});
|
|
1386
1436
|
}
|
|
@@ -1388,13 +1438,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1388
1438
|
return { stage: "base", referenceSequenceNumber: summaryRefSeqNum, error };
|
|
1389
1439
|
}
|
|
1390
1440
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
1441
|
+
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
1442
|
+
this.messageAtLastSummary = this.deltaManager.lastMessage;
|
|
1391
1443
|
// Counting dataStores and handles
|
|
1392
1444
|
// Because handles are unchanged dataStores in the current logic,
|
|
1393
1445
|
// summarized dataStore count is total dataStore count minus handle count
|
|
1394
1446
|
const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
|
|
1395
1447
|
assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1396
1448
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
|
|
1397
|
-
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount }, partialStats);
|
|
1449
|
+
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);
|
|
1398
1450
|
const generateSummaryData = {
|
|
1399
1451
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1400
1452
|
summaryTree,
|
|
@@ -1410,7 +1462,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1410
1462
|
const summaryContext = lastAck === undefined
|
|
1411
1463
|
? {
|
|
1412
1464
|
proposalHandle: undefined,
|
|
1413
|
-
ackHandle: (
|
|
1465
|
+
ackHandle: (_b = this.context.getLoadedFromVersion()) === null || _b === void 0 ? void 0 : _b.id,
|
|
1414
1466
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1415
1467
|
}
|
|
1416
1468
|
: {
|
|
@@ -1505,6 +1557,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1505
1557
|
};
|
|
1506
1558
|
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
1507
1559
|
}
|
|
1560
|
+
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1561
|
+
const aliasMessage = contents;
|
|
1562
|
+
if (!isDataStoreAliasMessage(aliasMessage)) {
|
|
1563
|
+
throw new UsageError("malformedDataStoreAliasMessage");
|
|
1564
|
+
}
|
|
1565
|
+
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
1566
|
+
}
|
|
1508
1567
|
async uploadBlob(blob) {
|
|
1509
1568
|
this.verifyNotClosed();
|
|
1510
1569
|
return this.blobManager.createBlob(blob);
|