@fluidframework/container-runtime 2.0.0-internal.5.3.2 → 2.0.0-internal.6.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/CHANGELOG.md +80 -0
- package/dist/batchTracker.d.ts +2 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +13 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +103 -25
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +12 -4
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +69 -22
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +344 -238
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +11 -2
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +40 -44
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +21 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +102 -58
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +2 -0
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +5 -5
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +29 -25
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.js +13 -11
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +1 -0
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +5 -6
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +4 -6
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +44 -33
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/id-compressor/idCompressor.d.ts +3 -3
- package/dist/id-compressor/idCompressor.d.ts.map +1 -1
- package/dist/id-compressor/idCompressor.js +52 -52
- package/dist/id-compressor/idCompressor.js.map +1 -1
- package/dist/id-compressor/idRange.js +2 -2
- package/dist/id-compressor/idRange.js.map +1 -1
- package/dist/id-compressor/sessionIdNormalizer.js +11 -16
- package/dist/id-compressor/sessionIdNormalizer.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.js +10 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +2 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +7 -2
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +2 -2
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +12 -10
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +13 -5
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +2 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +11 -7
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +6 -5
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +6 -14
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +6 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +8 -2
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.js +1 -2
- package/dist/opProperties.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +6 -3
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +41 -32
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +15 -11
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +2 -1
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +18 -19
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +3 -5
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +42 -66
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.js +5 -8
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.js +5 -9
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +8 -12
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +5 -5
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +26 -22
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -4
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -3
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +13 -16
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summaryCollection.js +3 -5
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.js +1 -2
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +67 -21
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +2 -3
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +9 -7
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +2 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +2 -2
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +13 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +103 -25
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +13 -5
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +69 -22
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +343 -238
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +11 -2
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +42 -46
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +2 -2
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +21 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +103 -59
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +2 -0
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +6 -6
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +30 -26
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.js +13 -11
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +1 -0
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +5 -6
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +4 -6
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +45 -34
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/id-compressor/idCompressor.d.ts +3 -3
- package/lib/id-compressor/idCompressor.d.ts.map +1 -1
- package/lib/id-compressor/idCompressor.js +52 -52
- package/lib/id-compressor/idCompressor.js.map +1 -1
- package/lib/id-compressor/idRange.js +2 -2
- package/lib/id-compressor/idRange.js.map +1 -1
- package/lib/id-compressor/sessionIdNormalizer.js +11 -16
- package/lib/id-compressor/sessionIdNormalizer.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.js +10 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +2 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +8 -3
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +2 -2
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +13 -11
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +13 -5
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +2 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +12 -8
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +6 -5
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -15
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +6 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +8 -2
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.js +1 -2
- package/lib/opProperties.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +6 -3
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +41 -32
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +16 -12
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +2 -1
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +19 -20
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +3 -5
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +43 -67
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.js +6 -9
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.js +5 -9
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +8 -12
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +5 -5
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +27 -23
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -4
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -3
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +14 -17
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summaryCollection.js +3 -5
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.js +1 -2
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +68 -22
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +2 -3
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +10 -8
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +30 -18
- package/src/batchTracker.ts +4 -3
- package/src/blobManager.ts +113 -15
- package/src/connectionTelemetry.ts +7 -3
- package/src/containerRuntime.ts +354 -194
- package/src/dataStore.ts +10 -1
- package/src/dataStoreContext.ts +31 -33
- package/src/dataStoreContexts.ts +2 -2
- package/src/dataStores.ts +108 -71
- package/src/deltaManagerSummarizerProxy.ts +2 -0
- package/src/deltaScheduler.ts +6 -10
- package/src/gc/garbageCollection.ts +13 -8
- package/src/gc/gcHelpers.ts +1 -0
- package/src/gc/gcTelemetry.ts +13 -10
- package/src/id-compressor/idCompressor.ts +6 -5
- package/src/index.ts +0 -1
- package/src/opLifecycle/opCompressor.ts +4 -3
- package/src/opLifecycle/opDecompressor.ts +4 -3
- package/src/opLifecycle/opSplitter.ts +4 -3
- package/src/opLifecycle/outbox.ts +13 -25
- package/src/opLifecycle/remoteMessageProcessor.ts +8 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +34 -25
- package/src/scheduleManager.ts +2 -2
- package/src/summary/orderedClientElection.ts +4 -3
- package/src/summary/runningSummarizer.ts +18 -44
- package/src/summary/summarizer.ts +2 -2
- package/src/summary/summarizerNode/summarizerNode.ts +13 -15
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +8 -7
- package/src/summary/summaryGenerator.ts +6 -2
- package/src/summary/summaryManager.ts +9 -5
package/lib/containerRuntime.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
-
var t = {};
|
|
3
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
-
t[p] = s[p];
|
|
5
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
-
t[p[i]] = s[p[i]];
|
|
9
|
-
}
|
|
10
|
-
return t;
|
|
11
|
-
};
|
|
12
1
|
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
13
2
|
import { assert, delay, Trace, TypedEventEmitter, unreachableCase, } from "@fluidframework/common-utils";
|
|
14
3
|
import { LazyPromise } from "@fluidframework/core-utils";
|
|
15
|
-
import {
|
|
4
|
+
import { createChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, wrapError, createChildMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
16
5
|
import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
|
|
17
6
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
18
7
|
import { DataCorruptionError, DataProcessingError, GenericError, UsageError, } from "@fluidframework/container-utils";
|
|
@@ -115,7 +104,7 @@ const defaultChunkSizeInBytes = 204800;
|
|
|
115
104
|
* of the current system, we should close the summarizer and let it recover.
|
|
116
105
|
* This delay's goal is to prevent tight restart loops
|
|
117
106
|
*/
|
|
118
|
-
const defaultCloseSummarizerDelayMs =
|
|
107
|
+
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
119
108
|
/**
|
|
120
109
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
121
110
|
*/
|
|
@@ -151,9 +140,23 @@ export function getDeviceSpec() {
|
|
|
151
140
|
};
|
|
152
141
|
}
|
|
153
142
|
}
|
|
154
|
-
catch
|
|
143
|
+
catch { }
|
|
155
144
|
return {};
|
|
156
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
|
|
148
|
+
* Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
|
|
149
|
+
* we can provide a partially-applied function to keep those items private to the ContainerRuntime.
|
|
150
|
+
*/
|
|
151
|
+
export const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
|
|
152
|
+
for (const message of batch.content) {
|
|
153
|
+
submitFn(MessageType.Operation,
|
|
154
|
+
// For back-compat (submitFn only works on deserialized content)
|
|
155
|
+
message.contents === undefined ? undefined : JSON.parse(message.contents), true, // batch
|
|
156
|
+
message.metadata);
|
|
157
|
+
}
|
|
158
|
+
deltaManager.flush();
|
|
159
|
+
};
|
|
157
160
|
/**
|
|
158
161
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
159
162
|
* It will define the store level mappings.
|
|
@@ -162,11 +165,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
162
165
|
/**
|
|
163
166
|
* @internal
|
|
164
167
|
*/
|
|
165
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
169
|
+
// the defaults
|
|
170
|
+
...DefaultSummaryConfiguration,
|
|
171
|
+
// the runtime configuration overrides
|
|
172
|
+
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
173
|
+
}, initializeEntryPoint) {
|
|
168
174
|
super();
|
|
169
|
-
this.context = context;
|
|
170
175
|
this.registry = registry;
|
|
171
176
|
this.runtimeOptions = runtimeOptions;
|
|
172
177
|
this.containerScope = containerScope;
|
|
@@ -196,7 +201,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
196
201
|
trackingSignalSequenceNumber: undefined,
|
|
197
202
|
};
|
|
198
203
|
this.summarizeOnDemand = (...args) => {
|
|
199
|
-
if (this.
|
|
204
|
+
if (this.isSummarizerClient) {
|
|
200
205
|
return this.summarizer.summarizeOnDemand(...args);
|
|
201
206
|
}
|
|
202
207
|
else if (this.summaryManager !== undefined) {
|
|
@@ -210,7 +215,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
210
215
|
}
|
|
211
216
|
};
|
|
212
217
|
this.enqueueSummarize = (...args) => {
|
|
213
|
-
if (this.
|
|
218
|
+
if (this.isSummarizerClient) {
|
|
214
219
|
return this.summarizer.enqueueSummarize(...args);
|
|
215
220
|
}
|
|
216
221
|
else if (this.summaryManager !== undefined) {
|
|
@@ -223,23 +228,56 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
223
228
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
224
229
|
}
|
|
225
230
|
};
|
|
226
|
-
|
|
227
|
-
this.
|
|
228
|
-
this.
|
|
231
|
+
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, } = context;
|
|
232
|
+
this.innerDeltaManager = deltaManager;
|
|
233
|
+
this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
234
|
+
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
235
|
+
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
236
|
+
this.submitFn = submitFn;
|
|
237
|
+
this.submitBatchFn = submitBatchFn;
|
|
238
|
+
this.submitSummaryFn = submitSummaryFn;
|
|
239
|
+
this.submitSignalFn = submitSignalFn;
|
|
240
|
+
this.options = options;
|
|
241
|
+
this.clientDetails = clientDetails;
|
|
242
|
+
this.isSummarizerClient = this.clientDetails.type === summarizerClientType;
|
|
243
|
+
this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
|
|
244
|
+
this._getClientId = () => context.clientId;
|
|
245
|
+
this._getAttachState = () => context.attachState;
|
|
246
|
+
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
247
|
+
if (context.getAbsoluteUrl === undefined) {
|
|
248
|
+
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
249
|
+
}
|
|
250
|
+
if (this.attachState !== AttachState.Attached) {
|
|
251
|
+
return undefined;
|
|
252
|
+
}
|
|
253
|
+
return context.getAbsoluteUrl(relativeUrl);
|
|
254
|
+
};
|
|
255
|
+
// TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
|
|
256
|
+
// customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
|
|
257
|
+
this.on("dirty", () => context.updateDirtyContainerState(true));
|
|
258
|
+
this.on("saved", () => context.updateDirtyContainerState(false));
|
|
259
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
260
|
+
this.disposeFn = disposeFn ?? closeFn;
|
|
261
|
+
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
262
|
+
this.closeFn = this.isSummarizerClient ? this.disposeFn : closeFn;
|
|
263
|
+
this.mc = createChildMonitoringContext({
|
|
264
|
+
logger: this.logger,
|
|
265
|
+
namespace: "ContainerRuntime",
|
|
266
|
+
});
|
|
229
267
|
let loadSummaryNumber;
|
|
230
268
|
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
231
269
|
// get the values from the metadata blob.
|
|
232
270
|
if (existing) {
|
|
233
271
|
this.createContainerMetadata = {
|
|
234
|
-
createContainerRuntimeVersion: metadata
|
|
235
|
-
createContainerTimestamp: metadata
|
|
272
|
+
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
273
|
+
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
236
274
|
};
|
|
237
275
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
238
276
|
// the count is reset to 0.
|
|
239
|
-
loadSummaryNumber =
|
|
277
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
240
278
|
// Enabling the IdCompressor is a one-way operation and we only want to
|
|
241
279
|
// allow new containers to turn it on
|
|
242
|
-
this.idCompressorEnabled =
|
|
280
|
+
this.idCompressorEnabled = metadata?.idCompressorEnabled ?? false;
|
|
243
281
|
}
|
|
244
282
|
else {
|
|
245
283
|
this.createContainerMetadata = {
|
|
@@ -248,24 +286,27 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
248
286
|
};
|
|
249
287
|
loadSummaryNumber = 0;
|
|
250
288
|
this.idCompressorEnabled =
|
|
251
|
-
|
|
289
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.IdCompressorEnabled") ??
|
|
290
|
+
idCompressor !== undefined;
|
|
252
291
|
}
|
|
253
292
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
254
|
-
this.messageAtLastSummary = metadata
|
|
255
|
-
|
|
256
|
-
|
|
293
|
+
this.messageAtLastSummary = metadata?.message;
|
|
294
|
+
// Note that we only need to pull the *initial* connected state from the context.
|
|
295
|
+
// Later updates come through calls to setConnectionState.
|
|
296
|
+
this._connected = connected;
|
|
297
|
+
this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */, this.runtimeOptions.gcOptions[gcTombstoneGenerationOptionName] /* current */);
|
|
257
298
|
this.mc.logger.sendTelemetryEvent({
|
|
258
299
|
eventName: "GCFeatureMatrix",
|
|
259
|
-
metadataValue: JSON.stringify(metadata
|
|
300
|
+
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
260
301
|
inputs: JSON.stringify({
|
|
261
302
|
gcOptions_gcTombstoneGeneration: this.runtimeOptions.gcOptions[gcTombstoneGenerationOptionName],
|
|
262
303
|
}),
|
|
263
304
|
});
|
|
264
|
-
this.telemetryDocumentId =
|
|
305
|
+
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? uuid();
|
|
265
306
|
this.disableAttachReorder = this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder");
|
|
266
307
|
const disableChunking = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionChunkingDisabled");
|
|
267
308
|
const opGroupingManager = new OpGroupingManager(this.groupedBatchingEnabled);
|
|
268
|
-
const opSplitter = new OpSplitter(chunks, this.
|
|
309
|
+
const opSplitter = new OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
269
310
|
this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor(this.mc.logger), opGroupingManager);
|
|
270
311
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
271
312
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -284,9 +325,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
284
325
|
this.idCompressor = idCompressor;
|
|
285
326
|
}
|
|
286
327
|
this.maxConsecutiveReconnects =
|
|
287
|
-
|
|
328
|
+
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ??
|
|
329
|
+
this.defaultMaxConsecutiveReconnects;
|
|
288
330
|
if (runtimeOptions.flushMode === FlushModeExperimental.Async &&
|
|
289
|
-
|
|
331
|
+
supportedFeatures?.get("referenceSequenceNumbers") !== true) {
|
|
290
332
|
// The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
|
|
291
333
|
this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
|
|
292
334
|
this._flushMode = FlushMode.TurnBased;
|
|
@@ -294,8 +336,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
294
336
|
else {
|
|
295
337
|
this._flushMode = runtimeOptions.flushMode;
|
|
296
338
|
}
|
|
297
|
-
const pendingRuntimeState =
|
|
298
|
-
const maxSnapshotCacheDurationMs =
|
|
339
|
+
const pendingRuntimeState = pendingLocalState;
|
|
340
|
+
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
299
341
|
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
300
342
|
maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
301
343
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
@@ -306,27 +348,27 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
306
348
|
this.garbageCollector = GarbageCollector.create({
|
|
307
349
|
runtime: this,
|
|
308
350
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
309
|
-
baseSnapshot
|
|
351
|
+
baseSnapshot,
|
|
310
352
|
baseLogger: this.mc.logger,
|
|
311
353
|
existing,
|
|
312
354
|
metadata,
|
|
313
355
|
createContainerMetadata: this.createContainerMetadata,
|
|
314
|
-
isSummarizerClient: this.
|
|
356
|
+
isSummarizerClient: this.isSummarizerClient,
|
|
315
357
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
316
|
-
getLastSummaryTimestampMs: () =>
|
|
358
|
+
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
317
359
|
readAndParseBlob: async (id) => readAndParse(this.storage, id),
|
|
318
360
|
// GC runs in summarizer client and needs access to the real (non-proxy) active information. The proxy
|
|
319
361
|
// delta manager would always return false for summarizer client.
|
|
320
362
|
activeConnection: () => this.innerDeltaManager.active,
|
|
321
363
|
});
|
|
322
364
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
323
|
-
this.summarizerNode = createRootSummarizerNodeWithGC(
|
|
365
|
+
this.summarizerNode = createRootSummarizerNodeWithGC(createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
|
|
324
366
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
325
367
|
async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
326
368
|
// Latest change sequence number, no changes since summary applied yet
|
|
327
369
|
loadedFromSequenceNumber,
|
|
328
370
|
// Summary reference sequence number, undefined if no summary yet
|
|
329
|
-
|
|
371
|
+
baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined, {
|
|
330
372
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
331
373
|
// a summary with an older protocol state.
|
|
332
374
|
canReuseHandle: false,
|
|
@@ -340,19 +382,19 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
340
382
|
async (fullGC) => this.getGCDataInternal(fullGC),
|
|
341
383
|
// Function to get the GC details from the base snapshot we loaded from.
|
|
342
384
|
async () => this.garbageCollector.getBaseGCDetails());
|
|
343
|
-
if (
|
|
344
|
-
this.summarizerNode.updateBaseSummaryState(
|
|
385
|
+
if (baseSnapshot) {
|
|
386
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
345
387
|
}
|
|
346
|
-
this.dataStores = new DataStores(getSummaryForDatastores(
|
|
388
|
+
this.dataStores = new DataStores(getSummaryForDatastores(baseSnapshot, metadata), this, (attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }), (id, createParam) => (summarizeInternal, getGCDataFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap));
|
|
347
389
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
348
390
|
if (!this.disposed) {
|
|
349
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
391
|
+
this.submit({ type: ContainerMessageType.BlobAttach, contents: undefined }, undefined, {
|
|
350
392
|
localId,
|
|
351
393
|
blobId,
|
|
352
394
|
});
|
|
353
395
|
}
|
|
354
|
-
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState
|
|
355
|
-
this.scheduleManager = new ScheduleManager(
|
|
396
|
+
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState?.pendingAttachmentBlobs, (error) => this.closeFn(error));
|
|
397
|
+
this.scheduleManager = new ScheduleManager(this.innerDeltaManager, this, () => this.clientId, createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }));
|
|
356
398
|
this.pendingStateManager = new PendingStateManager({
|
|
357
399
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
358
400
|
clientId: () => this.clientId,
|
|
@@ -360,7 +402,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
360
402
|
connected: () => this.connected,
|
|
361
403
|
reSubmit: this.reSubmit.bind(this),
|
|
362
404
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
363
|
-
|
|
405
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
406
|
+
}, pendingRuntimeState?.pending, this.logger);
|
|
364
407
|
const disableCompression = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
|
|
365
408
|
const compressionOptions = disableCompression === true
|
|
366
409
|
? {
|
|
@@ -369,10 +412,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
369
412
|
}
|
|
370
413
|
: runtimeOptions.compressionOptions;
|
|
371
414
|
const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
|
|
415
|
+
const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
|
|
372
416
|
this.outbox = new Outbox({
|
|
373
417
|
shouldSend: () => this.canSendOps(),
|
|
374
418
|
pendingStateManager: this.pendingStateManager,
|
|
375
|
-
|
|
419
|
+
submitBatchFn: this.submitBatchFn,
|
|
420
|
+
legacySendBatchFn,
|
|
376
421
|
compressor: new OpCompressor(this.mc.logger),
|
|
377
422
|
splitter: opSplitter,
|
|
378
423
|
config: {
|
|
@@ -391,44 +436,53 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
391
436
|
opReentrancy: () => this.ensureNoDataModelChangesCalls > 0,
|
|
392
437
|
closeContainer: this.closeFn,
|
|
393
438
|
});
|
|
394
|
-
this.
|
|
439
|
+
this._quorum = quorum;
|
|
440
|
+
this._quorum.on("removeMember", (clientId) => {
|
|
395
441
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
396
442
|
});
|
|
443
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
444
|
+
this._audience = audience;
|
|
397
445
|
this.summaryStateUpdateMethod = this.mc.config.getString("Fluid.ContainerRuntime.Test.SummaryStateUpdateMethodV2");
|
|
398
446
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
399
|
-
this.closeSummarizerDelayMs = closeSummarizerDelayOverride
|
|
447
|
+
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
400
448
|
this.validateSummaryBeforeUpload =
|
|
401
|
-
|
|
449
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.Test.ValidateSummaryBeforeUpload") ??
|
|
450
|
+
false;
|
|
402
451
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
403
452
|
this.dirtyContainer =
|
|
404
|
-
this.
|
|
453
|
+
this.attachState !== AttachState.Attached ||
|
|
405
454
|
this.pendingStateManager.hasPendingMessages();
|
|
406
|
-
|
|
455
|
+
context.updateDirtyContainerState(this.dirtyContainer);
|
|
407
456
|
if (this.summariesDisabled) {
|
|
408
457
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
409
458
|
}
|
|
410
459
|
else {
|
|
411
|
-
const orderedClientLogger =
|
|
412
|
-
|
|
413
|
-
|
|
460
|
+
const orderedClientLogger = createChildLogger({
|
|
461
|
+
logger: this.logger,
|
|
462
|
+
namespace: "OrderedClientElection",
|
|
463
|
+
});
|
|
464
|
+
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.innerDeltaManager, this._quorum);
|
|
465
|
+
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
414
466
|
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
|
|
415
|
-
if (this.
|
|
467
|
+
if (this.isSummarizerClient) {
|
|
416
468
|
this._summarizer = new Summarizer(this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime,
|
|
417
469
|
// Summarization runs in summarizer client and needs access to the real (non-proxy) active
|
|
418
470
|
// information. The proxy delta manager would always return false for summarizer client.
|
|
419
471
|
() => this.innerDeltaManager.active));
|
|
420
472
|
}
|
|
421
|
-
else if (SummarizerClientElection.clientDetailsPermitElection(this.
|
|
473
|
+
else if (SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
|
|
422
474
|
// Only create a SummaryManager and SummarizerClientElection
|
|
423
475
|
// if summaries are enabled and we are not the summarizer client.
|
|
424
476
|
const defaultAction = () => {
|
|
425
477
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
426
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
478
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
427
479
|
// unregister default to no log on every op after falling behind
|
|
428
480
|
// and register summary ack handler to re-register this handler
|
|
429
481
|
// after successful summary
|
|
430
482
|
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
431
|
-
this.logger.sendTelemetryEvent({
|
|
483
|
+
this.mc.logger.sendTelemetryEvent({
|
|
484
|
+
eventName: "SummaryStatus:CaughtUp",
|
|
485
|
+
});
|
|
432
486
|
// we've caught up, so re-register the default action to monitor for
|
|
433
487
|
// falling behind, and unregister ourself
|
|
434
488
|
this.summaryCollection.on("default", defaultAction);
|
|
@@ -439,7 +493,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
439
493
|
this.summaryCollection.on("default", defaultAction);
|
|
440
494
|
// Create the SummaryManager and mark the initial state
|
|
441
495
|
this.summaryManager = new SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
442
|
-
this.summaryCollection, this.logger, this.formRequestSummarizerFn(
|
|
496
|
+
this.summaryCollection, this.logger, this.formRequestSummarizerFn(loader), new Throttler(60 * 1000, // 60 sec delay window
|
|
443
497
|
30 * 1000, // 30 sec max delay
|
|
444
498
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
445
499
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -469,8 +523,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
469
523
|
this.replayPendingStates();
|
|
470
524
|
});
|
|
471
525
|
// logging hardware telemetry
|
|
472
|
-
logger.sendTelemetryEvent(
|
|
473
|
-
|
|
526
|
+
logger.sendTelemetryEvent({
|
|
527
|
+
eventName: "DeviceSpec",
|
|
528
|
+
...getDeviceSpec(),
|
|
529
|
+
});
|
|
530
|
+
this.mc.logger.sendTelemetryEvent({
|
|
531
|
+
eventName: "ContainerLoadStats",
|
|
532
|
+
...this.createContainerMetadata,
|
|
533
|
+
...this.dataStores.containerLoadStats,
|
|
534
|
+
summaryNumber: loadSummaryNumber,
|
|
535
|
+
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
536
|
+
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
537
|
+
gcVersion: metadata?.gcFeature,
|
|
538
|
+
options: JSON.stringify(runtimeOptions),
|
|
539
|
+
featureGates: JSON.stringify({
|
|
474
540
|
disableCompression,
|
|
475
541
|
disableOpReentryCheck,
|
|
476
542
|
disableChunking,
|
|
@@ -479,17 +545,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
479
545
|
idCompressorEnabled: this.idCompressorEnabled,
|
|
480
546
|
summaryStateUpdateMethod: this.summaryStateUpdateMethod,
|
|
481
547
|
closeSummarizerDelayOverride,
|
|
482
|
-
}),
|
|
483
|
-
|
|
548
|
+
}),
|
|
549
|
+
telemetryDocumentId: this.telemetryDocumentId,
|
|
550
|
+
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
551
|
+
});
|
|
552
|
+
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this.logger);
|
|
484
553
|
BindBatchTracker(this, this.logger);
|
|
485
554
|
this.entryPoint = new LazyPromise(async () => {
|
|
486
|
-
if (this.
|
|
555
|
+
if (this.isSummarizerClient) {
|
|
487
556
|
assert(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
488
557
|
return this._summarizer;
|
|
489
558
|
}
|
|
490
|
-
return initializeEntryPoint
|
|
559
|
+
return initializeEntryPoint?.(this);
|
|
491
560
|
});
|
|
492
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
564
|
+
*/
|
|
493
565
|
get IFluidRouter() {
|
|
494
566
|
return this;
|
|
495
567
|
}
|
|
@@ -535,22 +607,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
535
607
|
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
536
608
|
*/
|
|
537
609
|
static async loadRuntime(params) {
|
|
538
|
-
var _a, _b, _c, _d, _e, _f;
|
|
539
610
|
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, initializeEntryPoint, } = params;
|
|
540
611
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
541
612
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
542
613
|
const backCompatContext = context;
|
|
543
|
-
const passLogger =
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
614
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
615
|
+
new TaggedLoggerAdapter(backCompatContext.logger);
|
|
616
|
+
const logger = createChildLogger({
|
|
617
|
+
logger: passLogger,
|
|
618
|
+
properties: {
|
|
619
|
+
all: {
|
|
620
|
+
runtimeVersion: pkgVersion,
|
|
621
|
+
},
|
|
547
622
|
},
|
|
548
623
|
});
|
|
549
624
|
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
550
625
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
551
626
|
const tryFetchBlob = async (blobName) => {
|
|
552
|
-
|
|
553
|
-
const blobId = (_a = context.baseSnapshot) === null || _a === void 0 ? void 0 : _a.blobs[blobName];
|
|
627
|
+
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
554
628
|
if (context.baseSnapshot && blobId) {
|
|
555
629
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
556
630
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
@@ -565,16 +639,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
565
639
|
tryFetchBlob(aliasBlobName),
|
|
566
640
|
tryFetchBlob(idCompressorBlobName),
|
|
567
641
|
]);
|
|
568
|
-
const loadExisting = existing === true || context.existing === true;
|
|
569
642
|
// read snapshot blobs needed for BlobManager to load
|
|
570
|
-
const blobManagerSnapshot = await BlobManager.load(
|
|
643
|
+
const blobManagerSnapshot = await BlobManager.load(context.baseSnapshot?.trees[blobsTreeName], async (id) => {
|
|
571
644
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
572
645
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
573
646
|
assert(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
574
647
|
return readAndParse(context.storage, id);
|
|
575
648
|
});
|
|
576
649
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
577
|
-
const runtimeSequenceNumber =
|
|
650
|
+
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
578
651
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
579
652
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
580
653
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
@@ -589,13 +662,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
589
662
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
590
663
|
}
|
|
591
664
|
else {
|
|
592
|
-
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
593
665
|
context.closeFn(error);
|
|
594
|
-
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
595
666
|
}
|
|
596
667
|
}
|
|
597
668
|
}
|
|
598
|
-
const idCompressorEnabled =
|
|
669
|
+
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
599
670
|
let idCompressor;
|
|
600
671
|
if (idCompressorEnabled) {
|
|
601
672
|
const { IdCompressor, createSessionId } = await import("./id-compressor");
|
|
@@ -604,7 +675,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
604
675
|
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
605
676
|
: new IdCompressor(createSessionId(), logger);
|
|
606
677
|
}
|
|
607
|
-
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks
|
|
678
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
608
679
|
summaryOptions,
|
|
609
680
|
gcOptions,
|
|
610
681
|
loadSequenceNumberVerification,
|
|
@@ -615,7 +686,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
615
686
|
enableRuntimeIdCompressor,
|
|
616
687
|
enableOpReentryCheck,
|
|
617
688
|
enableGroupedBatching,
|
|
618
|
-
}, containerScope, logger,
|
|
689
|
+
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, requestHandler, undefined, // summaryConfiguration
|
|
619
690
|
initializeEntryPoint);
|
|
620
691
|
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
621
692
|
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
@@ -624,38 +695,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
624
695
|
await runtime.initializeBaseState();
|
|
625
696
|
return runtime;
|
|
626
697
|
}
|
|
627
|
-
get options() {
|
|
628
|
-
return this.context.options;
|
|
629
|
-
}
|
|
630
698
|
get clientId() {
|
|
631
|
-
return this.
|
|
632
|
-
}
|
|
633
|
-
get clientDetails() {
|
|
634
|
-
return this.context.clientDetails;
|
|
699
|
+
return this._getClientId();
|
|
635
700
|
}
|
|
636
701
|
get storage() {
|
|
637
702
|
return this._storage;
|
|
638
703
|
}
|
|
704
|
+
/** @deprecated - The functionality is no longer exposed publicly */
|
|
639
705
|
get reSubmitFn() {
|
|
640
|
-
|
|
641
|
-
return this.reSubmitCore;
|
|
642
|
-
}
|
|
643
|
-
get disposeFn() {
|
|
644
|
-
var _a;
|
|
645
|
-
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
646
|
-
return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
|
|
647
|
-
}
|
|
648
|
-
get closeFn() {
|
|
649
|
-
if (this._summarizer !== undefined) {
|
|
650
|
-
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
651
|
-
return this.disposeFn;
|
|
652
|
-
}
|
|
653
|
-
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
654
|
-
return (error) => {
|
|
655
|
-
var _a, _b;
|
|
656
|
-
this.context.closeFn(error);
|
|
657
|
-
(_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
658
|
-
};
|
|
706
|
+
return (type, contents, localOpMetadata, opMetadata) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
|
|
659
707
|
}
|
|
660
708
|
get flushMode() {
|
|
661
709
|
return this._flushMode;
|
|
@@ -667,7 +715,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
667
715
|
return this.registry;
|
|
668
716
|
}
|
|
669
717
|
get attachState() {
|
|
670
|
-
return this.
|
|
718
|
+
return this._getAttachState();
|
|
671
719
|
}
|
|
672
720
|
get IFluidHandleContext() {
|
|
673
721
|
return this.handleContext;
|
|
@@ -694,8 +742,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
694
742
|
}
|
|
695
743
|
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
696
744
|
get summarizerClientId() {
|
|
697
|
-
|
|
698
|
-
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
745
|
+
return this.summarizerClientElection?.electedClientId;
|
|
699
746
|
}
|
|
700
747
|
get disposed() {
|
|
701
748
|
return this._disposed;
|
|
@@ -732,12 +779,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
732
779
|
await this.garbageCollector.initializeBaseState();
|
|
733
780
|
}
|
|
734
781
|
dispose(error) {
|
|
735
|
-
var _a;
|
|
736
782
|
if (this._disposed) {
|
|
737
783
|
return;
|
|
738
784
|
}
|
|
739
785
|
this._disposed = true;
|
|
740
|
-
this.logger.sendTelemetryEvent({
|
|
786
|
+
this.mc.logger.sendTelemetryEvent({
|
|
741
787
|
eventName: "ContainerRuntimeDisposed",
|
|
742
788
|
isDirty: this.isDirty,
|
|
743
789
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
@@ -747,7 +793,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
747
793
|
this.summaryManager.dispose();
|
|
748
794
|
}
|
|
749
795
|
this.garbageCollector.dispose();
|
|
750
|
-
|
|
796
|
+
this._summarizer?.dispose();
|
|
751
797
|
this.dataStores.dispose();
|
|
752
798
|
this.pendingStateManager.dispose();
|
|
753
799
|
this.emit("dispose");
|
|
@@ -756,6 +802,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
756
802
|
/**
|
|
757
803
|
* Notifies this object about the request made to the container.
|
|
758
804
|
* @param request - Request made to the handler.
|
|
805
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
759
806
|
*/
|
|
760
807
|
async request(request) {
|
|
761
808
|
try {
|
|
@@ -822,19 +869,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
822
869
|
return this.entryPoint;
|
|
823
870
|
}
|
|
824
871
|
internalId(maybeAlias) {
|
|
825
|
-
|
|
826
|
-
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
872
|
+
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
827
873
|
}
|
|
828
874
|
async getDataStoreFromRequest(id, request) {
|
|
829
|
-
var _a, _b, _c;
|
|
830
875
|
const headerData = {};
|
|
831
|
-
if (typeof
|
|
876
|
+
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
832
877
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
833
878
|
}
|
|
834
|
-
if (typeof
|
|
879
|
+
if (typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean") {
|
|
835
880
|
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
836
881
|
}
|
|
837
|
-
if (typeof
|
|
882
|
+
if (typeof request.headers?.[AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
838
883
|
headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
|
|
839
884
|
}
|
|
840
885
|
await this.dataStores.waitIfPendingAlias(id);
|
|
@@ -844,22 +889,27 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
844
889
|
// Remove query params, leading and trailing slashes from the url. This is done to make sure the format is
|
|
845
890
|
// the same as GC nodes id.
|
|
846
891
|
const urlWithoutQuery = trimLeadingAndTrailingSlashes(request.url.split("?")[0]);
|
|
847
|
-
this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request
|
|
892
|
+
this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request?.headers);
|
|
848
893
|
return dataStoreChannel;
|
|
849
894
|
}
|
|
850
895
|
/** Adds the container's metadata to the given summary tree. */
|
|
851
896
|
addMetadataToSummary(summaryTree) {
|
|
852
|
-
|
|
853
|
-
|
|
897
|
+
const metadata = {
|
|
898
|
+
...this.createContainerMetadata,
|
|
854
899
|
// Increment the summary number for the next summary that will be generated.
|
|
855
|
-
summaryNumber: this.nextSummaryNumber++,
|
|
900
|
+
summaryNumber: this.nextSummaryNumber++,
|
|
901
|
+
summaryFormatVersion: 1,
|
|
902
|
+
...this.garbageCollector.getMetadata(),
|
|
856
903
|
// The last message processed at the time of summary. If there are no new messages, use the message from the
|
|
857
904
|
// last summary.
|
|
858
|
-
message:
|
|
905
|
+
message: extractSummaryMetadataMessage(this.deltaManager.lastMessage) ??
|
|
906
|
+
this.messageAtLastSummary,
|
|
907
|
+
telemetryDocumentId: this.telemetryDocumentId,
|
|
908
|
+
idCompressorEnabled: this.idCompressorEnabled ? true : undefined,
|
|
909
|
+
};
|
|
859
910
|
addBlobToSummary(summaryTree, metadataBlobName, JSON.stringify(metadata));
|
|
860
911
|
}
|
|
861
912
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
862
|
-
var _a;
|
|
863
913
|
this.addMetadataToSummary(summaryTree);
|
|
864
914
|
if (this.idCompressorEnabled) {
|
|
865
915
|
assert(this.idCompressor !== undefined, 0x67a /* IdCompressor should be defined if enabled */);
|
|
@@ -875,7 +925,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
875
925
|
addBlobToSummary(summaryTree, aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
876
926
|
}
|
|
877
927
|
if (this.summarizerClientElection) {
|
|
878
|
-
const electedSummarizerContent = JSON.stringify(
|
|
928
|
+
const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
|
|
879
929
|
addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
|
|
880
930
|
}
|
|
881
931
|
const blobManagerSummary = this.blobManager.summarize();
|
|
@@ -922,7 +972,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
922
972
|
// in their own batches before the originating batch is sent.
|
|
923
973
|
// Therefore, receiving them while attempting to send the originating batch
|
|
924
974
|
// does not mean that the container is making any progress.
|
|
925
|
-
if (
|
|
975
|
+
if (message?.type !== ContainerMessageType.ChunkedOp) {
|
|
926
976
|
this.consecutiveReconnects = 0;
|
|
927
977
|
}
|
|
928
978
|
}
|
|
@@ -972,9 +1022,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
972
1022
|
*/
|
|
973
1023
|
parseOpContent(serializedContent) {
|
|
974
1024
|
assert(serializedContent !== undefined, 0x6d5 /* content must be defined */);
|
|
975
|
-
const
|
|
976
|
-
assert(
|
|
977
|
-
return { type
|
|
1025
|
+
const { type, contents } = JSON.parse(serializedContent);
|
|
1026
|
+
assert(type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1027
|
+
return { type, contents };
|
|
978
1028
|
}
|
|
979
1029
|
async applyStashedOp(op) {
|
|
980
1030
|
// Need to parse from string for back-compat
|
|
@@ -1033,6 +1083,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1033
1083
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1034
1084
|
const changeOfState = this._connected !== connected;
|
|
1035
1085
|
const reconnection = changeOfState && !connected;
|
|
1086
|
+
// We need to flush the ops currently collected by Outbox to preserve original order.
|
|
1087
|
+
// This flush NEEDS to happen before we set the ContainerRuntime to "connected".
|
|
1088
|
+
// We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
|
|
1089
|
+
if (changeOfState && connected) {
|
|
1090
|
+
this.flush();
|
|
1091
|
+
}
|
|
1036
1092
|
this._connected = connected;
|
|
1037
1093
|
if (!connected) {
|
|
1038
1094
|
this._perfSignalData.signalsLost = 0;
|
|
@@ -1071,13 +1127,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1071
1127
|
// or something different, like a system message.
|
|
1072
1128
|
const runtimeMessage = messageArg.type === MessageType.Operation;
|
|
1073
1129
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1074
|
-
const messageCopy =
|
|
1130
|
+
const messageCopy = { ...messageArg };
|
|
1075
1131
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1076
1132
|
this.processCore(message, local, runtimeMessage);
|
|
1077
1133
|
}
|
|
1078
1134
|
}
|
|
1135
|
+
/**
|
|
1136
|
+
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
1137
|
+
* @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
|
|
1138
|
+
* @param local - Did this client send the op?
|
|
1139
|
+
* @param runtimeMessage - Does this appear like a current ContainerRuntimeMessage? If true, certain validation will occur.
|
|
1140
|
+
*/
|
|
1079
1141
|
processCore(message, local, runtimeMessage) {
|
|
1080
|
-
var _a;
|
|
1081
1142
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1082
1143
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1083
1144
|
// messages once a batch has been fully processed.
|
|
@@ -1122,16 +1183,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1122
1183
|
local,
|
|
1123
1184
|
type: message.type,
|
|
1124
1185
|
contentType: typeof message.contents,
|
|
1125
|
-
batch:
|
|
1186
|
+
batch: message.metadata?.batch,
|
|
1126
1187
|
compression: message.compression,
|
|
1127
1188
|
});
|
|
1128
1189
|
this.closeFn(error);
|
|
1129
1190
|
throw error;
|
|
1130
1191
|
}
|
|
1131
1192
|
}
|
|
1132
|
-
|
|
1133
|
-
this.emit("op", message, runtimeMessage);
|
|
1134
|
-
}
|
|
1193
|
+
this.emit("op", message, runtimeMessage);
|
|
1135
1194
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1136
1195
|
if (local) {
|
|
1137
1196
|
// If we have processed a local op, this means that the container is
|
|
@@ -1154,7 +1213,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1154
1213
|
*/
|
|
1155
1214
|
sendSignalTelemetryEvent(clientSignalSequenceNumber) {
|
|
1156
1215
|
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
1157
|
-
this.logger.sendPerformanceEvent({
|
|
1216
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1158
1217
|
eventName: "SignalLatency",
|
|
1159
1218
|
duration,
|
|
1160
1219
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -1177,7 +1236,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1177
1236
|
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1178
1237
|
this._perfSignalData.signalsLost++;
|
|
1179
1238
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1180
|
-
this.logger.sendErrorEvent({
|
|
1239
|
+
this.mc.logger.sendErrorEvent({
|
|
1181
1240
|
eventName: "SignalLost",
|
|
1182
1241
|
type: envelope.contents.type,
|
|
1183
1242
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -1201,6 +1260,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1201
1260
|
}
|
|
1202
1261
|
this.dataStores.processSignal(envelope.address, transformed, local);
|
|
1203
1262
|
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Returns the runtime of the data store.
|
|
1265
|
+
* @param id - Id supplied during creating the data store.
|
|
1266
|
+
* @param wait - True if you want to wait for it.
|
|
1267
|
+
* @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1268
|
+
*/
|
|
1204
1269
|
async getRootDataStore(id, wait = true) {
|
|
1205
1270
|
return this.getRootDataStoreChannel(id, wait);
|
|
1206
1271
|
}
|
|
@@ -1262,9 +1327,25 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1262
1327
|
}
|
|
1263
1328
|
return result;
|
|
1264
1329
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1330
|
+
/**
|
|
1331
|
+
* Returns the aliased data store's entryPoint, given the alias.
|
|
1332
|
+
* @param alias - The alias for the data store.
|
|
1333
|
+
* @returns - The data store's entry point (IFluidHandle) if it exists and is aliased. Returns undefined if no
|
|
1334
|
+
* data store has been assigned the given alias.
|
|
1335
|
+
*/
|
|
1336
|
+
async getAliasedDataStoreEntryPoint(alias) {
|
|
1337
|
+
await this.dataStores.waitIfPendingAlias(alias);
|
|
1338
|
+
const internalId = this.internalId(alias);
|
|
1339
|
+
const context = await this.dataStores.getDataStoreIfAvailable(internalId, { wait: false });
|
|
1340
|
+
// If the data store is not available or not an alias, return undefined.
|
|
1341
|
+
if (context === undefined || !(await context.isRoot())) {
|
|
1342
|
+
return undefined;
|
|
1343
|
+
}
|
|
1344
|
+
const channel = await context.realize();
|
|
1345
|
+
if (channel.entryPoint === undefined) {
|
|
1346
|
+
throw new UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
|
|
1347
|
+
}
|
|
1348
|
+
return channel.entryPoint;
|
|
1268
1349
|
}
|
|
1269
1350
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1270
1351
|
if (rootDataStoreId.includes("/")) {
|
|
@@ -1275,16 +1356,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1275
1356
|
createDetachedDataStore(pkg) {
|
|
1276
1357
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1277
1358
|
}
|
|
1278
|
-
async
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
.
|
|
1282
|
-
|
|
1359
|
+
async createDataStore(pkg) {
|
|
1360
|
+
const id = uuid();
|
|
1361
|
+
return channelToDataStore(await this.dataStores
|
|
1362
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id)
|
|
1363
|
+
.realize(), id, this, this.dataStores, this.mc.logger);
|
|
1283
1364
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1365
|
+
/**
|
|
1366
|
+
* @deprecated 0.16 Issue #1537, #3631
|
|
1367
|
+
* @internal
|
|
1368
|
+
*/
|
|
1369
|
+
async _createDataStoreWithProps(pkg, props, id = uuid()) {
|
|
1370
|
+
return channelToDataStore(await this.dataStores
|
|
1286
1371
|
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1287
|
-
.realize();
|
|
1372
|
+
.realize(), id, this, this.dataStores, this.mc.logger);
|
|
1288
1373
|
}
|
|
1289
1374
|
canSendOps() {
|
|
1290
1375
|
// Note that the real (non-proxy) delta manager is needed here to get the readonly info. This is because
|
|
@@ -1298,11 +1383,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1298
1383
|
return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
1299
1384
|
}
|
|
1300
1385
|
getQuorum() {
|
|
1301
|
-
return this.
|
|
1386
|
+
return this._quorum;
|
|
1302
1387
|
}
|
|
1303
1388
|
getAudience() {
|
|
1304
|
-
|
|
1305
|
-
return this.context.audience;
|
|
1389
|
+
return this._audience;
|
|
1306
1390
|
}
|
|
1307
1391
|
/**
|
|
1308
1392
|
* Returns true of container is dirty, i.e. there are some pending local changes that
|
|
@@ -1311,7 +1395,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1311
1395
|
get isDirty() {
|
|
1312
1396
|
return this.dirtyContainer;
|
|
1313
1397
|
}
|
|
1314
|
-
isContainerMessageDirtyable(type, contents) {
|
|
1398
|
+
isContainerMessageDirtyable({ type, contents }) {
|
|
1315
1399
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
1316
1400
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
1317
1401
|
if (type === ContainerMessageType.Attach) {
|
|
@@ -1351,11 +1435,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1351
1435
|
submitSignal(type, content) {
|
|
1352
1436
|
this.verifyNotClosed();
|
|
1353
1437
|
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
1354
|
-
return this.
|
|
1438
|
+
return this.submitSignalFn(envelope);
|
|
1355
1439
|
}
|
|
1356
1440
|
submitDataStoreSignal(address, type, content) {
|
|
1357
1441
|
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
1358
|
-
return this.
|
|
1442
|
+
return this.submitSignalFn(envelope);
|
|
1359
1443
|
}
|
|
1360
1444
|
setAttachState(attachState) {
|
|
1361
1445
|
if (attachState === AttachState.Attaching) {
|
|
@@ -1388,22 +1472,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1388
1472
|
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
|
|
1389
1473
|
return summarizeResult.summary;
|
|
1390
1474
|
}
|
|
1391
|
-
async getAbsoluteUrl(relativeUrl) {
|
|
1392
|
-
if (this.context.getAbsoluteUrl === undefined) {
|
|
1393
|
-
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
1394
|
-
}
|
|
1395
|
-
if (this.attachState !== AttachState.Attached) {
|
|
1396
|
-
return undefined;
|
|
1397
|
-
}
|
|
1398
|
-
return this.context.getAbsoluteUrl(relativeUrl);
|
|
1399
|
-
}
|
|
1400
1475
|
async summarizeInternal(fullTree, trackState, telemetryContext) {
|
|
1401
1476
|
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
1402
1477
|
// Wrap data store summaries in .channels subtree.
|
|
1403
1478
|
wrapSummaryInChannelsTree(summarizeResult);
|
|
1404
1479
|
const pathPartsForChildren = [channelsTreeName];
|
|
1405
1480
|
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
1406
|
-
return
|
|
1481
|
+
return {
|
|
1482
|
+
...summarizeResult,
|
|
1483
|
+
id: "",
|
|
1484
|
+
pathPartsForChildren,
|
|
1485
|
+
};
|
|
1407
1486
|
}
|
|
1408
1487
|
/**
|
|
1409
1488
|
* Returns a summary of the runtime at the current sequence number.
|
|
@@ -1421,16 +1500,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1421
1500
|
runSweep,
|
|
1422
1501
|
});
|
|
1423
1502
|
try {
|
|
1424
|
-
let gcStats;
|
|
1425
1503
|
if (runGC) {
|
|
1426
|
-
|
|
1504
|
+
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC }, telemetryContext);
|
|
1427
1505
|
}
|
|
1428
1506
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1429
1507
|
assert(summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1430
|
-
return { stats, summary
|
|
1508
|
+
return { stats, summary };
|
|
1431
1509
|
}
|
|
1432
1510
|
finally {
|
|
1433
|
-
this.logger.sendTelemetryEvent({
|
|
1511
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1434
1512
|
eventName: "SummarizeTelemetry",
|
|
1435
1513
|
details: telemetryContext.serialize(),
|
|
1436
1514
|
});
|
|
@@ -1512,21 +1590,19 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1512
1590
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
1513
1591
|
*/
|
|
1514
1592
|
getCurrentReferenceTimestampMs() {
|
|
1515
|
-
var _a, _b, _c;
|
|
1516
1593
|
// Use the timestamp of the last message seen by this client as that is server generated. If no messages have
|
|
1517
1594
|
// been processed, use the timestamp of the message from the last summary.
|
|
1518
|
-
return
|
|
1595
|
+
return this.deltaManager.lastMessage?.timestamp ?? this.messageAtLastSummary?.timestamp;
|
|
1519
1596
|
}
|
|
1520
1597
|
/**
|
|
1521
1598
|
* Returns the type of the GC node. Currently, there are nodes that belong to the root ("/"), data stores or
|
|
1522
1599
|
* blob manager.
|
|
1523
1600
|
*/
|
|
1524
1601
|
getNodeType(nodePath) {
|
|
1525
|
-
var _a;
|
|
1526
1602
|
if (this.isBlobPath(nodePath)) {
|
|
1527
1603
|
return GCNodeType.Blob;
|
|
1528
1604
|
}
|
|
1529
|
-
return
|
|
1605
|
+
return this.dataStores.getGCNodeType(nodePath) ?? GCNodeType.Other;
|
|
1530
1606
|
}
|
|
1531
1607
|
/**
|
|
1532
1608
|
* Called by GC to retrieve the package path of the node with the given path. The node should belong to a
|
|
@@ -1597,18 +1673,24 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1597
1673
|
* @param options - options controlling how the summary is generated or submitted
|
|
1598
1674
|
*/
|
|
1599
1675
|
async submitSummary(options) {
|
|
1600
|
-
var _a, _b, _c;
|
|
1601
1676
|
const { fullTree = false, refreshLatestAck, summaryLogger } = options;
|
|
1602
1677
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
1603
1678
|
// use it for all events logged during this summary.
|
|
1604
1679
|
const summaryNumber = this.nextSummaryNumber;
|
|
1605
|
-
const summaryNumberLogger =
|
|
1606
|
-
|
|
1680
|
+
const summaryNumberLogger = createChildLogger({
|
|
1681
|
+
logger: summaryLogger,
|
|
1682
|
+
properties: {
|
|
1683
|
+
all: { summaryNumber },
|
|
1684
|
+
},
|
|
1607
1685
|
});
|
|
1608
1686
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1609
1687
|
let latestSnapshotVersionId;
|
|
1610
1688
|
if (refreshLatestAck) {
|
|
1611
|
-
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
|
|
1689
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(createChildLogger({
|
|
1690
|
+
logger: summaryNumberLogger,
|
|
1691
|
+
namespace: undefined,
|
|
1692
|
+
properties: { all: { safeSummary: true } },
|
|
1693
|
+
}));
|
|
1612
1694
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1613
1695
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1614
1696
|
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
@@ -1628,7 +1710,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1628
1710
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
|
|
1629
1711
|
// Helper function to check whether we should still continue between each async step.
|
|
1630
1712
|
const checkContinue = () => {
|
|
1631
|
-
var _a;
|
|
1632
1713
|
// Do not check for loss of connectivity directly! Instead leave it up to
|
|
1633
1714
|
// RunWhileConnectedCoordinator to control policy in a single place.
|
|
1634
1715
|
// This will allow easier change of design if we chose to. For example, we may chose to allow
|
|
@@ -1651,7 +1732,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1651
1732
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1652
1733
|
};
|
|
1653
1734
|
}
|
|
1654
|
-
assert(summaryRefSeqNum ===
|
|
1735
|
+
assert(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber, 0x395 /* it's one and the same thing */);
|
|
1655
1736
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1656
1737
|
return {
|
|
1657
1738
|
continue: false,
|
|
@@ -1702,7 +1783,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1702
1783
|
const gcSummaryTreeStats = summaryTree.tree[gcTreeKey]
|
|
1703
1784
|
? calculateStats(summaryTree.tree[gcTreeKey])
|
|
1704
1785
|
: undefined;
|
|
1705
|
-
const summaryStats =
|
|
1786
|
+
const summaryStats = {
|
|
1787
|
+
dataStoreCount: this.dataStores.size,
|
|
1788
|
+
summarizedDataStoreCount: this.dataStores.size - handleCount,
|
|
1789
|
+
gcStateUpdatedDataStoreCount: this.garbageCollector.updatedDSCountSinceLastSummary,
|
|
1790
|
+
gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
|
|
1791
|
+
gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
|
|
1792
|
+
summaryNumber,
|
|
1793
|
+
...partialStats,
|
|
1794
|
+
};
|
|
1706
1795
|
const generateSummaryData = {
|
|
1707
1796
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1708
1797
|
minimumSequenceNumber,
|
|
@@ -1716,14 +1805,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1716
1805
|
if (this.validateSummaryBeforeUpload) {
|
|
1717
1806
|
const validateResult = this.summarizerNode.validateSummary();
|
|
1718
1807
|
if (!validateResult.success) {
|
|
1719
|
-
const { success
|
|
1720
|
-
const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds,
|
|
1721
|
-
return
|
|
1808
|
+
const { success, ...loggingProps } = validateResult;
|
|
1809
|
+
const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
1810
|
+
return { stage: "base", ...generateSummaryData, error };
|
|
1722
1811
|
}
|
|
1723
1812
|
}
|
|
1724
1813
|
continueResult = checkContinue();
|
|
1725
1814
|
if (!continueResult.continue) {
|
|
1726
|
-
return
|
|
1815
|
+
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
1727
1816
|
}
|
|
1728
1817
|
// It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
|
|
1729
1818
|
// summary. So if the previous summarizer closes just after submitting the summary and before
|
|
@@ -1731,7 +1820,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1731
1820
|
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1732
1821
|
// the one fetched from storage as parent as that is the latest.
|
|
1733
1822
|
let summaryContext;
|
|
1734
|
-
if (
|
|
1823
|
+
if (lastAck?.summaryAck.contents.handle !== latestSnapshotVersionId &&
|
|
1735
1824
|
latestSnapshotVersionId !== undefined) {
|
|
1736
1825
|
summaryContext = {
|
|
1737
1826
|
proposalHandle: undefined,
|
|
@@ -1742,7 +1831,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1742
1831
|
else if (lastAck === undefined) {
|
|
1743
1832
|
summaryContext = {
|
|
1744
1833
|
proposalHandle: undefined,
|
|
1745
|
-
ackHandle:
|
|
1834
|
+
ackHandle: this.loadedFromVersionId,
|
|
1746
1835
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1747
1836
|
};
|
|
1748
1837
|
}
|
|
@@ -1758,7 +1847,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1758
1847
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
1759
1848
|
}
|
|
1760
1849
|
catch (error) {
|
|
1761
|
-
return
|
|
1850
|
+
return { stage: "generate", ...generateSummaryData, error };
|
|
1762
1851
|
}
|
|
1763
1852
|
const parent = summaryContext.ackHandle;
|
|
1764
1853
|
const summaryMessage = {
|
|
@@ -1768,25 +1857,34 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1768
1857
|
message,
|
|
1769
1858
|
parents: parent ? [parent] : [],
|
|
1770
1859
|
};
|
|
1771
|
-
const uploadData =
|
|
1860
|
+
const uploadData = {
|
|
1861
|
+
...generateSummaryData,
|
|
1862
|
+
handle,
|
|
1863
|
+
uploadDuration: trace.trace().duration,
|
|
1864
|
+
};
|
|
1772
1865
|
continueResult = checkContinue();
|
|
1773
1866
|
if (!continueResult.continue) {
|
|
1774
|
-
return
|
|
1867
|
+
return { stage: "upload", ...uploadData, error: continueResult.error };
|
|
1775
1868
|
}
|
|
1776
1869
|
let clientSequenceNumber;
|
|
1777
1870
|
try {
|
|
1778
1871
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
1779
1872
|
}
|
|
1780
1873
|
catch (error) {
|
|
1781
|
-
return
|
|
1874
|
+
return { stage: "upload", ...uploadData, error };
|
|
1782
1875
|
}
|
|
1783
|
-
const submitData =
|
|
1876
|
+
const submitData = {
|
|
1877
|
+
stage: "submit",
|
|
1878
|
+
...uploadData,
|
|
1879
|
+
clientSequenceNumber,
|
|
1880
|
+
submitOpDuration: trace.trace().duration,
|
|
1881
|
+
};
|
|
1784
1882
|
try {
|
|
1785
1883
|
// If validateSummaryBeforeUpload is false, the summary should be validated in this step.
|
|
1786
1884
|
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
1787
1885
|
}
|
|
1788
1886
|
catch (error) {
|
|
1789
|
-
return
|
|
1887
|
+
return { stage: "upload", ...uploadData, error };
|
|
1790
1888
|
}
|
|
1791
1889
|
return submitData;
|
|
1792
1890
|
}
|
|
@@ -1794,7 +1892,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1794
1892
|
// Cleanup wip summary in case of failure
|
|
1795
1893
|
this.summarizerNode.clearSummary();
|
|
1796
1894
|
// ! This needs to happen before we resume inbound queues to ensure heuristics are tracked correctly
|
|
1797
|
-
|
|
1895
|
+
this._summarizer?.recordSummaryAttempt?.(summaryRefSeqNum);
|
|
1798
1896
|
// Restart the delta manager
|
|
1799
1897
|
this.deltaManager.inbound.resume();
|
|
1800
1898
|
if (shouldPauseInboundSignal) {
|
|
@@ -1819,7 +1917,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1819
1917
|
this.dirtyContainer = dirty;
|
|
1820
1918
|
if (this.emitDirtyDocumentEvent) {
|
|
1821
1919
|
this.emit(dirty ? "dirty" : "saved");
|
|
1822
|
-
this.context.updateDirtyContainerState(dirty);
|
|
1823
1920
|
}
|
|
1824
1921
|
}
|
|
1825
1922
|
submitDataStoreOp(id, contents, localOpMetadata = undefined) {
|
|
@@ -1827,21 +1924,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1827
1924
|
address: id,
|
|
1828
1925
|
contents,
|
|
1829
1926
|
};
|
|
1830
|
-
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
1927
|
+
this.submit({ type: ContainerMessageType.FluidDataStoreOp, contents: envelope }, localOpMetadata);
|
|
1831
1928
|
}
|
|
1832
1929
|
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1833
1930
|
const aliasMessage = contents;
|
|
1834
1931
|
if (!isDataStoreAliasMessage(aliasMessage)) {
|
|
1835
1932
|
throw new UsageError("malformedDataStoreAliasMessage");
|
|
1836
1933
|
}
|
|
1837
|
-
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
1934
|
+
this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
1838
1935
|
}
|
|
1839
|
-
async uploadBlob(blob) {
|
|
1936
|
+
async uploadBlob(blob, signal) {
|
|
1840
1937
|
this.verifyNotClosed();
|
|
1841
|
-
return this.blobManager.createBlob(blob);
|
|
1938
|
+
return this.blobManager.createBlob(blob, signal);
|
|
1842
1939
|
}
|
|
1843
1940
|
maybeSubmitIdAllocationOp(type) {
|
|
1844
|
-
var _a, _b;
|
|
1845
1941
|
if (type !== ContainerMessageType.IdAllocation) {
|
|
1846
1942
|
let idAllocationBatchMessage;
|
|
1847
1943
|
let idRange;
|
|
@@ -1849,7 +1945,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1849
1945
|
assert(this.idCompressor !== undefined, 0x67d /* IdCompressor should be defined if enabled */);
|
|
1850
1946
|
idRange = this.idCompressor.takeNextCreationRange();
|
|
1851
1947
|
// Don't include the idRange if there weren't any Ids allocated
|
|
1852
|
-
idRange =
|
|
1948
|
+
idRange = idRange?.ids?.first !== undefined ? idRange : undefined;
|
|
1853
1949
|
}
|
|
1854
1950
|
if (idRange !== undefined) {
|
|
1855
1951
|
const idAllocationMessage = {
|
|
@@ -1860,7 +1956,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1860
1956
|
contents: JSON.stringify(idAllocationMessage),
|
|
1861
1957
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1862
1958
|
metadata: undefined,
|
|
1863
|
-
localOpMetadata:
|
|
1959
|
+
localOpMetadata: this.idCompressor?.serialize(true),
|
|
1864
1960
|
type: ContainerMessageType.IdAllocation,
|
|
1865
1961
|
};
|
|
1866
1962
|
}
|
|
@@ -1869,20 +1965,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1869
1965
|
}
|
|
1870
1966
|
}
|
|
1871
1967
|
}
|
|
1872
|
-
submit(
|
|
1968
|
+
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata = undefined) {
|
|
1873
1969
|
this.verifyNotClosed();
|
|
1874
1970
|
this.verifyCanSubmitOps();
|
|
1875
1971
|
// There should be no ops in detached container state!
|
|
1876
1972
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1877
|
-
const serializedContent = JSON.stringify(
|
|
1973
|
+
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
1878
1974
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
1879
1975
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
1880
1976
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
1881
|
-
this.logger.sendTelemetryEvent({
|
|
1977
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1882
1978
|
eventName: "SubmitOpInReadonly",
|
|
1883
1979
|
connected: this.connected,
|
|
1884
1980
|
});
|
|
1885
1981
|
}
|
|
1982
|
+
const type = containerRuntimeMessage.type;
|
|
1886
1983
|
const message = {
|
|
1887
1984
|
contents: serializedContent,
|
|
1888
1985
|
type,
|
|
@@ -1939,7 +2036,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1939
2036
|
this.closeFn(error);
|
|
1940
2037
|
throw error;
|
|
1941
2038
|
}
|
|
1942
|
-
if (this.isContainerMessageDirtyable(
|
|
2039
|
+
if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
|
|
1943
2040
|
this.updateDocumentDirtyState(true);
|
|
1944
2041
|
}
|
|
1945
2042
|
}
|
|
@@ -1982,9 +2079,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1982
2079
|
// System message should not be sent in the middle of the batch.
|
|
1983
2080
|
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1984
2081
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1985
|
-
return this.
|
|
1986
|
-
? this.
|
|
1987
|
-
: this.
|
|
2082
|
+
return this.submitSummaryFn !== undefined
|
|
2083
|
+
? this.submitSummaryFn(contents, referenceSequenceNumber)
|
|
2084
|
+
: this.submitFn(MessageType.Summarize, contents, false);
|
|
1988
2085
|
}
|
|
1989
2086
|
/**
|
|
1990
2087
|
* Throw an error if the runtime is closed. Methods that are expected to potentially
|
|
@@ -2031,32 +2128,33 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2031
2128
|
}
|
|
2032
2129
|
reSubmit(message) {
|
|
2033
2130
|
// Need to parse from string for back-compat
|
|
2034
|
-
const
|
|
2035
|
-
this.reSubmitCore(
|
|
2131
|
+
const containerRuntimeMessage = this.parseOpContent(message.content);
|
|
2132
|
+
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2036
2133
|
}
|
|
2037
2134
|
/**
|
|
2038
2135
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2039
2136
|
* reconnect and there are pending messages.
|
|
2040
|
-
* @param
|
|
2137
|
+
* @param message - The original ContainerRuntimeMessage.
|
|
2041
2138
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2042
2139
|
*/
|
|
2043
|
-
reSubmitCore(
|
|
2044
|
-
|
|
2140
|
+
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2141
|
+
const contents = message.contents;
|
|
2142
|
+
switch (message.type) {
|
|
2045
2143
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2046
2144
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2047
2145
|
// and trigger resubmission on it.
|
|
2048
|
-
this.dataStores.resubmitDataStoreOp(
|
|
2146
|
+
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2049
2147
|
break;
|
|
2050
2148
|
case ContainerMessageType.Attach:
|
|
2051
2149
|
case ContainerMessageType.Alias:
|
|
2052
|
-
this.submit(
|
|
2150
|
+
this.submit(message, localOpMetadata);
|
|
2053
2151
|
break;
|
|
2054
2152
|
case ContainerMessageType.IdAllocation:
|
|
2055
2153
|
// Remove the stashedState from the op if it's a stashed op
|
|
2056
|
-
if (
|
|
2057
|
-
delete
|
|
2154
|
+
if (contents.stashedState !== undefined) {
|
|
2155
|
+
delete contents.stashedState;
|
|
2058
2156
|
}
|
|
2059
|
-
this.submit(
|
|
2157
|
+
this.submit(message, localOpMetadata);
|
|
2060
2158
|
break;
|
|
2061
2159
|
case ContainerMessageType.ChunkedOp:
|
|
2062
2160
|
throw new Error(`chunkedOp not expected here`);
|
|
@@ -2064,10 +2162,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2064
2162
|
this.blobManager.reSubmit(opMetadata);
|
|
2065
2163
|
break;
|
|
2066
2164
|
case ContainerMessageType.Rejoin:
|
|
2067
|
-
this.submit(
|
|
2165
|
+
this.submit(message);
|
|
2068
2166
|
break;
|
|
2069
2167
|
default:
|
|
2070
|
-
unreachableCase(type, `Unknown ContainerMessageType: ${type}`);
|
|
2168
|
+
unreachableCase(message.type, `Unknown ContainerMessageType [type: ${message.type}]`);
|
|
2071
2169
|
}
|
|
2072
2170
|
}
|
|
2073
2171
|
rollback(content, localOpMetadata) {
|
|
@@ -2114,7 +2212,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2114
2212
|
* change that started fetching latest snapshot always.
|
|
2115
2213
|
*/
|
|
2116
2214
|
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
2117
|
-
fetchResult = await this.
|
|
2215
|
+
fetchResult = await this.fetchSnapshotFromStorageAndClose(summaryLogger, {
|
|
2118
2216
|
eventName: "RefreshLatestSummaryAckFetchBackCompat",
|
|
2119
2217
|
ackHandle,
|
|
2120
2218
|
targetSequenceNumber: summaryRefSeq,
|
|
@@ -2136,7 +2234,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2136
2234
|
summaryRefSeq,
|
|
2137
2235
|
fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
2138
2236
|
});
|
|
2139
|
-
this.
|
|
2237
|
+
this.disposeFn(error);
|
|
2140
2238
|
throw error;
|
|
2141
2239
|
}
|
|
2142
2240
|
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
@@ -2172,10 +2270,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2172
2270
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
2173
2271
|
}
|
|
2174
2272
|
async fetchLatestSnapshotFromStorage(logger, event, readAndParseBlob) {
|
|
2175
|
-
return this.
|
|
2273
|
+
return this.fetchSnapshotFromStorageAndClose(logger, event, readAndParseBlob, null /* latest */);
|
|
2176
2274
|
}
|
|
2177
|
-
async
|
|
2178
|
-
var _a;
|
|
2275
|
+
async fetchSnapshotFromStorageAndClose(logger, event, readAndParseBlob, versionId) {
|
|
2179
2276
|
const snapshotResults = await PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2180
2277
|
const stats = {};
|
|
2181
2278
|
const trace = Trace.start();
|
|
@@ -2198,29 +2295,37 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2198
2295
|
// We choose to close the summarizer after the snapshot cache is updated to avoid
|
|
2199
2296
|
// situations which the main client (which is likely to be re-elected as the leader again)
|
|
2200
2297
|
// loads the summarizer from cache.
|
|
2201
|
-
if (this.summaryStateUpdateMethod
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2298
|
+
if (this.summaryStateUpdateMethod !== "refreshFromSnapshot") {
|
|
2299
|
+
this.mc.logger.sendTelemetryEvent({
|
|
2300
|
+
...event,
|
|
2301
|
+
eventName: "ClosingSummarizerOnSummaryStale",
|
|
2302
|
+
codePath: event.eventName,
|
|
2303
|
+
message: "Stopping fetch from storage",
|
|
2304
|
+
versionId: versionId != null ? versionId : undefined,
|
|
2305
|
+
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
2306
|
+
}, new GenericError("Restarting summarizer instead of refreshing"));
|
|
2307
|
+
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2205
2308
|
await delay(this.closeSummarizerDelayMs);
|
|
2206
|
-
|
|
2207
|
-
this.
|
|
2208
|
-
throw error;
|
|
2309
|
+
this._summarizer?.stop("latestSummaryStateStale");
|
|
2310
|
+
this.disposeFn();
|
|
2209
2311
|
}
|
|
2210
2312
|
return snapshotResults;
|
|
2211
2313
|
}
|
|
2212
2314
|
notifyAttaching() { } // do nothing (deprecated method)
|
|
2213
|
-
getPendingLocalState() {
|
|
2315
|
+
async getPendingLocalState(props) {
|
|
2316
|
+
this.verifyNotClosed();
|
|
2317
|
+
const waitBlobsToAttach = props?.notifyImminentClosure;
|
|
2214
2318
|
if (this._orderSequentiallyCalls !== 0) {
|
|
2215
2319
|
throw new UsageError("can't get state during orderSequentially");
|
|
2216
2320
|
}
|
|
2321
|
+
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(waitBlobsToAttach);
|
|
2217
2322
|
// Flush pending batch.
|
|
2218
2323
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2219
2324
|
// to close current batch.
|
|
2220
2325
|
this.flush();
|
|
2221
2326
|
return {
|
|
2222
2327
|
pending: this.pendingStateManager.getLocalState(),
|
|
2223
|
-
pendingAttachmentBlobs
|
|
2328
|
+
pendingAttachmentBlobs,
|
|
2224
2329
|
};
|
|
2225
2330
|
}
|
|
2226
2331
|
/**
|