@fluidframework/container-runtime 0.59.4001 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.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.js +3 -3
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +125 -29
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +242 -110
- package/dist/containerRuntime.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/dataStores.d.ts +4 -3
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +9 -3
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +3 -3
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +10 -8
- 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.js +3 -3
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +125 -29
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +243 -111
- package/lib/containerRuntime.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/dataStores.d.ts +4 -3
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +9 -3
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +3 -3
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +10 -8
- 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 +46 -31
- package/src/blobManager.ts +29 -15
- package/src/connectionTelemetry.ts +3 -3
- package/src/containerRuntime.ts +388 -135
- package/src/dataStoreContext.ts +27 -5
- package/src/dataStores.ts +15 -3
- package/src/garbageCollection.ts +21 -14
- 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/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,
|
|
3
|
+
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, loggerToMonitoringContext, TelemetryDataTag, } from "@fluidframework/telemetry-utils";
|
|
4
4
|
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
5
5
|
import { readAndParse } 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, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats,
|
|
9
|
+
import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
|
|
10
10
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
12
12
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
@@ -28,6 +28,7 @@ import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
|
28
28
|
import { GarbageCollector, GCNodeType, gcTreeKey, } from "./garbageCollection";
|
|
29
29
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
30
30
|
import { BindBatchTracker } from "./batchTracker";
|
|
31
|
+
import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
31
32
|
import { OpTracker } from "./opTelemetry";
|
|
32
33
|
export var ContainerMessageType;
|
|
33
34
|
(function (ContainerMessageType) {
|
|
@@ -44,17 +45,16 @@ export var ContainerMessageType;
|
|
|
44
45
|
// Sets the alias of a root data store
|
|
45
46
|
ContainerMessageType["Alias"] = "alias";
|
|
46
47
|
})(ContainerMessageType || (ContainerMessageType = {}));
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
maxAckWaitTime: 600000,
|
|
48
|
+
export const DefaultSummaryConfiguration = {
|
|
49
|
+
state: "enabled",
|
|
50
|
+
idleTime: 5000 * 3,
|
|
51
|
+
maxTime: 5000 * 12,
|
|
52
|
+
maxOps: 100,
|
|
53
|
+
minOpsForLastSummaryAttempt: 10,
|
|
54
|
+
maxAckWaitTime: 6 * 10 * 1000,
|
|
55
|
+
maxOpsSinceLastSummary: 7000,
|
|
56
|
+
initialSummarizerDelayMs: 5000,
|
|
57
|
+
summarizerClientElection: false,
|
|
58
58
|
};
|
|
59
59
|
/**
|
|
60
60
|
* Accepted header keys for requests coming to the runtime.
|
|
@@ -372,44 +372,26 @@ export function getDeviceSpec() {
|
|
|
372
372
|
* It will define the store level mappings.
|
|
373
373
|
*/
|
|
374
374
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
375
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler) {
|
|
376
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j
|
|
375
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
376
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
377
|
+
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
377
378
|
super();
|
|
378
379
|
this.context = context;
|
|
379
380
|
this.registry = registry;
|
|
380
381
|
this.runtimeOptions = runtimeOptions;
|
|
381
382
|
this.containerScope = containerScope;
|
|
382
383
|
this.logger = logger;
|
|
384
|
+
this._storage = _storage;
|
|
383
385
|
this.requestHandler = requestHandler;
|
|
386
|
+
this.summaryConfiguration = summaryConfiguration;
|
|
384
387
|
this.defaultMaxConsecutiveReconnects = 15;
|
|
385
388
|
this._orderSequentiallyCalls = 0;
|
|
386
389
|
this.needsFlush = false;
|
|
387
390
|
this.flushTrigger = false;
|
|
388
|
-
this.
|
|
391
|
+
this.savedOps = [];
|
|
389
392
|
this.consecutiveReconnects = 0;
|
|
390
393
|
this._disposed = false;
|
|
391
394
|
this.emitDirtyDocumentEvent = true;
|
|
392
|
-
/**
|
|
393
|
-
* Used to apply stashed ops at their reference sequence number.
|
|
394
|
-
* Normal op processing is synchronous, but applying stashed ops is async since the
|
|
395
|
-
* data store may not be loaded yet, so we pause DeltaManager between ops.
|
|
396
|
-
* It's also important that we see each op so we know all stashed ops have
|
|
397
|
-
* been applied by "connected" event, but process() doesn't see system ops,
|
|
398
|
-
* so we listen directly from DeltaManager instead.
|
|
399
|
-
*/
|
|
400
|
-
this.onOp = (op) => {
|
|
401
|
-
assert(!this.paused, 0x128 /* "Container should not already be paused before applying stashed ops" */);
|
|
402
|
-
this.paused = true;
|
|
403
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
404
|
-
this.context.deltaManager.inbound.pause();
|
|
405
|
-
const stashP = this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
|
|
406
|
-
stashP.then(() => {
|
|
407
|
-
this.paused = false;
|
|
408
|
-
this.context.deltaManager.inbound.resume();
|
|
409
|
-
}, (error) => {
|
|
410
|
-
this.closeFn(normalizeError(error));
|
|
411
|
-
});
|
|
412
|
-
};
|
|
413
395
|
this.summarizeOnDemand = (...args) => {
|
|
414
396
|
if (this.clientDetails.type === summarizerClientType) {
|
|
415
397
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -440,27 +422,34 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
440
422
|
};
|
|
441
423
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
442
424
|
// Default to false (enabled).
|
|
443
|
-
this.disableIsolatedChannels = (
|
|
425
|
+
this.disableIsolatedChannels = (_b = this.runtimeOptions.summaryOptions.disableIsolatedChannels) !== null && _b !== void 0 ? _b : false;
|
|
444
426
|
this._connected = this.context.connected;
|
|
445
427
|
this.chunkMap = new Map(chunks);
|
|
446
428
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
447
429
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
430
|
+
this.summariesDisabled = this.isSummariesDisabled();
|
|
431
|
+
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
432
|
+
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
433
|
+
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
434
|
+
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
448
435
|
this._aliasingEnabled =
|
|
449
|
-
((
|
|
450
|
-
((
|
|
451
|
-
this._maxOpSizeInBytes = ((
|
|
436
|
+
((_c = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _c !== void 0 ? _c : false) ||
|
|
437
|
+
((_d = runtimeOptions.useDataStoreAliasing) !== null && _d !== void 0 ? _d : false);
|
|
438
|
+
this._maxOpSizeInBytes = ((_e = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _e !== void 0 ? _e : defaultMaxOpSizeInBytes);
|
|
452
439
|
this.maxConsecutiveReconnects =
|
|
453
|
-
(
|
|
440
|
+
(_f = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _f !== void 0 ? _f : this.defaultMaxConsecutiveReconnects;
|
|
454
441
|
this._flushMode = runtimeOptions.flushMode;
|
|
455
|
-
|
|
442
|
+
const pendingRuntimeState = context.pendingLocalState;
|
|
443
|
+
const baseSnapshot = (_g = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _g !== void 0 ? _g : context.baseSnapshot;
|
|
444
|
+
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (nodePath) => this.getGCNodePackagePath(nodePath), () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata, this.context.clientDetails.type === summarizerClientType);
|
|
456
445
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
457
446
|
this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
|
|
458
447
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
459
|
-
async (fullTree, trackState) => this.summarizeInternal(fullTree, trackState),
|
|
448
|
+
async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
460
449
|
// Latest change sequence number, no changes since summary applied yet
|
|
461
450
|
loadedFromSequenceNumber,
|
|
462
451
|
// Summary reference sequence number, undefined if no summary yet
|
|
463
|
-
|
|
452
|
+
baseSnapshot ? loadedFromSequenceNumber : undefined, {
|
|
464
453
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
465
454
|
// a summary with an older protocol state.
|
|
466
455
|
canReuseHandle: false,
|
|
@@ -470,26 +459,31 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
470
459
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
471
460
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
472
461
|
});
|
|
473
|
-
if (
|
|
474
|
-
this.summarizerNode.loadBaseSummaryWithoutDifferential(
|
|
462
|
+
if (baseSnapshot) {
|
|
463
|
+
this.summarizerNode.loadBaseSummaryWithoutDifferential(baseSnapshot);
|
|
475
464
|
}
|
|
476
|
-
this.dataStores = new DataStores(getSummaryForDatastores(
|
|
465
|
+
this.dataStores = new DataStores(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);
|
|
477
466
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, this.logger);
|
|
478
467
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
479
468
|
this.deltaSender = this.deltaManager;
|
|
480
|
-
this.pendingStateManager = new PendingStateManager(
|
|
469
|
+
this.pendingStateManager = new PendingStateManager({
|
|
470
|
+
applyStashedOp: this.applyStashedOp.bind(this),
|
|
471
|
+
clientId: () => this.clientId,
|
|
472
|
+
close: this.closeFn,
|
|
473
|
+
connected: () => this.connected,
|
|
474
|
+
flush: this.flush.bind(this),
|
|
475
|
+
flushMode: () => this.flushMode,
|
|
476
|
+
reSubmit: this.reSubmit.bind(this),
|
|
477
|
+
rollback: this.rollback.bind(this),
|
|
478
|
+
setFlushMode: (mode) => this.setFlushMode(mode),
|
|
479
|
+
}, this._flushMode, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
481
480
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
482
481
|
this.clearPartialChunks(clientId);
|
|
483
482
|
});
|
|
484
483
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|| ((_f = pendingLocalState) === null || _f === void 0 ? void 0 : _f.pendingStates.length) > 0;
|
|
484
|
+
this.dirtyContainer = this.context.attachState !== AttachState.Attached
|
|
485
|
+
|| this.pendingStateManager.hasPendingMessages();
|
|
488
486
|
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
489
|
-
// Map the deprecated generateSummaries flag to disableSummaries.
|
|
490
|
-
if (this.runtimeOptions.summaryOptions.generateSummaries === false) {
|
|
491
|
-
this.runtimeOptions.summaryOptions.disableSummaries = true;
|
|
492
|
-
}
|
|
493
487
|
if (this.summariesDisabled) {
|
|
494
488
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
495
489
|
}
|
|
@@ -497,9 +491,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
497
491
|
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
498
492
|
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
499
493
|
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
500
|
-
|
|
501
|
-
const maxOpsSinceLastSummary = (_j = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _j !== void 0 ? _j : 7000;
|
|
502
|
-
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
494
|
+
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary, this.summarizerClientElectionEnabled);
|
|
503
495
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
504
496
|
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
505
497
|
}
|
|
@@ -507,7 +499,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
507
499
|
// Only create a SummaryManager and SummarizerClientElection
|
|
508
500
|
// if summaries are enabled and we are not the summarizer client.
|
|
509
501
|
const defaultAction = () => {
|
|
510
|
-
if (this.summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
502
|
+
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
511
503
|
this.logger.sendErrorEvent({ eventName: "SummaryStatus:Behind" });
|
|
512
504
|
// unregister default to no log on every op after falling behind
|
|
513
505
|
// and register summary ack handler to re-register this handler
|
|
@@ -528,8 +520,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
528
520
|
30 * 1000, // 30 sec max delay
|
|
529
521
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
530
522
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
531
|
-
initialDelayMs: this.
|
|
532
|
-
}, this.
|
|
523
|
+
initialDelayMs: this.initialSummarizerDelayMs,
|
|
524
|
+
}, this.heuristicsDisabled);
|
|
533
525
|
this.summaryManager.start();
|
|
534
526
|
}
|
|
535
527
|
}
|
|
@@ -552,9 +544,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
552
544
|
assert(!readonly || !this.connected, 0x125 /* "Unsafe to transition to read-only state!" */);
|
|
553
545
|
this.replayPendingStates();
|
|
554
546
|
});
|
|
555
|
-
if (context.pendingLocalState !== undefined) {
|
|
556
|
-
this.deltaManager.on("op", this.onOp);
|
|
557
|
-
}
|
|
558
547
|
// logging hardware telemetry
|
|
559
548
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
560
549
|
let loadSummaryNumber;
|
|
@@ -567,7 +556,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
567
556
|
};
|
|
568
557
|
// back-compat 0.59.3000 - Older document may either write summaryCount or not write it at all. If it does
|
|
569
558
|
// not write it, initialize summaryNumber to 0.
|
|
570
|
-
loadSummaryNumber = (
|
|
559
|
+
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;
|
|
571
560
|
}
|
|
572
561
|
else {
|
|
573
562
|
this.createContainerMetadata = {
|
|
@@ -603,13 +592,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
603
592
|
runtimeVersion: pkgVersion,
|
|
604
593
|
},
|
|
605
594
|
});
|
|
606
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, } = runtimeOptions;
|
|
607
|
-
const
|
|
595
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, enableOfflineLoad = false, } = runtimeOptions;
|
|
596
|
+
const pendingRuntimeState = context.pendingLocalState;
|
|
597
|
+
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
598
|
+
const storage = !pendingRuntimeState ?
|
|
599
|
+
context.storage :
|
|
600
|
+
new SerializedSnapshotStorage(() => { return context.storage; }, pendingRuntimeState.snapshotBlobs);
|
|
608
601
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
609
602
|
const tryFetchBlob = async (blobName) => {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
if (context.baseSnapshot && blobId) {
|
|
603
|
+
const blobId = baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.blobs[blobName];
|
|
604
|
+
if (baseSnapshot && blobId) {
|
|
613
605
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
614
606
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
615
607
|
assert(storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
@@ -624,7 +616,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
624
616
|
]);
|
|
625
617
|
const loadExisting = existing === true || context.existing === true;
|
|
626
618
|
// read snapshot blobs needed for BlobManager to load
|
|
627
|
-
const blobManagerSnapshot = await BlobManager.load(
|
|
619
|
+
const blobManagerSnapshot = await BlobManager.load(baseSnapshot === null || baseSnapshot === void 0 ? void 0 : baseSnapshot.trees[blobsTreeName], async (id) => {
|
|
628
620
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
629
621
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
630
622
|
assert(storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
@@ -654,7 +646,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
654
646
|
loadSequenceNumberVerification,
|
|
655
647
|
useDataStoreAliasing,
|
|
656
648
|
flushMode,
|
|
657
|
-
|
|
649
|
+
enableOfflineLoad,
|
|
650
|
+
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
651
|
+
if (pendingRuntimeState) {
|
|
652
|
+
await runtime.processSavedOps(pendingRuntimeState);
|
|
653
|
+
// delete these once runtime has seen them to save space
|
|
654
|
+
pendingRuntimeState.savedOps = [];
|
|
655
|
+
}
|
|
656
|
+
await runtime.getSnapshotBlobs();
|
|
658
657
|
return runtime;
|
|
659
658
|
}
|
|
660
659
|
get options() {
|
|
@@ -670,7 +669,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
670
669
|
return this.context.deltaManager;
|
|
671
670
|
}
|
|
672
671
|
get storage() {
|
|
673
|
-
return this.
|
|
672
|
+
return this._storage;
|
|
674
673
|
}
|
|
675
674
|
get reSubmitFn() {
|
|
676
675
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
@@ -702,19 +701,70 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
702
701
|
var _a;
|
|
703
702
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
704
703
|
}
|
|
705
|
-
get summaryConfiguration() {
|
|
706
|
-
var _a;
|
|
707
|
-
return Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = this.runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides);
|
|
708
|
-
}
|
|
709
704
|
get disposed() { return this._disposed; }
|
|
710
705
|
get summarizer() {
|
|
711
706
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
712
707
|
return this._summarizer;
|
|
713
708
|
}
|
|
714
|
-
|
|
709
|
+
isSummariesDisabled() {
|
|
710
|
+
// back-compat: disableSummaries was moved from ISummaryRuntimeOptions
|
|
711
|
+
// to ISummaryConfiguration in 0.60.
|
|
712
|
+
if (this.runtimeOptions.summaryOptions.disableSummaries === true) {
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
return this.summaryConfiguration.state === "disabled";
|
|
716
|
+
}
|
|
717
|
+
isHeuristicsDisabled() {
|
|
715
718
|
var _a;
|
|
716
|
-
|
|
717
|
-
|
|
719
|
+
// back-compat: disableHeuristics was moved from ISummarizerOptions
|
|
720
|
+
// to ISummaryConfiguration in 0.60.
|
|
721
|
+
if (((_a = this.runtimeOptions.summaryOptions.summarizerOptions) === null || _a === void 0 ? void 0 : _a.disableHeuristics) === true) {
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
return this.summaryConfiguration.state === "disableHeuristics";
|
|
725
|
+
}
|
|
726
|
+
isSummarizerClientElectionEnabled() {
|
|
727
|
+
var _a;
|
|
728
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) {
|
|
729
|
+
return (_a = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _a !== void 0 ? _a : true;
|
|
730
|
+
}
|
|
731
|
+
// back-compat: summarizerClientElection was moved from ISummaryRuntimeOptions
|
|
732
|
+
// to ISummaryConfiguration in 0.60.
|
|
733
|
+
if (this.runtimeOptions.summaryOptions.summarizerClientElection === true) {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
737
|
+
return this.summaryConfiguration.summarizerClientElection === true;
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
getMaxOpsSinceLastSummary() {
|
|
744
|
+
// back-compat: maxOpsSinceLastSummary was moved from ISummaryRuntimeOptions
|
|
745
|
+
// to ISummaryConfiguration in 0.60.
|
|
746
|
+
if (this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary !== undefined) {
|
|
747
|
+
return this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary;
|
|
748
|
+
}
|
|
749
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
750
|
+
return this.summaryConfiguration.maxOpsSinceLastSummary;
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
return 0;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
getInitialSummarizerDelayMs() {
|
|
757
|
+
// back-compat: initialSummarizerDelayMs was moved from ISummaryRuntimeOptions
|
|
758
|
+
// to ISummaryConfiguration in 0.60.
|
|
759
|
+
if (this.runtimeOptions.summaryOptions.initialSummarizerDelayMs !== undefined) {
|
|
760
|
+
return this.runtimeOptions.summaryOptions.initialSummarizerDelayMs;
|
|
761
|
+
}
|
|
762
|
+
if (this.summaryConfiguration.state !== "disabled") {
|
|
763
|
+
return this.summaryConfiguration.initialSummarizerDelayMs;
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
return 0;
|
|
767
|
+
}
|
|
718
768
|
}
|
|
719
769
|
dispose(error) {
|
|
720
770
|
var _a;
|
|
@@ -855,7 +905,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
855
905
|
message: (_a = extractSummaryMetadataMessage(this.deltaManager.lastMessage)) !== null && _a !== void 0 ? _a : this.messageAtLastSummary });
|
|
856
906
|
addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(metadata));
|
|
857
907
|
}
|
|
858
|
-
addContainerStateToSummary(summaryTree, fullTree, trackState) {
|
|
908
|
+
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
859
909
|
var _a;
|
|
860
910
|
this.addMetadataToSummary(summaryTree);
|
|
861
911
|
if (this.chunkMap.size > 0) {
|
|
@@ -877,7 +927,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
877
927
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
878
928
|
}
|
|
879
929
|
if (this.garbageCollector.writeDataAtRoot) {
|
|
880
|
-
const gcSummary = this.garbageCollector.summarize(fullTree, trackState);
|
|
930
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
881
931
|
if (gcSummary !== undefined) {
|
|
882
932
|
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
883
933
|
}
|
|
@@ -898,7 +948,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
898
948
|
this.resetReconnectCount();
|
|
899
949
|
return true;
|
|
900
950
|
}
|
|
901
|
-
this.consecutiveReconnects++;
|
|
902
951
|
if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
|
|
903
952
|
// If we're halfway through the max reconnects, send an event in order
|
|
904
953
|
// to better identify false positives, if any. If the rate of this event
|
|
@@ -907,6 +956,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
907
956
|
this.mc.logger.sendTelemetryEvent({
|
|
908
957
|
eventName: "ReconnectsWithNoProgress",
|
|
909
958
|
attempts: this.consecutiveReconnects,
|
|
959
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
910
960
|
});
|
|
911
961
|
}
|
|
912
962
|
return this.consecutiveReconnects < this.maxConsecutiveReconnects;
|
|
@@ -962,29 +1012,37 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
962
1012
|
this.verifyNotClosed();
|
|
963
1013
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
964
1014
|
const changeOfState = this._connected !== connected;
|
|
1015
|
+
const reconnection = changeOfState && connected;
|
|
965
1016
|
this._connected = connected;
|
|
966
|
-
if (
|
|
967
|
-
this.
|
|
968
|
-
this.context.pendingLocalState = undefined;
|
|
1017
|
+
if (reconnection) {
|
|
1018
|
+
this.consecutiveReconnects++;
|
|
969
1019
|
if (!this.shouldContinueReconnecting()) {
|
|
970
1020
|
this.closeFn(new GenericError(
|
|
971
1021
|
// pre-0.58 error message: MaxReconnectsWithNoProgress
|
|
972
1022
|
"Runtime detected too many reconnects with no progress syncing local ops", undefined, // error
|
|
973
|
-
{
|
|
1023
|
+
{
|
|
1024
|
+
attempts: this.consecutiveReconnects,
|
|
1025
|
+
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
1026
|
+
}));
|
|
974
1027
|
return;
|
|
975
1028
|
}
|
|
1029
|
+
}
|
|
1030
|
+
if (changeOfState) {
|
|
976
1031
|
this.replayPendingStates();
|
|
977
1032
|
}
|
|
978
1033
|
this.dataStores.setConnectionState(connected, clientId);
|
|
979
1034
|
raiseConnectedEvent(this.mc.logger, this, connected, clientId);
|
|
980
1035
|
}
|
|
981
1036
|
process(messageArg, local) {
|
|
982
|
-
var _a;
|
|
1037
|
+
var _a, _b;
|
|
983
1038
|
this.verifyNotClosed();
|
|
984
1039
|
// If it's not message for runtime, bail out right away.
|
|
985
1040
|
if (!isRuntimeMessage(messageArg)) {
|
|
986
1041
|
return;
|
|
987
1042
|
}
|
|
1043
|
+
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
1044
|
+
this.savedOps.push(messageArg);
|
|
1045
|
+
}
|
|
988
1046
|
// Do shallow copy of message, as methods below will modify it.
|
|
989
1047
|
// There might be multiple container instances receiving same message
|
|
990
1048
|
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
@@ -999,8 +1057,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
999
1057
|
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
1000
1058
|
// once all pieces are available
|
|
1001
1059
|
message = this.processRemoteChunkedMessage(message);
|
|
1002
|
-
|
|
1003
|
-
|
|
1060
|
+
let localOpMetadata;
|
|
1061
|
+
if (local) {
|
|
1062
|
+
// Call the PendingStateManager to process local messages.
|
|
1063
|
+
// Do not process local chunked ops until all pieces are available.
|
|
1064
|
+
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1065
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1004
1068
|
// If there are no more pending messages after processing a local message,
|
|
1005
1069
|
// the document is no longer dirty.
|
|
1006
1070
|
if (!this.pendingStateManager.hasPendingMessages()) {
|
|
@@ -1008,17 +1072,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1008
1072
|
}
|
|
1009
1073
|
switch (message.type) {
|
|
1010
1074
|
case ContainerMessageType.Attach:
|
|
1011
|
-
this.dataStores.processAttachMessage(message, local
|
|
1075
|
+
this.dataStores.processAttachMessage(message, local);
|
|
1012
1076
|
break;
|
|
1013
1077
|
case ContainerMessageType.Alias:
|
|
1014
1078
|
this.processAliasMessage(message, localOpMetadata, local);
|
|
1015
1079
|
break;
|
|
1016
1080
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1017
|
-
|
|
1018
|
-
this.dataStores.processFluidDataStoreOp(message, local || localAck, localOpMetadata);
|
|
1081
|
+
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1019
1082
|
break;
|
|
1020
1083
|
case ContainerMessageType.BlobAttach:
|
|
1021
|
-
assert((
|
|
1084
|
+
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" */);
|
|
1022
1085
|
this.blobManager.processBlobAttachOp(message.metadata.blobId, local);
|
|
1023
1086
|
break;
|
|
1024
1087
|
default:
|
|
@@ -1110,18 +1173,32 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1110
1173
|
}
|
|
1111
1174
|
const savedFlushMode = this.flushMode;
|
|
1112
1175
|
this.setFlushMode(FlushMode.TurnBased);
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1176
|
+
try {
|
|
1177
|
+
this.trackOrderSequentiallyCalls(callback);
|
|
1178
|
+
this.flush();
|
|
1179
|
+
}
|
|
1180
|
+
finally {
|
|
1181
|
+
this.setFlushMode(savedFlushMode);
|
|
1182
|
+
}
|
|
1116
1183
|
}
|
|
1117
1184
|
trackOrderSequentiallyCalls(callback) {
|
|
1185
|
+
let checkpoint;
|
|
1186
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1187
|
+
checkpoint = this.pendingStateManager.checkpoint();
|
|
1188
|
+
}
|
|
1118
1189
|
try {
|
|
1119
1190
|
this._orderSequentiallyCalls++;
|
|
1120
1191
|
callback();
|
|
1121
1192
|
}
|
|
1122
1193
|
catch (error) {
|
|
1123
|
-
|
|
1124
|
-
|
|
1194
|
+
if (checkpoint) {
|
|
1195
|
+
// This will throw and close the container if rollback fails
|
|
1196
|
+
checkpoint.rollback();
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
// pre-0.58 error message: orderSequentiallyCallbackException
|
|
1200
|
+
this.closeFn(new GenericError("orderSequentially callback exception", error));
|
|
1201
|
+
}
|
|
1125
1202
|
throw error; // throw the original error for the consumer of the runtime
|
|
1126
1203
|
}
|
|
1127
1204
|
finally {
|
|
@@ -1293,17 +1370,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1293
1370
|
* @param blobRedirectTable - A table passed during the attach process. While detached, blob upload is supported
|
|
1294
1371
|
* using IDs generated locally. After attach, these IDs cannot be used, so this table maps the old local IDs to the
|
|
1295
1372
|
* new storage IDs so requests can be redirected.
|
|
1373
|
+
* @param telemetryContext - summary data passed through the layers for telemetry purposes
|
|
1296
1374
|
*/
|
|
1297
|
-
createSummary(blobRedirectTable) {
|
|
1375
|
+
createSummary(blobRedirectTable, telemetryContext) {
|
|
1298
1376
|
if (blobRedirectTable) {
|
|
1299
1377
|
this.blobManager.setRedirectTable(blobRedirectTable);
|
|
1300
1378
|
}
|
|
1301
|
-
const summarizeResult = this.dataStores.createSummary();
|
|
1379
|
+
const summarizeResult = this.dataStores.createSummary(telemetryContext);
|
|
1302
1380
|
if (!this.disableIsolatedChannels) {
|
|
1303
1381
|
// Wrap data store summaries in .channels subtree.
|
|
1304
1382
|
wrapSummaryInChannelsTree(summarizeResult);
|
|
1305
1383
|
}
|
|
1306
|
-
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState
|
|
1384
|
+
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
|
|
1307
1385
|
return summarizeResult.summary;
|
|
1308
1386
|
}
|
|
1309
1387
|
async getAbsoluteUrl(relativeUrl) {
|
|
@@ -1315,15 +1393,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1315
1393
|
}
|
|
1316
1394
|
return this.context.getAbsoluteUrl(relativeUrl);
|
|
1317
1395
|
}
|
|
1318
|
-
async summarizeInternal(fullTree, trackState) {
|
|
1319
|
-
const summarizeResult = await this.dataStores.summarize(fullTree, trackState);
|
|
1396
|
+
async summarizeInternal(fullTree, trackState, telemetryContext) {
|
|
1397
|
+
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
1320
1398
|
let pathPartsForChildren;
|
|
1321
1399
|
if (!this.disableIsolatedChannels) {
|
|
1322
1400
|
// Wrap data store summaries in .channels subtree.
|
|
1323
1401
|
wrapSummaryInChannelsTree(summarizeResult);
|
|
1324
1402
|
pathPartsForChildren = [channelsTreeName];
|
|
1325
1403
|
}
|
|
1326
|
-
this.addContainerStateToSummary(summarizeResult, fullTree, trackState);
|
|
1404
|
+
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
1327
1405
|
return Object.assign(Object.assign({}, summarizeResult), { id: "", pathPartsForChildren });
|
|
1328
1406
|
}
|
|
1329
1407
|
/**
|
|
@@ -1336,7 +1414,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1336
1414
|
if (runGC) {
|
|
1337
1415
|
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1338
1416
|
}
|
|
1339
|
-
const
|
|
1417
|
+
const telemetryContext = new TelemetryContext();
|
|
1418
|
+
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1419
|
+
this.logger.sendTelemetryEvent({ eventName: "SummarizeTelemetry", details: telemetryContext.serialize() });
|
|
1340
1420
|
assert(summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1341
1421
|
return { stats, summary, gcStats };
|
|
1342
1422
|
}
|
|
@@ -1700,9 +1780,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1700
1780
|
}
|
|
1701
1781
|
submit(type, content, localOpMetadata = undefined, opMetadata = undefined) {
|
|
1702
1782
|
this.verifyNotClosed();
|
|
1703
|
-
if (this.context.pendingLocalState !== undefined) {
|
|
1704
|
-
this.closeFn(new GenericError("containerRuntimeSubmitWithPendingLocalState"));
|
|
1705
|
-
}
|
|
1706
1783
|
// There should be no ops in detached container state!
|
|
1707
1784
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1708
1785
|
let clientSequenceNumber = -1;
|
|
@@ -1833,6 +1910,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1833
1910
|
unreachableCase(type, `Unknown ContainerMessageType: ${type}`);
|
|
1834
1911
|
}
|
|
1835
1912
|
}
|
|
1913
|
+
rollback(type, content, localOpMetadata) {
|
|
1914
|
+
switch (type) {
|
|
1915
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
1916
|
+
// For operations, call rollbackDataStoreOp which will find the right store
|
|
1917
|
+
// and trigger rollback on it.
|
|
1918
|
+
this.dataStores.rollbackDataStoreOp(content, localOpMetadata);
|
|
1919
|
+
break;
|
|
1920
|
+
default:
|
|
1921
|
+
throw new Error(`Can't rollback ${type}`);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1836
1924
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1837
1925
|
async refreshLatestSummaryAck(proposalHandle, ackHandle, summaryRefSeq, summaryLogger) {
|
|
1838
1926
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
@@ -1877,8 +1965,43 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1877
1965
|
return maybeSnapshot;
|
|
1878
1966
|
});
|
|
1879
1967
|
}
|
|
1968
|
+
notifyAttaching(snapshot) {
|
|
1969
|
+
var _a;
|
|
1970
|
+
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
1971
|
+
this.baseSnapshotBlobs = SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
async getSnapshotBlobs() {
|
|
1975
|
+
var _a;
|
|
1976
|
+
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1977
|
+
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
assert(!!this.context.baseSnapshot, 0x2e5 /* "Must have a base snapshot" */);
|
|
1981
|
+
this.baseSnapshotBlobs = await SerializedSnapshotStorage.serializeTree(this.context.baseSnapshot, this.storage);
|
|
1982
|
+
}
|
|
1880
1983
|
getPendingLocalState() {
|
|
1881
|
-
|
|
1984
|
+
var _a;
|
|
1985
|
+
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad)) {
|
|
1986
|
+
throw new UsageError("can't get state when offline load disabled");
|
|
1987
|
+
}
|
|
1988
|
+
const previousPendingState = this.context.pendingLocalState;
|
|
1989
|
+
if (previousPendingState) {
|
|
1990
|
+
return {
|
|
1991
|
+
pending: this.pendingStateManager.getLocalState(),
|
|
1992
|
+
snapshotBlobs: previousPendingState.snapshotBlobs,
|
|
1993
|
+
baseSnapshot: previousPendingState.baseSnapshot,
|
|
1994
|
+
savedOps: this.savedOps,
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
assert(!!this.context.baseSnapshot, 0x2e6 /* "Must have a base snapshot" */);
|
|
1998
|
+
assert(!!this.baseSnapshotBlobs, 0x2e7 /* "Must serialize base snapshot blobs before getting runtime state" */);
|
|
1999
|
+
return {
|
|
2000
|
+
pending: this.pendingStateManager.getLocalState(),
|
|
2001
|
+
snapshotBlobs: this.baseSnapshotBlobs,
|
|
2002
|
+
baseSnapshot: this.context.baseSnapshot,
|
|
2003
|
+
savedOps: this.savedOps,
|
|
2004
|
+
};
|
|
1882
2005
|
}
|
|
1883
2006
|
/**
|
|
1884
2007
|
* * Forms a function that will request a Summarizer.
|
|
@@ -1906,6 +2029,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1906
2029
|
return summarizer;
|
|
1907
2030
|
};
|
|
1908
2031
|
}
|
|
2032
|
+
async processSavedOps(state) {
|
|
2033
|
+
for (const op of state.savedOps) {
|
|
2034
|
+
this.process(op, false);
|
|
2035
|
+
await this.pendingStateManager.applyStashedOpsAt(op.sequenceNumber);
|
|
2036
|
+
}
|
|
2037
|
+
// we may not have seen every sequence number (because of system ops) so apply everything once we
|
|
2038
|
+
// don't have any more saved ops
|
|
2039
|
+
await this.pendingStateManager.applyStashedOpsAt();
|
|
2040
|
+
}
|
|
1909
2041
|
}
|
|
1910
2042
|
/**
|
|
1911
2043
|
* Wait for a specific sequence number. Promise should resolve when we reach that number,
|