@fluidframework/container-runtime 2.0.0-rc.3.0.3 → 2.0.0-rc.4.0.1
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 +46 -0
- package/api-report/container-runtime.api.md +72 -34
- package/dist/batchTracker.d.ts +1 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +7 -7
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +2 -4
- package/dist/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +6 -4
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +20 -7
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +2 -2
- 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 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +194 -163
- 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 +9 -6
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +19 -5
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts +81 -0
- package/dist/deltaManagerProxies.d.ts.map +1 -0
- package/dist/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +75 -20
- package/dist/deltaManagerProxies.js.map +1 -0
- package/dist/deltaScheduler.d.ts +2 -2
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -1
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +1 -1
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +1 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.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/legacy.d.ts +6 -1
- package/dist/messageTypes.d.ts +5 -2
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -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 +4 -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/outbox.d.ts +0 -4
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +7 -38
- 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 +9 -2
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +26 -10
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts +2 -2
- 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 +25 -7
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/index.d.ts +1 -1
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +2 -2
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runningSummarizer.js +10 -10
- package/dist/summary/runningSummarizer.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 +3 -5
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +25 -5
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.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 +12 -11
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +5 -5
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +7 -7
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +3 -5
- package/lib/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +6 -4
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +21 -8
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +2 -2
- 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 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +195 -164
- 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 +9 -6
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +21 -7
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts +81 -0
- package/lib/deltaManagerProxies.d.ts.map +1 -0
- package/lib/{deltaManagerSummarizerProxy.js → deltaManagerProxies.js} +72 -19
- package/lib/deltaManagerProxies.js.map +1 -0
- package/lib/deltaScheduler.d.ts +2 -2
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -1
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +1 -1
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +1 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.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/legacy.d.ts +6 -1
- package/lib/messageTypes.d.ts +5 -2
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -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 +4 -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/outbox.d.ts +0 -4
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +7 -38
- 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 +9 -2
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +27 -11
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts +2 -2
- 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 +25 -7
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/index.d.ts +1 -1
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +2 -2
- 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/runningSummarizer.js +1 -1
- package/lib/summary/runningSummarizer.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 +3 -5
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +25 -5
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.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 +5 -4
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +2 -2
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +28 -50
- package/src/batchTracker.ts +1 -2
- package/src/blobManager.ts +11 -10
- package/src/channelCollection.ts +30 -12
- package/src/connectionTelemetry.ts +59 -4
- package/src/containerRuntime.ts +262 -239
- package/src/dataStore.ts +7 -4
- package/src/dataStoreContext.ts +57 -16
- package/src/dataStoreContexts.ts +1 -2
- package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
- package/src/deltaScheduler.ts +2 -3
- package/src/gc/garbageCollection.ts +1 -1
- package/src/gc/gcDefinitions.ts +1 -1
- package/src/gc/gcTelemetry.ts +1 -3
- package/src/index.ts +5 -0
- package/src/messageTypes.ts +4 -2
- package/src/metadata.ts +2 -2
- package/src/opLifecycle/README.md +4 -4
- package/src/opLifecycle/batchManager.ts +5 -14
- package/src/opLifecycle/outbox.ts +7 -53
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +38 -15
- package/src/scheduleManager.ts +2 -2
- package/src/summary/documentSchema.ts +37 -12
- package/src/summary/index.ts +4 -0
- package/src/summary/orderedClientElection.ts +6 -3
- package/src/summary/runningSummarizer.ts +1 -1
- 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 +6 -5
- package/src/summary/summaryCollection.ts +2 -2
- package/src/summary/summaryFormat.ts +30 -4
- package/src/summary/summaryGenerator.ts +20 -9
- package/src/summary/summaryManager.ts +6 -3
- package/dist/deltaManagerSummarizerProxy.d.ts +0 -44
- package/dist/deltaManagerSummarizerProxy.d.ts.map +0 -1
- package/dist/deltaManagerSummarizerProxy.js.map +0 -1
- package/lib/deltaManagerSummarizerProxy.d.ts +0 -44
- package/lib/deltaManagerSummarizerProxy.d.ts.map +0 -1
- package/lib/deltaManagerSummarizerProxy.js.map +0 -1
package/lib/containerRuntime.js
CHANGED
|
@@ -22,10 +22,10 @@ import { ReportOpPerfTelemetry } from "./connectionTelemetry.js";
|
|
|
22
22
|
import { ContainerFluidHandleContext } from "./containerHandleContext.js";
|
|
23
23
|
import { channelToDataStore } from "./dataStore.js";
|
|
24
24
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
|
|
25
|
-
import { DeltaManagerSummarizerProxy } from "./
|
|
25
|
+
import { DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy } from "./deltaManagerProxies.js";
|
|
26
26
|
import { GCNodeType, GarbageCollector, gcGenerationOptionName, } from "./gc/index.js";
|
|
27
27
|
import { ContainerMessageType, } from "./messageTypes.js";
|
|
28
|
-
import { OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor,
|
|
28
|
+
import { OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, } from "./opLifecycle/index.js";
|
|
29
29
|
import { pkgVersion } from "./packageVersion.js";
|
|
30
30
|
import { PendingStateManager, } from "./pendingStateManager.js";
|
|
31
31
|
import { ScheduleManager } from "./scheduleManager.js";
|
|
@@ -246,7 +246,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
246
246
|
},
|
|
247
247
|
});
|
|
248
248
|
const mc = loggerToMonitoringContext(logger);
|
|
249
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes,
|
|
249
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor, chunkSizeInBytes = defaultChunkSizeInBytes, enableGroupedBatching = true, explicitSchemaControl = false, } = runtimeOptions;
|
|
250
250
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
251
251
|
const tryFetchBlob = async (blobName) => {
|
|
252
252
|
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
@@ -274,9 +274,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
274
274
|
const messageAtLastSummary = lastMessageFromMetadata(metadata);
|
|
275
275
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
276
276
|
const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
|
|
277
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
277
278
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
278
279
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
279
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
280
280
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
281
281
|
if (loadSequenceNumberVerification !== "bypass" &&
|
|
282
282
|
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
@@ -368,23 +368,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
368
368
|
return createIdCompressor(compressorLogger);
|
|
369
369
|
}
|
|
370
370
|
};
|
|
371
|
-
const disableGroupedBatching = mc.config.getBoolean("Fluid.ContainerRuntime.DisableGroupedBatching");
|
|
372
371
|
const disableCompression = mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
|
|
373
372
|
const compressionLz4 = disableCompression !== true &&
|
|
374
373
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
375
374
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
376
|
-
const
|
|
377
|
-
const documentSchemaController = new DocumentsSchemaController(existing, metadata?.documentSchema, {
|
|
375
|
+
const documentSchemaController = new DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
|
|
378
376
|
explicitSchemaControl,
|
|
379
377
|
compressionLz4,
|
|
380
378
|
idCompressorMode,
|
|
381
|
-
opGroupingEnabled,
|
|
379
|
+
opGroupingEnabled: enableGroupedBatching,
|
|
382
380
|
disallowedVersions: [],
|
|
383
381
|
}, (schema) => {
|
|
384
382
|
runtime.onSchemaChange(schema);
|
|
385
383
|
});
|
|
386
384
|
const featureGatesForTelemetry = {
|
|
387
|
-
disableGroupedBatching,
|
|
388
385
|
disableCompression,
|
|
389
386
|
};
|
|
390
387
|
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
@@ -397,7 +394,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
397
394
|
chunkSizeInBytes,
|
|
398
395
|
// Requires<> drops undefined from IdCompressorType
|
|
399
396
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor,
|
|
400
|
-
enableOpReentryCheck,
|
|
401
397
|
enableGroupedBatching,
|
|
402
398
|
explicitSchemaControl,
|
|
403
399
|
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined);
|
|
@@ -436,11 +432,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
436
432
|
get attachState() {
|
|
437
433
|
return this._getAttachState();
|
|
438
434
|
}
|
|
439
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Current session schema - defines what options are on & off.
|
|
437
|
+
* It's overlap of document schema (controlled by summary & ops) and options controlling this session.
|
|
438
|
+
* For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
|
|
439
|
+
* In such case it will be off in session schema (i.e. this session should not use compression), but this client
|
|
440
|
+
* has to deal with compressed ops as other clients might send them.
|
|
441
|
+
* And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
|
|
442
|
+
* In such case it will be off in session schema, however this client will propose change to schema, and once / if
|
|
443
|
+
* this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
|
|
444
|
+
*/
|
|
445
|
+
get sessionSchema() {
|
|
440
446
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
441
447
|
}
|
|
442
448
|
get idCompressorMode() {
|
|
443
|
-
return this.
|
|
449
|
+
return this.sessionSchema.idCompressorMode;
|
|
444
450
|
}
|
|
445
451
|
/**
|
|
446
452
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -548,13 +554,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
548
554
|
this.flushTaskExists = false;
|
|
549
555
|
this.consecutiveReconnects = 0;
|
|
550
556
|
this.ensureNoDataModelChangesCalls = 0;
|
|
551
|
-
/**
|
|
552
|
-
* Tracks the number of detected reentrant ops to report,
|
|
553
|
-
* in order to self-throttle the telemetry events.
|
|
554
|
-
*
|
|
555
|
-
* This should be removed as part of ADO:2322
|
|
556
|
-
*/
|
|
557
|
-
this.opReentryCallsToReport = 5;
|
|
558
557
|
this._disposed = false;
|
|
559
558
|
this.emitDirtyDocumentEvent = true;
|
|
560
559
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -571,7 +570,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
571
570
|
this.snapshotCacheForLoadingGroupIds = new PromiseCache({
|
|
572
571
|
expiry: { policy: "absolute", durationMs: 60000 },
|
|
573
572
|
});
|
|
574
|
-
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, } = context;
|
|
573
|
+
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, snapshotWithContents, } = context;
|
|
575
574
|
this.mc = createChildMonitoringContext({
|
|
576
575
|
logger: this.logger,
|
|
577
576
|
namespace: "ContainerRuntime",
|
|
@@ -581,13 +580,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
581
580
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
582
581
|
// compression.
|
|
583
582
|
const compressionOptions = {
|
|
584
|
-
minimumBatchSizeInBytes: this.
|
|
583
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
585
584
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
586
585
|
: Number.POSITIVE_INFINITY,
|
|
587
586
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
588
587
|
};
|
|
589
588
|
this.innerDeltaManager = deltaManager;
|
|
590
|
-
this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
591
589
|
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
592
590
|
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
593
591
|
this.submitFn = submitFn;
|
|
@@ -661,15 +659,36 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
661
659
|
}, this.mc.logger);
|
|
662
660
|
const opSplitter = new OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
663
661
|
this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor(this.mc.logger), opGroupingManager);
|
|
662
|
+
const pendingRuntimeState = pendingLocalState;
|
|
663
|
+
this.pendingStateManager = new PendingStateManager({
|
|
664
|
+
applyStashedOp: this.applyStashedOp.bind(this),
|
|
665
|
+
clientId: () => this.clientId,
|
|
666
|
+
close: this.closeFn,
|
|
667
|
+
connected: () => this.connected,
|
|
668
|
+
reSubmit: (message) => {
|
|
669
|
+
this.reSubmit(message);
|
|
670
|
+
this.flush();
|
|
671
|
+
},
|
|
672
|
+
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
673
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
674
|
+
isAttached: () => this.attachState !== AttachState.Detached,
|
|
675
|
+
}, pendingRuntimeState?.pending, this.logger);
|
|
676
|
+
let outerDeltaManager;
|
|
677
|
+
const useDeltaManagerOpsProxy = this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") !== false;
|
|
678
|
+
// The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
|
|
679
|
+
const summarizerDeltaManagerProxy = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
680
|
+
outerDeltaManager = summarizerDeltaManagerProxy;
|
|
681
|
+
// The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
|
|
682
|
+
// It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
|
|
683
|
+
if (useDeltaManagerOpsProxy) {
|
|
684
|
+
const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(summarizerDeltaManagerProxy, this.pendingStateManager);
|
|
685
|
+
outerDeltaManager = pendingOpsDeltaManagerProxy;
|
|
686
|
+
}
|
|
687
|
+
this.deltaManager = outerDeltaManager;
|
|
664
688
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
665
689
|
if (this.summaryConfiguration.state === "enabled") {
|
|
666
690
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
667
691
|
}
|
|
668
|
-
const disableOpReentryCheck = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck");
|
|
669
|
-
this.enableOpReentryCheck =
|
|
670
|
-
runtimeOptions.enableOpReentryCheck === true &&
|
|
671
|
-
// Allow for a break-glass config to override the options
|
|
672
|
-
disableOpReentryCheck !== true;
|
|
673
692
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
674
693
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
675
694
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -685,7 +704,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
685
704
|
else {
|
|
686
705
|
this._flushMode = runtimeOptions.flushMode;
|
|
687
706
|
}
|
|
688
|
-
const pendingRuntimeState = pendingLocalState;
|
|
689
707
|
if (context.attachState === AttachState.Attached) {
|
|
690
708
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
691
709
|
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
@@ -741,7 +759,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
741
759
|
const envelope2 = this.createNewSignalEnvelope(envelope1.address, type, envelope1.contents);
|
|
742
760
|
return this.submitSignalFn(envelope2, targetClientId);
|
|
743
761
|
};
|
|
744
|
-
|
|
762
|
+
let snapshot = getSummaryForDatastores(baseSnapshot, metadata);
|
|
763
|
+
if (snapshot !== undefined && snapshotWithContents !== undefined) {
|
|
764
|
+
snapshot = {
|
|
765
|
+
...snapshotWithContents,
|
|
766
|
+
snapshotTree: snapshot,
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
this.channelCollection = new ChannelCollection(snapshot, parentContext, this.mc.logger, (props) => this.garbageCollector.nodeUpdated(props), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
|
|
745
770
|
this.blobManager = new BlobManager({
|
|
746
771
|
routeContext: this.handleContext,
|
|
747
772
|
snapshot: blobManagerSnapshot,
|
|
@@ -764,19 +789,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
764
789
|
closeContainer: (error) => this.closeFn(error),
|
|
765
790
|
});
|
|
766
791
|
this.scheduleManager = new ScheduleManager(this.innerDeltaManager, this, () => this.clientId, createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }));
|
|
767
|
-
this.pendingStateManager = new PendingStateManager({
|
|
768
|
-
applyStashedOp: this.applyStashedOp.bind(this),
|
|
769
|
-
clientId: () => this.clientId,
|
|
770
|
-
close: this.closeFn,
|
|
771
|
-
connected: () => this.connected,
|
|
772
|
-
reSubmit: (message) => {
|
|
773
|
-
this.reSubmit(message);
|
|
774
|
-
this.flush();
|
|
775
|
-
},
|
|
776
|
-
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
777
|
-
isActiveConnection: () => this.innerDeltaManager.active,
|
|
778
|
-
isAttached: () => this.attachState !== AttachState.Detached,
|
|
779
|
-
}, pendingRuntimeState?.pending, this.logger);
|
|
780
792
|
const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
|
|
781
793
|
const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
|
|
782
794
|
this.outbox = new Outbox({
|
|
@@ -805,12 +817,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
805
817
|
this._quorum.on("removeMember", (clientId) => {
|
|
806
818
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
807
819
|
});
|
|
808
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
809
820
|
this._audience = audience;
|
|
821
|
+
if (audience.getSelf === undefined) {
|
|
822
|
+
// back-compat, added in 2.0 RC3.
|
|
823
|
+
// Purpose: deal with cases when we run against old loader that does not have newly added capabilities
|
|
824
|
+
audience.getSelf = () => {
|
|
825
|
+
const clientId = this._getClientId();
|
|
826
|
+
return clientId === undefined
|
|
827
|
+
? undefined
|
|
828
|
+
: ({
|
|
829
|
+
clientId,
|
|
830
|
+
client: audience.getMember(clientId),
|
|
831
|
+
});
|
|
832
|
+
};
|
|
833
|
+
let oldClientId = this.clientId;
|
|
834
|
+
this.on("connected", () => {
|
|
835
|
+
const clientId = this.clientId;
|
|
836
|
+
assert(clientId !== undefined, 0x975 /* can't be undefined */);
|
|
837
|
+
audience.emit("selfChanged", { clientId: oldClientId }, { clientId, client: audience.getMember(clientId) });
|
|
838
|
+
oldClientId = clientId;
|
|
839
|
+
});
|
|
840
|
+
}
|
|
810
841
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
811
842
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
812
|
-
this.validateSummaryBeforeUpload =
|
|
813
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
814
843
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
815
844
|
this.dirtyContainer =
|
|
816
845
|
this.attachState !== AttachState.Attached || this.hasPendingMessages();
|
|
@@ -883,9 +912,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
883
912
|
options: JSON.stringify(runtimeOptions),
|
|
884
913
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
885
914
|
idCompressorMode: this.idCompressorMode,
|
|
915
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
886
916
|
featureGates: JSON.stringify({
|
|
887
917
|
...featureGatesForTelemetry,
|
|
888
|
-
disableOpReentryCheck,
|
|
889
918
|
disableChunking,
|
|
890
919
|
disableAttachReorder: this.disableAttachReorder,
|
|
891
920
|
disablePartialFlush,
|
|
@@ -893,6 +922,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
893
922
|
}),
|
|
894
923
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
895
924
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
925
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
896
926
|
});
|
|
897
927
|
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
|
|
898
928
|
BindBatchTracker(this, this.logger);
|
|
@@ -908,6 +938,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
908
938
|
this.skipSavedCompressorOps = pendingRuntimeState?.pendingIdCompressorState !== undefined;
|
|
909
939
|
}
|
|
910
940
|
onSchemaChange(schema) {
|
|
941
|
+
this.logger.sendTelemetryEvent({
|
|
942
|
+
eventName: "SchemaChangeAccept",
|
|
943
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
944
|
+
});
|
|
911
945
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
912
946
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
913
947
|
// this is not done.
|
|
@@ -940,9 +974,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
940
974
|
async initializeBaseState() {
|
|
941
975
|
if (this.idCompressorMode === "on" ||
|
|
942
976
|
(this.idCompressorMode === "delayed" && this.connected)) {
|
|
977
|
+
this._idCompressor = await this.createIdCompressor();
|
|
943
978
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
944
979
|
assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
945
|
-
this._idCompressor = await this.createIdCompressor();
|
|
946
980
|
}
|
|
947
981
|
await this.garbageCollector.initializeBaseState();
|
|
948
982
|
}
|
|
@@ -1240,6 +1274,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1240
1274
|
this.emitDirtyDocumentEvent = false;
|
|
1241
1275
|
let newState;
|
|
1242
1276
|
try {
|
|
1277
|
+
this.submitIdAllocationOpIfNeeded(true);
|
|
1243
1278
|
// replay the ops
|
|
1244
1279
|
this.pendingStateManager.replayPendingStates();
|
|
1245
1280
|
}
|
|
@@ -1287,8 +1322,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1287
1322
|
return;
|
|
1288
1323
|
case ContainerMessageType.BlobAttach:
|
|
1289
1324
|
return;
|
|
1290
|
-
case ContainerMessageType.ChunkedOp:
|
|
1291
|
-
throw new Error("chunkedOp not expected here");
|
|
1292
1325
|
case ContainerMessageType.Rejoin:
|
|
1293
1326
|
throw new Error("rejoin not expected here");
|
|
1294
1327
|
case ContainerMessageType.GC:
|
|
@@ -1300,7 +1333,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1300
1333
|
// e.g. if an app rolled back its container version
|
|
1301
1334
|
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1302
1335
|
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
1303
|
-
const error = DataProcessingError.create("Stashed runtime message of
|
|
1336
|
+
const error = DataProcessingError.create("Stashed runtime message of unexpected type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1304
1337
|
messageDetails: JSON.stringify({
|
|
1305
1338
|
type: opContents.type,
|
|
1306
1339
|
compatBehavior,
|
|
@@ -1320,12 +1353,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1320
1353
|
this._loadIdCompressor === undefined) {
|
|
1321
1354
|
this._loadIdCompressor = this.createIdCompressor()
|
|
1322
1355
|
.then((compressor) => {
|
|
1323
|
-
this._idCompressor = compressor;
|
|
1324
1356
|
// Finalize any ranges we received while the compressor was turned off.
|
|
1325
|
-
|
|
1326
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
1327
|
-
}
|
|
1357
|
+
const ops = this.pendingIdCompressorOps;
|
|
1328
1358
|
this.pendingIdCompressorOps = [];
|
|
1359
|
+
for (const range of ops) {
|
|
1360
|
+
compressor.finalizeCreationRange(range);
|
|
1361
|
+
}
|
|
1362
|
+
assert(this.pendingIdCompressorOps.length === 0, 0x976 /* No new ops added */);
|
|
1363
|
+
this._idCompressor = compressor;
|
|
1329
1364
|
})
|
|
1330
1365
|
.catch((error) => {
|
|
1331
1366
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -1335,6 +1370,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1335
1370
|
return this._loadIdCompressor;
|
|
1336
1371
|
}
|
|
1337
1372
|
setConnectionState(connected, clientId) {
|
|
1373
|
+
// Validate we have consistent state
|
|
1374
|
+
const currentClientId = this._audience.getSelf()?.clientId;
|
|
1375
|
+
assert(clientId === currentClientId, 0x977 /* input clientId does not match Audience */);
|
|
1376
|
+
assert(this.clientId === currentClientId, 0x978 /* this.clientId does not match Audience */);
|
|
1338
1377
|
if (connected && this.idCompressorMode === "delayed") {
|
|
1339
1378
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1340
1379
|
this.loadIdCompressor();
|
|
@@ -1416,9 +1455,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1416
1455
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1417
1456
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
1418
1457
|
const messageCopy = { ...messageArg };
|
|
1458
|
+
const savedOp = messageCopy.metadata?.savedOp;
|
|
1419
1459
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1420
|
-
|
|
1421
|
-
|
|
1460
|
+
const msg = modernRuntimeMessage
|
|
1461
|
+
? {
|
|
1422
1462
|
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1423
1463
|
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1424
1464
|
// the result messages will be so. In the end modern bool being true only directs to
|
|
@@ -1426,12 +1466,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1426
1466
|
message: message,
|
|
1427
1467
|
local,
|
|
1428
1468
|
modernRuntimeMessage,
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1469
|
+
}
|
|
1470
|
+
: // Unrecognized message will be ignored.
|
|
1471
|
+
{
|
|
1472
|
+
message,
|
|
1473
|
+
local,
|
|
1474
|
+
modernRuntimeMessage,
|
|
1475
|
+
};
|
|
1476
|
+
msg.savedOp = savedOp;
|
|
1477
|
+
// ensure that we observe any re-entrancy, and if needed, rebase ops
|
|
1478
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
1435
1479
|
}
|
|
1436
1480
|
}
|
|
1437
1481
|
/**
|
|
@@ -1439,6 +1483,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1439
1483
|
*/
|
|
1440
1484
|
processCore(messageWithContext) {
|
|
1441
1485
|
const { message, local } = messageWithContext;
|
|
1486
|
+
// Intercept to reduce minimum sequence number to the delta manager's minimum sequence number.
|
|
1487
|
+
// Sequence numbers are not guaranteed to follow any sort of order. Re-entrancy is one of those situations
|
|
1488
|
+
if (this.deltaManager.minimumSequenceNumber <
|
|
1489
|
+
messageWithContext.message.minimumSequenceNumber) {
|
|
1490
|
+
messageWithContext.message.minimumSequenceNumber =
|
|
1491
|
+
this.deltaManager.minimumSequenceNumber;
|
|
1492
|
+
}
|
|
1442
1493
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1443
1494
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1444
1495
|
// messages once a batch has been fully processed.
|
|
@@ -1496,9 +1547,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1496
1547
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
1497
1548
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
1498
1549
|
// thus we need to process all the ops.
|
|
1499
|
-
if (!(this.skipSavedCompressorOps &&
|
|
1500
|
-
messageWithContext.message.metadata?.savedOp ===
|
|
1501
|
-
true)) {
|
|
1550
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
1502
1551
|
const range = messageWithContext.message.contents;
|
|
1503
1552
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
1504
1553
|
// put it in a pending queue and delay finalization.
|
|
@@ -1507,6 +1556,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1507
1556
|
this.pendingIdCompressorOps.push(range);
|
|
1508
1557
|
}
|
|
1509
1558
|
else {
|
|
1559
|
+
assert(this.pendingIdCompressorOps.length === 0, 0x979 /* there should be no pending ops! */);
|
|
1510
1560
|
this._idCompressor.finalizeCreationRange(range);
|
|
1511
1561
|
}
|
|
1512
1562
|
}
|
|
@@ -1627,9 +1677,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1627
1677
|
let checkpoint;
|
|
1628
1678
|
let result;
|
|
1629
1679
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1630
|
-
// Note: we are not touching
|
|
1631
|
-
// 1. It would not help, as
|
|
1632
|
-
// 2. There is no way to undo process of data store creation.
|
|
1680
|
+
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
1681
|
+
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
1682
|
+
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
1633
1683
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1634
1684
|
}
|
|
1635
1685
|
try {
|
|
@@ -2030,10 +2080,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2030
2080
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
2031
2081
|
// use it for all events logged during this summary.
|
|
2032
2082
|
const summaryNumber = this.nextSummaryNumber;
|
|
2083
|
+
let summaryRefSeqNum;
|
|
2033
2084
|
const summaryNumberLogger = createChildLogger({
|
|
2034
2085
|
logger: summaryLogger,
|
|
2035
2086
|
properties: {
|
|
2036
|
-
all: {
|
|
2087
|
+
all: {
|
|
2088
|
+
summaryNumber,
|
|
2089
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
2090
|
+
},
|
|
2037
2091
|
},
|
|
2038
2092
|
});
|
|
2039
2093
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
@@ -2047,7 +2101,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2047
2101
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
2048
2102
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
2049
2103
|
// saved within the timeout, check if it should be failed or can continue.
|
|
2050
|
-
if (this.
|
|
2104
|
+
if (this.isDirty) {
|
|
2051
2105
|
const countBefore = this.pendingMessagesCount;
|
|
2052
2106
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
2053
2107
|
const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
|
|
@@ -2080,7 +2134,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2080
2134
|
}
|
|
2081
2135
|
const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
|
|
2082
2136
|
const shouldValidatePreSummaryState = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState") === true;
|
|
2083
|
-
let summaryRefSeqNum;
|
|
2084
2137
|
try {
|
|
2085
2138
|
await this.deltaManager.inbound.pause();
|
|
2086
2139
|
if (shouldPauseInboundSignal) {
|
|
@@ -2113,7 +2166,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2113
2166
|
stage: "base",
|
|
2114
2167
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2115
2168
|
minimumSequenceNumber,
|
|
2116
|
-
error: `Summarizer node state inconsistent with summarizer state
|
|
2169
|
+
error: new LoggingError(`Summarizer node state inconsistent with summarizer state.`),
|
|
2117
2170
|
};
|
|
2118
2171
|
}
|
|
2119
2172
|
}
|
|
@@ -2156,7 +2209,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2156
2209
|
stage: "base",
|
|
2157
2210
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2158
2211
|
minimumSequenceNumber,
|
|
2159
|
-
error: continueResult.error,
|
|
2212
|
+
error: new LoggingError(continueResult.error),
|
|
2160
2213
|
};
|
|
2161
2214
|
}
|
|
2162
2215
|
const trace = Trace.start();
|
|
@@ -2173,6 +2226,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2173
2226
|
});
|
|
2174
2227
|
}
|
|
2175
2228
|
catch (error) {
|
|
2229
|
+
return {
|
|
2230
|
+
stage: "base",
|
|
2231
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2232
|
+
minimumSequenceNumber,
|
|
2233
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2236
|
+
// Validate that the summary generated by summarizer nodes is correct before uploading.
|
|
2237
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
2238
|
+
if (!validateResult.success) {
|
|
2239
|
+
const { success, ...loggingProps } = validateResult;
|
|
2240
|
+
const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
2176
2241
|
return {
|
|
2177
2242
|
stage: "base",
|
|
2178
2243
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
@@ -2180,24 +2245,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2180
2245
|
error,
|
|
2181
2246
|
};
|
|
2182
2247
|
}
|
|
2183
|
-
// If
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
const { success, ...loggingProps } = validateResult;
|
|
2189
|
-
const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
2190
|
-
return {
|
|
2191
|
-
stage: "base",
|
|
2192
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
2193
|
-
minimumSequenceNumber,
|
|
2194
|
-
error,
|
|
2195
|
-
};
|
|
2196
|
-
}
|
|
2197
|
-
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
2198
|
-
if (pendingMessagesFailResult !== undefined) {
|
|
2199
|
-
return pendingMessagesFailResult;
|
|
2200
|
-
}
|
|
2248
|
+
// If there are pending unacked ops, this summary attempt may fail as the uploaded
|
|
2249
|
+
// summary would be eventually inconsistent.
|
|
2250
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
2251
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
2252
|
+
return pendingMessagesFailResult;
|
|
2201
2253
|
}
|
|
2202
2254
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
2203
2255
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -2230,7 +2282,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2230
2282
|
};
|
|
2231
2283
|
continueResult = checkContinue();
|
|
2232
2284
|
if (!continueResult.continue) {
|
|
2233
|
-
return {
|
|
2285
|
+
return {
|
|
2286
|
+
stage: "generate",
|
|
2287
|
+
...generateSummaryData,
|
|
2288
|
+
error: new LoggingError(continueResult.error),
|
|
2289
|
+
};
|
|
2234
2290
|
}
|
|
2235
2291
|
const summaryContext = lastAck === undefined
|
|
2236
2292
|
? {
|
|
@@ -2248,7 +2304,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2248
2304
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
2249
2305
|
}
|
|
2250
2306
|
catch (error) {
|
|
2251
|
-
return {
|
|
2307
|
+
return {
|
|
2308
|
+
stage: "generate",
|
|
2309
|
+
...generateSummaryData,
|
|
2310
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2311
|
+
};
|
|
2252
2312
|
}
|
|
2253
2313
|
const parent = summaryContext.ackHandle;
|
|
2254
2314
|
const summaryMessage = {
|
|
@@ -2265,14 +2325,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2265
2325
|
};
|
|
2266
2326
|
continueResult = checkContinue();
|
|
2267
2327
|
if (!continueResult.continue) {
|
|
2268
|
-
return {
|
|
2328
|
+
return {
|
|
2329
|
+
stage: "upload",
|
|
2330
|
+
...uploadData,
|
|
2331
|
+
error: new LoggingError(continueResult.error),
|
|
2332
|
+
};
|
|
2269
2333
|
}
|
|
2270
2334
|
let clientSequenceNumber;
|
|
2271
2335
|
try {
|
|
2272
2336
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
2273
2337
|
}
|
|
2274
2338
|
catch (error) {
|
|
2275
|
-
return {
|
|
2339
|
+
return {
|
|
2340
|
+
stage: "upload",
|
|
2341
|
+
...uploadData,
|
|
2342
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2343
|
+
};
|
|
2276
2344
|
}
|
|
2277
2345
|
const submitData = {
|
|
2278
2346
|
stage: "submit",
|
|
@@ -2281,11 +2349,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2281
2349
|
submitOpDuration: trace.trace().duration,
|
|
2282
2350
|
};
|
|
2283
2351
|
try {
|
|
2284
|
-
|
|
2285
|
-
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
2352
|
+
this.summarizerNode.completeSummary(handle);
|
|
2286
2353
|
}
|
|
2287
2354
|
catch (error) {
|
|
2288
|
-
return {
|
|
2355
|
+
return {
|
|
2356
|
+
stage: "upload",
|
|
2357
|
+
...uploadData,
|
|
2358
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2359
|
+
};
|
|
2289
2360
|
}
|
|
2290
2361
|
return submitData;
|
|
2291
2362
|
}
|
|
@@ -2374,9 +2445,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2374
2445
|
this.verifyNotClosed();
|
|
2375
2446
|
return this.blobManager.createBlob(blob, signal);
|
|
2376
2447
|
}
|
|
2377
|
-
submitIdAllocationOpIfNeeded() {
|
|
2448
|
+
submitIdAllocationOpIfNeeded(resubmitOutstandingRanges = false) {
|
|
2378
2449
|
if (this._idCompressor) {
|
|
2379
|
-
const idRange =
|
|
2450
|
+
const idRange = resubmitOutstandingRanges
|
|
2451
|
+
? this.idCompressor?.takeUnfinalizedCreationRange()
|
|
2452
|
+
: this._idCompressor.takeNextCreationRange();
|
|
2380
2453
|
// Don't include the idRange if there weren't any Ids allocated
|
|
2381
2454
|
if (idRange?.ids !== undefined) {
|
|
2382
2455
|
const idAllocationMessage = {
|
|
@@ -2393,7 +2466,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2393
2466
|
}
|
|
2394
2467
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
2395
2468
|
this.verifyNotClosed();
|
|
2396
|
-
this.verifyCanSubmitOps();
|
|
2397
2469
|
// There should be no ops in detached container state!
|
|
2398
2470
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
2399
2471
|
assert(metadata === undefined ||
|
|
@@ -2429,6 +2501,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2429
2501
|
// on this callback to do actual sending.
|
|
2430
2502
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
2431
2503
|
if (contents) {
|
|
2504
|
+
this.logger.sendTelemetryEvent({
|
|
2505
|
+
eventName: "SchemaChangeProposal",
|
|
2506
|
+
refSeq: contents.refSeq,
|
|
2507
|
+
version: contents.version,
|
|
2508
|
+
newRuntimeSchema: JSON.stringify(contents.runtime),
|
|
2509
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
2510
|
+
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
2511
|
+
});
|
|
2432
2512
|
const msg = {
|
|
2433
2513
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
2434
2514
|
contents,
|
|
@@ -2438,32 +2518,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2438
2518
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2439
2519
|
});
|
|
2440
2520
|
}
|
|
2441
|
-
|
|
2442
|
-
// Is it safe:
|
|
2443
|
-
// Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
|
|
2444
|
-
// They become visible only when aliased, or handle to some sub-element of newly created datastore
|
|
2445
|
-
// is stored in some DDS, i.e. only after some other op.
|
|
2446
|
-
// Why:
|
|
2447
|
-
// Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
|
|
2448
|
-
// stores are created, causing issues like relay service throttling (too many ops) and catastrophic
|
|
2449
|
-
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
2450
|
-
// these issues.
|
|
2451
|
-
// Cons:
|
|
2452
|
-
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
2453
|
-
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
2454
|
-
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
2455
|
-
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
2456
|
-
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
2457
|
-
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
2458
|
-
// issue than sending.
|
|
2459
|
-
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2460
|
-
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2461
|
-
if (this.currentlyBatching() &&
|
|
2462
|
-
type === ContainerMessageType.Attach &&
|
|
2463
|
-
this.disableAttachReorder !== true) {
|
|
2464
|
-
this.outbox.submitAttach(message);
|
|
2465
|
-
}
|
|
2466
|
-
else if (type === ContainerMessageType.BlobAttach) {
|
|
2521
|
+
if (type === ContainerMessageType.BlobAttach) {
|
|
2467
2522
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
2468
2523
|
this.outbox.submitBlobAttach(message);
|
|
2469
2524
|
}
|
|
@@ -2538,32 +2593,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2538
2593
|
throw new Error("Runtime is closed");
|
|
2539
2594
|
}
|
|
2540
2595
|
}
|
|
2541
|
-
verifyCanSubmitOps() {
|
|
2542
|
-
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
2543
|
-
const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
2544
|
-
if (this.opReentryCallsToReport > 0) {
|
|
2545
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
|
|
2546
|
-
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
2547
|
-
getLongStack(() => new UsageError(errorMessage)));
|
|
2548
|
-
this.opReentryCallsToReport--;
|
|
2549
|
-
}
|
|
2550
|
-
// Creating ops while processing ops can lead
|
|
2551
|
-
// to undefined behavior and events observed in the wrong order.
|
|
2552
|
-
// For example, we have two callbacks registered for a DDS, A and B.
|
|
2553
|
-
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
2554
|
-
//
|
|
2555
|
-
// A because of #1
|
|
2556
|
-
// A because of #2
|
|
2557
|
-
// B because of #2
|
|
2558
|
-
// B because of #1
|
|
2559
|
-
//
|
|
2560
|
-
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
2561
|
-
// while ops are being processed.
|
|
2562
|
-
if (this.enableOpReentryCheck) {
|
|
2563
|
-
throw new UsageError(errorMessage);
|
|
2564
|
-
}
|
|
2565
|
-
}
|
|
2566
|
-
}
|
|
2567
2596
|
reSubmitBatch(batch) {
|
|
2568
2597
|
this.orderSequentially(() => {
|
|
2569
2598
|
for (const message of batch) {
|
|
@@ -2595,11 +2624,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2595
2624
|
this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
|
|
2596
2625
|
break;
|
|
2597
2626
|
case ContainerMessageType.IdAllocation: {
|
|
2598
|
-
|
|
2627
|
+
// Allocation ops are never resubmitted/rebased. This is because they require special handling to
|
|
2628
|
+
// avoid being submitted out of order. For example, if the pending state manager contained
|
|
2629
|
+
// [idOp1, dataOp1, idOp2, dataOp2] and the resubmission of dataOp1 generated idOp3, that would be
|
|
2630
|
+
// placed into the outbox in the same batch as idOp1, but before idOp2 is resubmitted.
|
|
2631
|
+
// To avoid this, allocation ops are simply never resubmitted. Prior to invoking the pending state
|
|
2632
|
+
// manager to replay pending ops, the runtime will always submit a new allocation range that includes
|
|
2633
|
+
// all pending IDs. The resubmitted allocation ops are then ignored here.
|
|
2599
2634
|
break;
|
|
2600
2635
|
}
|
|
2601
|
-
case ContainerMessageType.ChunkedOp:
|
|
2602
|
-
throw new Error(`chunkedOp not expected here`);
|
|
2603
2636
|
case ContainerMessageType.BlobAttach:
|
|
2604
2637
|
this.blobManager.reSubmit(opMetadata);
|
|
2605
2638
|
break;
|
|
@@ -2626,7 +2659,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2626
2659
|
});
|
|
2627
2660
|
}
|
|
2628
2661
|
else {
|
|
2629
|
-
const error = DataProcessingError.create("Resubmitting runtime message of
|
|
2662
|
+
const error = DataProcessingError.create("Resubmitting runtime message of unexpected type", "reSubmitCore", undefined /* sequencedMessage */, {
|
|
2630
2663
|
messageDetails: JSON.stringify({
|
|
2631
2664
|
type: message.type,
|
|
2632
2665
|
compatBehavior,
|
|
@@ -2693,7 +2726,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2693
2726
|
await this.closeStaleSummarizer();
|
|
2694
2727
|
return {
|
|
2695
2728
|
stage: "base",
|
|
2696
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2729
|
+
error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
2697
2730
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2698
2731
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2699
2732
|
};
|
|
@@ -2737,16 +2770,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2737
2770
|
}
|
|
2738
2771
|
this.imminentClosure || (this.imminentClosure = props?.notifyImminentClosure ?? false);
|
|
2739
2772
|
const getSyncState = (pendingAttachmentBlobs) => {
|
|
2740
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
2741
|
-
|
|
2742
|
-
return; // no pending state to save
|
|
2743
|
-
}
|
|
2773
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
2774
|
+
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
2744
2775
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
2745
2776
|
return {
|
|
2746
2777
|
pending,
|
|
2747
2778
|
pendingIdCompressorState,
|
|
2748
2779
|
pendingAttachmentBlobs,
|
|
2749
|
-
sessionExpiryTimerStarted
|
|
2780
|
+
sessionExpiryTimerStarted,
|
|
2750
2781
|
};
|
|
2751
2782
|
};
|
|
2752
2783
|
const perfEvent = {
|
|
@@ -2816,7 +2847,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2816
2847
|
}
|
|
2817
2848
|
}
|
|
2818
2849
|
get groupedBatchingEnabled() {
|
|
2819
|
-
return this.
|
|
2850
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
2820
2851
|
}
|
|
2821
2852
|
}
|
|
2822
2853
|
//# sourceMappingURL=containerRuntime.js.map
|