@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/dist/containerRuntime.js
CHANGED
|
@@ -18,19 +18,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
18
|
__setModuleDefault(result, mod);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
22
|
-
var t = {};
|
|
23
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
24
|
-
t[p] = s[p];
|
|
25
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
26
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
27
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
28
|
-
t[p[i]] = s[p[i]];
|
|
29
|
-
}
|
|
30
|
-
return t;
|
|
31
|
-
};
|
|
32
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.TombstoneResponseHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
22
|
+
exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.TombstoneResponseHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
34
23
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
35
24
|
const common_utils_1 = require("@fluidframework/common-utils");
|
|
36
25
|
const core_utils_1 = require("@fluidframework/core-utils");
|
|
@@ -137,7 +126,7 @@ const defaultChunkSizeInBytes = 204800;
|
|
|
137
126
|
* of the current system, we should close the summarizer and let it recover.
|
|
138
127
|
* This delay's goal is to prevent tight restart loops
|
|
139
128
|
*/
|
|
140
|
-
const defaultCloseSummarizerDelayMs =
|
|
129
|
+
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
141
130
|
/**
|
|
142
131
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
143
132
|
*/
|
|
@@ -174,10 +163,25 @@ function getDeviceSpec() {
|
|
|
174
163
|
};
|
|
175
164
|
}
|
|
176
165
|
}
|
|
177
|
-
catch
|
|
166
|
+
catch { }
|
|
178
167
|
return {};
|
|
179
168
|
}
|
|
180
169
|
exports.getDeviceSpec = getDeviceSpec;
|
|
170
|
+
/**
|
|
171
|
+
* Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
|
|
172
|
+
* Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
|
|
173
|
+
* we can provide a partially-applied function to keep those items private to the ContainerRuntime.
|
|
174
|
+
*/
|
|
175
|
+
const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
|
|
176
|
+
for (const message of batch.content) {
|
|
177
|
+
submitFn(protocol_definitions_1.MessageType.Operation,
|
|
178
|
+
// For back-compat (submitFn only works on deserialized content)
|
|
179
|
+
message.contents === undefined ? undefined : JSON.parse(message.contents), true, // batch
|
|
180
|
+
message.metadata);
|
|
181
|
+
}
|
|
182
|
+
deltaManager.flush();
|
|
183
|
+
};
|
|
184
|
+
exports.makeLegacySendBatchFn = makeLegacySendBatchFn;
|
|
181
185
|
/**
|
|
182
186
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
183
187
|
* It will define the store level mappings.
|
|
@@ -186,11 +190,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
186
190
|
/**
|
|
187
191
|
* @internal
|
|
188
192
|
*/
|
|
189
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration
|
|
190
|
-
|
|
191
|
-
|
|
193
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
194
|
+
// the defaults
|
|
195
|
+
...exports.DefaultSummaryConfiguration,
|
|
196
|
+
// the runtime configuration overrides
|
|
197
|
+
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
198
|
+
}, initializeEntryPoint) {
|
|
192
199
|
super();
|
|
193
|
-
this.context = context;
|
|
194
200
|
this.registry = registry;
|
|
195
201
|
this.runtimeOptions = runtimeOptions;
|
|
196
202
|
this.containerScope = containerScope;
|
|
@@ -220,7 +226,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
220
226
|
trackingSignalSequenceNumber: undefined,
|
|
221
227
|
};
|
|
222
228
|
this.summarizeOnDemand = (...args) => {
|
|
223
|
-
if (this.
|
|
229
|
+
if (this.isSummarizerClient) {
|
|
224
230
|
return this.summarizer.summarizeOnDemand(...args);
|
|
225
231
|
}
|
|
226
232
|
else if (this.summaryManager !== undefined) {
|
|
@@ -234,7 +240,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
234
240
|
}
|
|
235
241
|
};
|
|
236
242
|
this.enqueueSummarize = (...args) => {
|
|
237
|
-
if (this.
|
|
243
|
+
if (this.isSummarizerClient) {
|
|
238
244
|
return this.summarizer.enqueueSummarize(...args);
|
|
239
245
|
}
|
|
240
246
|
else if (this.summaryManager !== undefined) {
|
|
@@ -247,23 +253,56 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
247
253
|
throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
248
254
|
}
|
|
249
255
|
};
|
|
250
|
-
|
|
251
|
-
this.
|
|
252
|
-
this.
|
|
256
|
+
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, } = context;
|
|
257
|
+
this.innerDeltaManager = deltaManager;
|
|
258
|
+
this.deltaManager = new deltaManagerSummarizerProxy_1.DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
259
|
+
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
260
|
+
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
261
|
+
this.submitFn = submitFn;
|
|
262
|
+
this.submitBatchFn = submitBatchFn;
|
|
263
|
+
this.submitSummaryFn = submitSummaryFn;
|
|
264
|
+
this.submitSignalFn = submitSignalFn;
|
|
265
|
+
this.options = options;
|
|
266
|
+
this.clientDetails = clientDetails;
|
|
267
|
+
this.isSummarizerClient = this.clientDetails.type === summary_1.summarizerClientType;
|
|
268
|
+
this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
|
|
269
|
+
this._getClientId = () => context.clientId;
|
|
270
|
+
this._getAttachState = () => context.attachState;
|
|
271
|
+
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
272
|
+
if (context.getAbsoluteUrl === undefined) {
|
|
273
|
+
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
274
|
+
}
|
|
275
|
+
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
return context.getAbsoluteUrl(relativeUrl);
|
|
279
|
+
};
|
|
280
|
+
// TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
|
|
281
|
+
// customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
|
|
282
|
+
this.on("dirty", () => context.updateDirtyContainerState(true));
|
|
283
|
+
this.on("saved", () => context.updateDirtyContainerState(false));
|
|
284
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
285
|
+
this.disposeFn = disposeFn ?? closeFn;
|
|
286
|
+
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
287
|
+
this.closeFn = this.isSummarizerClient ? this.disposeFn : closeFn;
|
|
288
|
+
this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({
|
|
289
|
+
logger: this.logger,
|
|
290
|
+
namespace: "ContainerRuntime",
|
|
291
|
+
});
|
|
253
292
|
let loadSummaryNumber;
|
|
254
293
|
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
255
294
|
// get the values from the metadata blob.
|
|
256
295
|
if (existing) {
|
|
257
296
|
this.createContainerMetadata = {
|
|
258
|
-
createContainerRuntimeVersion: metadata
|
|
259
|
-
createContainerTimestamp: metadata
|
|
297
|
+
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
298
|
+
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
260
299
|
};
|
|
261
300
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
262
301
|
// the count is reset to 0.
|
|
263
|
-
loadSummaryNumber =
|
|
302
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
264
303
|
// Enabling the IdCompressor is a one-way operation and we only want to
|
|
265
304
|
// allow new containers to turn it on
|
|
266
|
-
this.idCompressorEnabled =
|
|
305
|
+
this.idCompressorEnabled = metadata?.idCompressorEnabled ?? false;
|
|
267
306
|
}
|
|
268
307
|
else {
|
|
269
308
|
this.createContainerMetadata = {
|
|
@@ -272,24 +311,27 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
272
311
|
};
|
|
273
312
|
loadSummaryNumber = 0;
|
|
274
313
|
this.idCompressorEnabled =
|
|
275
|
-
|
|
314
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.IdCompressorEnabled") ??
|
|
315
|
+
idCompressor !== undefined;
|
|
276
316
|
}
|
|
277
317
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
278
|
-
this.messageAtLastSummary = metadata
|
|
279
|
-
|
|
280
|
-
|
|
318
|
+
this.messageAtLastSummary = metadata?.message;
|
|
319
|
+
// Note that we only need to pull the *initial* connected state from the context.
|
|
320
|
+
// Later updates come through calls to setConnectionState.
|
|
321
|
+
this._connected = connected;
|
|
322
|
+
this.gcTombstoneEnforcementAllowed = (0, gc_1.shouldAllowGcTombstoneEnforcement)(metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */, this.runtimeOptions.gcOptions[gc_1.gcTombstoneGenerationOptionName] /* current */);
|
|
281
323
|
this.mc.logger.sendTelemetryEvent({
|
|
282
324
|
eventName: "GCFeatureMatrix",
|
|
283
|
-
metadataValue: JSON.stringify(metadata
|
|
325
|
+
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
284
326
|
inputs: JSON.stringify({
|
|
285
327
|
gcOptions_gcTombstoneGeneration: this.runtimeOptions.gcOptions[gc_1.gcTombstoneGenerationOptionName],
|
|
286
328
|
}),
|
|
287
329
|
});
|
|
288
|
-
this.telemetryDocumentId =
|
|
330
|
+
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? (0, uuid_1.v4)();
|
|
289
331
|
this.disableAttachReorder = this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder");
|
|
290
332
|
const disableChunking = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionChunkingDisabled");
|
|
291
333
|
const opGroupingManager = new opLifecycle_1.OpGroupingManager(this.groupedBatchingEnabled);
|
|
292
|
-
const opSplitter = new opLifecycle_1.OpSplitter(chunks, this.
|
|
334
|
+
const opSplitter = new opLifecycle_1.OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
293
335
|
this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(opSplitter, new opLifecycle_1.OpDecompressor(this.mc.logger), opGroupingManager);
|
|
294
336
|
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
295
337
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -308,9 +350,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
308
350
|
this.idCompressor = idCompressor;
|
|
309
351
|
}
|
|
310
352
|
this.maxConsecutiveReconnects =
|
|
311
|
-
|
|
353
|
+
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ??
|
|
354
|
+
this.defaultMaxConsecutiveReconnects;
|
|
312
355
|
if (runtimeOptions.flushMode === runtime_definitions_1.FlushModeExperimental.Async &&
|
|
313
|
-
|
|
356
|
+
supportedFeatures?.get("referenceSequenceNumbers") !== true) {
|
|
314
357
|
// The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
|
|
315
358
|
this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
|
|
316
359
|
this._flushMode = runtime_definitions_1.FlushMode.TurnBased;
|
|
@@ -318,8 +361,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
318
361
|
else {
|
|
319
362
|
this._flushMode = runtimeOptions.flushMode;
|
|
320
363
|
}
|
|
321
|
-
const pendingRuntimeState =
|
|
322
|
-
const maxSnapshotCacheDurationMs =
|
|
364
|
+
const pendingRuntimeState = pendingLocalState;
|
|
365
|
+
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
323
366
|
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
324
367
|
maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
325
368
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
@@ -330,27 +373,27 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
330
373
|
this.garbageCollector = gc_1.GarbageCollector.create({
|
|
331
374
|
runtime: this,
|
|
332
375
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
333
|
-
baseSnapshot
|
|
376
|
+
baseSnapshot,
|
|
334
377
|
baseLogger: this.mc.logger,
|
|
335
378
|
existing,
|
|
336
379
|
metadata,
|
|
337
380
|
createContainerMetadata: this.createContainerMetadata,
|
|
338
|
-
isSummarizerClient: this.
|
|
381
|
+
isSummarizerClient: this.isSummarizerClient,
|
|
339
382
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
340
|
-
getLastSummaryTimestampMs: () =>
|
|
383
|
+
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
341
384
|
readAndParseBlob: async (id) => (0, driver_utils_1.readAndParse)(this.storage, id),
|
|
342
385
|
// GC runs in summarizer client and needs access to the real (non-proxy) active information. The proxy
|
|
343
386
|
// delta manager would always return false for summarizer client.
|
|
344
387
|
activeConnection: () => this.innerDeltaManager.active,
|
|
345
388
|
});
|
|
346
389
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
347
|
-
this.summarizerNode = (0, summary_1.createRootSummarizerNodeWithGC)(telemetry_utils_1.
|
|
390
|
+
this.summarizerNode = (0, summary_1.createRootSummarizerNodeWithGC)((0, telemetry_utils_1.createChildLogger)({ logger: this.logger, namespace: "SummarizerNode" }),
|
|
348
391
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
349
392
|
async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
350
393
|
// Latest change sequence number, no changes since summary applied yet
|
|
351
394
|
loadedFromSequenceNumber,
|
|
352
395
|
// Summary reference sequence number, undefined if no summary yet
|
|
353
|
-
|
|
396
|
+
baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined, {
|
|
354
397
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
355
398
|
// a summary with an older protocol state.
|
|
356
399
|
canReuseHandle: false,
|
|
@@ -364,19 +407,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
364
407
|
async (fullGC) => this.getGCDataInternal(fullGC),
|
|
365
408
|
// Function to get the GC details from the base snapshot we loaded from.
|
|
366
409
|
async () => this.garbageCollector.getBaseGCDetails());
|
|
367
|
-
if (
|
|
368
|
-
this.summarizerNode.updateBaseSummaryState(
|
|
410
|
+
if (baseSnapshot) {
|
|
411
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
369
412
|
}
|
|
370
|
-
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(
|
|
413
|
+
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.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));
|
|
371
414
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
372
415
|
if (!this.disposed) {
|
|
373
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
416
|
+
this.submit({ type: ContainerMessageType.BlobAttach, contents: undefined }, undefined, {
|
|
374
417
|
localId,
|
|
375
418
|
blobId,
|
|
376
419
|
});
|
|
377
420
|
}
|
|
378
|
-
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState
|
|
379
|
-
this.scheduleManager = new scheduleManager_1.ScheduleManager(
|
|
421
|
+
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState?.pendingAttachmentBlobs, (error) => this.closeFn(error));
|
|
422
|
+
this.scheduleManager = new scheduleManager_1.ScheduleManager(this.innerDeltaManager, this, () => this.clientId, (0, telemetry_utils_1.createChildLogger)({ logger: this.logger, namespace: "ScheduleManager" }));
|
|
380
423
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
381
424
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
382
425
|
clientId: () => this.clientId,
|
|
@@ -384,7 +427,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
384
427
|
connected: () => this.connected,
|
|
385
428
|
reSubmit: this.reSubmit.bind(this),
|
|
386
429
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
387
|
-
|
|
430
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
431
|
+
}, pendingRuntimeState?.pending, this.logger);
|
|
388
432
|
const disableCompression = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
|
|
389
433
|
const compressionOptions = disableCompression === true
|
|
390
434
|
? {
|
|
@@ -393,10 +437,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
393
437
|
}
|
|
394
438
|
: runtimeOptions.compressionOptions;
|
|
395
439
|
const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
|
|
440
|
+
const legacySendBatchFn = (0, exports.makeLegacySendBatchFn)(this.submitFn, this.innerDeltaManager);
|
|
396
441
|
this.outbox = new opLifecycle_1.Outbox({
|
|
397
442
|
shouldSend: () => this.canSendOps(),
|
|
398
443
|
pendingStateManager: this.pendingStateManager,
|
|
399
|
-
|
|
444
|
+
submitBatchFn: this.submitBatchFn,
|
|
445
|
+
legacySendBatchFn,
|
|
400
446
|
compressor: new opLifecycle_1.OpCompressor(this.mc.logger),
|
|
401
447
|
splitter: opSplitter,
|
|
402
448
|
config: {
|
|
@@ -415,44 +461,53 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
415
461
|
opReentrancy: () => this.ensureNoDataModelChangesCalls > 0,
|
|
416
462
|
closeContainer: this.closeFn,
|
|
417
463
|
});
|
|
418
|
-
this.
|
|
464
|
+
this._quorum = quorum;
|
|
465
|
+
this._quorum.on("removeMember", (clientId) => {
|
|
419
466
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
420
467
|
});
|
|
468
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
469
|
+
this._audience = audience;
|
|
421
470
|
this.summaryStateUpdateMethod = this.mc.config.getString("Fluid.ContainerRuntime.Test.SummaryStateUpdateMethodV2");
|
|
422
471
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
423
|
-
this.closeSummarizerDelayMs = closeSummarizerDelayOverride
|
|
472
|
+
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
424
473
|
this.validateSummaryBeforeUpload =
|
|
425
|
-
|
|
474
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.Test.ValidateSummaryBeforeUpload") ??
|
|
475
|
+
false;
|
|
426
476
|
this.summaryCollection = new summary_1.SummaryCollection(this.deltaManager, this.logger);
|
|
427
477
|
this.dirtyContainer =
|
|
428
|
-
this.
|
|
478
|
+
this.attachState !== container_definitions_1.AttachState.Attached ||
|
|
429
479
|
this.pendingStateManager.hasPendingMessages();
|
|
430
|
-
|
|
480
|
+
context.updateDirtyContainerState(this.dirtyContainer);
|
|
431
481
|
if (this.summariesDisabled) {
|
|
432
482
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
433
483
|
}
|
|
434
484
|
else {
|
|
435
|
-
const orderedClientLogger =
|
|
436
|
-
|
|
437
|
-
|
|
485
|
+
const orderedClientLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
486
|
+
logger: this.logger,
|
|
487
|
+
namespace: "OrderedClientElection",
|
|
488
|
+
});
|
|
489
|
+
const orderedClientCollection = new summary_1.OrderedClientCollection(orderedClientLogger, this.innerDeltaManager, this._quorum);
|
|
490
|
+
const orderedClientElectionForSummarizer = new summary_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber, summary_1.SummarizerClientElection.isClientEligible);
|
|
438
491
|
this.summarizerClientElection = new summary_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
|
|
439
|
-
if (this.
|
|
492
|
+
if (this.isSummarizerClient) {
|
|
440
493
|
this._summarizer = new summary_1.Summarizer(this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => summary_1.RunWhileConnectedCoordinator.create(runtime,
|
|
441
494
|
// Summarization runs in summarizer client and needs access to the real (non-proxy) active
|
|
442
495
|
// information. The proxy delta manager would always return false for summarizer client.
|
|
443
496
|
() => this.innerDeltaManager.active));
|
|
444
497
|
}
|
|
445
|
-
else if (summary_1.SummarizerClientElection.clientDetailsPermitElection(this.
|
|
498
|
+
else if (summary_1.SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
|
|
446
499
|
// Only create a SummaryManager and SummarizerClientElection
|
|
447
500
|
// if summaries are enabled and we are not the summarizer client.
|
|
448
501
|
const defaultAction = () => {
|
|
449
502
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
450
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
503
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
451
504
|
// unregister default to no log on every op after falling behind
|
|
452
505
|
// and register summary ack handler to re-register this handler
|
|
453
506
|
// after successful summary
|
|
454
507
|
this.summaryCollection.once(protocol_definitions_1.MessageType.SummaryAck, () => {
|
|
455
|
-
this.logger.sendTelemetryEvent({
|
|
508
|
+
this.mc.logger.sendTelemetryEvent({
|
|
509
|
+
eventName: "SummaryStatus:CaughtUp",
|
|
510
|
+
});
|
|
456
511
|
// we've caught up, so re-register the default action to monitor for
|
|
457
512
|
// falling behind, and unregister ourself
|
|
458
513
|
this.summaryCollection.on("default", defaultAction);
|
|
@@ -463,7 +518,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
463
518
|
this.summaryCollection.on("default", defaultAction);
|
|
464
519
|
// Create the SummaryManager and mark the initial state
|
|
465
520
|
this.summaryManager = new summary_1.SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
466
|
-
this.summaryCollection, this.logger, this.formRequestSummarizerFn(
|
|
521
|
+
this.summaryCollection, this.logger, this.formRequestSummarizerFn(loader), new throttler_1.Throttler(60 * 1000, // 60 sec delay window
|
|
467
522
|
30 * 1000, // 30 sec max delay
|
|
468
523
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
469
524
|
(0, throttler_1.formExponentialFn)({ coefficient: 20, initialDelay: 0 })), {
|
|
@@ -493,8 +548,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
493
548
|
this.replayPendingStates();
|
|
494
549
|
});
|
|
495
550
|
// logging hardware telemetry
|
|
496
|
-
logger.sendTelemetryEvent(
|
|
497
|
-
|
|
551
|
+
logger.sendTelemetryEvent({
|
|
552
|
+
eventName: "DeviceSpec",
|
|
553
|
+
...getDeviceSpec(),
|
|
554
|
+
});
|
|
555
|
+
this.mc.logger.sendTelemetryEvent({
|
|
556
|
+
eventName: "ContainerLoadStats",
|
|
557
|
+
...this.createContainerMetadata,
|
|
558
|
+
...this.dataStores.containerLoadStats,
|
|
559
|
+
summaryNumber: loadSummaryNumber,
|
|
560
|
+
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
561
|
+
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
562
|
+
gcVersion: metadata?.gcFeature,
|
|
563
|
+
options: JSON.stringify(runtimeOptions),
|
|
564
|
+
featureGates: JSON.stringify({
|
|
498
565
|
disableCompression,
|
|
499
566
|
disableOpReentryCheck,
|
|
500
567
|
disableChunking,
|
|
@@ -503,17 +570,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
503
570
|
idCompressorEnabled: this.idCompressorEnabled,
|
|
504
571
|
summaryStateUpdateMethod: this.summaryStateUpdateMethod,
|
|
505
572
|
closeSummarizerDelayOverride,
|
|
506
|
-
}),
|
|
507
|
-
|
|
573
|
+
}),
|
|
574
|
+
telemetryDocumentId: this.telemetryDocumentId,
|
|
575
|
+
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
576
|
+
});
|
|
577
|
+
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.clientId, this.deltaManager, this.logger);
|
|
508
578
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
509
579
|
this.entryPoint = new core_utils_1.LazyPromise(async () => {
|
|
510
|
-
if (this.
|
|
580
|
+
if (this.isSummarizerClient) {
|
|
511
581
|
(0, common_utils_1.assert)(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
512
582
|
return this._summarizer;
|
|
513
583
|
}
|
|
514
|
-
return initializeEntryPoint
|
|
584
|
+
return initializeEntryPoint?.(this);
|
|
515
585
|
});
|
|
516
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
589
|
+
*/
|
|
517
590
|
get IFluidRouter() {
|
|
518
591
|
return this;
|
|
519
592
|
}
|
|
@@ -559,22 +632,24 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
559
632
|
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
560
633
|
*/
|
|
561
634
|
static async loadRuntime(params) {
|
|
562
|
-
var _a, _b, _c, _d, _e, _f;
|
|
563
635
|
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, initializeEntryPoint, } = params;
|
|
564
636
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
565
637
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
566
638
|
const backCompatContext = context;
|
|
567
|
-
const passLogger =
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
639
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
640
|
+
new telemetry_utils_1.TaggedLoggerAdapter(backCompatContext.logger);
|
|
641
|
+
const logger = (0, telemetry_utils_1.createChildLogger)({
|
|
642
|
+
logger: passLogger,
|
|
643
|
+
properties: {
|
|
644
|
+
all: {
|
|
645
|
+
runtimeVersion: packageVersion_1.pkgVersion,
|
|
646
|
+
},
|
|
571
647
|
},
|
|
572
648
|
});
|
|
573
649
|
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
574
650
|
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
575
651
|
const tryFetchBlob = async (blobName) => {
|
|
576
|
-
|
|
577
|
-
const blobId = (_a = context.baseSnapshot) === null || _a === void 0 ? void 0 : _a.blobs[blobName];
|
|
652
|
+
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
578
653
|
if (context.baseSnapshot && blobId) {
|
|
579
654
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
580
655
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
@@ -589,16 +664,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
589
664
|
tryFetchBlob(summary_1.aliasBlobName),
|
|
590
665
|
tryFetchBlob(summary_1.idCompressorBlobName),
|
|
591
666
|
]);
|
|
592
|
-
const loadExisting = existing === true || context.existing === true;
|
|
593
667
|
// read snapshot blobs needed for BlobManager to load
|
|
594
|
-
const blobManagerSnapshot = await blobManager_1.BlobManager.load(
|
|
668
|
+
const blobManagerSnapshot = await blobManager_1.BlobManager.load(context.baseSnapshot?.trees[summary_1.blobsTreeName], async (id) => {
|
|
595
669
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
596
670
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
597
671
|
(0, common_utils_1.assert)(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
598
672
|
return (0, driver_utils_1.readAndParse)(context.storage, id);
|
|
599
673
|
});
|
|
600
674
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
601
|
-
const runtimeSequenceNumber =
|
|
675
|
+
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
602
676
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
603
677
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
604
678
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
@@ -613,13 +687,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
613
687
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
614
688
|
}
|
|
615
689
|
else {
|
|
616
|
-
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
617
690
|
context.closeFn(error);
|
|
618
|
-
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
619
691
|
}
|
|
620
692
|
}
|
|
621
693
|
}
|
|
622
|
-
const idCompressorEnabled =
|
|
694
|
+
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
623
695
|
let idCompressor;
|
|
624
696
|
if (idCompressorEnabled) {
|
|
625
697
|
const { IdCompressor, createSessionId } = await Promise.resolve().then(() => __importStar(require("./id-compressor")));
|
|
@@ -628,7 +700,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
628
700
|
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
629
701
|
: new IdCompressor(createSessionId(), logger);
|
|
630
702
|
}
|
|
631
|
-
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks
|
|
703
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
632
704
|
summaryOptions,
|
|
633
705
|
gcOptions,
|
|
634
706
|
loadSequenceNumberVerification,
|
|
@@ -639,7 +711,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
639
711
|
enableRuntimeIdCompressor,
|
|
640
712
|
enableOpReentryCheck,
|
|
641
713
|
enableGroupedBatching,
|
|
642
|
-
}, containerScope, logger,
|
|
714
|
+
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, requestHandler, undefined, // summaryConfiguration
|
|
643
715
|
initializeEntryPoint);
|
|
644
716
|
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
645
717
|
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
@@ -648,38 +720,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
648
720
|
await runtime.initializeBaseState();
|
|
649
721
|
return runtime;
|
|
650
722
|
}
|
|
651
|
-
get options() {
|
|
652
|
-
return this.context.options;
|
|
653
|
-
}
|
|
654
723
|
get clientId() {
|
|
655
|
-
return this.
|
|
656
|
-
}
|
|
657
|
-
get clientDetails() {
|
|
658
|
-
return this.context.clientDetails;
|
|
724
|
+
return this._getClientId();
|
|
659
725
|
}
|
|
660
726
|
get storage() {
|
|
661
727
|
return this._storage;
|
|
662
728
|
}
|
|
729
|
+
/** @deprecated - The functionality is no longer exposed publicly */
|
|
663
730
|
get reSubmitFn() {
|
|
664
|
-
|
|
665
|
-
return this.reSubmitCore;
|
|
666
|
-
}
|
|
667
|
-
get disposeFn() {
|
|
668
|
-
var _a;
|
|
669
|
-
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
670
|
-
return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
|
|
671
|
-
}
|
|
672
|
-
get closeFn() {
|
|
673
|
-
if (this._summarizer !== undefined) {
|
|
674
|
-
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
675
|
-
return this.disposeFn;
|
|
676
|
-
}
|
|
677
|
-
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
678
|
-
return (error) => {
|
|
679
|
-
var _a, _b;
|
|
680
|
-
this.context.closeFn(error);
|
|
681
|
-
(_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
682
|
-
};
|
|
731
|
+
return (type, contents, localOpMetadata, opMetadata) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
|
|
683
732
|
}
|
|
684
733
|
get flushMode() {
|
|
685
734
|
return this._flushMode;
|
|
@@ -691,7 +740,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
691
740
|
return this.registry;
|
|
692
741
|
}
|
|
693
742
|
get attachState() {
|
|
694
|
-
return this.
|
|
743
|
+
return this._getAttachState();
|
|
695
744
|
}
|
|
696
745
|
get IFluidHandleContext() {
|
|
697
746
|
return this.handleContext;
|
|
@@ -718,8 +767,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
718
767
|
}
|
|
719
768
|
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
720
769
|
get summarizerClientId() {
|
|
721
|
-
|
|
722
|
-
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
770
|
+
return this.summarizerClientElection?.electedClientId;
|
|
723
771
|
}
|
|
724
772
|
get disposed() {
|
|
725
773
|
return this._disposed;
|
|
@@ -756,12 +804,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
756
804
|
await this.garbageCollector.initializeBaseState();
|
|
757
805
|
}
|
|
758
806
|
dispose(error) {
|
|
759
|
-
var _a;
|
|
760
807
|
if (this._disposed) {
|
|
761
808
|
return;
|
|
762
809
|
}
|
|
763
810
|
this._disposed = true;
|
|
764
|
-
this.logger.sendTelemetryEvent({
|
|
811
|
+
this.mc.logger.sendTelemetryEvent({
|
|
765
812
|
eventName: "ContainerRuntimeDisposed",
|
|
766
813
|
isDirty: this.isDirty,
|
|
767
814
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
@@ -771,7 +818,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
771
818
|
this.summaryManager.dispose();
|
|
772
819
|
}
|
|
773
820
|
this.garbageCollector.dispose();
|
|
774
|
-
|
|
821
|
+
this._summarizer?.dispose();
|
|
775
822
|
this.dataStores.dispose();
|
|
776
823
|
this.pendingStateManager.dispose();
|
|
777
824
|
this.emit("dispose");
|
|
@@ -780,6 +827,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
780
827
|
/**
|
|
781
828
|
* Notifies this object about the request made to the container.
|
|
782
829
|
* @param request - Request made to the handler.
|
|
830
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
783
831
|
*/
|
|
784
832
|
async request(request) {
|
|
785
833
|
try {
|
|
@@ -846,19 +894,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
846
894
|
return this.entryPoint;
|
|
847
895
|
}
|
|
848
896
|
internalId(maybeAlias) {
|
|
849
|
-
|
|
850
|
-
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
897
|
+
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
851
898
|
}
|
|
852
899
|
async getDataStoreFromRequest(id, request) {
|
|
853
|
-
var _a, _b, _c;
|
|
854
900
|
const headerData = {};
|
|
855
|
-
if (typeof
|
|
901
|
+
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
856
902
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
857
903
|
}
|
|
858
|
-
if (typeof
|
|
904
|
+
if (typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean") {
|
|
859
905
|
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
860
906
|
}
|
|
861
|
-
if (typeof
|
|
907
|
+
if (typeof request.headers?.[exports.AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
862
908
|
headerData.allowTombstone = request.headers[exports.AllowTombstoneRequestHeaderKey];
|
|
863
909
|
}
|
|
864
910
|
await this.dataStores.waitIfPendingAlias(id);
|
|
@@ -868,22 +914,27 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
868
914
|
// Remove query params, leading and trailing slashes from the url. This is done to make sure the format is
|
|
869
915
|
// the same as GC nodes id.
|
|
870
916
|
const urlWithoutQuery = (0, gc_1.trimLeadingAndTrailingSlashes)(request.url.split("?")[0]);
|
|
871
|
-
this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request
|
|
917
|
+
this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request?.headers);
|
|
872
918
|
return dataStoreChannel;
|
|
873
919
|
}
|
|
874
920
|
/** Adds the container's metadata to the given summary tree. */
|
|
875
921
|
addMetadataToSummary(summaryTree) {
|
|
876
|
-
|
|
877
|
-
|
|
922
|
+
const metadata = {
|
|
923
|
+
...this.createContainerMetadata,
|
|
878
924
|
// Increment the summary number for the next summary that will be generated.
|
|
879
|
-
summaryNumber: this.nextSummaryNumber++,
|
|
925
|
+
summaryNumber: this.nextSummaryNumber++,
|
|
926
|
+
summaryFormatVersion: 1,
|
|
927
|
+
...this.garbageCollector.getMetadata(),
|
|
880
928
|
// The last message processed at the time of summary. If there are no new messages, use the message from the
|
|
881
929
|
// last summary.
|
|
882
|
-
message: (
|
|
930
|
+
message: (0, summary_1.extractSummaryMetadataMessage)(this.deltaManager.lastMessage) ??
|
|
931
|
+
this.messageAtLastSummary,
|
|
932
|
+
telemetryDocumentId: this.telemetryDocumentId,
|
|
933
|
+
idCompressorEnabled: this.idCompressorEnabled ? true : undefined,
|
|
934
|
+
};
|
|
883
935
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.metadataBlobName, JSON.stringify(metadata));
|
|
884
936
|
}
|
|
885
937
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
886
|
-
var _a;
|
|
887
938
|
this.addMetadataToSummary(summaryTree);
|
|
888
939
|
if (this.idCompressorEnabled) {
|
|
889
940
|
(0, common_utils_1.assert)(this.idCompressor !== undefined, 0x67a /* IdCompressor should be defined if enabled */);
|
|
@@ -899,7 +950,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
899
950
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
900
951
|
}
|
|
901
952
|
if (this.summarizerClientElection) {
|
|
902
|
-
const electedSummarizerContent = JSON.stringify(
|
|
953
|
+
const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
|
|
903
954
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.electedSummarizerBlobName, electedSummarizerContent);
|
|
904
955
|
}
|
|
905
956
|
const blobManagerSummary = this.blobManager.summarize();
|
|
@@ -946,7 +997,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
946
997
|
// in their own batches before the originating batch is sent.
|
|
947
998
|
// Therefore, receiving them while attempting to send the originating batch
|
|
948
999
|
// does not mean that the container is making any progress.
|
|
949
|
-
if (
|
|
1000
|
+
if (message?.type !== ContainerMessageType.ChunkedOp) {
|
|
950
1001
|
this.consecutiveReconnects = 0;
|
|
951
1002
|
}
|
|
952
1003
|
}
|
|
@@ -996,9 +1047,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
996
1047
|
*/
|
|
997
1048
|
parseOpContent(serializedContent) {
|
|
998
1049
|
(0, common_utils_1.assert)(serializedContent !== undefined, 0x6d5 /* content must be defined */);
|
|
999
|
-
const
|
|
1000
|
-
(0, common_utils_1.assert)(
|
|
1001
|
-
return { type
|
|
1050
|
+
const { type, contents } = JSON.parse(serializedContent);
|
|
1051
|
+
(0, common_utils_1.assert)(type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1052
|
+
return { type, contents };
|
|
1002
1053
|
}
|
|
1003
1054
|
async applyStashedOp(op) {
|
|
1004
1055
|
// Need to parse from string for back-compat
|
|
@@ -1057,6 +1108,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1057
1108
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1058
1109
|
const changeOfState = this._connected !== connected;
|
|
1059
1110
|
const reconnection = changeOfState && !connected;
|
|
1111
|
+
// We need to flush the ops currently collected by Outbox to preserve original order.
|
|
1112
|
+
// This flush NEEDS to happen before we set the ContainerRuntime to "connected".
|
|
1113
|
+
// We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
|
|
1114
|
+
if (changeOfState && connected) {
|
|
1115
|
+
this.flush();
|
|
1116
|
+
}
|
|
1060
1117
|
this._connected = connected;
|
|
1061
1118
|
if (!connected) {
|
|
1062
1119
|
this._perfSignalData.signalsLost = 0;
|
|
@@ -1095,13 +1152,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1095
1152
|
// or something different, like a system message.
|
|
1096
1153
|
const runtimeMessage = messageArg.type === protocol_definitions_1.MessageType.Operation;
|
|
1097
1154
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1098
|
-
const messageCopy =
|
|
1155
|
+
const messageCopy = { ...messageArg };
|
|
1099
1156
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1100
1157
|
this.processCore(message, local, runtimeMessage);
|
|
1101
1158
|
}
|
|
1102
1159
|
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
1162
|
+
* @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
|
|
1163
|
+
* @param local - Did this client send the op?
|
|
1164
|
+
* @param runtimeMessage - Does this appear like a current ContainerRuntimeMessage? If true, certain validation will occur.
|
|
1165
|
+
*/
|
|
1103
1166
|
processCore(message, local, runtimeMessage) {
|
|
1104
|
-
var _a;
|
|
1105
1167
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1106
1168
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1107
1169
|
// messages once a batch has been fully processed.
|
|
@@ -1146,16 +1208,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1146
1208
|
local,
|
|
1147
1209
|
type: message.type,
|
|
1148
1210
|
contentType: typeof message.contents,
|
|
1149
|
-
batch:
|
|
1211
|
+
batch: message.metadata?.batch,
|
|
1150
1212
|
compression: message.compression,
|
|
1151
1213
|
});
|
|
1152
1214
|
this.closeFn(error);
|
|
1153
1215
|
throw error;
|
|
1154
1216
|
}
|
|
1155
1217
|
}
|
|
1156
|
-
|
|
1157
|
-
this.emit("op", message, runtimeMessage);
|
|
1158
|
-
}
|
|
1218
|
+
this.emit("op", message, runtimeMessage);
|
|
1159
1219
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1160
1220
|
if (local) {
|
|
1161
1221
|
// If we have processed a local op, this means that the container is
|
|
@@ -1178,7 +1238,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1178
1238
|
*/
|
|
1179
1239
|
sendSignalTelemetryEvent(clientSignalSequenceNumber) {
|
|
1180
1240
|
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
1181
|
-
this.logger.sendPerformanceEvent({
|
|
1241
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1182
1242
|
eventName: "SignalLatency",
|
|
1183
1243
|
duration,
|
|
1184
1244
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -1201,7 +1261,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1201
1261
|
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1202
1262
|
this._perfSignalData.signalsLost++;
|
|
1203
1263
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1204
|
-
this.logger.sendErrorEvent({
|
|
1264
|
+
this.mc.logger.sendErrorEvent({
|
|
1205
1265
|
eventName: "SignalLost",
|
|
1206
1266
|
type: envelope.contents.type,
|
|
1207
1267
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -1225,6 +1285,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1225
1285
|
}
|
|
1226
1286
|
this.dataStores.processSignal(envelope.address, transformed, local);
|
|
1227
1287
|
}
|
|
1288
|
+
/**
|
|
1289
|
+
* Returns the runtime of the data store.
|
|
1290
|
+
* @param id - Id supplied during creating the data store.
|
|
1291
|
+
* @param wait - True if you want to wait for it.
|
|
1292
|
+
* @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1293
|
+
*/
|
|
1228
1294
|
async getRootDataStore(id, wait = true) {
|
|
1229
1295
|
return this.getRootDataStoreChannel(id, wait);
|
|
1230
1296
|
}
|
|
@@ -1286,9 +1352,25 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1286
1352
|
}
|
|
1287
1353
|
return result;
|
|
1288
1354
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1355
|
+
/**
|
|
1356
|
+
* Returns the aliased data store's entryPoint, given the alias.
|
|
1357
|
+
* @param alias - The alias for the data store.
|
|
1358
|
+
* @returns - The data store's entry point (IFluidHandle) if it exists and is aliased. Returns undefined if no
|
|
1359
|
+
* data store has been assigned the given alias.
|
|
1360
|
+
*/
|
|
1361
|
+
async getAliasedDataStoreEntryPoint(alias) {
|
|
1362
|
+
await this.dataStores.waitIfPendingAlias(alias);
|
|
1363
|
+
const internalId = this.internalId(alias);
|
|
1364
|
+
const context = await this.dataStores.getDataStoreIfAvailable(internalId, { wait: false });
|
|
1365
|
+
// If the data store is not available or not an alias, return undefined.
|
|
1366
|
+
if (context === undefined || !(await context.isRoot())) {
|
|
1367
|
+
return undefined;
|
|
1368
|
+
}
|
|
1369
|
+
const channel = await context.realize();
|
|
1370
|
+
if (channel.entryPoint === undefined) {
|
|
1371
|
+
throw new container_utils_1.UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
|
|
1372
|
+
}
|
|
1373
|
+
return channel.entryPoint;
|
|
1292
1374
|
}
|
|
1293
1375
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1294
1376
|
if (rootDataStoreId.includes("/")) {
|
|
@@ -1299,16 +1381,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1299
1381
|
createDetachedDataStore(pkg) {
|
|
1300
1382
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1301
1383
|
}
|
|
1302
|
-
async
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
.
|
|
1306
|
-
|
|
1384
|
+
async createDataStore(pkg) {
|
|
1385
|
+
const id = (0, uuid_1.v4)();
|
|
1386
|
+
return (0, dataStore_1.channelToDataStore)(await this.dataStores
|
|
1387
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id)
|
|
1388
|
+
.realize(), id, this, this.dataStores, this.mc.logger);
|
|
1307
1389
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1390
|
+
/**
|
|
1391
|
+
* @deprecated 0.16 Issue #1537, #3631
|
|
1392
|
+
* @internal
|
|
1393
|
+
*/
|
|
1394
|
+
async _createDataStoreWithProps(pkg, props, id = (0, uuid_1.v4)()) {
|
|
1395
|
+
return (0, dataStore_1.channelToDataStore)(await this.dataStores
|
|
1310
1396
|
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1311
|
-
.realize();
|
|
1397
|
+
.realize(), id, this, this.dataStores, this.mc.logger);
|
|
1312
1398
|
}
|
|
1313
1399
|
canSendOps() {
|
|
1314
1400
|
// Note that the real (non-proxy) delta manager is needed here to get the readonly info. This is because
|
|
@@ -1322,11 +1408,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1322
1408
|
return this.flushMode !== runtime_definitions_1.FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
1323
1409
|
}
|
|
1324
1410
|
getQuorum() {
|
|
1325
|
-
return this.
|
|
1411
|
+
return this._quorum;
|
|
1326
1412
|
}
|
|
1327
1413
|
getAudience() {
|
|
1328
|
-
|
|
1329
|
-
return this.context.audience;
|
|
1414
|
+
return this._audience;
|
|
1330
1415
|
}
|
|
1331
1416
|
/**
|
|
1332
1417
|
* Returns true of container is dirty, i.e. there are some pending local changes that
|
|
@@ -1335,7 +1420,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1335
1420
|
get isDirty() {
|
|
1336
1421
|
return this.dirtyContainer;
|
|
1337
1422
|
}
|
|
1338
|
-
isContainerMessageDirtyable(type, contents) {
|
|
1423
|
+
isContainerMessageDirtyable({ type, contents }) {
|
|
1339
1424
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
1340
1425
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
1341
1426
|
if (type === ContainerMessageType.Attach) {
|
|
@@ -1375,11 +1460,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1375
1460
|
submitSignal(type, content) {
|
|
1376
1461
|
this.verifyNotClosed();
|
|
1377
1462
|
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
1378
|
-
return this.
|
|
1463
|
+
return this.submitSignalFn(envelope);
|
|
1379
1464
|
}
|
|
1380
1465
|
submitDataStoreSignal(address, type, content) {
|
|
1381
1466
|
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
1382
|
-
return this.
|
|
1467
|
+
return this.submitSignalFn(envelope);
|
|
1383
1468
|
}
|
|
1384
1469
|
setAttachState(attachState) {
|
|
1385
1470
|
if (attachState === container_definitions_1.AttachState.Attaching) {
|
|
@@ -1412,22 +1497,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1412
1497
|
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
|
|
1413
1498
|
return summarizeResult.summary;
|
|
1414
1499
|
}
|
|
1415
|
-
async getAbsoluteUrl(relativeUrl) {
|
|
1416
|
-
if (this.context.getAbsoluteUrl === undefined) {
|
|
1417
|
-
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
1418
|
-
}
|
|
1419
|
-
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
1420
|
-
return undefined;
|
|
1421
|
-
}
|
|
1422
|
-
return this.context.getAbsoluteUrl(relativeUrl);
|
|
1423
|
-
}
|
|
1424
1500
|
async summarizeInternal(fullTree, trackState, telemetryContext) {
|
|
1425
1501
|
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
1426
1502
|
// Wrap data store summaries in .channels subtree.
|
|
1427
1503
|
(0, summary_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1428
1504
|
const pathPartsForChildren = [runtime_definitions_1.channelsTreeName];
|
|
1429
1505
|
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
1430
|
-
return
|
|
1506
|
+
return {
|
|
1507
|
+
...summarizeResult,
|
|
1508
|
+
id: "",
|
|
1509
|
+
pathPartsForChildren,
|
|
1510
|
+
};
|
|
1431
1511
|
}
|
|
1432
1512
|
/**
|
|
1433
1513
|
* Returns a summary of the runtime at the current sequence number.
|
|
@@ -1445,16 +1525,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1445
1525
|
runSweep,
|
|
1446
1526
|
});
|
|
1447
1527
|
try {
|
|
1448
|
-
let gcStats;
|
|
1449
1528
|
if (runGC) {
|
|
1450
|
-
|
|
1529
|
+
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC }, telemetryContext);
|
|
1451
1530
|
}
|
|
1452
1531
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1453
1532
|
(0, common_utils_1.assert)(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1454
|
-
return { stats, summary
|
|
1533
|
+
return { stats, summary };
|
|
1455
1534
|
}
|
|
1456
1535
|
finally {
|
|
1457
|
-
this.logger.sendTelemetryEvent({
|
|
1536
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1458
1537
|
eventName: "SummarizeTelemetry",
|
|
1459
1538
|
details: telemetryContext.serialize(),
|
|
1460
1539
|
});
|
|
@@ -1536,21 +1615,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1536
1615
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
1537
1616
|
*/
|
|
1538
1617
|
getCurrentReferenceTimestampMs() {
|
|
1539
|
-
var _a, _b, _c;
|
|
1540
1618
|
// Use the timestamp of the last message seen by this client as that is server generated. If no messages have
|
|
1541
1619
|
// been processed, use the timestamp of the message from the last summary.
|
|
1542
|
-
return
|
|
1620
|
+
return this.deltaManager.lastMessage?.timestamp ?? this.messageAtLastSummary?.timestamp;
|
|
1543
1621
|
}
|
|
1544
1622
|
/**
|
|
1545
1623
|
* Returns the type of the GC node. Currently, there are nodes that belong to the root ("/"), data stores or
|
|
1546
1624
|
* blob manager.
|
|
1547
1625
|
*/
|
|
1548
1626
|
getNodeType(nodePath) {
|
|
1549
|
-
var _a;
|
|
1550
1627
|
if (this.isBlobPath(nodePath)) {
|
|
1551
1628
|
return gc_1.GCNodeType.Blob;
|
|
1552
1629
|
}
|
|
1553
|
-
return
|
|
1630
|
+
return this.dataStores.getGCNodeType(nodePath) ?? gc_1.GCNodeType.Other;
|
|
1554
1631
|
}
|
|
1555
1632
|
/**
|
|
1556
1633
|
* Called by GC to retrieve the package path of the node with the given path. The node should belong to a
|
|
@@ -1621,18 +1698,24 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1621
1698
|
* @param options - options controlling how the summary is generated or submitted
|
|
1622
1699
|
*/
|
|
1623
1700
|
async submitSummary(options) {
|
|
1624
|
-
var _a, _b, _c;
|
|
1625
1701
|
const { fullTree = false, refreshLatestAck, summaryLogger } = options;
|
|
1626
1702
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
1627
1703
|
// use it for all events logged during this summary.
|
|
1628
1704
|
const summaryNumber = this.nextSummaryNumber;
|
|
1629
|
-
const summaryNumberLogger = telemetry_utils_1.
|
|
1630
|
-
|
|
1705
|
+
const summaryNumberLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
1706
|
+
logger: summaryLogger,
|
|
1707
|
+
properties: {
|
|
1708
|
+
all: { summaryNumber },
|
|
1709
|
+
},
|
|
1631
1710
|
});
|
|
1632
1711
|
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1633
1712
|
let latestSnapshotVersionId;
|
|
1634
1713
|
if (refreshLatestAck) {
|
|
1635
|
-
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.
|
|
1714
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer((0, telemetry_utils_1.createChildLogger)({
|
|
1715
|
+
logger: summaryNumberLogger,
|
|
1716
|
+
namespace: undefined,
|
|
1717
|
+
properties: { all: { safeSummary: true } },
|
|
1718
|
+
}));
|
|
1636
1719
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1637
1720
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1638
1721
|
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
@@ -1652,7 +1735,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1652
1735
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
|
|
1653
1736
|
// Helper function to check whether we should still continue between each async step.
|
|
1654
1737
|
const checkContinue = () => {
|
|
1655
|
-
var _a;
|
|
1656
1738
|
// Do not check for loss of connectivity directly! Instead leave it up to
|
|
1657
1739
|
// RunWhileConnectedCoordinator to control policy in a single place.
|
|
1658
1740
|
// This will allow easier change of design if we chose to. For example, we may chose to allow
|
|
@@ -1675,7 +1757,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1675
1757
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1676
1758
|
};
|
|
1677
1759
|
}
|
|
1678
|
-
(0, common_utils_1.assert)(summaryRefSeqNum ===
|
|
1760
|
+
(0, common_utils_1.assert)(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber, 0x395 /* it's one and the same thing */);
|
|
1679
1761
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1680
1762
|
return {
|
|
1681
1763
|
continue: false,
|
|
@@ -1726,7 +1808,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1726
1808
|
const gcSummaryTreeStats = summaryTree.tree[runtime_definitions_1.gcTreeKey]
|
|
1727
1809
|
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[runtime_definitions_1.gcTreeKey])
|
|
1728
1810
|
: undefined;
|
|
1729
|
-
const summaryStats =
|
|
1811
|
+
const summaryStats = {
|
|
1812
|
+
dataStoreCount: this.dataStores.size,
|
|
1813
|
+
summarizedDataStoreCount: this.dataStores.size - handleCount,
|
|
1814
|
+
gcStateUpdatedDataStoreCount: this.garbageCollector.updatedDSCountSinceLastSummary,
|
|
1815
|
+
gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
|
|
1816
|
+
gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
|
|
1817
|
+
summaryNumber,
|
|
1818
|
+
...partialStats,
|
|
1819
|
+
};
|
|
1730
1820
|
const generateSummaryData = {
|
|
1731
1821
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1732
1822
|
minimumSequenceNumber,
|
|
@@ -1740,14 +1830,14 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1740
1830
|
if (this.validateSummaryBeforeUpload) {
|
|
1741
1831
|
const validateResult = this.summarizerNode.validateSummary();
|
|
1742
1832
|
if (!validateResult.success) {
|
|
1743
|
-
const { success
|
|
1744
|
-
const error = new summary_1.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds,
|
|
1745
|
-
return
|
|
1833
|
+
const { success, ...loggingProps } = validateResult;
|
|
1834
|
+
const error = new summary_1.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
1835
|
+
return { stage: "base", ...generateSummaryData, error };
|
|
1746
1836
|
}
|
|
1747
1837
|
}
|
|
1748
1838
|
continueResult = checkContinue();
|
|
1749
1839
|
if (!continueResult.continue) {
|
|
1750
|
-
return
|
|
1840
|
+
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
1751
1841
|
}
|
|
1752
1842
|
// It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
|
|
1753
1843
|
// summary. So if the previous summarizer closes just after submitting the summary and before
|
|
@@ -1755,7 +1845,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1755
1845
|
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1756
1846
|
// the one fetched from storage as parent as that is the latest.
|
|
1757
1847
|
let summaryContext;
|
|
1758
|
-
if (
|
|
1848
|
+
if (lastAck?.summaryAck.contents.handle !== latestSnapshotVersionId &&
|
|
1759
1849
|
latestSnapshotVersionId !== undefined) {
|
|
1760
1850
|
summaryContext = {
|
|
1761
1851
|
proposalHandle: undefined,
|
|
@@ -1766,7 +1856,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1766
1856
|
else if (lastAck === undefined) {
|
|
1767
1857
|
summaryContext = {
|
|
1768
1858
|
proposalHandle: undefined,
|
|
1769
|
-
ackHandle:
|
|
1859
|
+
ackHandle: this.loadedFromVersionId,
|
|
1770
1860
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1771
1861
|
};
|
|
1772
1862
|
}
|
|
@@ -1782,7 +1872,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1782
1872
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
1783
1873
|
}
|
|
1784
1874
|
catch (error) {
|
|
1785
|
-
return
|
|
1875
|
+
return { stage: "generate", ...generateSummaryData, error };
|
|
1786
1876
|
}
|
|
1787
1877
|
const parent = summaryContext.ackHandle;
|
|
1788
1878
|
const summaryMessage = {
|
|
@@ -1792,25 +1882,34 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1792
1882
|
message,
|
|
1793
1883
|
parents: parent ? [parent] : [],
|
|
1794
1884
|
};
|
|
1795
|
-
const uploadData =
|
|
1885
|
+
const uploadData = {
|
|
1886
|
+
...generateSummaryData,
|
|
1887
|
+
handle,
|
|
1888
|
+
uploadDuration: trace.trace().duration,
|
|
1889
|
+
};
|
|
1796
1890
|
continueResult = checkContinue();
|
|
1797
1891
|
if (!continueResult.continue) {
|
|
1798
|
-
return
|
|
1892
|
+
return { stage: "upload", ...uploadData, error: continueResult.error };
|
|
1799
1893
|
}
|
|
1800
1894
|
let clientSequenceNumber;
|
|
1801
1895
|
try {
|
|
1802
1896
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
1803
1897
|
}
|
|
1804
1898
|
catch (error) {
|
|
1805
|
-
return
|
|
1899
|
+
return { stage: "upload", ...uploadData, error };
|
|
1806
1900
|
}
|
|
1807
|
-
const submitData =
|
|
1901
|
+
const submitData = {
|
|
1902
|
+
stage: "submit",
|
|
1903
|
+
...uploadData,
|
|
1904
|
+
clientSequenceNumber,
|
|
1905
|
+
submitOpDuration: trace.trace().duration,
|
|
1906
|
+
};
|
|
1808
1907
|
try {
|
|
1809
1908
|
// If validateSummaryBeforeUpload is false, the summary should be validated in this step.
|
|
1810
1909
|
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
1811
1910
|
}
|
|
1812
1911
|
catch (error) {
|
|
1813
|
-
return
|
|
1912
|
+
return { stage: "upload", ...uploadData, error };
|
|
1814
1913
|
}
|
|
1815
1914
|
return submitData;
|
|
1816
1915
|
}
|
|
@@ -1818,7 +1917,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1818
1917
|
// Cleanup wip summary in case of failure
|
|
1819
1918
|
this.summarizerNode.clearSummary();
|
|
1820
1919
|
// ! This needs to happen before we resume inbound queues to ensure heuristics are tracked correctly
|
|
1821
|
-
|
|
1920
|
+
this._summarizer?.recordSummaryAttempt?.(summaryRefSeqNum);
|
|
1822
1921
|
// Restart the delta manager
|
|
1823
1922
|
this.deltaManager.inbound.resume();
|
|
1824
1923
|
if (shouldPauseInboundSignal) {
|
|
@@ -1843,7 +1942,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1843
1942
|
this.dirtyContainer = dirty;
|
|
1844
1943
|
if (this.emitDirtyDocumentEvent) {
|
|
1845
1944
|
this.emit(dirty ? "dirty" : "saved");
|
|
1846
|
-
this.context.updateDirtyContainerState(dirty);
|
|
1847
1945
|
}
|
|
1848
1946
|
}
|
|
1849
1947
|
submitDataStoreOp(id, contents, localOpMetadata = undefined) {
|
|
@@ -1851,21 +1949,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1851
1949
|
address: id,
|
|
1852
1950
|
contents,
|
|
1853
1951
|
};
|
|
1854
|
-
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
1952
|
+
this.submit({ type: ContainerMessageType.FluidDataStoreOp, contents: envelope }, localOpMetadata);
|
|
1855
1953
|
}
|
|
1856
1954
|
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1857
1955
|
const aliasMessage = contents;
|
|
1858
1956
|
if (!(0, dataStore_1.isDataStoreAliasMessage)(aliasMessage)) {
|
|
1859
1957
|
throw new container_utils_1.UsageError("malformedDataStoreAliasMessage");
|
|
1860
1958
|
}
|
|
1861
|
-
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
1959
|
+
this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
1862
1960
|
}
|
|
1863
|
-
async uploadBlob(blob) {
|
|
1961
|
+
async uploadBlob(blob, signal) {
|
|
1864
1962
|
this.verifyNotClosed();
|
|
1865
|
-
return this.blobManager.createBlob(blob);
|
|
1963
|
+
return this.blobManager.createBlob(blob, signal);
|
|
1866
1964
|
}
|
|
1867
1965
|
maybeSubmitIdAllocationOp(type) {
|
|
1868
|
-
var _a, _b;
|
|
1869
1966
|
if (type !== ContainerMessageType.IdAllocation) {
|
|
1870
1967
|
let idAllocationBatchMessage;
|
|
1871
1968
|
let idRange;
|
|
@@ -1873,7 +1970,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1873
1970
|
(0, common_utils_1.assert)(this.idCompressor !== undefined, 0x67d /* IdCompressor should be defined if enabled */);
|
|
1874
1971
|
idRange = this.idCompressor.takeNextCreationRange();
|
|
1875
1972
|
// Don't include the idRange if there weren't any Ids allocated
|
|
1876
|
-
idRange =
|
|
1973
|
+
idRange = idRange?.ids?.first !== undefined ? idRange : undefined;
|
|
1877
1974
|
}
|
|
1878
1975
|
if (idRange !== undefined) {
|
|
1879
1976
|
const idAllocationMessage = {
|
|
@@ -1884,7 +1981,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1884
1981
|
contents: JSON.stringify(idAllocationMessage),
|
|
1885
1982
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1886
1983
|
metadata: undefined,
|
|
1887
|
-
localOpMetadata:
|
|
1984
|
+
localOpMetadata: this.idCompressor?.serialize(true),
|
|
1888
1985
|
type: ContainerMessageType.IdAllocation,
|
|
1889
1986
|
};
|
|
1890
1987
|
}
|
|
@@ -1893,20 +1990,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1893
1990
|
}
|
|
1894
1991
|
}
|
|
1895
1992
|
}
|
|
1896
|
-
submit(
|
|
1993
|
+
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata = undefined) {
|
|
1897
1994
|
this.verifyNotClosed();
|
|
1898
1995
|
this.verifyCanSubmitOps();
|
|
1899
1996
|
// There should be no ops in detached container state!
|
|
1900
1997
|
(0, common_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1901
|
-
const serializedContent = JSON.stringify(
|
|
1998
|
+
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
1902
1999
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
1903
2000
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
1904
2001
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
1905
|
-
this.logger.sendTelemetryEvent({
|
|
2002
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1906
2003
|
eventName: "SubmitOpInReadonly",
|
|
1907
2004
|
connected: this.connected,
|
|
1908
2005
|
});
|
|
1909
2006
|
}
|
|
2007
|
+
const type = containerRuntimeMessage.type;
|
|
1910
2008
|
const message = {
|
|
1911
2009
|
contents: serializedContent,
|
|
1912
2010
|
type,
|
|
@@ -1963,7 +2061,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1963
2061
|
this.closeFn(error);
|
|
1964
2062
|
throw error;
|
|
1965
2063
|
}
|
|
1966
|
-
if (this.isContainerMessageDirtyable(
|
|
2064
|
+
if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
|
|
1967
2065
|
this.updateDocumentDirtyState(true);
|
|
1968
2066
|
}
|
|
1969
2067
|
}
|
|
@@ -2006,9 +2104,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2006
2104
|
// System message should not be sent in the middle of the batch.
|
|
2007
2105
|
(0, common_utils_1.assert)(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
2008
2106
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
2009
|
-
return this.
|
|
2010
|
-
? this.
|
|
2011
|
-
: this.
|
|
2107
|
+
return this.submitSummaryFn !== undefined
|
|
2108
|
+
? this.submitSummaryFn(contents, referenceSequenceNumber)
|
|
2109
|
+
: this.submitFn(protocol_definitions_1.MessageType.Summarize, contents, false);
|
|
2012
2110
|
}
|
|
2013
2111
|
/**
|
|
2014
2112
|
* Throw an error if the runtime is closed. Methods that are expected to potentially
|
|
@@ -2055,32 +2153,33 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2055
2153
|
}
|
|
2056
2154
|
reSubmit(message) {
|
|
2057
2155
|
// Need to parse from string for back-compat
|
|
2058
|
-
const
|
|
2059
|
-
this.reSubmitCore(
|
|
2156
|
+
const containerRuntimeMessage = this.parseOpContent(message.content);
|
|
2157
|
+
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2060
2158
|
}
|
|
2061
2159
|
/**
|
|
2062
2160
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2063
2161
|
* reconnect and there are pending messages.
|
|
2064
|
-
* @param
|
|
2162
|
+
* @param message - The original ContainerRuntimeMessage.
|
|
2065
2163
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2066
2164
|
*/
|
|
2067
|
-
reSubmitCore(
|
|
2068
|
-
|
|
2165
|
+
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2166
|
+
const contents = message.contents;
|
|
2167
|
+
switch (message.type) {
|
|
2069
2168
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2070
2169
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2071
2170
|
// and trigger resubmission on it.
|
|
2072
|
-
this.dataStores.resubmitDataStoreOp(
|
|
2171
|
+
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2073
2172
|
break;
|
|
2074
2173
|
case ContainerMessageType.Attach:
|
|
2075
2174
|
case ContainerMessageType.Alias:
|
|
2076
|
-
this.submit(
|
|
2175
|
+
this.submit(message, localOpMetadata);
|
|
2077
2176
|
break;
|
|
2078
2177
|
case ContainerMessageType.IdAllocation:
|
|
2079
2178
|
// Remove the stashedState from the op if it's a stashed op
|
|
2080
|
-
if (
|
|
2081
|
-
delete
|
|
2179
|
+
if (contents.stashedState !== undefined) {
|
|
2180
|
+
delete contents.stashedState;
|
|
2082
2181
|
}
|
|
2083
|
-
this.submit(
|
|
2182
|
+
this.submit(message, localOpMetadata);
|
|
2084
2183
|
break;
|
|
2085
2184
|
case ContainerMessageType.ChunkedOp:
|
|
2086
2185
|
throw new Error(`chunkedOp not expected here`);
|
|
@@ -2088,10 +2187,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2088
2187
|
this.blobManager.reSubmit(opMetadata);
|
|
2089
2188
|
break;
|
|
2090
2189
|
case ContainerMessageType.Rejoin:
|
|
2091
|
-
this.submit(
|
|
2190
|
+
this.submit(message);
|
|
2092
2191
|
break;
|
|
2093
2192
|
default:
|
|
2094
|
-
(0, common_utils_1.unreachableCase)(type, `Unknown ContainerMessageType: ${type}`);
|
|
2193
|
+
(0, common_utils_1.unreachableCase)(message.type, `Unknown ContainerMessageType [type: ${message.type}]`);
|
|
2095
2194
|
}
|
|
2096
2195
|
}
|
|
2097
2196
|
rollback(content, localOpMetadata) {
|
|
@@ -2138,7 +2237,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2138
2237
|
* change that started fetching latest snapshot always.
|
|
2139
2238
|
*/
|
|
2140
2239
|
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
2141
|
-
fetchResult = await this.
|
|
2240
|
+
fetchResult = await this.fetchSnapshotFromStorageAndClose(summaryLogger, {
|
|
2142
2241
|
eventName: "RefreshLatestSummaryAckFetchBackCompat",
|
|
2143
2242
|
ackHandle,
|
|
2144
2243
|
targetSequenceNumber: summaryRefSeq,
|
|
@@ -2160,7 +2259,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2160
2259
|
summaryRefSeq,
|
|
2161
2260
|
fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
2162
2261
|
});
|
|
2163
|
-
this.
|
|
2262
|
+
this.disposeFn(error);
|
|
2164
2263
|
throw error;
|
|
2165
2264
|
}
|
|
2166
2265
|
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
@@ -2196,10 +2295,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2196
2295
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
2197
2296
|
}
|
|
2198
2297
|
async fetchLatestSnapshotFromStorage(logger, event, readAndParseBlob) {
|
|
2199
|
-
return this.
|
|
2298
|
+
return this.fetchSnapshotFromStorageAndClose(logger, event, readAndParseBlob, null /* latest */);
|
|
2200
2299
|
}
|
|
2201
|
-
async
|
|
2202
|
-
var _a;
|
|
2300
|
+
async fetchSnapshotFromStorageAndClose(logger, event, readAndParseBlob, versionId) {
|
|
2203
2301
|
const snapshotResults = await telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2204
2302
|
const stats = {};
|
|
2205
2303
|
const trace = common_utils_1.Trace.start();
|
|
@@ -2222,29 +2320,37 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2222
2320
|
// We choose to close the summarizer after the snapshot cache is updated to avoid
|
|
2223
2321
|
// situations which the main client (which is likely to be re-elected as the leader again)
|
|
2224
2322
|
// loads the summarizer from cache.
|
|
2225
|
-
if (this.summaryStateUpdateMethod
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2323
|
+
if (this.summaryStateUpdateMethod !== "refreshFromSnapshot") {
|
|
2324
|
+
this.mc.logger.sendTelemetryEvent({
|
|
2325
|
+
...event,
|
|
2326
|
+
eventName: "ClosingSummarizerOnSummaryStale",
|
|
2327
|
+
codePath: event.eventName,
|
|
2328
|
+
message: "Stopping fetch from storage",
|
|
2329
|
+
versionId: versionId != null ? versionId : undefined,
|
|
2330
|
+
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
2331
|
+
}, new container_utils_1.GenericError("Restarting summarizer instead of refreshing"));
|
|
2332
|
+
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2229
2333
|
await (0, common_utils_1.delay)(this.closeSummarizerDelayMs);
|
|
2230
|
-
|
|
2231
|
-
this.
|
|
2232
|
-
throw error;
|
|
2334
|
+
this._summarizer?.stop("latestSummaryStateStale");
|
|
2335
|
+
this.disposeFn();
|
|
2233
2336
|
}
|
|
2234
2337
|
return snapshotResults;
|
|
2235
2338
|
}
|
|
2236
2339
|
notifyAttaching() { } // do nothing (deprecated method)
|
|
2237
|
-
getPendingLocalState() {
|
|
2340
|
+
async getPendingLocalState(props) {
|
|
2341
|
+
this.verifyNotClosed();
|
|
2342
|
+
const waitBlobsToAttach = props?.notifyImminentClosure;
|
|
2238
2343
|
if (this._orderSequentiallyCalls !== 0) {
|
|
2239
2344
|
throw new container_utils_1.UsageError("can't get state during orderSequentially");
|
|
2240
2345
|
}
|
|
2346
|
+
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(waitBlobsToAttach);
|
|
2241
2347
|
// Flush pending batch.
|
|
2242
2348
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2243
2349
|
// to close current batch.
|
|
2244
2350
|
this.flush();
|
|
2245
2351
|
return {
|
|
2246
2352
|
pending: this.pendingStateManager.getLocalState(),
|
|
2247
|
-
pendingAttachmentBlobs
|
|
2353
|
+
pendingAttachmentBlobs,
|
|
2248
2354
|
};
|
|
2249
2355
|
}
|
|
2250
2356
|
/**
|