@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254674 → 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/lib/containerRuntime.js
CHANGED
|
@@ -25,7 +25,7 @@ import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
|
|
|
25
25
|
import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy.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";
|
|
@@ -60,6 +60,11 @@ export const DefaultSummaryConfiguration = {
|
|
|
60
60
|
runtimeOpWeight: 1.0,
|
|
61
61
|
nonRuntimeHeuristicThreshold: 20,
|
|
62
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Error responses when requesting a deleted object will have this header set to true
|
|
65
|
+
* @alpha
|
|
66
|
+
*/
|
|
67
|
+
export const DeletedResponseHeaderKey = "wasDeleted";
|
|
63
68
|
/**
|
|
64
69
|
* Tombstone error responses will have this header set to true
|
|
65
70
|
* @alpha
|
|
@@ -241,7 +246,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
241
246
|
},
|
|
242
247
|
});
|
|
243
248
|
const mc = loggerToMonitoringContext(logger);
|
|
244
|
-
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 = false, explicitSchemaControl = false, } = runtimeOptions;
|
|
245
250
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
246
251
|
const tryFetchBlob = async (blobName) => {
|
|
247
252
|
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
@@ -269,9 +274,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
269
274
|
const messageAtLastSummary = lastMessageFromMetadata(metadata);
|
|
270
275
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
271
276
|
const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
|
|
277
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
272
278
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
273
279
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
274
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
275
280
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
276
281
|
if (loadSequenceNumberVerification !== "bypass" &&
|
|
277
282
|
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
@@ -323,7 +328,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
323
328
|
?.idCompressorMode;
|
|
324
329
|
// This is the only exception to the rule above - we have proper plumbing to load ID compressor on schema change
|
|
325
330
|
// event. It is loaded async (relative to op processing), so this conversion is only safe for off -> delayed conversion!
|
|
326
|
-
|
|
331
|
+
// Clients do not expect ID compressor ops unless ID compressor is On for them, and that could be achieved only through
|
|
332
|
+
// explicit schema change, i.e. only if explicitSchemaControl is on.
|
|
333
|
+
// Note: it would be better if we throw on combination of options (explicitSchemaControl = off, desiredIdCompressorMode === "delayed")
|
|
334
|
+
// that is not supported. But our service tests are oblivious to these problems and throwing here will cause a ton of failures
|
|
335
|
+
// We ignored incompatible ID compressor changes from the start (they were sticky), so that's not a new problem being introduced...
|
|
336
|
+
if (idCompressorMode === undefined &&
|
|
337
|
+
desiredIdCompressorMode === "delayed" &&
|
|
338
|
+
explicitSchemaControl) {
|
|
327
339
|
idCompressorMode = desiredIdCompressorMode;
|
|
328
340
|
}
|
|
329
341
|
}
|
|
@@ -362,7 +374,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
362
374
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
363
375
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
364
376
|
const opGroupingEnabled = disableGroupedBatching !== true && enableGroupedBatching;
|
|
365
|
-
const documentSchemaController = new DocumentsSchemaController(existing, metadata?.documentSchema, {
|
|
377
|
+
const documentSchemaController = new DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
|
|
366
378
|
explicitSchemaControl,
|
|
367
379
|
compressionLz4,
|
|
368
380
|
idCompressorMode,
|
|
@@ -385,7 +397,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
385
397
|
chunkSizeInBytes,
|
|
386
398
|
// Requires<> drops undefined from IdCompressorType
|
|
387
399
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor,
|
|
388
|
-
enableOpReentryCheck,
|
|
389
400
|
enableGroupedBatching,
|
|
390
401
|
explicitSchemaControl,
|
|
391
402
|
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined);
|
|
@@ -424,11 +435,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
424
435
|
get attachState() {
|
|
425
436
|
return this._getAttachState();
|
|
426
437
|
}
|
|
427
|
-
|
|
438
|
+
/**
|
|
439
|
+
* Current session schema - defines what options are on & off.
|
|
440
|
+
* It's overlap of document schema (controlled by summary & ops) and options controlling this session.
|
|
441
|
+
* For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
|
|
442
|
+
* In such case it will be off in session schema (i.e. this session should not use compression), but this client
|
|
443
|
+
* has to deal with compressed ops as other clients might send them.
|
|
444
|
+
* And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
|
|
445
|
+
* In such case it will be off in session schema, however this client will propose change to schema, and once / if
|
|
446
|
+
* this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
|
|
447
|
+
*/
|
|
448
|
+
get sessionSchema() {
|
|
428
449
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
429
450
|
}
|
|
430
451
|
get idCompressorMode() {
|
|
431
|
-
return this.
|
|
452
|
+
return this.sessionSchema.idCompressorMode;
|
|
432
453
|
}
|
|
433
454
|
/**
|
|
434
455
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -536,13 +557,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
536
557
|
this.flushTaskExists = false;
|
|
537
558
|
this.consecutiveReconnects = 0;
|
|
538
559
|
this.ensureNoDataModelChangesCalls = 0;
|
|
539
|
-
/**
|
|
540
|
-
* Tracks the number of detected reentrant ops to report,
|
|
541
|
-
* in order to self-throttle the telemetry events.
|
|
542
|
-
*
|
|
543
|
-
* This should be removed as part of ADO:2322
|
|
544
|
-
*/
|
|
545
|
-
this.opReentryCallsToReport = 5;
|
|
546
560
|
this._disposed = false;
|
|
547
561
|
this.emitDirtyDocumentEvent = true;
|
|
548
562
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -569,7 +583,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
569
583
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
570
584
|
// compression.
|
|
571
585
|
const compressionOptions = {
|
|
572
|
-
minimumBatchSizeInBytes: this.
|
|
586
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
573
587
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
574
588
|
: Number.POSITIVE_INFINITY,
|
|
575
589
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
@@ -653,11 +667,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
653
667
|
if (this.summaryConfiguration.state === "enabled") {
|
|
654
668
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
655
669
|
}
|
|
656
|
-
const disableOpReentryCheck = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck");
|
|
657
|
-
this.enableOpReentryCheck =
|
|
658
|
-
runtimeOptions.enableOpReentryCheck === true &&
|
|
659
|
-
// Allow for a break-glass config to override the options
|
|
660
|
-
disableOpReentryCheck !== true;
|
|
661
670
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
662
671
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
663
672
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -729,7 +738,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
729
738
|
const envelope2 = this.createNewSignalEnvelope(envelope1.address, type, envelope1.contents);
|
|
730
739
|
return this.submitSignalFn(envelope2, targetClientId);
|
|
731
740
|
};
|
|
732
|
-
this.channelCollection = new ChannelCollection(getSummaryForDatastores(baseSnapshot, metadata), parentContext, this.mc.logger, (
|
|
741
|
+
this.channelCollection = new ChannelCollection(getSummaryForDatastores(baseSnapshot, metadata), parentContext, this.mc.logger, (props) => this.garbageCollector.nodeUpdated(props), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
|
|
733
742
|
this.blobManager = new BlobManager({
|
|
734
743
|
routeContext: this.handleContext,
|
|
735
744
|
snapshot: blobManagerSnapshot,
|
|
@@ -742,7 +751,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
742
751
|
});
|
|
743
752
|
}
|
|
744
753
|
},
|
|
745
|
-
blobRequested: (blobPath) => this.garbageCollector.nodeUpdated(
|
|
754
|
+
blobRequested: (blobPath) => this.garbageCollector.nodeUpdated({
|
|
755
|
+
node: { type: "Blob", path: blobPath },
|
|
756
|
+
reason: "Loaded",
|
|
757
|
+
}),
|
|
746
758
|
isBlobDeleted: (blobPath) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
747
759
|
runtime: this,
|
|
748
760
|
stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
|
|
@@ -790,12 +802,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
790
802
|
this._quorum.on("removeMember", (clientId) => {
|
|
791
803
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
792
804
|
});
|
|
793
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
794
805
|
this._audience = audience;
|
|
806
|
+
if (audience.getSelf === undefined) {
|
|
807
|
+
// back-compat, added in 2.0 RC3.
|
|
808
|
+
// Purpose: deal with cases when we run against old loader that does not have newly added capabilities
|
|
809
|
+
audience.getSelf = () => {
|
|
810
|
+
const clientId = this._getClientId();
|
|
811
|
+
return clientId === undefined
|
|
812
|
+
? undefined
|
|
813
|
+
: ({
|
|
814
|
+
clientId,
|
|
815
|
+
client: audience.getMember(clientId),
|
|
816
|
+
});
|
|
817
|
+
};
|
|
818
|
+
let oldClientId = this.clientId;
|
|
819
|
+
this.on("connected", () => {
|
|
820
|
+
const clientId = this.clientId;
|
|
821
|
+
assert(clientId !== undefined, "can't be undefined");
|
|
822
|
+
audience.emit("selfChanged", { clientId: oldClientId }, { clientId, client: audience.getMember(clientId) });
|
|
823
|
+
oldClientId = clientId;
|
|
824
|
+
});
|
|
825
|
+
}
|
|
795
826
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
796
827
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
797
|
-
this.validateSummaryBeforeUpload =
|
|
798
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
799
828
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
800
829
|
this.dirtyContainer =
|
|
801
830
|
this.attachState !== AttachState.Attached || this.hasPendingMessages();
|
|
@@ -868,9 +897,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
868
897
|
options: JSON.stringify(runtimeOptions),
|
|
869
898
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
870
899
|
idCompressorMode: this.idCompressorMode,
|
|
900
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
871
901
|
featureGates: JSON.stringify({
|
|
872
902
|
...featureGatesForTelemetry,
|
|
873
|
-
disableOpReentryCheck,
|
|
874
903
|
disableChunking,
|
|
875
904
|
disableAttachReorder: this.disableAttachReorder,
|
|
876
905
|
disablePartialFlush,
|
|
@@ -878,6 +907,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
878
907
|
}),
|
|
879
908
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
880
909
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
910
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
881
911
|
});
|
|
882
912
|
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
|
|
883
913
|
BindBatchTracker(this, this.logger);
|
|
@@ -893,6 +923,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
893
923
|
this.skipSavedCompressorOps = pendingRuntimeState?.pendingIdCompressorState !== undefined;
|
|
894
924
|
}
|
|
895
925
|
onSchemaChange(schema) {
|
|
926
|
+
this.logger.sendTelemetryEvent({
|
|
927
|
+
eventName: "SchemaChangeAccept",
|
|
928
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
929
|
+
});
|
|
896
930
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
897
931
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
898
932
|
// this is not done.
|
|
@@ -925,9 +959,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
925
959
|
async initializeBaseState() {
|
|
926
960
|
if (this.idCompressorMode === "on" ||
|
|
927
961
|
(this.idCompressorMode === "delayed" && this.connected)) {
|
|
962
|
+
this._idCompressor = await this.createIdCompressor();
|
|
928
963
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
929
964
|
assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
930
|
-
this._idCompressor = await this.createIdCompressor();
|
|
931
965
|
}
|
|
932
966
|
await this.garbageCollector.initializeBaseState();
|
|
933
967
|
}
|
|
@@ -1305,12 +1339,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1305
1339
|
this._loadIdCompressor === undefined) {
|
|
1306
1340
|
this._loadIdCompressor = this.createIdCompressor()
|
|
1307
1341
|
.then((compressor) => {
|
|
1308
|
-
this._idCompressor = compressor;
|
|
1309
1342
|
// Finalize any ranges we received while the compressor was turned off.
|
|
1310
|
-
|
|
1311
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
1312
|
-
}
|
|
1343
|
+
const ops = this.pendingIdCompressorOps;
|
|
1313
1344
|
this.pendingIdCompressorOps = [];
|
|
1345
|
+
for (const range of ops) {
|
|
1346
|
+
compressor.finalizeCreationRange(range);
|
|
1347
|
+
}
|
|
1348
|
+
assert(this.pendingIdCompressorOps.length === 0, "No new ops added");
|
|
1349
|
+
this._idCompressor = compressor;
|
|
1314
1350
|
})
|
|
1315
1351
|
.catch((error) => {
|
|
1316
1352
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -1320,6 +1356,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1320
1356
|
return this._loadIdCompressor;
|
|
1321
1357
|
}
|
|
1322
1358
|
setConnectionState(connected, clientId) {
|
|
1359
|
+
// Validate we have consistent state
|
|
1360
|
+
const currentClientId = this._audience.getSelf()?.clientId;
|
|
1361
|
+
assert(clientId === currentClientId, "input clientId does not match Audience");
|
|
1362
|
+
assert(this.clientId === currentClientId, "this.clientId does not match Audience");
|
|
1323
1363
|
if (connected && this.idCompressorMode === "delayed") {
|
|
1324
1364
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1325
1365
|
this.loadIdCompressor();
|
|
@@ -1401,9 +1441,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1401
1441
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1402
1442
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
1403
1443
|
const messageCopy = { ...messageArg };
|
|
1444
|
+
const savedOp = messageCopy.metadata?.savedOp;
|
|
1404
1445
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1405
|
-
|
|
1406
|
-
|
|
1446
|
+
const msg = modernRuntimeMessage
|
|
1447
|
+
? {
|
|
1407
1448
|
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1408
1449
|
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1409
1450
|
// the result messages will be so. In the end modern bool being true only directs to
|
|
@@ -1411,12 +1452,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1411
1452
|
message: message,
|
|
1412
1453
|
local,
|
|
1413
1454
|
modernRuntimeMessage,
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1455
|
+
}
|
|
1456
|
+
: // Unrecognized message will be ignored.
|
|
1457
|
+
{
|
|
1458
|
+
message,
|
|
1459
|
+
local,
|
|
1460
|
+
modernRuntimeMessage,
|
|
1461
|
+
};
|
|
1462
|
+
msg.savedOp = savedOp;
|
|
1463
|
+
// ensure that we observe any re-entrancy, and if needed, rebase ops
|
|
1464
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
1420
1465
|
}
|
|
1421
1466
|
}
|
|
1422
1467
|
/**
|
|
@@ -1434,7 +1479,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1434
1479
|
// These calls should be made for all but chunked ops:
|
|
1435
1480
|
// 1) this.pendingStateManager.processPendingLocalMessage() below
|
|
1436
1481
|
// 2) this.resetReconnectCount() below
|
|
1437
|
-
assert(message.type !== ContainerMessageType.ChunkedOp,
|
|
1482
|
+
assert(message.type !== ContainerMessageType.ChunkedOp, 0x93b /* we should never get here with chunked ops */);
|
|
1438
1483
|
let localOpMetadata;
|
|
1439
1484
|
if (local && messageWithContext.modernRuntimeMessage) {
|
|
1440
1485
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
@@ -1481,17 +1526,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1481
1526
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
1482
1527
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
1483
1528
|
// thus we need to process all the ops.
|
|
1484
|
-
if (!(this.skipSavedCompressorOps &&
|
|
1485
|
-
messageWithContext.message.metadata?.savedOp ===
|
|
1486
|
-
true)) {
|
|
1529
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
1487
1530
|
const range = messageWithContext.message.contents;
|
|
1488
1531
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
1489
1532
|
// put it in a pending queue and delay finalization.
|
|
1490
1533
|
if (this._idCompressor === undefined) {
|
|
1491
|
-
assert(this.idCompressorMode !== undefined,
|
|
1534
|
+
assert(this.idCompressorMode !== undefined, 0x93c /* id compressor should be enabled */);
|
|
1492
1535
|
this.pendingIdCompressorOps.push(range);
|
|
1493
1536
|
}
|
|
1494
1537
|
else {
|
|
1538
|
+
assert(this.pendingIdCompressorOps.length === 0, "there should be no pending ops!");
|
|
1495
1539
|
this._idCompressor.finalizeCreationRange(range);
|
|
1496
1540
|
}
|
|
1497
1541
|
}
|
|
@@ -1502,7 +1546,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1502
1546
|
case ContainerMessageType.ChunkedOp:
|
|
1503
1547
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
1504
1548
|
// Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
|
|
1505
|
-
assert(false,
|
|
1549
|
+
assert(false, 0x93d /* should not even get here */);
|
|
1506
1550
|
case ContainerMessageType.Rejoin:
|
|
1507
1551
|
break;
|
|
1508
1552
|
case ContainerMessageType.DocumentSchemaChange:
|
|
@@ -1612,9 +1656,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1612
1656
|
let checkpoint;
|
|
1613
1657
|
let result;
|
|
1614
1658
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1615
|
-
// Note: we are not touching
|
|
1616
|
-
// 1. It would not help, as
|
|
1617
|
-
// 2. There is no way to undo process of data store creation.
|
|
1659
|
+
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
1660
|
+
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
1661
|
+
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
1618
1662
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1619
1663
|
}
|
|
1620
1664
|
try {
|
|
@@ -1677,7 +1721,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1677
1721
|
if (channel.entryPoint === undefined) {
|
|
1678
1722
|
throw new UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
|
|
1679
1723
|
}
|
|
1680
|
-
this.garbageCollector.nodeUpdated(
|
|
1724
|
+
this.garbageCollector.nodeUpdated({
|
|
1725
|
+
node: { type: "DataStore", path: `/${internalId}` },
|
|
1726
|
+
reason: "Loaded",
|
|
1727
|
+
packagePath: context.packagePath,
|
|
1728
|
+
});
|
|
1681
1729
|
return channel.entryPoint;
|
|
1682
1730
|
}
|
|
1683
1731
|
createDetachedDataStore(pkg, loadingGroupId) {
|
|
@@ -1801,7 +1849,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1801
1849
|
// We can finalize any allocated IDs since we're the only client
|
|
1802
1850
|
const idRange = this._idCompressor?.takeNextCreationRange();
|
|
1803
1851
|
if (idRange !== undefined) {
|
|
1804
|
-
assert(idRange.ids === undefined || idRange.ids.firstGenCount === 1,
|
|
1852
|
+
assert(idRange.ids === undefined || idRange.ids.firstGenCount === 1, 0x93e /* No other ranges should be taken while container is detached. */);
|
|
1805
1853
|
this._idCompressor?.finalizeCreationRange(idRange);
|
|
1806
1854
|
}
|
|
1807
1855
|
const summarizeResult = this.channelCollection.getAttachSummary(telemetryContext);
|
|
@@ -2011,10 +2059,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2011
2059
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
2012
2060
|
// use it for all events logged during this summary.
|
|
2013
2061
|
const summaryNumber = this.nextSummaryNumber;
|
|
2062
|
+
let summaryRefSeqNum;
|
|
2014
2063
|
const summaryNumberLogger = createChildLogger({
|
|
2015
2064
|
logger: summaryLogger,
|
|
2016
2065
|
properties: {
|
|
2017
|
-
all: {
|
|
2066
|
+
all: {
|
|
2067
|
+
summaryNumber,
|
|
2068
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
2069
|
+
},
|
|
2018
2070
|
},
|
|
2019
2071
|
});
|
|
2020
2072
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
@@ -2028,7 +2080,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2028
2080
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
2029
2081
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
2030
2082
|
// saved within the timeout, check if it should be failed or can continue.
|
|
2031
|
-
if (this.
|
|
2083
|
+
if (this.isDirty) {
|
|
2032
2084
|
const countBefore = this.pendingMessagesCount;
|
|
2033
2085
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
2034
2086
|
const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
|
|
@@ -2061,7 +2113,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2061
2113
|
}
|
|
2062
2114
|
const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
|
|
2063
2115
|
const shouldValidatePreSummaryState = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState") === true;
|
|
2064
|
-
let summaryRefSeqNum;
|
|
2065
2116
|
try {
|
|
2066
2117
|
await this.deltaManager.inbound.pause();
|
|
2067
2118
|
if (shouldPauseInboundSignal) {
|
|
@@ -2072,9 +2123,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2072
2123
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
2073
2124
|
const lastAck = this.summaryCollection.latestAck;
|
|
2074
2125
|
const startSummaryResult = this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger, latestSummaryRefSeqNum);
|
|
2126
|
+
/**
|
|
2127
|
+
* This was added to validate that the summarizer node tree has the same reference sequence number from the
|
|
2128
|
+
* top running summarizer down to the lowest summarizer node.
|
|
2129
|
+
*
|
|
2130
|
+
* The order of mismatch numbers goes (validate sequence number)-(node sequence number).
|
|
2131
|
+
* Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
|
|
2132
|
+
* summarizer nodes.
|
|
2133
|
+
*/
|
|
2075
2134
|
if (startSummaryResult.invalidNodes > 0 ||
|
|
2076
2135
|
startSummaryResult.mismatchNumbers.size > 0) {
|
|
2077
|
-
summaryLogger.
|
|
2136
|
+
summaryLogger.sendTelemetryEvent({
|
|
2078
2137
|
eventName: "LatestSummaryRefSeqNumMismatch",
|
|
2079
2138
|
details: {
|
|
2080
2139
|
...startSummaryResult,
|
|
@@ -2086,7 +2145,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2086
2145
|
stage: "base",
|
|
2087
2146
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2088
2147
|
minimumSequenceNumber,
|
|
2089
|
-
error: `Summarizer node state inconsistent with summarizer state
|
|
2148
|
+
error: new LoggingError(`Summarizer node state inconsistent with summarizer state.`),
|
|
2090
2149
|
};
|
|
2091
2150
|
}
|
|
2092
2151
|
}
|
|
@@ -2129,7 +2188,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2129
2188
|
stage: "base",
|
|
2130
2189
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2131
2190
|
minimumSequenceNumber,
|
|
2132
|
-
error: continueResult.error,
|
|
2191
|
+
error: new LoggingError(continueResult.error),
|
|
2133
2192
|
};
|
|
2134
2193
|
}
|
|
2135
2194
|
const trace = Trace.start();
|
|
@@ -2146,6 +2205,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2146
2205
|
});
|
|
2147
2206
|
}
|
|
2148
2207
|
catch (error) {
|
|
2208
|
+
return {
|
|
2209
|
+
stage: "base",
|
|
2210
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2211
|
+
minimumSequenceNumber,
|
|
2212
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
// Validate that the summary generated by summarizer nodes is correct before uploading.
|
|
2216
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
2217
|
+
if (!validateResult.success) {
|
|
2218
|
+
const { success, ...loggingProps } = validateResult;
|
|
2219
|
+
const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
2149
2220
|
return {
|
|
2150
2221
|
stage: "base",
|
|
2151
2222
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
@@ -2153,24 +2224,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2153
2224
|
error,
|
|
2154
2225
|
};
|
|
2155
2226
|
}
|
|
2156
|
-
// If
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
const { success, ...loggingProps } = validateResult;
|
|
2162
|
-
const error = new RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
2163
|
-
return {
|
|
2164
|
-
stage: "base",
|
|
2165
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
2166
|
-
minimumSequenceNumber,
|
|
2167
|
-
error,
|
|
2168
|
-
};
|
|
2169
|
-
}
|
|
2170
|
-
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
2171
|
-
if (pendingMessagesFailResult !== undefined) {
|
|
2172
|
-
return pendingMessagesFailResult;
|
|
2173
|
-
}
|
|
2227
|
+
// If there are pending unacked ops, this summary attempt may fail as the uploaded
|
|
2228
|
+
// summary would be eventually inconsistent.
|
|
2229
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
2230
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
2231
|
+
return pendingMessagesFailResult;
|
|
2174
2232
|
}
|
|
2175
2233
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
2176
2234
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -2203,7 +2261,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2203
2261
|
};
|
|
2204
2262
|
continueResult = checkContinue();
|
|
2205
2263
|
if (!continueResult.continue) {
|
|
2206
|
-
return {
|
|
2264
|
+
return {
|
|
2265
|
+
stage: "generate",
|
|
2266
|
+
...generateSummaryData,
|
|
2267
|
+
error: new LoggingError(continueResult.error),
|
|
2268
|
+
};
|
|
2207
2269
|
}
|
|
2208
2270
|
const summaryContext = lastAck === undefined
|
|
2209
2271
|
? {
|
|
@@ -2221,7 +2283,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2221
2283
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
2222
2284
|
}
|
|
2223
2285
|
catch (error) {
|
|
2224
|
-
return {
|
|
2286
|
+
return {
|
|
2287
|
+
stage: "generate",
|
|
2288
|
+
...generateSummaryData,
|
|
2289
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2290
|
+
};
|
|
2225
2291
|
}
|
|
2226
2292
|
const parent = summaryContext.ackHandle;
|
|
2227
2293
|
const summaryMessage = {
|
|
@@ -2238,14 +2304,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2238
2304
|
};
|
|
2239
2305
|
continueResult = checkContinue();
|
|
2240
2306
|
if (!continueResult.continue) {
|
|
2241
|
-
return {
|
|
2307
|
+
return {
|
|
2308
|
+
stage: "upload",
|
|
2309
|
+
...uploadData,
|
|
2310
|
+
error: new LoggingError(continueResult.error),
|
|
2311
|
+
};
|
|
2242
2312
|
}
|
|
2243
2313
|
let clientSequenceNumber;
|
|
2244
2314
|
try {
|
|
2245
2315
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
2246
2316
|
}
|
|
2247
2317
|
catch (error) {
|
|
2248
|
-
return {
|
|
2318
|
+
return {
|
|
2319
|
+
stage: "upload",
|
|
2320
|
+
...uploadData,
|
|
2321
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2322
|
+
};
|
|
2249
2323
|
}
|
|
2250
2324
|
const submitData = {
|
|
2251
2325
|
stage: "submit",
|
|
@@ -2254,11 +2328,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2254
2328
|
submitOpDuration: trace.trace().duration,
|
|
2255
2329
|
};
|
|
2256
2330
|
try {
|
|
2257
|
-
|
|
2258
|
-
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
2331
|
+
this.summarizerNode.completeSummary(handle);
|
|
2259
2332
|
}
|
|
2260
2333
|
catch (error) {
|
|
2261
|
-
return {
|
|
2334
|
+
return {
|
|
2335
|
+
stage: "upload",
|
|
2336
|
+
...uploadData,
|
|
2337
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2338
|
+
};
|
|
2262
2339
|
}
|
|
2263
2340
|
return submitData;
|
|
2264
2341
|
}
|
|
@@ -2366,11 +2443,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2366
2443
|
}
|
|
2367
2444
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
2368
2445
|
this.verifyNotClosed();
|
|
2369
|
-
this.verifyCanSubmitOps();
|
|
2370
2446
|
// There should be no ops in detached container state!
|
|
2371
2447
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
2372
2448
|
assert(metadata === undefined ||
|
|
2373
|
-
containerRuntimeMessage.type === ContainerMessageType.BlobAttach,
|
|
2449
|
+
containerRuntimeMessage.type === ContainerMessageType.BlobAttach, 0x93f /* metadata */);
|
|
2374
2450
|
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
2375
2451
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
2376
2452
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
@@ -2402,6 +2478,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2402
2478
|
// on this callback to do actual sending.
|
|
2403
2479
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
2404
2480
|
if (contents) {
|
|
2481
|
+
this.logger.sendTelemetryEvent({
|
|
2482
|
+
eventName: "SchemaChangeProposal",
|
|
2483
|
+
refSeq: contents.refSeq,
|
|
2484
|
+
version: contents.version,
|
|
2485
|
+
newRuntimeSchema: JSON.stringify(contents.runtime),
|
|
2486
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
2487
|
+
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
2488
|
+
});
|
|
2405
2489
|
const msg = {
|
|
2406
2490
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
2407
2491
|
contents,
|
|
@@ -2411,32 +2495,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2411
2495
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2412
2496
|
});
|
|
2413
2497
|
}
|
|
2414
|
-
|
|
2415
|
-
// Is it safe:
|
|
2416
|
-
// Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
|
|
2417
|
-
// They become visible only when aliased, or handle to some sub-element of newly created datastore
|
|
2418
|
-
// is stored in some DDS, i.e. only after some other op.
|
|
2419
|
-
// Why:
|
|
2420
|
-
// Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
|
|
2421
|
-
// stores are created, causing issues like relay service throttling (too many ops) and catastrophic
|
|
2422
|
-
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
2423
|
-
// these issues.
|
|
2424
|
-
// Cons:
|
|
2425
|
-
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
2426
|
-
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
2427
|
-
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
2428
|
-
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
2429
|
-
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
2430
|
-
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
2431
|
-
// issue than sending.
|
|
2432
|
-
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
2433
|
-
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
2434
|
-
if (this.currentlyBatching() &&
|
|
2435
|
-
type === ContainerMessageType.Attach &&
|
|
2436
|
-
this.disableAttachReorder !== true) {
|
|
2437
|
-
this.outbox.submitAttach(message);
|
|
2438
|
-
}
|
|
2439
|
-
else if (type === ContainerMessageType.BlobAttach) {
|
|
2498
|
+
if (type === ContainerMessageType.BlobAttach) {
|
|
2440
2499
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
2441
2500
|
this.outbox.submitBlobAttach(message);
|
|
2442
2501
|
}
|
|
@@ -2511,32 +2570,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2511
2570
|
throw new Error("Runtime is closed");
|
|
2512
2571
|
}
|
|
2513
2572
|
}
|
|
2514
|
-
verifyCanSubmitOps() {
|
|
2515
|
-
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
2516
|
-
const errorMessage = "Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
2517
|
-
if (this.opReentryCallsToReport > 0) {
|
|
2518
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
|
|
2519
|
-
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
2520
|
-
getLongStack(() => new UsageError(errorMessage)));
|
|
2521
|
-
this.opReentryCallsToReport--;
|
|
2522
|
-
}
|
|
2523
|
-
// Creating ops while processing ops can lead
|
|
2524
|
-
// to undefined behavior and events observed in the wrong order.
|
|
2525
|
-
// For example, we have two callbacks registered for a DDS, A and B.
|
|
2526
|
-
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
2527
|
-
//
|
|
2528
|
-
// A because of #1
|
|
2529
|
-
// A because of #2
|
|
2530
|
-
// B because of #2
|
|
2531
|
-
// B because of #1
|
|
2532
|
-
//
|
|
2533
|
-
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
2534
|
-
// while ops are being processed.
|
|
2535
|
-
if (this.enableOpReentryCheck) {
|
|
2536
|
-
throw new UsageError(errorMessage);
|
|
2537
|
-
}
|
|
2538
|
-
}
|
|
2539
|
-
}
|
|
2540
2573
|
reSubmitBatch(batch) {
|
|
2541
2574
|
this.orderSequentially(() => {
|
|
2542
2575
|
for (const message of batch) {
|
|
@@ -2666,7 +2699,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2666
2699
|
await this.closeStaleSummarizer();
|
|
2667
2700
|
return {
|
|
2668
2701
|
stage: "base",
|
|
2669
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2702
|
+
error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
2670
2703
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2671
2704
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2672
2705
|
};
|
|
@@ -2710,16 +2743,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2710
2743
|
}
|
|
2711
2744
|
this.imminentClosure || (this.imminentClosure = props?.notifyImminentClosure ?? false);
|
|
2712
2745
|
const getSyncState = (pendingAttachmentBlobs) => {
|
|
2713
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
2714
|
-
|
|
2715
|
-
return; // no pending state to save
|
|
2716
|
-
}
|
|
2746
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
2747
|
+
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
2717
2748
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
2718
2749
|
return {
|
|
2719
2750
|
pending,
|
|
2720
2751
|
pendingIdCompressorState,
|
|
2721
2752
|
pendingAttachmentBlobs,
|
|
2722
|
-
sessionExpiryTimerStarted
|
|
2753
|
+
sessionExpiryTimerStarted,
|
|
2723
2754
|
};
|
|
2724
2755
|
};
|
|
2725
2756
|
const perfEvent = {
|
|
@@ -2789,7 +2820,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2789
2820
|
}
|
|
2790
2821
|
}
|
|
2791
2822
|
get groupedBatchingEnabled() {
|
|
2792
|
-
return this.
|
|
2823
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
2793
2824
|
}
|
|
2794
2825
|
}
|
|
2795
2826
|
//# sourceMappingURL=containerRuntime.js.map
|