@fluidframework/container-runtime 0.59.4001 → 1.1.0-75972
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/.eslintrc.js +1 -1
- package/dist/blobManager.d.ts +2 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +12 -11
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +19 -0
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +23 -23
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +137 -29
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +338 -118
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +14 -3
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +4 -2
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +16 -5
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +0 -4
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js +12 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +4 -3
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +13 -7
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +23 -27
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +44 -119
- 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/orderedClientElection.js +0 -4
- package/dist/orderedClientElection.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/pendingStateManager.d.ts +30 -29
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +72 -109
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +4 -3
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +11 -6
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/serializedSnapshotStorage.d.ts +58 -0
- package/dist/serializedSnapshotStorage.d.ts.map +1 -0
- package/dist/serializedSnapshotStorage.js +108 -0
- package/dist/serializedSnapshotStorage.js.map +1 -0
- package/dist/summarizer.d.ts +11 -4
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +18 -9
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts +5 -3
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +10 -3
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +4 -2
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryManager.d.ts +3 -3
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +7 -7
- package/dist/summaryManager.js.map +1 -1
- package/garbageCollection.md +9 -1
- package/lib/blobManager.d.ts +2 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +12 -11
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +19 -0
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +23 -23
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +137 -29
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +341 -121
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +15 -4
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +4 -2
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +16 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +0 -4
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js +12 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +4 -3
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +14 -8
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +23 -27
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +43 -117
- 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/orderedClientElection.js +0 -4
- package/lib/orderedClientElection.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/pendingStateManager.d.ts +30 -29
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +72 -109
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +4 -3
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +11 -6
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/serializedSnapshotStorage.d.ts +58 -0
- package/lib/serializedSnapshotStorage.d.ts.map +1 -0
- package/lib/serializedSnapshotStorage.js +104 -0
- package/lib/serializedSnapshotStorage.js.map +1 -0
- package/lib/summarizer.d.ts +11 -4
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +18 -9
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts +5 -3
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +10 -3
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +4 -2
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryManager.d.ts +3 -3
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +7 -7
- package/lib/summaryManager.js.map +1 -1
- package/package.json +19 -32
- package/src/blobManager.ts +29 -15
- package/src/connectionTelemetry.ts +60 -39
- package/src/containerRuntime.ts +502 -156
- package/src/dataStore.ts +21 -4
- package/src/dataStoreContext.ts +27 -5
- package/src/dataStoreRegistry.ts +8 -1
- package/src/dataStores.ts +21 -8
- package/src/garbageCollection.ts +81 -166
- package/src/index.ts +7 -1
- package/src/orderedClientElection.ts +1 -1
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +104 -123
- package/src/runningSummarizer.ts +20 -10
- package/src/serializedSnapshotStorage.ts +146 -0
- package/src/summarizer.ts +20 -16
- package/src/summarizerHeuristics.ts +21 -5
- package/src/summarizerTypes.ts +4 -2
- package/src/summaryManager.ts +5 -6
package/dist/containerRuntime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.ContainerMessageType = void 0;
|
|
3
|
+
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.ScheduleManager = exports.unpackRuntimeMessage = exports.isRuntimeMessage = exports.RuntimeMessage = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
4
4
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
5
5
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
6
6
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
@@ -31,6 +31,7 @@ const runWhileConnectedCoordinator_1 = require("./runWhileConnectedCoordinator")
|
|
|
31
31
|
const garbageCollection_1 = require("./garbageCollection");
|
|
32
32
|
const dataStore_1 = require("./dataStore");
|
|
33
33
|
const batchTracker_1 = require("./batchTracker");
|
|
34
|
+
const serializedSnapshotStorage_1 = require("./serializedSnapshotStorage");
|
|
34
35
|
const opTelemetry_1 = require("./opTelemetry");
|
|
35
36
|
var ContainerMessageType;
|
|
36
37
|
(function (ContainerMessageType) {
|
|
@@ -47,17 +48,16 @@ var ContainerMessageType;
|
|
|
47
48
|
// Sets the alias of a root data store
|
|
48
49
|
ContainerMessageType["Alias"] = "alias";
|
|
49
50
|
})(ContainerMessageType = exports.ContainerMessageType || (exports.ContainerMessageType = {}));
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
maxAckWaitTime: 600000,
|
|
51
|
+
exports.DefaultSummaryConfiguration = {
|
|
52
|
+
state: "enabled",
|
|
53
|
+
idleTime: 5000 * 3,
|
|
54
|
+
maxTime: 5000 * 12,
|
|
55
|
+
maxOps: 100,
|
|
56
|
+
minOpsForLastSummaryAttempt: 10,
|
|
57
|
+
maxAckWaitTime: 6 * 10 * 1000,
|
|
58
|
+
maxOpsSinceLastSummary: 7000,
|
|
59
|
+
initialSummarizerDelayMs: 5000,
|
|
60
|
+
summarizerClientElection: false,
|
|
61
61
|
};
|
|
62
62
|
/**
|
|
63
63
|
* Accepted header keys for requests coming to the runtime.
|
|
@@ -208,7 +208,7 @@ class ScheduleManagerCore {
|
|
|
208
208
|
}
|
|
209
209
|
resumeQueue(startBatch, messageEndBatch) {
|
|
210
210
|
const endBatch = messageEndBatch.sequenceNumber;
|
|
211
|
-
const duration = common_utils_1.performance.now() - this.timePaused;
|
|
211
|
+
const duration = this.localPaused ? (common_utils_1.performance.now() - this.timePaused) : undefined;
|
|
212
212
|
this.batchCount++;
|
|
213
213
|
if (this.batchCount % 1000 === 1) {
|
|
214
214
|
this.logger.sendTelemetryEvent({
|
|
@@ -227,7 +227,7 @@ class ScheduleManagerCore {
|
|
|
227
227
|
}
|
|
228
228
|
this.localPaused = false;
|
|
229
229
|
// Random round number - we want to know when batch waiting paused op processing.
|
|
230
|
-
if (duration > connectionTelemetry_1.latencyThreshold) {
|
|
230
|
+
if (duration !== undefined && duration > connectionTelemetry_1.latencyThreshold) {
|
|
231
231
|
this.logger.sendErrorEvent({
|
|
232
232
|
eventName: "MaxBatchWaitTimeExceeded",
|
|
233
233
|
duration,
|
|
@@ -379,43 +379,32 @@ exports.getDeviceSpec = getDeviceSpec;
|
|
|
379
379
|
* It will define the store level mappings.
|
|
380
380
|
*/
|
|
381
381
|
class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
382
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler) {
|
|
383
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j
|
|
382
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
383
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
384
|
+
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, exports.DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
384
385
|
super();
|
|
385
386
|
this.context = context;
|
|
386
387
|
this.registry = registry;
|
|
387
388
|
this.runtimeOptions = runtimeOptions;
|
|
388
389
|
this.containerScope = containerScope;
|
|
389
390
|
this.logger = logger;
|
|
391
|
+
this._storage = _storage;
|
|
390
392
|
this.requestHandler = requestHandler;
|
|
393
|
+
this.summaryConfiguration = summaryConfiguration;
|
|
391
394
|
this.defaultMaxConsecutiveReconnects = 15;
|
|
392
395
|
this._orderSequentiallyCalls = 0;
|
|
393
396
|
this.needsFlush = false;
|
|
394
397
|
this.flushTrigger = false;
|
|
395
|
-
this.
|
|
398
|
+
this.savedOps = [];
|
|
396
399
|
this.consecutiveReconnects = 0;
|
|
397
400
|
this._disposed = false;
|
|
398
401
|
this.emitDirtyDocumentEvent = true;
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
* so we listen directly from DeltaManager instead.
|
|
406
|
-
*/
|
|
407
|
-
this.onOp = (op) => {
|
|
408
|
-
(0, common_utils_1.assert)(!this.paused, 0x128 /* "Container should not already be paused before applying stashed ops" */);
|
|
409
|
-
this.paused = true;
|
|
410
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
411
|
-
this.context.deltaManager.inbound.pause();
|
|
412
|
-
const stashP = this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
|
|
413
|
-
stashP.then(() => {
|
|
414
|
-
this.paused = false;
|
|
415
|
-
this.context.deltaManager.inbound.resume();
|
|
416
|
-
}, (error) => {
|
|
417
|
-
this.closeFn((0, telemetry_utils_1.normalizeError)(error));
|
|
418
|
-
});
|
|
402
|
+
this.defaultTelemetrySignalSampleCount = 100;
|
|
403
|
+
this._perfSignalData = {
|
|
404
|
+
signalsLost: 0,
|
|
405
|
+
signalSequenceNumber: 0,
|
|
406
|
+
signalTimestamp: 0,
|
|
407
|
+
trackingSignalSequenceNumber: undefined,
|
|
419
408
|
};
|
|
420
409
|
this.summarizeOnDemand = (...args) => {
|
|
421
410
|
if (this.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
@@ -447,27 +436,45 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
447
436
|
};
|
|
448
437
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
449
438
|
// Default to false (enabled).
|
|
450
|
-
this.disableIsolatedChannels = (
|
|
439
|
+
this.disableIsolatedChannels = (_b = this.runtimeOptions.summaryOptions.disableIsolatedChannels) !== null && _b !== void 0 ? _b : false;
|
|
451
440
|
this._connected = this.context.connected;
|
|
452
441
|
this.chunkMap = new Map(chunks);
|
|
453
442
|
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
454
443
|
this.mc = (0, telemetry_utils_1.loggerToMonitoringContext)(telemetry_utils_1.ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
444
|
+
this.summariesDisabled = this.isSummariesDisabled();
|
|
445
|
+
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
446
|
+
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
447
|
+
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
448
|
+
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
455
449
|
this._aliasingEnabled =
|
|
456
|
-
((
|
|
457
|
-
((
|
|
458
|
-
this._maxOpSizeInBytes = ((
|
|
450
|
+
((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
|
|
451
|
+
((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
|
|
452
|
+
this._maxOpSizeInBytes = ((_e = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _e !== void 0 ? _e : defaultMaxOpSizeInBytes);
|
|
459
453
|
this.maxConsecutiveReconnects =
|
|
460
|
-
(
|
|
454
|
+
(_f = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _f !== void 0 ? _f : this.defaultMaxConsecutiveReconnects;
|
|
461
455
|
this._flushMode = runtimeOptions.flushMode;
|
|
462
|
-
|
|
456
|
+
const pendingRuntimeState = context.pendingLocalState;
|
|
457
|
+
const baseSnapshot = (_g = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _g !== void 0 ? _g : context.baseSnapshot;
|
|
458
|
+
this.garbageCollector = garbageCollection_1.GarbageCollector.create({
|
|
459
|
+
runtime: this,
|
|
460
|
+
gcOptions: this.runtimeOptions.gcOptions,
|
|
461
|
+
baseSnapshot,
|
|
462
|
+
baseLogger: this.mc.logger,
|
|
463
|
+
existing,
|
|
464
|
+
metadata,
|
|
465
|
+
isSummarizerClient: this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType,
|
|
466
|
+
getNodePackagePath: (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
467
|
+
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
468
|
+
readAndParseBlob: async (id) => (0, driver_utils_1.readAndParse)(this.storage, id),
|
|
469
|
+
});
|
|
463
470
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
464
471
|
this.summarizerNode = (0, runtime_utils_1.createRootSummarizerNodeWithGC)(telemetry_utils_1.ChildLogger.create(this.logger, "SummarizerNode"),
|
|
465
472
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
466
|
-
async (fullTree, trackState) => this.summarizeInternal(fullTree, trackState),
|
|
473
|
+
async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
467
474
|
// Latest change sequence number, no changes since summary applied yet
|
|
468
475
|
loadedFromSequenceNumber,
|
|
469
476
|
// Summary reference sequence number, undefined if no summary yet
|
|
470
|
-
|
|
477
|
+
baseSnapshot ? loadedFromSequenceNumber : undefined, {
|
|
471
478
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
472
479
|
// a summary with an older protocol state.
|
|
473
480
|
canReuseHandle: false,
|
|
@@ -477,26 +484,31 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
477
484
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
478
485
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
479
486
|
});
|
|
480
|
-
if (
|
|
481
|
-
this.summarizerNode.loadBaseSummaryWithoutDifferential(
|
|
487
|
+
if (baseSnapshot) {
|
|
488
|
+
this.summarizerNode.loadBaseSummaryWithoutDifferential(baseSnapshot);
|
|
482
489
|
}
|
|
483
|
-
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(
|
|
490
|
+
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(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.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap), this.garbageCollector.writeDataAtRoot);
|
|
484
491
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, this.logger);
|
|
485
492
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, telemetry_utils_1.ChildLogger.create(this.logger, "ScheduleManager"));
|
|
486
493
|
this.deltaSender = this.deltaManager;
|
|
487
|
-
this.pendingStateManager = new pendingStateManager_1.PendingStateManager(
|
|
494
|
+
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
495
|
+
applyStashedOp: this.applyStashedOp.bind(this),
|
|
496
|
+
clientId: () => this.clientId,
|
|
497
|
+
close: this.closeFn,
|
|
498
|
+
connected: () => this.connected,
|
|
499
|
+
flush: this.flush.bind(this),
|
|
500
|
+
flushMode: () => this.flushMode,
|
|
501
|
+
reSubmit: this.reSubmit.bind(this),
|
|
502
|
+
rollback: this.rollback.bind(this),
|
|
503
|
+
setFlushMode: (mode) => this.setFlushMode(mode),
|
|
504
|
+
}, this._flushMode, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
488
505
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
489
506
|
this.clearPartialChunks(clientId);
|
|
490
507
|
});
|
|
491
508
|
this.summaryCollection = new summaryCollection_1.SummaryCollection(this.deltaManager, this.logger);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|| ((_f = pendingLocalState) === null || _f === void 0 ? void 0 : _f.pendingStates.length) > 0;
|
|
509
|
+
this.dirtyContainer = this.context.attachState !== container_definitions_1.AttachState.Attached
|
|
510
|
+
|| this.pendingStateManager.hasPendingMessages();
|
|
495
511
|
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
496
|
-
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
497
|
-
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
498
|
-
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
499
|
-
}
|
|
500
512
|
if (this.summariesDisabled) {
|
|
501
513
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
502
514
|
}
|
|
@@ -504,9 +516,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
504
516
|
const orderedClientLogger = telemetry_utils_1.ChildLogger.create(this.logger, "OrderedClientElection");
|
|
505
517
|
const orderedClientCollection = new orderedClientElection_1.OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
506
518
|
const orderedClientElectionForSummarizer = new orderedClientElection_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, summarizerClientElection_1.SummarizerClientElection.isClientEligible);
|
|
507
|
-
|
|
508
|
-
const maxOpsSinceLastSummary = (_j = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _j !== void 0 ? _j : 7000;
|
|
509
|
-
this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
519
|
+
this.summarizerClientElection = new summarizerClientElection_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary, this.summarizerClientElectionEnabled);
|
|
510
520
|
if (this.context.clientDetails.type === summarizerClientElection_1.summarizerClientType) {
|
|
511
521
|
this._summarizer = new summarizer_1.Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => runWhileConnectedCoordinator_1.RunWhileConnectedCoordinator.create(runtime));
|
|
512
522
|
}
|
|
@@ -514,7 +524,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
514
524
|
// Only create a SummaryManager and SummarizerClientElection
|
|
515
525
|
// if summaries are enabled and we are not the summarizer client.
|
|
516
526
|
const defaultAction = () => {
|
|
517
|
-
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
527
|
+
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
518
528
|
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
519
529
|
// unregister default to no log on every op after falling behind
|
|
520
530
|
// and register summary ack handler to re-register this handler
|
|
@@ -535,8 +545,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
535
545
|
30 * 1000, // 30 sec max delay
|
|
536
546
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
537
547
|
(0, throttler_1.formExponentialFn)({ coefficient: 20, initialDelay: 0 })), {
|
|
538
|
-
initialDelayMs: this.
|
|
539
|
-
}, this.
|
|
548
|
+
initialDelayMs: this.initialSummarizerDelayMs,
|
|
549
|
+
}, this.heuristicsDisabled);
|
|
540
550
|
this.summaryManager.start();
|
|
541
551
|
}
|
|
542
552
|
}
|
|
@@ -559,9 +569,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
559
569
|
(0, common_utils_1.assert)(!readonly || !this.connected, 0x125 /* "Unsafe to transition to read-only state!" */);
|
|
560
570
|
this.replayPendingStates();
|
|
561
571
|
});
|
|
562
|
-
if (context.pendingLocalState !== undefined) {
|
|
563
|
-
this.deltaManager.on("op", this.onOp);
|
|
564
|
-
}
|
|
565
572
|
// logging hardware telemetry
|
|
566
573
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
567
574
|
let loadSummaryNumber;
|
|
@@ -574,7 +581,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
574
581
|
};
|
|
575
582
|
// back-compat 0.59.3000 - Older document may either write summaryCount or not write it at all. If it does
|
|
576
583
|
// not write it, initialize summaryNumber to 0.
|
|
577
|
-
loadSummaryNumber = (
|
|
584
|
+
loadSummaryNumber = (_j = (_h = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _h !== void 0 ? _h : metadata === null || metadata === void 0 ? void 0 : metadata.summaryCount) !== null && _j !== void 0 ? _j : 0;
|
|
578
585
|
}
|
|
579
586
|
else {
|
|
580
587
|
this.createContainerMetadata = {
|
|
@@ -610,13 +617,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
610
617
|
runtimeVersion: packageVersion_1.pkgVersion,
|
|
611
618
|
},
|
|
612
619
|
});
|
|
613
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, } = runtimeOptions;
|
|
614
|
-
const
|
|
620
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
|
|
621
|
+
const pendingRuntimeState = context.pendingLocalState;
|
|
622
|
+
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
623
|
+
const storage = !pendingRuntimeState ?
|
|
624
|
+
context.storage :
|
|
625
|
+
new serializedSnapshotStorage_1.SerializedSnapshotStorage(() => { return context.storage; }, pendingRuntimeState.snapshotBlobs);
|
|
615
626
|
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
616
627
|
const tryFetchBlob = async (blobName) => {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
if (context.baseSnapshot && blobId) {
|
|
628
|
+
const blobId = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.blobs[blobName];
|
|
629
|
+
if (baseSnapshot && blobId) {
|
|
620
630
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
621
631
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
622
632
|
(0, common_utils_1.assert)(storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
@@ -631,7 +641,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
631
641
|
]);
|
|
632
642
|
const loadExisting = existing === true || context.existing === true;
|
|
633
643
|
// read snapshot blobs needed for BlobManager to load
|
|
634
|
-
const blobManagerSnapshot = await blobManager_1.BlobManager.load(
|
|
644
|
+
const blobManagerSnapshot = await blobManager_1.BlobManager.load(baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[summaryFormat_1.blobsTreeName], async (id) => {
|
|
635
645
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
636
646
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
637
647
|
(0, common_utils_1.assert)(storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
@@ -639,7 +649,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
639
649
|
});
|
|
640
650
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
641
651
|
const runtimeSequenceNumber = (_c = metadata === null || metadata === void 0 ? void 0 : metadata.message) === null || _c === void 0 ? void 0 : _c.sequenceNumber;
|
|
642
|
-
|
|
652
|
+
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
653
|
+
if (!pendingRuntimeState && runtimeSequenceNumber !== undefined) {
|
|
643
654
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
644
655
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
645
656
|
if (loadSequenceNumberVerification !== "bypass" && runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
@@ -661,7 +672,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
661
672
|
loadSequenceNumberVerification,
|
|
662
673
|
useDataStoreAliasing,
|
|
663
674
|
flushMode,
|
|
664
|
-
|
|
675
|
+
enableOfflineLoad,
|
|
676
|
+
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
677
|
+
if (pendingRuntimeState) {
|
|
678
|
+
await runtime.processSavedOps(pendingRuntimeState);
|
|
679
|
+
// delete these once runtime has seen them to save space
|
|
680
|
+
pendingRuntimeState.savedOps = [];
|
|
681
|
+
}
|
|
682
|
+
await runtime.getSnapshotBlobs();
|
|
665
683
|
return runtime;
|
|
666
684
|
}
|
|
667
685
|
get options() {
|
|
@@ -677,7 +695,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
677
695
|
return this.context.deltaManager;
|
|
678
696
|
}
|
|
679
697
|
get storage() {
|
|
680
|
-
return this.
|
|
698
|
+
return this._storage;
|
|
681
699
|
}
|
|
682
700
|
get reSubmitFn() {
|
|
683
701
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
@@ -709,19 +727,70 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
709
727
|
var _a;
|
|
710
728
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
711
729
|
}
|
|
712
|
-
get summaryConfiguration() {
|
|
713
|
-
var _a;
|
|
714
|
-
return Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = this.runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides);
|
|
715
|
-
}
|
|
716
730
|
get disposed() { return this._disposed; }
|
|
717
731
|
get summarizer() {
|
|
718
732
|
(0, common_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
719
733
|
return this._summarizer;
|
|
720
734
|
}
|
|
721
|
-
|
|
735
|
+
isSummariesDisabled() {
|
|
736
|
+
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
737
|
+
// to ISummaryConfiguration in 0.60.
|
|
738
|
+
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
return this.summaryConfiguration.state === "disabled";
|
|
742
|
+
}
|
|
743
|
+
isHeuristicsDisabled() {
|
|
722
744
|
var _a;
|
|
723
|
-
|
|
724
|
-
|
|
745
|
+
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
746
|
+
// to ISummaryConfiguration in 0.60.
|
|
747
|
+
if (((_a = this.runtimeOptions.summaryOptions.summarizerOptions) === null || _a === void 0 ? void 0 : _a.disableHeuristics) === true) {
|
|
748
|
+
return true;
|
|
749
|
+
}
|
|
750
|
+
return this.summaryConfiguration.state === "disableHeuristics";
|
|
751
|
+
}
|
|
752
|
+
isSummarizerClientElectionEnabled() {
|
|
753
|
+
var _a;
|
|
754
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
755
|
+
return (_a = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _a !== void 0 ? _a : true;
|
|
756
|
+
}
|
|
757
|
+
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
758
|
+
// to ISummaryConfiguration in 0.60.
|
|
759
|
+
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
763
|
+
return this.summaryConfiguration.summarizerClientElection === true;
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
return false;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
getMaxOpsSinceLastSummary() {
|
|
770
|
+
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
771
|
+
// to ISummaryConfiguration in 0.60.
|
|
772
|
+
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
773
|
+
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
774
|
+
}
|
|
775
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
776
|
+
return this.summaryConfiguration.maxOpsSinceLastSummary;
|
|
777
|
+
}
|
|
778
|
+
else {
|
|
779
|
+
return 0;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
getInitialSummarizerDelayMs() {
|
|
783
|
+
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
784
|
+
// to ISummaryConfiguration in 0.60.
|
|
785
|
+
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
786
|
+
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
787
|
+
}
|
|
788
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
789
|
+
return this.summaryConfiguration.initialSummarizerDelayMs;
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
return 0;
|
|
793
|
+
}
|
|
725
794
|
}
|
|
726
795
|
dispose(error) {
|
|
727
796
|
var _a;
|
|
@@ -820,12 +889,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
820
889
|
return (0, runtime_utils_1.exceptionToResponse)(error);
|
|
821
890
|
}
|
|
822
891
|
}
|
|
892
|
+
internalId(maybeAlias) {
|
|
893
|
+
var _a;
|
|
894
|
+
return (_a = this.dataStores.aliases().get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
895
|
+
}
|
|
823
896
|
async getDataStoreFromRequest(id, request) {
|
|
824
897
|
var _a, _b, _c;
|
|
825
898
|
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
826
899
|
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
|
|
827
900
|
: true;
|
|
828
|
-
const
|
|
901
|
+
const internalId = this.internalId(id);
|
|
902
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
829
903
|
/**
|
|
830
904
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
831
905
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -862,7 +936,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
862
936
|
message: (_a = (0, summaryFormat_1.extractSummaryMetadataMessage)(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary });
|
|
863
937
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summaryFormat_1.metadataBlobName, JSON.stringify(metadata));
|
|
864
938
|
}
|
|
865
|
-
addContainerStateToSummary(summaryTree, fullTree, trackState) {
|
|
939
|
+
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
866
940
|
var _a;
|
|
867
941
|
this.addMetadataToSummary(summaryTree);
|
|
868
942
|
if (this.chunkMap.size > 0) {
|
|
@@ -884,7 +958,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
884
958
|
(0, runtime_utils_1.addTreeToSummary)(summaryTree, summaryFormat_1.blobsTreeName, blobManagerSummary);
|
|
885
959
|
}
|
|
886
960
|
if (this.garbageCollector.writeDataAtRoot) {
|
|
887
|
-
const gcSummary = this.garbageCollector.summarize(fullTree, trackState);
|
|
961
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
888
962
|
if (gcSummary !== undefined) {
|
|
889
963
|
(0, runtime_utils_1.addSummarizeResultToSummary)(summaryTree, garbageCollection_1.gcTreeKey, gcSummary);
|
|
890
964
|
}
|
|
@@ -905,7 +979,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
905
979
|
this.resetReconnectCount();
|
|
906
980
|
return true;
|
|
907
981
|
}
|
|
908
|
-
this.consecutiveReconnects++;
|
|
909
982
|
if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
|
|
910
983
|
// If we're halfway through the max reconnects, send an event in order
|
|
911
984
|
// to better identify false positives, if any. If the rate of this event
|
|
@@ -914,6 +987,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
914
987
|
this.mc.logger.sendTelemetryEvent({
|
|
915
988
|
eventName: "ReconnectsWithNoProgress",
|
|
916
989
|
attempts: this.consecutiveReconnects,
|
|
990
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
917
991
|
});
|
|
918
992
|
}
|
|
919
993
|
return this.consecutiveReconnects < this.maxConsecutiveReconnects;
|
|
@@ -969,29 +1043,42 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
969
1043
|
this.verifyNotClosed();
|
|
970
1044
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
971
1045
|
const changeOfState = this._connected !== connected;
|
|
1046
|
+
const reconnection = changeOfState && connected;
|
|
972
1047
|
this._connected = connected;
|
|
973
|
-
if (
|
|
974
|
-
this.
|
|
975
|
-
this.
|
|
1048
|
+
if (!connected) {
|
|
1049
|
+
this._perfSignalData.signalsLost = 0;
|
|
1050
|
+
this._perfSignalData.signalTimestamp = 0;
|
|
1051
|
+
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1052
|
+
}
|
|
1053
|
+
if (reconnection) {
|
|
1054
|
+
this.consecutiveReconnects++;
|
|
976
1055
|
if (!this.shouldContinueReconnecting()) {
|
|
977
|
-
this.closeFn(
|
|
1056
|
+
this.closeFn(
|
|
978
1057
|
// pre-0.58 error message: MaxReconnectsWithNoProgress
|
|
979
|
-
"Runtime detected too many reconnects with no progress syncing local ops", undefined,
|
|
980
|
-
|
|
1058
|
+
container_utils_1.DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops", "setConnectionState", undefined, {
|
|
1059
|
+
dataLoss: 1,
|
|
1060
|
+
attempts: this.consecutiveReconnects,
|
|
1061
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
1062
|
+
}));
|
|
981
1063
|
return;
|
|
982
1064
|
}
|
|
1065
|
+
}
|
|
1066
|
+
if (changeOfState) {
|
|
983
1067
|
this.replayPendingStates();
|
|
984
1068
|
}
|
|
985
1069
|
this.dataStores.setConnectionState(connected, clientId);
|
|
986
1070
|
(0, telemetry_utils_1.raiseConnectedEvent)(this.mc.logger, this, connected, clientId);
|
|
987
1071
|
}
|
|
988
1072
|
process(messageArg, local) {
|
|
989
|
-
var _a;
|
|
1073
|
+
var _a, _b;
|
|
990
1074
|
this.verifyNotClosed();
|
|
991
1075
|
// If it's not message for runtime, bail out right away.
|
|
992
1076
|
if (!isRuntimeMessage(messageArg)) {
|
|
993
1077
|
return;
|
|
994
1078
|
}
|
|
1079
|
+
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
1080
|
+
this.savedOps.push(messageArg);
|
|
1081
|
+
}
|
|
995
1082
|
// Do shallow copy of message, as methods below will modify it.
|
|
996
1083
|
// There might be multiple container instances receiving same message
|
|
997
1084
|
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
@@ -1006,8 +1093,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1006
1093
|
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
1007
1094
|
// once all pieces are available
|
|
1008
1095
|
message = this.processRemoteChunkedMessage(message);
|
|
1009
|
-
|
|
1010
|
-
|
|
1096
|
+
let localOpMetadata;
|
|
1097
|
+
if (local) {
|
|
1098
|
+
// Call the PendingStateManager to process local messages.
|
|
1099
|
+
// Do not process local chunked ops until all pieces are available.
|
|
1100
|
+
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1101
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1011
1104
|
// If there are no more pending messages after processing a local message,
|
|
1012
1105
|
// the document is no longer dirty.
|
|
1013
1106
|
if (!this.pendingStateManager.hasPendingMessages()) {
|
|
@@ -1015,17 +1108,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1015
1108
|
}
|
|
1016
1109
|
switch (message.type) {
|
|
1017
1110
|
case ContainerMessageType.Attach:
|
|
1018
|
-
this.dataStores.processAttachMessage(message, local
|
|
1111
|
+
this.dataStores.processAttachMessage(message, local);
|
|
1019
1112
|
break;
|
|
1020
1113
|
case ContainerMessageType.Alias:
|
|
1021
1114
|
this.processAliasMessage(message, localOpMetadata, local);
|
|
1022
1115
|
break;
|
|
1023
1116
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1024
|
-
|
|
1025
|
-
this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
|
|
1117
|
+
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1026
1118
|
break;
|
|
1027
1119
|
case ContainerMessageType.BlobAttach:
|
|
1028
|
-
(0, common_utils_1.assert)((
|
|
1120
|
+
(0, common_utils_1.assert)((_b = message === null || message === void 0 ? void 0 : message.metadata) === null || _b === void 0 ? void 0 : _b.blobId, 0x12a /* "Missing blob id on metadata" */);
|
|
1029
1121
|
this.blobManager.processBlobAttachOp(message.metadata.blobId, local);
|
|
1030
1122
|
break;
|
|
1031
1123
|
default:
|
|
@@ -1047,6 +1139,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1047
1139
|
processAliasMessage(message, localOpMetadata, local) {
|
|
1048
1140
|
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1049
1141
|
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Emits the Signal event and update the perf signal data.
|
|
1144
|
+
* @param clientSignalSequenceNumber - is the client signal sequence number to be uploaded.
|
|
1145
|
+
*/
|
|
1146
|
+
sendSignalTelemetryEvent(clientSignalSequenceNumber) {
|
|
1147
|
+
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
1148
|
+
this.logger.sendPerformanceEvent({
|
|
1149
|
+
eventName: "SignalLatency",
|
|
1150
|
+
duration,
|
|
1151
|
+
signalsLost: this._perfSignalData.signalsLost,
|
|
1152
|
+
});
|
|
1153
|
+
this._perfSignalData.signalsLost = 0;
|
|
1154
|
+
this._perfSignalData.signalTimestamp = 0;
|
|
1155
|
+
}
|
|
1050
1156
|
processSignal(message, local) {
|
|
1051
1157
|
const envelope = message.content;
|
|
1052
1158
|
const transformed = {
|
|
@@ -1054,6 +1160,26 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1054
1160
|
content: envelope.contents.content,
|
|
1055
1161
|
type: envelope.contents.type,
|
|
1056
1162
|
};
|
|
1163
|
+
// Only collect signal telemetry for messages sent by the current client.
|
|
1164
|
+
if (message.clientId === this.clientId && this.connected) {
|
|
1165
|
+
// Check to see if the signal was lost.
|
|
1166
|
+
if (this._perfSignalData.trackingSignalSequenceNumber !== undefined &&
|
|
1167
|
+
envelope.clientSignalSequenceNumber > this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1168
|
+
this._perfSignalData.signalsLost++;
|
|
1169
|
+
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1170
|
+
this.logger.sendErrorEvent({
|
|
1171
|
+
eventName: "SignalLost",
|
|
1172
|
+
type: envelope.contents.type,
|
|
1173
|
+
signalsLost: this._perfSignalData.signalsLost,
|
|
1174
|
+
trackingSequenceNumber: this._perfSignalData.trackingSignalSequenceNumber,
|
|
1175
|
+
clientSignalSequenceNumber: envelope.clientSignalSequenceNumber,
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
else if (envelope.clientSignalSequenceNumber === this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1179
|
+
this.sendSignalTelemetryEvent(envelope.clientSignalSequenceNumber);
|
|
1180
|
+
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1057
1183
|
if (envelope.address === undefined) {
|
|
1058
1184
|
// No address indicates a container signal message.
|
|
1059
1185
|
this.emit("signal", transformed, local);
|
|
@@ -1062,7 +1188,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1062
1188
|
this.dataStores.processSignal(envelope.address, transformed, local);
|
|
1063
1189
|
}
|
|
1064
1190
|
async getRootDataStore(id, wait = true) {
|
|
1065
|
-
const
|
|
1191
|
+
const internalId = this.internalId(id);
|
|
1192
|
+
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1066
1193
|
(0, common_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1067
1194
|
return context.realize();
|
|
1068
1195
|
}
|
|
@@ -1117,18 +1244,32 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1117
1244
|
}
|
|
1118
1245
|
const savedFlushMode = this.flushMode;
|
|
1119
1246
|
this.setFlushMode(runtime_definitions_1.FlushMode.TurnBased);
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1247
|
+
try {
|
|
1248
|
+
this.trackOrderSequentiallyCalls(callback);
|
|
1249
|
+
this.flush();
|
|
1250
|
+
}
|
|
1251
|
+
finally {
|
|
1252
|
+
this.setFlushMode(savedFlushMode);
|
|
1253
|
+
}
|
|
1123
1254
|
}
|
|
1124
1255
|
trackOrderSequentiallyCalls(callback) {
|
|
1256
|
+
let checkpoint;
|
|
1257
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1258
|
+
checkpoint = this.pendingStateManager.checkpoint();
|
|
1259
|
+
}
|
|
1125
1260
|
try {
|
|
1126
1261
|
this._orderSequentiallyCalls++;
|
|
1127
1262
|
callback();
|
|
1128
1263
|
}
|
|
1129
1264
|
catch (error) {
|
|
1130
|
-
|
|
1131
|
-
|
|
1265
|
+
if (checkpoint) {
|
|
1266
|
+
// This will throw and close the container if rollback fails
|
|
1267
|
+
checkpoint.rollback();
|
|
1268
|
+
}
|
|
1269
|
+
else {
|
|
1270
|
+
// pre-0.58 error message: orderSequentiallyCallbackException
|
|
1271
|
+
this.closeFn(new container_utils_1.GenericError("orderSequentially callback exception", error));
|
|
1272
|
+
}
|
|
1132
1273
|
throw error; // throw the original error for the consumer of the runtime
|
|
1133
1274
|
}
|
|
1134
1275
|
finally {
|
|
@@ -1157,7 +1298,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1157
1298
|
}
|
|
1158
1299
|
return fluidDataStore;
|
|
1159
1300
|
}
|
|
1301
|
+
/**
|
|
1302
|
+
* @deprecated - will be removed in an upcoming release. See #9660.
|
|
1303
|
+
*/
|
|
1160
1304
|
async createRootDataStore(pkg, rootDataStoreId) {
|
|
1305
|
+
if (rootDataStoreId.includes("/")) {
|
|
1306
|
+
throw new container_utils_1.UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
1307
|
+
}
|
|
1161
1308
|
return this._aliasingEnabled === true ?
|
|
1162
1309
|
this.createAndAliasDataStore(pkg, rootDataStoreId) :
|
|
1163
1310
|
this.createRootDataStoreLegacy(pkg, rootDataStoreId);
|
|
@@ -1194,6 +1341,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1194
1341
|
return aliasedDataStore;
|
|
1195
1342
|
}
|
|
1196
1343
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1344
|
+
if (rootDataStoreId.includes("/")) {
|
|
1345
|
+
throw new container_utils_1.UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
1346
|
+
}
|
|
1197
1347
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1198
1348
|
}
|
|
1199
1349
|
createDetachedDataStore(pkg) {
|
|
@@ -1267,6 +1417,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1267
1417
|
}
|
|
1268
1418
|
return true;
|
|
1269
1419
|
}
|
|
1420
|
+
createNewSignalEnvelope(address, type, content) {
|
|
1421
|
+
const newSequenceNumber = ++this._perfSignalData.signalSequenceNumber;
|
|
1422
|
+
const newEnvelope = {
|
|
1423
|
+
address,
|
|
1424
|
+
clientSignalSequenceNumber: newSequenceNumber,
|
|
1425
|
+
contents: { type, content },
|
|
1426
|
+
};
|
|
1427
|
+
// We should not track any signals in case we already have a tracking number.
|
|
1428
|
+
if (newSequenceNumber % this.defaultTelemetrySignalSampleCount === 1 &&
|
|
1429
|
+
this._perfSignalData.trackingSignalSequenceNumber === undefined) {
|
|
1430
|
+
this._perfSignalData.signalTimestamp = Date.now();
|
|
1431
|
+
this._perfSignalData.trackingSignalSequenceNumber = newSequenceNumber;
|
|
1432
|
+
}
|
|
1433
|
+
return newEnvelope;
|
|
1434
|
+
}
|
|
1270
1435
|
/**
|
|
1271
1436
|
* Submits the signal to be sent to other clients.
|
|
1272
1437
|
* @param type - Type of the signal.
|
|
@@ -1274,11 +1439,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1274
1439
|
*/
|
|
1275
1440
|
submitSignal(type, content) {
|
|
1276
1441
|
this.verifyNotClosed();
|
|
1277
|
-
const envelope =
|
|
1442
|
+
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
1278
1443
|
return this.context.submitSignalFn(envelope);
|
|
1279
1444
|
}
|
|
1280
1445
|
submitDataStoreSignal(address, type, content) {
|
|
1281
|
-
const envelope =
|
|
1446
|
+
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
1282
1447
|
return this.context.submitSignalFn(envelope);
|
|
1283
1448
|
}
|
|
1284
1449
|
setAttachState(attachState) {
|
|
@@ -1300,17 +1465,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1300
1465
|
* @param blobRedirectTable - A table passed during the attach process. While detached, blob upload is supported
|
|
1301
1466
|
* using IDs generated locally. After attach, these IDs cannot be used, so this table maps the old local IDs to the
|
|
1302
1467
|
* new storage IDs so requests can be redirected.
|
|
1468
|
+
* @param telemetryContext - summary data passed through the layers for telemetry purposes
|
|
1303
1469
|
*/
|
|
1304
|
-
createSummary(blobRedirectTable) {
|
|
1470
|
+
createSummary(blobRedirectTable, telemetryContext) {
|
|
1305
1471
|
if (blobRedirectTable) {
|
|
1306
1472
|
this.blobManager.setRedirectTable(blobRedirectTable);
|
|
1307
1473
|
}
|
|
1308
|
-
const summarizeResult = this.dataStores.createSummary();
|
|
1474
|
+
const summarizeResult = this.dataStores.createSummary(telemetryContext);
|
|
1309
1475
|
if (!this.disableIsolatedChannels) {
|
|
1310
1476
|
// Wrap data store summaries in .channels subtree.
|
|
1311
1477
|
(0, summaryFormat_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1312
1478
|
}
|
|
1313
|
-
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState
|
|
1479
|
+
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
|
|
1314
1480
|
return summarizeResult.summary;
|
|
1315
1481
|
}
|
|
1316
1482
|
async getAbsoluteUrl(relativeUrl) {
|
|
@@ -1322,15 +1488,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1322
1488
|
}
|
|
1323
1489
|
return this.context.getAbsoluteUrl(relativeUrl);
|
|
1324
1490
|
}
|
|
1325
|
-
async summarizeInternal(fullTree, trackState) {
|
|
1326
|
-
const summarizeResult = await this.dataStores.summarize(fullTree, trackState);
|
|
1491
|
+
async summarizeInternal(fullTree, trackState, telemetryContext) {
|
|
1492
|
+
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
1327
1493
|
let pathPartsForChildren;
|
|
1328
1494
|
if (!this.disableIsolatedChannels) {
|
|
1329
1495
|
// Wrap data store summaries in .channels subtree.
|
|
1330
1496
|
(0, summaryFormat_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1331
1497
|
pathPartsForChildren = [runtime_definitions_1.channelsTreeName];
|
|
1332
1498
|
}
|
|
1333
|
-
this.addContainerStateToSummary(summarizeResult, fullTree, trackState);
|
|
1499
|
+
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
1334
1500
|
return Object.assign(Object.assign({}, summarizeResult), { id: "", pathPartsForChildren });
|
|
1335
1501
|
}
|
|
1336
1502
|
/**
|
|
@@ -1343,7 +1509,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1343
1509
|
if (runGC) {
|
|
1344
1510
|
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1345
1511
|
}
|
|
1346
|
-
const
|
|
1512
|
+
const telemetryContext = new runtime_utils_1.TelemetryContext();
|
|
1513
|
+
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1514
|
+
this.logger.sendTelemetryEvent({ eventName: "SummarizeTelemetry", details: telemetryContext.serialize() });
|
|
1347
1515
|
(0, common_utils_1.assert)(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1348
1516
|
return { stats, summary, gcStats };
|
|
1349
1517
|
}
|
|
@@ -1707,9 +1875,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1707
1875
|
}
|
|
1708
1876
|
submit(type, content, localOpMetadata = undefined, opMetadata = undefined) {
|
|
1709
1877
|
this.verifyNotClosed();
|
|
1710
|
-
if (this.context.pendingLocalState !== undefined) {
|
|
1711
|
-
this.closeFn(new container_utils_1.GenericError("containerRuntimeSubmitWithPendingLocalState"));
|
|
1712
|
-
}
|
|
1713
1878
|
// There should be no ops in detached container state!
|
|
1714
1879
|
(0, common_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1715
1880
|
let clientSequenceNumber = -1;
|
|
@@ -1840,6 +2005,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1840
2005
|
(0, common_utils_1.unreachableCase)(type, `Unknown ContainerMessageType: ${type}`);
|
|
1841
2006
|
}
|
|
1842
2007
|
}
|
|
2008
|
+
rollback(type, content, localOpMetadata) {
|
|
2009
|
+
switch (type) {
|
|
2010
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
2011
|
+
// For operations, call rollbackDataStoreOp which will find the right store
|
|
2012
|
+
// and trigger rollback on it.
|
|
2013
|
+
this.dataStores.rollbackDataStoreOp(content, localOpMetadata);
|
|
2014
|
+
break;
|
|
2015
|
+
default:
|
|
2016
|
+
throw new Error(`Can't rollback ${type}`);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
1843
2019
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1844
2020
|
async refreshLatestSummaryAck(proposalHandle, ackHandle, summaryRefSeq, summaryLogger) {
|
|
1845
2021
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
@@ -1884,8 +2060,43 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1884
2060
|
return maybeSnapshot;
|
|
1885
2061
|
});
|
|
1886
2062
|
}
|
|
2063
|
+
notifyAttaching(snapshot) {
|
|
2064
|
+
var _a;
|
|
2065
|
+
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
2066
|
+
this.baseSnapshotBlobs = serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
async getSnapshotBlobs() {
|
|
2070
|
+
var _a;
|
|
2071
|
+
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
2072
|
+
this.attachState !== container_definitions_1.AttachState.Attached || this.context.pendingLocalState) {
|
|
2073
|
+
return;
|
|
2074
|
+
}
|
|
2075
|
+
(0, common_utils_1.assert)(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
|
|
2076
|
+
this.baseSnapshotBlobs = await serializedSnapshotStorage_1.SerializedSnapshotStorage.serializeTree(this.context.baseSnapshot, this.storage);
|
|
2077
|
+
}
|
|
1887
2078
|
getPendingLocalState() {
|
|
1888
|
-
|
|
2079
|
+
var _a;
|
|
2080
|
+
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad)) {
|
|
2081
|
+
throw new container_utils_1.UsageError("can't get state when offline load disabled");
|
|
2082
|
+
}
|
|
2083
|
+
const previousPendingState = this.context.pendingLocalState;
|
|
2084
|
+
if (previousPendingState) {
|
|
2085
|
+
return {
|
|
2086
|
+
pending: this.pendingStateManager.getLocalState(),
|
|
2087
|
+
snapshotBlobs: previousPendingState.snapshotBlobs,
|
|
2088
|
+
baseSnapshot: previousPendingState.baseSnapshot,
|
|
2089
|
+
savedOps: this.savedOps,
|
|
2090
|
+
};
|
|
2091
|
+
}
|
|
2092
|
+
(0, common_utils_1.assert)(!!this.context.baseSnapshot, 0x2e6 /* "Must have a base snapshot" */);
|
|
2093
|
+
(0, common_utils_1.assert)(!!this.baseSnapshotBlobs, 0x2e7 /* "Must serialize base snapshot blobs before getting runtime state" */);
|
|
2094
|
+
return {
|
|
2095
|
+
pending: this.pendingStateManager.getLocalState(),
|
|
2096
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
2097
|
+
baseSnapshot: this.context.baseSnapshot,
|
|
2098
|
+
savedOps: this.savedOps,
|
|
2099
|
+
};
|
|
1889
2100
|
}
|
|
1890
2101
|
/**
|
|
1891
2102
|
* * Forms a function that will request a Summarizer.
|
|
@@ -1913,6 +2124,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1913
2124
|
return summarizer;
|
|
1914
2125
|
};
|
|
1915
2126
|
}
|
|
2127
|
+
async processSavedOps(state) {
|
|
2128
|
+
for (const op of state.savedOps) {
|
|
2129
|
+
this.process(op, false);
|
|
2130
|
+
await this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
|
|
2131
|
+
}
|
|
2132
|
+
// we may not have seen every sequence number (because of system ops) so apply everything once we
|
|
2133
|
+
// don't have any more saved ops
|
|
2134
|
+
await this.pendingStateManager.applyStashedOpsAt();
|
|
2135
|
+
}
|
|
1916
2136
|
}
|
|
1917
2137
|
exports.ContainerRuntime = ContainerRuntime;
|
|
1918
2138
|
/**
|