@fluidframework/container-runtime 0.49.2 → 0.50.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/containerRuntime.d.ts +2 -2
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +73 -71
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +2 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -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/summarizer.d.ts +4 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +1 -1
- package/dist/summarizerClientElection.js +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summaryManager.d.ts +10 -3
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +9 -6
- package/dist/summaryManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +2 -2
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +73 -71
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +2 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +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/summarizer.d.ts +4 -1
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +3 -3
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +1 -1
- package/lib/summarizerClientElection.js +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summaryManager.d.ts +10 -3
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +9 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +16 -16
- package/src/containerRuntime.ts +106 -96
- package/src/dataStoreContext.ts +2 -1
- package/src/dataStores.ts +1 -1
- package/src/index.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/summarizer.ts +5 -2
- package/src/summarizerClientElection.ts +1 -1
- package/src/summaryManager.ts +17 -7
package/lib/containerRuntime.js
CHANGED
|
@@ -29,6 +29,7 @@ import { getLocalStorageFeatureGate } from "./localStorageFeatureGates";
|
|
|
29
29
|
import { OrderedClientCollection, OrderedClientElection } from "./orderedClientElection";
|
|
30
30
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
31
31
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
32
|
+
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
32
33
|
export var ContainerMessageType;
|
|
33
34
|
(function (ContainerMessageType) {
|
|
34
35
|
// An op to be delivered to store
|
|
@@ -291,6 +292,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
291
292
|
this.closeFn(normalizeError(error));
|
|
292
293
|
});
|
|
293
294
|
};
|
|
295
|
+
// @deprecated Needs to become private
|
|
294
296
|
this.raiseContainerWarning = (warning) => {
|
|
295
297
|
this.context.raiseContainerWarning(warning);
|
|
296
298
|
};
|
|
@@ -384,40 +386,45 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
384
386
|
}
|
|
385
387
|
});
|
|
386
388
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
387
|
-
const maxOpsSinceLastSummary = (_d = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _d !== void 0 ? _d : 7000;
|
|
388
|
-
const defaultAction = () => {
|
|
389
|
-
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
390
|
-
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
391
|
-
// unregister default to no log on every op after falling behind
|
|
392
|
-
// and register summary ack handler to re-register this handler
|
|
393
|
-
// after successful summary
|
|
394
|
-
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
395
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
|
|
396
|
-
// we've caught up, so re-register the default action to monitor for
|
|
397
|
-
// falling behind, and unregister ourself
|
|
398
|
-
this.summaryCollection.on("default", defaultAction);
|
|
399
|
-
});
|
|
400
|
-
this.summaryCollection.off("default", defaultAction);
|
|
401
|
-
}
|
|
402
|
-
};
|
|
403
|
-
this.summaryCollection.on("default", defaultAction);
|
|
404
|
-
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
405
|
-
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
406
|
-
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
407
|
-
const summarizerClientElectionEnabled = (_e = getLocalStorageFeatureGate("summarizerClientElection")) !== null && _e !== void 0 ? _e : ((_f = this.runtimeOptions.summaryOptions) === null || _f === void 0 ? void 0 : _f.summarizerClientElection) === true;
|
|
408
|
-
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
409
389
|
// Only create a SummaryManager if summaries are enabled and we are not the summarizer client
|
|
410
390
|
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
411
391
|
this._logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
412
392
|
}
|
|
413
393
|
else {
|
|
394
|
+
const maxOpsSinceLastSummary = (_d = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _d !== void 0 ? _d : 7000;
|
|
395
|
+
const defaultAction = () => {
|
|
396
|
+
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
397
|
+
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
398
|
+
// unregister default to no log on every op after falling behind
|
|
399
|
+
// and register summary ack handler to re-register this handler
|
|
400
|
+
// after successful summary
|
|
401
|
+
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
402
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:CaughtUp" });
|
|
403
|
+
// we've caught up, so re-register the default action to monitor for
|
|
404
|
+
// falling behind, and unregister ourself
|
|
405
|
+
this.summaryCollection.on("default", defaultAction);
|
|
406
|
+
});
|
|
407
|
+
this.summaryCollection.off("default", defaultAction);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
this.summaryCollection.on("default", defaultAction);
|
|
411
|
+
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
412
|
+
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
413
|
+
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
414
|
+
const summarizerClientElectionEnabled = (_e = getLocalStorageFeatureGate("summarizerClientElection")) !== null && _e !== void 0 ? _e : ((_f = this.runtimeOptions.summaryOptions) === null || _f === void 0 ? void 0 : _f.summarizerClientElection) === true;
|
|
415
|
+
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
414
416
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
415
|
-
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.IFluidHandleContext, this.summaryCollection);
|
|
417
|
+
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.IFluidHandleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
416
418
|
}
|
|
417
419
|
else if (SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)) {
|
|
418
420
|
// Create the SummaryManager and mark the initial state
|
|
421
|
+
const requestOptions = {
|
|
422
|
+
cache: false,
|
|
423
|
+
reconnect: false,
|
|
424
|
+
summarizingClient: true,
|
|
425
|
+
};
|
|
419
426
|
this.summaryManager = new SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
420
|
-
this.summaryCollection, this.logger, formRequestSummarizerFn(this.context.loader, this.context.deltaManager), new Throttler(60 * 1000, // 60 sec delay window
|
|
427
|
+
this.summaryCollection, this.logger, formRequestSummarizerFn(this.context.loader, this.context.deltaManager.lastSequenceNumber, requestOptions), new Throttler(60 * 1000, // 60 sec delay window
|
|
421
428
|
30 * 1000, // 30 sec max delay
|
|
422
429
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
423
430
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -592,7 +599,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
592
599
|
}
|
|
593
600
|
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
594
601
|
get summarizerClientId() {
|
|
595
|
-
|
|
602
|
+
var _a;
|
|
603
|
+
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
596
604
|
}
|
|
597
605
|
get summaryConfiguration() {
|
|
598
606
|
var _a, _b, _c;
|
|
@@ -790,6 +798,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
790
798
|
return root;
|
|
791
799
|
}
|
|
792
800
|
addContainerBlobsToSummary(summaryTree) {
|
|
801
|
+
var _a;
|
|
793
802
|
if (this.shouldWriteMetadata) {
|
|
794
803
|
addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(this.formMetadata()));
|
|
795
804
|
}
|
|
@@ -797,8 +806,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
797
806
|
const content = JSON.stringify([...this.chunkMap]);
|
|
798
807
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
799
808
|
}
|
|
800
|
-
|
|
801
|
-
|
|
809
|
+
if (this.summarizerClientElection) {
|
|
810
|
+
const electedSummarizerContent = JSON.stringify((_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.serialize());
|
|
811
|
+
addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
|
|
812
|
+
}
|
|
802
813
|
const snapshot = this.blobManager.snapshot();
|
|
803
814
|
// Some storage (like git) doesn't allow empty tree, so we can omit it.
|
|
804
815
|
// and the blob manager can handle the tree not existing when loading
|
|
@@ -1007,7 +1018,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1007
1018
|
}
|
|
1008
1019
|
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1009
1020
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1010
|
-
fluidDataStore.
|
|
1021
|
+
fluidDataStore.attachGraph();
|
|
1011
1022
|
return fluidDataStore;
|
|
1012
1023
|
}
|
|
1013
1024
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
@@ -1017,7 +1028,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1017
1028
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1018
1029
|
}
|
|
1019
1030
|
async _createDataStoreWithProps(pkg, props, id = uuid(), isRoot = false) {
|
|
1020
|
-
|
|
1031
|
+
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1032
|
+
if (isRoot) {
|
|
1033
|
+
fluidDataStore.attachGraph();
|
|
1034
|
+
}
|
|
1035
|
+
return fluidDataStore;
|
|
1021
1036
|
}
|
|
1022
1037
|
async _createDataStore(pkg, isRoot, id = uuid()) {
|
|
1023
1038
|
return this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot).realize();
|
|
@@ -1116,39 +1131,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1116
1131
|
return PerformanceEvent.timedExecAsync(logger, { eventName: "GarbageCollection" }, async (event) => {
|
|
1117
1132
|
var _a;
|
|
1118
1133
|
const gcStats = {};
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
this.dataStores.deleteUnusedRoutes(deletedNodeIds);
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
catch (error) {
|
|
1146
|
-
event.cancel(gcStats, error);
|
|
1147
|
-
throw error;
|
|
1134
|
+
// Get the container's GC data and run GC on the reference graph in it.
|
|
1135
|
+
const gcData = await this.dataStores.getGCData(fullGC);
|
|
1136
|
+
const { referencedNodeIds, deletedNodeIds } = runGarbageCollection(gcData.gcNodes, ["/"], this.logger);
|
|
1137
|
+
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1138
|
+
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1139
|
+
// always referenced, so the used routes is only self-route (empty string).
|
|
1140
|
+
this.summarizerNode.updateUsedRoutes([""]);
|
|
1141
|
+
// Remove this node's route ("/") and notify data stores of routes that are used in it.
|
|
1142
|
+
const usedRoutes = referencedNodeIds.filter((id) => { return id !== "/"; });
|
|
1143
|
+
const { dataStoreCount, unusedDataStoreCount } = this.dataStores.updateUsedRoutes(usedRoutes, (_a =
|
|
1144
|
+
// For now, we use the timestamp of the last op for gcTimestamp. However, there can be cases where
|
|
1145
|
+
// we don't have an op (on demand summaries for instance). In those cases, we will use the timestamp
|
|
1146
|
+
// of this client's connection - https://github.com/microsoft/FluidFramework/issues/7152.
|
|
1147
|
+
this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp);
|
|
1148
|
+
// Update stats to be reported in the peformance event.
|
|
1149
|
+
gcStats.deletedNodes = deletedNodeIds.length;
|
|
1150
|
+
gcStats.totalNodes = referencedNodeIds.length + deletedNodeIds.length;
|
|
1151
|
+
gcStats.deletedDataStores = unusedDataStoreCount;
|
|
1152
|
+
gcStats.totalDataStores = dataStoreCount;
|
|
1153
|
+
// If we are running in GC test mode, delete objects for unused routes. This enables testing scenarios
|
|
1154
|
+
// involving access to deleted data.
|
|
1155
|
+
if (this.gcTestMode) {
|
|
1156
|
+
this.dataStores.deleteUnusedRoutes(deletedNodeIds);
|
|
1148
1157
|
}
|
|
1149
1158
|
event.end(gcStats);
|
|
1150
1159
|
return gcStats;
|
|
1151
|
-
});
|
|
1160
|
+
}, { end: true, cancel: "error" });
|
|
1152
1161
|
}
|
|
1153
1162
|
async summarizeInternal(fullTree, trackState) {
|
|
1154
1163
|
const summarizeResult = await this.dataStores.summarize(fullTree, trackState);
|
|
@@ -1544,10 +1553,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1544
1553
|
}
|
|
1545
1554
|
}
|
|
1546
1555
|
async fetchSnapshotFromStorage(versionId, logger, event) {
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
let snapshot;
|
|
1550
|
-
try {
|
|
1556
|
+
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
1557
|
+
const stats = {};
|
|
1551
1558
|
const trace = Trace.start();
|
|
1552
1559
|
const versions = await this.storage.getVersions(versionId, 1);
|
|
1553
1560
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
@@ -1555,14 +1562,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1555
1562
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
1556
1563
|
assert(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
|
|
1557
1564
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
perfEvent.cancel(stats, error);
|
|
1562
|
-
throw error;
|
|
1563
|
-
}
|
|
1564
|
-
perfEvent.end(stats);
|
|
1565
|
-
return snapshot;
|
|
1565
|
+
perfEvent.end(stats);
|
|
1566
|
+
return maybeSnapshot;
|
|
1567
|
+
});
|
|
1566
1568
|
}
|
|
1567
1569
|
getPendingLocalState() {
|
|
1568
1570
|
return this.pendingStateManager.getLocalState();
|