@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254866 → 2.0.0-dev-rc.4.0.0.261659
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 +23 -0
- package/api-report/container-runtime.api.md +33 -19
- package/dist/batchTracker.d.ts +1 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/channelCollection.d.ts +5 -3
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +67 -15
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +54 -5
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +17 -27
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +174 -143
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +1 -0
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +2 -0
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -0
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.d.ts +18 -6
- package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +38 -19
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.d.ts +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +5 -12
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +45 -29
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +27 -6
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +5 -4
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +14 -2
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +14 -4
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +24 -21
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +2 -2
- package/dist/gc/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/{alpha.d.ts → legacy.d.ts} +3 -1
- package/dist/metadata.d.ts +2 -2
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +0 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +0 -10
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +6 -6
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +2 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +1 -1
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +0 -4
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +2 -34
- package/dist/opLifecycle/outbox.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 +3 -2
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +17 -10
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +3 -0
- package/dist/scheduleManager.d.ts +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +3 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +34 -16
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +1 -1
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/summarizer.d.ts +1 -2
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.d.ts +1 -1
- package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +4 -10
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +2 -3
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +1 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +1 -2
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +3 -2
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js.map +1 -1
- package/{dist/beta.d.ts → internal.d.ts} +2 -0
- package/{lib/beta.d.ts → legacy.d.ts} +2 -0
- package/lib/batchTracker.d.ts +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/channelCollection.d.ts +5 -3
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +69 -17
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +49 -0
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +17 -27
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +174 -143
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +1 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +1 -0
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +2 -0
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -0
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.d.ts +18 -6
- package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +36 -18
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.d.ts +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +5 -12
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +47 -31
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +27 -6
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +5 -4
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -1
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +14 -4
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +24 -21
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +1 -1
- package/lib/gc/index.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/{alpha.d.ts → legacy.d.ts} +3 -1
- package/lib/metadata.d.ts +2 -2
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +0 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +0 -10
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +6 -6
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +2 -2
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +0 -4
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +2 -34
- package/lib/opLifecycle/outbox.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 +3 -2
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +18 -11
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +3 -0
- package/lib/scheduleManager.d.ts +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +3 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +34 -16
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +1 -1
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +1 -1
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/summarizer.d.ts +1 -2
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.d.ts +1 -1
- package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +4 -10
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +2 -3
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +1 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +1 -2
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +4 -3
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +1 -1
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +33 -57
- package/src/batchTracker.ts +1 -2
- package/src/channelCollection.ts +87 -35
- package/src/connectionTelemetry.ts +58 -3
- package/src/containerRuntime.ts +214 -222
- package/src/dataStore.ts +5 -2
- package/src/dataStoreContext.ts +1 -0
- package/src/dataStoreContexts.ts +13 -2
- package/src/deltaManagerSummarizerProxy.ts +43 -21
- package/src/deltaScheduler.ts +1 -2
- package/src/gc/garbageCollection.ts +64 -42
- package/src/gc/gcDefinitions.ts +22 -10
- package/src/gc/gcHelpers.ts +14 -1
- package/src/gc/gcTelemetry.ts +57 -50
- package/src/gc/index.ts +2 -1
- package/src/index.ts +2 -0
- package/src/metadata.ts +2 -2
- package/src/opLifecycle/README.md +4 -4
- package/src/opLifecycle/batchManager.ts +0 -14
- package/src/opLifecycle/opDecompressor.ts +12 -6
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +1 -1
- package/src/opLifecycle/outbox.ts +2 -49
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +28 -15
- package/src/scheduleManager.ts +1 -1
- package/src/summary/documentSchema.ts +52 -18
- package/src/summary/orderedClientElection.ts +5 -2
- package/src/summary/summarizer.ts +1 -1
- package/src/summary/summarizerClientElection.ts +1 -1
- package/src/summary/summarizerHeuristics.ts +1 -1
- package/src/summary/summarizerNode/summarizerNode.ts +3 -12
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
- package/src/summary/summarizerTypes.ts +5 -3
- package/src/summary/summaryCollection.ts +1 -1
- package/src/summary/summaryGenerator.ts +19 -8
- package/src/summary/summaryManager.ts +5 -2
package/dist/containerRuntime.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.disabledCompressionConfig = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DefaultSummaryConfiguration = void 0;
|
|
7
|
+
exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.disabledCompressionConfig = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = exports.DefaultSummaryConfiguration = void 0;
|
|
8
8
|
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
9
9
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
10
10
|
const internal_1 = require("@fluidframework/container-definitions/internal");
|
|
@@ -61,6 +61,11 @@ exports.DefaultSummaryConfiguration = {
|
|
|
61
61
|
runtimeOpWeight: 1.0,
|
|
62
62
|
nonRuntimeHeuristicThreshold: 20,
|
|
63
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Error responses when requesting a deleted object will have this header set to true
|
|
66
|
+
* @alpha
|
|
67
|
+
*/
|
|
68
|
+
exports.DeletedResponseHeaderKey = "wasDeleted";
|
|
64
69
|
/**
|
|
65
70
|
* Tombstone error responses will have this header set to true
|
|
66
71
|
* @alpha
|
|
@@ -245,7 +250,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
245
250
|
},
|
|
246
251
|
});
|
|
247
252
|
const mc = (0, internal_7.loggerToMonitoringContext)(logger);
|
|
248
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes,
|
|
253
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes, enableGroupedBatching = false, explicitSchemaControl = false, } = runtimeOptions;
|
|
249
254
|
const registry = new dataStoreRegistry_js_1.FluidDataStoreRegistry(registryEntries);
|
|
250
255
|
const tryFetchBlob = async (blobName) => {
|
|
251
256
|
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
@@ -273,9 +278,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
273
278
|
const messageAtLastSummary = lastMessageFromMetadata(metadata);
|
|
274
279
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
275
280
|
const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
|
|
281
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
276
282
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
277
283
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
278
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
279
284
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
280
285
|
if (loadSequenceNumberVerification !== "bypass" &&
|
|
281
286
|
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
@@ -327,7 +332,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
327
332
|
?.idCompressorMode;
|
|
328
333
|
// This is the only exception to the rule above - we have proper plumbing to load ID compressor on schema change
|
|
329
334
|
// event. It is loaded async (relative to op processing), so this conversion is only safe for off -> delayed conversion!
|
|
330
|
-
|
|
335
|
+
// Clients do not expect ID compressor ops unless ID compressor is On for them, and that could be achieved only through
|
|
336
|
+
// explicit schema change, i.e. only if explicitSchemaControl is on.
|
|
337
|
+
// Note: it would be better if we throw on combination of options (explicitSchemaControl = off, desiredIdCompressorMode === "delayed")
|
|
338
|
+
// that is not supported. But our service tests are oblivious to these problems and throwing here will cause a ton of failures
|
|
339
|
+
// We ignored incompatible ID compressor changes from the start (they were sticky), so that's not a new problem being introduced...
|
|
340
|
+
if (idCompressorMode === undefined &&
|
|
341
|
+
desiredIdCompressorMode === "delayed" &&
|
|
342
|
+
explicitSchemaControl) {
|
|
331
343
|
idCompressorMode = desiredIdCompressorMode;
|
|
332
344
|
}
|
|
333
345
|
}
|
|
@@ -366,7 +378,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
366
378
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
367
379
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
368
380
|
const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
|
|
369
|
-
const documentSchemaController = new index_js_3.DocumentsSchemaController(existing, metadata?.documentSchema, {
|
|
381
|
+
const documentSchemaController = new index_js_3.DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
|
|
370
382
|
explicitSchemaControl,
|
|
371
383
|
compressionLz4,
|
|
372
384
|
idCompressorMode,
|
|
@@ -389,7 +401,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
389
401
|
chunkSizeInBytes,
|
|
390
402
|
// Requires<> drops undefined from IdCompressorType
|
|
391
403
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor,
|
|
392
|
-
enableOpReentryCheck,
|
|
393
404
|
enableGroupedBatching,
|
|
394
405
|
explicitSchemaControl,
|
|
395
406
|
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined);
|
|
@@ -428,11 +439,21 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
428
439
|
get attachState() {
|
|
429
440
|
return this._getAttachState();
|
|
430
441
|
}
|
|
431
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Current session schema - defines what options are on & off.
|
|
444
|
+
* It's overlap of document schema (controlled by summary & ops) and options controlling this session.
|
|
445
|
+
* For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
|
|
446
|
+
* In such case it will be off in session schema (i.e. this session should not use compression), but this client
|
|
447
|
+
* has to deal with compressed ops as other clients might send them.
|
|
448
|
+
* And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
|
|
449
|
+
* In such case it will be off in session schema, however this client will propose change to schema, and once / if
|
|
450
|
+
* this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
|
|
451
|
+
*/
|
|
452
|
+
get sessionSchema() {
|
|
432
453
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
433
454
|
}
|
|
434
455
|
get idCompressorMode() {
|
|
435
|
-
return this.
|
|
456
|
+
return this.sessionSchema.idCompressorMode;
|
|
436
457
|
}
|
|
437
458
|
/**
|
|
438
459
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -540,13 +561,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
540
561
|
this.flushTaskExists = false;
|
|
541
562
|
this.consecutiveReconnects = 0;
|
|
542
563
|
this.ensureNoDataModelChangesCalls = 0;
|
|
543
|
-
/**
|
|
544
|
-
* Tracks the number of detected reentrant ops to report,
|
|
545
|
-
* in order to self-throttle the telemetry events.
|
|
546
|
-
*
|
|
547
|
-
* This should be removed as part of ADO:2322
|
|
548
|
-
*/
|
|
549
|
-
this.opReentryCallsToReport = 5;
|
|
550
564
|
this._disposed = false;
|
|
551
565
|
this.emitDirtyDocumentEvent = true;
|
|
552
566
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -573,7 +587,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
573
587
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
574
588
|
// compression.
|
|
575
589
|
const compressionOptions = {
|
|
576
|
-
minimumBatchSizeInBytes: this.
|
|
590
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
577
591
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
578
592
|
: Number.POSITIVE_INFINITY,
|
|
579
593
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
@@ -657,11 +671,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
657
671
|
if (this.summaryConfiguration.state === "enabled") {
|
|
658
672
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
659
673
|
}
|
|
660
|
-
const disableOpReentryCheck = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck");
|
|
661
|
-
this.enableOpReentryCheck =
|
|
662
|
-
runtimeOptions.enableOpReentryCheck === true &&
|
|
663
|
-
// Allow for a break-glass config to override the options
|
|
664
|
-
disableOpReentryCheck !== true;
|
|
665
674
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
666
675
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
667
676
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -733,7 +742,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
733
742
|
const envelope2 = this.createNewSignalEnvelope(envelope1.address, type, envelope1.contents);
|
|
734
743
|
return this.submitSignalFn(envelope2, targetClientId);
|
|
735
744
|
};
|
|
736
|
-
this.channelCollection = new channelCollection_js_1.ChannelCollection((0, channelCollection_js_1.getSummaryForDatastores)(baseSnapshot, metadata), parentContext, this.mc.logger, (
|
|
745
|
+
this.channelCollection = new channelCollection_js_1.ChannelCollection((0, channelCollection_js_1.getSummaryForDatastores)(baseSnapshot, metadata), parentContext, this.mc.logger, (props) => this.garbageCollector.nodeUpdated(props), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
|
|
737
746
|
this.blobManager = new blobManager_js_1.BlobManager({
|
|
738
747
|
routeContext: this.handleContext,
|
|
739
748
|
snapshot: blobManagerSnapshot,
|
|
@@ -746,7 +755,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
746
755
|
});
|
|
747
756
|
}
|
|
748
757
|
},
|
|
749
|
-
blobRequested: (blobPath) => this.garbageCollector.nodeUpdated(
|
|
758
|
+
blobRequested: (blobPath) => this.garbageCollector.nodeUpdated({
|
|
759
|
+
node: { type: "Blob", path: blobPath },
|
|
760
|
+
reason: "Loaded",
|
|
761
|
+
}),
|
|
750
762
|
isBlobDeleted: (blobPath) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
751
763
|
runtime: this,
|
|
752
764
|
stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
|
|
@@ -794,12 +806,29 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
794
806
|
this._quorum.on("removeMember", (clientId) => {
|
|
795
807
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
796
808
|
});
|
|
797
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
798
809
|
this._audience = audience;
|
|
810
|
+
if (audience.getSelf === undefined) {
|
|
811
|
+
// back-compat, added in 2.0 RC3.
|
|
812
|
+
// Purpose: deal with cases when we run against old loader that does not have newly added capabilities
|
|
813
|
+
audience.getSelf = () => {
|
|
814
|
+
const clientId = this._getClientId();
|
|
815
|
+
return clientId === undefined
|
|
816
|
+
? undefined
|
|
817
|
+
: ({
|
|
818
|
+
clientId,
|
|
819
|
+
client: audience.getMember(clientId),
|
|
820
|
+
});
|
|
821
|
+
};
|
|
822
|
+
let oldClientId = this.clientId;
|
|
823
|
+
this.on("connected", () => {
|
|
824
|
+
const clientId = this.clientId;
|
|
825
|
+
(0, internal_2.assert)(clientId !== undefined, "can't be undefined");
|
|
826
|
+
audience.emit("selfChanged", { clientId: oldClientId }, { clientId, client: audience.getMember(clientId) });
|
|
827
|
+
oldClientId = clientId;
|
|
828
|
+
});
|
|
829
|
+
}
|
|
799
830
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
800
831
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
801
|
-
this.validateSummaryBeforeUpload =
|
|
802
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
803
832
|
this.summaryCollection = new index_js_3.SummaryCollection(this.deltaManager, this.logger);
|
|
804
833
|
this.dirtyContainer =
|
|
805
834
|
this.attachState !== container_definitions_1.AttachState.Attached || this.hasPendingMessages();
|
|
@@ -872,9 +901,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
872
901
|
options: JSON.stringify(runtimeOptions),
|
|
873
902
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
874
903
|
idCompressorMode: this.idCompressorMode,
|
|
904
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
875
905
|
featureGates: JSON.stringify({
|
|
876
906
|
...featureGatesForTelemetry,
|
|
877
|
-
disableOpReentryCheck,
|
|
878
907
|
disableChunking,
|
|
879
908
|
disableAttachReorder: this.disableAttachReorder,
|
|
880
909
|
disablePartialFlush,
|
|
@@ -882,6 +911,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
882
911
|
}),
|
|
883
912
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
884
913
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
914
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
885
915
|
});
|
|
886
916
|
(0, connectionTelemetry_js_1.ReportOpPerfTelemetry)(this.clientId, this.deltaManager, this, this.logger);
|
|
887
917
|
(0, batchTracker_js_1.BindBatchTracker)(this, this.logger);
|
|
@@ -897,6 +927,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
897
927
|
this.skipSavedCompressorOps = pendingRuntimeState?.pendingIdCompressorState !== undefined;
|
|
898
928
|
}
|
|
899
929
|
onSchemaChange(schema) {
|
|
930
|
+
this.logger.sendTelemetryEvent({
|
|
931
|
+
eventName: "SchemaChangeAccept",
|
|
932
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
933
|
+
});
|
|
900
934
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
901
935
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
902
936
|
// this is not done.
|
|
@@ -929,9 +963,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
929
963
|
async initializeBaseState() {
|
|
930
964
|
if (this.idCompressorMode === "on" ||
|
|
931
965
|
(this.idCompressorMode === "delayed" && this.connected)) {
|
|
966
|
+
this._idCompressor = await this.createIdCompressor();
|
|
932
967
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
933
968
|
(0, internal_2.assert)(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
934
|
-
this._idCompressor = await this.createIdCompressor();
|
|
935
969
|
}
|
|
936
970
|
await this.garbageCollector.initializeBaseState();
|
|
937
971
|
}
|
|
@@ -1309,12 +1343,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1309
1343
|
this._loadIdCompressor === undefined) {
|
|
1310
1344
|
this._loadIdCompressor = this.createIdCompressor()
|
|
1311
1345
|
.then((compressor) => {
|
|
1312
|
-
this._idCompressor = compressor;
|
|
1313
1346
|
// Finalize any ranges we received while the compressor was turned off.
|
|
1314
|
-
|
|
1315
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
1316
|
-
}
|
|
1347
|
+
const ops = this.pendingIdCompressorOps;
|
|
1317
1348
|
this.pendingIdCompressorOps = [];
|
|
1349
|
+
for (const range of ops) {
|
|
1350
|
+
compressor.finalizeCreationRange(range);
|
|
1351
|
+
}
|
|
1352
|
+
(0, internal_2.assert)(this.pendingIdCompressorOps.length === 0, "No new ops added");
|
|
1353
|
+
this._idCompressor = compressor;
|
|
1318
1354
|
})
|
|
1319
1355
|
.catch((error) => {
|
|
1320
1356
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -1324,6 +1360,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1324
1360
|
return this._loadIdCompressor;
|
|
1325
1361
|
}
|
|
1326
1362
|
setConnectionState(connected, clientId) {
|
|
1363
|
+
// Validate we have consistent state
|
|
1364
|
+
const currentClientId = this._audience.getSelf()?.clientId;
|
|
1365
|
+
(0, internal_2.assert)(clientId === currentClientId, "input clientId does not match Audience");
|
|
1366
|
+
(0, internal_2.assert)(this.clientId === currentClientId, "this.clientId does not match Audience");
|
|
1327
1367
|
if (connected && this.idCompressorMode === "delayed") {
|
|
1328
1368
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1329
1369
|
this.loadIdCompressor();
|
|
@@ -1405,9 +1445,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1405
1445
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1406
1446
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
1407
1447
|
const messageCopy = { ...messageArg };
|
|
1448
|
+
const savedOp = messageCopy.metadata?.savedOp;
|
|
1408
1449
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1409
|
-
|
|
1410
|
-
|
|
1450
|
+
const msg = modernRuntimeMessage
|
|
1451
|
+
? {
|
|
1411
1452
|
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1412
1453
|
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1413
1454
|
// the result messages will be so. In the end modern bool being true only directs to
|
|
@@ -1415,12 +1456,16 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1415
1456
|
message: message,
|
|
1416
1457
|
local,
|
|
1417
1458
|
modernRuntimeMessage,
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1459
|
+
}
|
|
1460
|
+
: // Unrecognized message will be ignored.
|
|
1461
|
+
{
|
|
1462
|
+
message,
|
|
1463
|
+
local,
|
|
1464
|
+
modernRuntimeMessage,
|
|
1465
|
+
};
|
|
1466
|
+
msg.savedOp = savedOp;
|
|
1467
|
+
// ensure that we observe any re-entrancy, and if needed, rebase ops
|
|
1468
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
1424
1469
|
}
|
|
1425
1470
|
}
|
|
1426
1471
|
/**
|
|
@@ -1438,7 +1483,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1438
1483
|
// These calls should be made for all but chunked ops:
|
|
1439
1484
|
// 1) this.pendingStateManager.processPendingLocalMessage() below
|
|
1440
1485
|
// 2) this.resetReconnectCount() below
|
|
1441
|
-
(0, internal_2.assert)(message.type !== messageTypes_js_1.ContainerMessageType.ChunkedOp,
|
|
1486
|
+
(0, internal_2.assert)(message.type !== messageTypes_js_1.ContainerMessageType.ChunkedOp, 0x93b /* we should never get here with chunked ops */);
|
|
1442
1487
|
let localOpMetadata;
|
|
1443
1488
|
if (local && messageWithContext.modernRuntimeMessage) {
|
|
1444
1489
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
@@ -1485,17 +1530,16 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1485
1530
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
1486
1531
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
1487
1532
|
// thus we need to process all the ops.
|
|
1488
|
-
if (!(this.skipSavedCompressorOps &&
|
|
1489
|
-
messageWithContext.message.metadata?.savedOp ===
|
|
1490
|
-
true)) {
|
|
1533
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
1491
1534
|
const range = messageWithContext.message.contents;
|
|
1492
1535
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
1493
1536
|
// put it in a pending queue and delay finalization.
|
|
1494
1537
|
if (this._idCompressor === undefined) {
|
|
1495
|
-
(0, internal_2.assert)(this.idCompressorMode !== undefined,
|
|
1538
|
+
(0, internal_2.assert)(this.idCompressorMode !== undefined, 0x93c /* id compressor should be enabled */);
|
|
1496
1539
|
this.pendingIdCompressorOps.push(range);
|
|
1497
1540
|
}
|
|
1498
1541
|
else {
|
|
1542
|
+
(0, internal_2.assert)(this.pendingIdCompressorOps.length === 0, "there should be no pending ops!");
|
|
1499
1543
|
this._idCompressor.finalizeCreationRange(range);
|
|
1500
1544
|
}
|
|
1501
1545
|
}
|
|
@@ -1506,7 +1550,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1506
1550
|
case messageTypes_js_1.ContainerMessageType.ChunkedOp:
|
|
1507
1551
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
1508
1552
|
// Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
|
|
1509
|
-
(0, internal_2.assert)(false,
|
|
1553
|
+
(0, internal_2.assert)(false, 0x93d /* should not even get here */);
|
|
1510
1554
|
case messageTypes_js_1.ContainerMessageType.Rejoin:
|
|
1511
1555
|
break;
|
|
1512
1556
|
case messageTypes_js_1.ContainerMessageType.DocumentSchemaChange:
|
|
@@ -1616,9 +1660,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1616
1660
|
let checkpoint;
|
|
1617
1661
|
let result;
|
|
1618
1662
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1619
|
-
// Note: we are not touching
|
|
1620
|
-
// 1. It would not help, as
|
|
1621
|
-
// 2. There is no way to undo process of data store creation.
|
|
1663
|
+
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
1664
|
+
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
1665
|
+
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
1622
1666
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1623
1667
|
}
|
|
1624
1668
|
try {
|
|
@@ -1681,7 +1725,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1681
1725
|
if (channel.entryPoint === undefined) {
|
|
1682
1726
|
throw new internal_7.UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
|
|
1683
1727
|
}
|
|
1684
|
-
this.garbageCollector.nodeUpdated(
|
|
1728
|
+
this.garbageCollector.nodeUpdated({
|
|
1729
|
+
node: { type: "DataStore", path: `/${internalId}` },
|
|
1730
|
+
reason: "Loaded",
|
|
1731
|
+
packagePath: context.packagePath,
|
|
1732
|
+
});
|
|
1685
1733
|
return channel.entryPoint;
|
|
1686
1734
|
}
|
|
1687
1735
|
createDetachedDataStore(pkg, loadingGroupId) {
|
|
@@ -1805,7 +1853,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1805
1853
|
// We can finalize any allocated IDs since we're the only client
|
|
1806
1854
|
const idRange = this._idCompressor?.takeNextCreationRange();
|
|
1807
1855
|
if (idRange !== undefined) {
|
|
1808
|
-
(0, internal_2.assert)(idRange.ids === undefined || idRange.ids.firstGenCount === 1,
|
|
1856
|
+
(0, internal_2.assert)(idRange.ids === undefined || idRange.ids.firstGenCount === 1, 0x93e /* No other ranges should be taken while container is detached. */);
|
|
1809
1857
|
this._idCompressor?.finalizeCreationRange(idRange);
|
|
1810
1858
|
}
|
|
1811
1859
|
const summarizeResult = this.channelCollection.getAttachSummary(telemetryContext);
|
|
@@ -2015,10 +2063,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2015
2063
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
2016
2064
|
// use it for all events logged during this summary.
|
|
2017
2065
|
const summaryNumber = this.nextSummaryNumber;
|
|
2066
|
+
let summaryRefSeqNum;
|
|
2018
2067
|
const summaryNumberLogger = (0, internal_7.createChildLogger)({
|
|
2019
2068
|
logger: summaryLogger,
|
|
2020
2069
|
properties: {
|
|
2021
|
-
all: {
|
|
2070
|
+
all: {
|
|
2071
|
+
summaryNumber,
|
|
2072
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
2073
|
+
},
|
|
2022
2074
|
},
|
|
2023
2075
|
});
|
|
2024
2076
|
(0, internal_2.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
@@ -2032,7 +2084,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2032
2084
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
2033
2085
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
2034
2086
|
// saved within the timeout, check if it should be failed or can continue.
|
|
2035
|
-
if (this.
|
|
2087
|
+
if (this.isDirty) {
|
|
2036
2088
|
const countBefore = this.pendingMessagesCount;
|
|
2037
2089
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
2038
2090
|
const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
|
|
@@ -2065,7 +2117,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2065
2117
|
}
|
|
2066
2118
|
const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
|
|
2067
2119
|
const shouldValidatePreSummaryState = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState") === true;
|
|
2068
|
-
let summaryRefSeqNum;
|
|
2069
2120
|
try {
|
|
2070
2121
|
await this.deltaManager.inbound.pause();
|
|
2071
2122
|
if (shouldPauseInboundSignal) {
|
|
@@ -2076,9 +2127,17 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2076
2127
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
2077
2128
|
const lastAck = this.summaryCollection.latestAck;
|
|
2078
2129
|
const startSummaryResult = this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger, latestSummaryRefSeqNum);
|
|
2130
|
+
/**
|
|
2131
|
+
* This was added to validate that the summarizer node tree has the same reference sequence number from the
|
|
2132
|
+
* top running summarizer down to the lowest summarizer node.
|
|
2133
|
+
*
|
|
2134
|
+
* The order of mismatch numbers goes (validate sequence number)-(node sequence number).
|
|
2135
|
+
* Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
|
|
2136
|
+
* summarizer nodes.
|
|
2137
|
+
*/
|
|
2079
2138
|
if (startSummaryResult.invalidNodes > 0 ||
|
|
2080
2139
|
startSummaryResult.mismatchNumbers.size > 0) {
|
|
2081
|
-
summaryLogger.
|
|
2140
|
+
summaryLogger.sendTelemetryEvent({
|
|
2082
2141
|
eventName: "LatestSummaryRefSeqNumMismatch",
|
|
2083
2142
|
details: {
|
|
2084
2143
|
...startSummaryResult,
|
|
@@ -2090,7 +2149,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2090
2149
|
stage: "base",
|
|
2091
2150
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2092
2151
|
minimumSequenceNumber,
|
|
2093
|
-
error: `Summarizer node state inconsistent with summarizer state
|
|
2152
|
+
error: new internal_7.LoggingError(`Summarizer node state inconsistent with summarizer state.`),
|
|
2094
2153
|
};
|
|
2095
2154
|
}
|
|
2096
2155
|
}
|
|
@@ -2133,7 +2192,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2133
2192
|
stage: "base",
|
|
2134
2193
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2135
2194
|
minimumSequenceNumber,
|
|
2136
|
-
error: continueResult.error,
|
|
2195
|
+
error: new internal_7.LoggingError(continueResult.error),
|
|
2137
2196
|
};
|
|
2138
2197
|
}
|
|
2139
2198
|
const trace = client_utils_1.Trace.start();
|
|
@@ -2150,6 +2209,18 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2150
2209
|
});
|
|
2151
2210
|
}
|
|
2152
2211
|
catch (error) {
|
|
2212
|
+
return {
|
|
2213
|
+
stage: "base",
|
|
2214
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2215
|
+
minimumSequenceNumber,
|
|
2216
|
+
error: (0, internal_7.wrapError)(error, (msg) => new internal_7.LoggingError(msg)),
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
// Validate that the summary generated by summarizer nodes is correct before uploading.
|
|
2220
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
2221
|
+
if (!validateResult.success) {
|
|
2222
|
+
const { success, ...loggingProps } = validateResult;
|
|
2223
|
+
const error = new index_js_3.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
2153
2224
|
return {
|
|
2154
2225
|
stage: "base",
|
|
2155
2226
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
@@ -2157,24 +2228,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2157
2228
|
error,
|
|
2158
2229
|
};
|
|
2159
2230
|
}
|
|
2160
|
-
// If
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
const { success, ...loggingProps } = validateResult;
|
|
2166
|
-
const error = new index_js_3.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
2167
|
-
return {
|
|
2168
|
-
stage: "base",
|
|
2169
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
2170
|
-
minimumSequenceNumber,
|
|
2171
|
-
error,
|
|
2172
|
-
};
|
|
2173
|
-
}
|
|
2174
|
-
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
2175
|
-
if (pendingMessagesFailResult !== undefined) {
|
|
2176
|
-
return pendingMessagesFailResult;
|
|
2177
|
-
}
|
|
2231
|
+
// If there are pending unacked ops, this summary attempt may fail as the uploaded
|
|
2232
|
+
// summary would be eventually inconsistent.
|
|
2233
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
2234
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
2235
|
+
return pendingMessagesFailResult;
|
|
2178
2236
|
}
|
|
2179
2237
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
2180
2238
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -2207,7 +2265,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2207
2265
|
};
|
|
2208
2266
|
continueResult = checkContinue();
|
|
2209
2267
|
if (!continueResult.continue) {
|
|
2210
|
-
return {
|
|
2268
|
+
return {
|
|
2269
|
+
stage: "generate",
|
|
2270
|
+
...generateSummaryData,
|
|
2271
|
+
error: new internal_7.LoggingError(continueResult.error),
|
|
2272
|
+
};
|
|
2211
2273
|
}
|
|
2212
2274
|
const summaryContext = lastAck === undefined
|
|
2213
2275
|
? {
|
|
@@ -2225,7 +2287,11 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2225
2287
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
2226
2288
|
}
|
|
2227
2289
|
catch (error) {
|
|
2228
|
-
return {
|
|
2290
|
+
return {
|
|
2291
|
+
stage: "generate",
|
|
2292
|
+
...generateSummaryData,
|
|
2293
|
+
error: (0, internal_7.wrapError)(error, (msg) => new internal_7.LoggingError(msg)),
|
|
2294
|
+
};
|
|
2229
2295
|
}
|
|
2230
2296
|
const parent = summaryContext.ackHandle;
|
|
2231
2297
|
const summaryMessage = {
|
|
@@ -2242,14 +2308,22 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2242
2308
|
};
|
|
2243
2309
|
continueResult = checkContinue();
|
|
2244
2310
|
if (!continueResult.continue) {
|
|
2245
|
-
return {
|
|
2311
|
+
return {
|
|
2312
|
+
stage: "upload",
|
|
2313
|
+
...uploadData,
|
|
2314
|
+
error: new internal_7.LoggingError(continueResult.error),
|
|
2315
|
+
};
|
|
2246
2316
|
}
|
|
2247
2317
|
let clientSequenceNumber;
|
|
2248
2318
|
try {
|
|
2249
2319
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
2250
2320
|
}
|
|
2251
2321
|
catch (error) {
|
|
2252
|
-
return {
|
|
2322
|
+
return {
|
|
2323
|
+
stage: "upload",
|
|
2324
|
+
...uploadData,
|
|
2325
|
+
error: (0, internal_7.wrapError)(error, (msg) => new internal_7.LoggingError(msg)),
|
|
2326
|
+
};
|
|
2253
2327
|
}
|
|
2254
2328
|
const submitData = {
|
|
2255
2329
|
stage: "submit",
|
|
@@ -2258,11 +2332,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2258
2332
|
submitOpDuration: trace.trace().duration,
|
|
2259
2333
|
};
|
|
2260
2334
|
try {
|
|
2261
|
-
|
|
2262
|
-
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
2335
|
+
this.summarizerNode.completeSummary(handle);
|
|
2263
2336
|
}
|
|
2264
2337
|
catch (error) {
|
|
2265
|
-
return {
|
|
2338
|
+
return {
|
|
2339
|
+
stage: "upload",
|
|
2340
|
+
...uploadData,
|
|
2341
|
+
error: (0, internal_7.wrapError)(error, (msg) => new internal_7.LoggingError(msg)),
|
|
2342
|
+
};
|
|
2266
2343
|
}
|
|
2267
2344
|
return submitData;
|
|
2268
2345
|
}
|
|
@@ -2370,11 +2447,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2370
2447
|
}
|
|
2371
2448
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
2372
2449
|
this.verifyNotClosed();
|
|
2373
|
-
this.verifyCanSubmitOps();
|
|
2374
2450
|
// There should be no ops in detached container state!
|
|
2375
2451
|
(0, internal_2.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
2376
2452
|
(0, internal_2.assert)(metadata === undefined ||
|
|
2377
|
-
containerRuntimeMessage.type === messageTypes_js_1.ContainerMessageType.BlobAttach,
|
|
2453
|
+
containerRuntimeMessage.type === messageTypes_js_1.ContainerMessageType.BlobAttach, 0x93f /* metadata */);
|
|
2378
2454
|
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
2379
2455
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
2380
2456
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
@@ -2406,6 +2482,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2406
2482
|
// on this callback to do actual sending.
|
|
2407
2483
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
2408
2484
|
if (contents) {
|
|
2485
|
+
this.logger.sendTelemetryEvent({
|
|
2486
|
+
eventName: "SchemaChangeProposal",
|
|
2487
|
+
refSeq: contents.refSeq,
|
|
2488
|
+
version: contents.version,
|
|
2489
|
+
newRuntimeSchema: JSON.stringify(contents.runtime),
|
|
2490
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
2491
|
+
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
2492
|
+
});
|
|
2409
2493
|
const msg = {
|
|
2410
2494
|
type: messageTypes_js_1.ContainerMessageType.DocumentSchemaChange,
|
|
2411
2495
|
contents,
|
|
@@ -2415,32 +2499,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2415
2499
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2416
2500
|
});
|
|
2417
2501
|
}
|
|
2418
|
-
|
|
2419
|
-
// Is it safe:
|
|
2420
|
-
// Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
|
|
2421
|
-
// They become visible only when aliased, or handle to some sub-element of newly created datastore
|
|
2422
|
-
// is stored in some DDS, i.e. only after some other op.
|
|
2423
|
-
// Why:
|
|
2424
|
-
// Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
|
|
2425
|
-
// stores are created, causing issues like relay service throttling (too many ops) and catastrophic
|
|
2426
|
-
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
2427
|
-
// these issues.
|
|
2428
|
-
// Cons:
|
|
2429
|
-
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
2430
|
-
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
2431
|
-
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
2432
|
-
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
2433
|
-
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
2434
|
-
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
2435
|
-
// issue than sending.
|
|
2436
|
-
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2437
|
-
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2438
|
-
if (this.currentlyBatching() &&
|
|
2439
|
-
type === messageTypes_js_1.ContainerMessageType.Attach &&
|
|
2440
|
-
this.disableAttachReorder !== true) {
|
|
2441
|
-
this.outbox.submitAttach(message);
|
|
2442
|
-
}
|
|
2443
|
-
else if (type === messageTypes_js_1.ContainerMessageType.BlobAttach) {
|
|
2502
|
+
if (type === messageTypes_js_1.ContainerMessageType.BlobAttach) {
|
|
2444
2503
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
2445
2504
|
this.outbox.submitBlobAttach(message);
|
|
2446
2505
|
}
|
|
@@ -2515,32 +2574,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2515
2574
|
throw new Error("Runtime is closed");
|
|
2516
2575
|
}
|
|
2517
2576
|
}
|
|
2518
|
-
verifyCanSubmitOps() {
|
|
2519
|
-
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
2520
|
-
const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
2521
|
-
if (this.opReentryCallsToReport > 0) {
|
|
2522
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
|
|
2523
|
-
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
2524
|
-
(0, index_js_2.getLongStack)(() => new internal_7.UsageError(errorMessage)));
|
|
2525
|
-
this.opReentryCallsToReport--;
|
|
2526
|
-
}
|
|
2527
|
-
// Creating ops while processing ops can lead
|
|
2528
|
-
// to undefined behavior and events observed in the wrong order.
|
|
2529
|
-
// For example, we have two callbacks registered for a DDS, A and B.
|
|
2530
|
-
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
2531
|
-
//
|
|
2532
|
-
// A because of #1
|
|
2533
|
-
// A because of #2
|
|
2534
|
-
// B because of #2
|
|
2535
|
-
// B because of #1
|
|
2536
|
-
//
|
|
2537
|
-
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
2538
|
-
// while ops are being processed.
|
|
2539
|
-
if (this.enableOpReentryCheck) {
|
|
2540
|
-
throw new internal_7.UsageError(errorMessage);
|
|
2541
|
-
}
|
|
2542
|
-
}
|
|
2543
|
-
}
|
|
2544
2577
|
reSubmitBatch(batch) {
|
|
2545
2578
|
this.orderSequentially(() => {
|
|
2546
2579
|
for (const message of batch) {
|
|
@@ -2670,7 +2703,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2670
2703
|
await this.closeStaleSummarizer();
|
|
2671
2704
|
return {
|
|
2672
2705
|
stage: "base",
|
|
2673
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2706
|
+
error: new internal_7.LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
2674
2707
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2675
2708
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2676
2709
|
};
|
|
@@ -2714,16 +2747,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2714
2747
|
}
|
|
2715
2748
|
this.imminentClosure || (this.imminentClosure = props?.notifyImminentClosure ?? false);
|
|
2716
2749
|
const getSyncState = (pendingAttachmentBlobs) => {
|
|
2717
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
2718
|
-
|
|
2719
|
-
return; // no pending state to save
|
|
2720
|
-
}
|
|
2750
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
2751
|
+
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
2721
2752
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
2722
2753
|
return {
|
|
2723
2754
|
pending,
|
|
2724
2755
|
pendingIdCompressorState,
|
|
2725
2756
|
pendingAttachmentBlobs,
|
|
2726
|
-
sessionExpiryTimerStarted
|
|
2757
|
+
sessionExpiryTimerStarted,
|
|
2727
2758
|
};
|
|
2728
2759
|
};
|
|
2729
2760
|
const perfEvent = {
|
|
@@ -2793,7 +2824,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2793
2824
|
}
|
|
2794
2825
|
}
|
|
2795
2826
|
get groupedBatchingEnabled() {
|
|
2796
|
-
return this.
|
|
2827
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
2797
2828
|
}
|
|
2798
2829
|
}
|
|
2799
2830
|
exports.ContainerRuntime = ContainerRuntime;
|