@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254866 → 2.0.0-dev-rc.5.0.0.263932
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 +69 -0
- package/api-report/container-runtime.api.md +93 -39
- 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 +10 -6
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +85 -22
- 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 +22 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +232 -174
- 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 +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/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 +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 +3 -3
- 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} +8 -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/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 +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/public.d.ts +3 -0
- 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 +34 -16
- 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/beta.d.ts → internal.d.ts} +2 -0
- package/{dist/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/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 +10 -6
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +88 -25
- 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 +22 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +232 -174
- 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 +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/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 +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 +3 -3
- 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} +8 -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/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 +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/public.d.ts +3 -0
- 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 +34 -16
- 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 +37 -59
- package/src/batchTracker.ts +1 -2
- package/src/blobManager.ts +11 -10
- package/src/channelCollection.ts +115 -47
- package/src/connectionTelemetry.ts +59 -4
- package/src/containerRuntime.ts +302 -270
- package/src/dataStore.ts +7 -4
- package/src/dataStoreContext.ts +57 -16
- package/src/dataStoreContexts.ts +13 -2
- package/src/{deltaManagerSummarizerProxy.ts → deltaManagerProxies.ts} +98 -24
- package/src/deltaScheduler.ts +2 -3
- 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 +7 -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/opDecompressor.ts +12 -6
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +1 -1
- 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 +52 -18
- 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";
|
|
@@ -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 = true, 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
|
}
|
|
@@ -356,23 +368,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
356
368
|
return createIdCompressor(compressorLogger);
|
|
357
369
|
}
|
|
358
370
|
};
|
|
359
|
-
const disableGroupedBatching = mc.config.getBoolean("Fluid.ContainerRuntime.DisableGroupedBatching");
|
|
360
371
|
const disableCompression = mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
|
|
361
372
|
const compressionLz4 = disableCompression !== true &&
|
|
362
373
|
compressionOptions.minimumBatchSizeInBytes !== Infinity &&
|
|
363
374
|
compressionOptions.compressionAlgorithm === "lz4";
|
|
364
|
-
const
|
|
365
|
-
const documentSchemaController = new DocumentsSchemaController(existing, metadata?.documentSchema, {
|
|
375
|
+
const documentSchemaController = new DocumentsSchemaController(existing, protocolSequenceNumber, metadata?.documentSchema, {
|
|
366
376
|
explicitSchemaControl,
|
|
367
377
|
compressionLz4,
|
|
368
378
|
idCompressorMode,
|
|
369
|
-
opGroupingEnabled,
|
|
379
|
+
opGroupingEnabled: enableGroupedBatching,
|
|
370
380
|
disallowedVersions: [],
|
|
371
381
|
}, (schema) => {
|
|
372
382
|
runtime.onSchemaChange(schema);
|
|
373
383
|
});
|
|
374
384
|
const featureGatesForTelemetry = {
|
|
375
|
-
disableGroupedBatching,
|
|
376
385
|
disableCompression,
|
|
377
386
|
};
|
|
378
387
|
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
@@ -385,7 +394,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
385
394
|
chunkSizeInBytes,
|
|
386
395
|
// Requires<> drops undefined from IdCompressorType
|
|
387
396
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor,
|
|
388
|
-
enableOpReentryCheck,
|
|
389
397
|
enableGroupedBatching,
|
|
390
398
|
explicitSchemaControl,
|
|
391
399
|
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined);
|
|
@@ -424,11 +432,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
424
432
|
get attachState() {
|
|
425
433
|
return this._getAttachState();
|
|
426
434
|
}
|
|
427
|
-
|
|
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() {
|
|
428
446
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
429
447
|
}
|
|
430
448
|
get idCompressorMode() {
|
|
431
|
-
return this.
|
|
449
|
+
return this.sessionSchema.idCompressorMode;
|
|
432
450
|
}
|
|
433
451
|
/**
|
|
434
452
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -536,13 +554,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
536
554
|
this.flushTaskExists = false;
|
|
537
555
|
this.consecutiveReconnects = 0;
|
|
538
556
|
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
557
|
this._disposed = false;
|
|
547
558
|
this.emitDirtyDocumentEvent = true;
|
|
548
559
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -559,7 +570,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
559
570
|
this.snapshotCacheForLoadingGroupIds = new PromiseCache({
|
|
560
571
|
expiry: { policy: "absolute", durationMs: 60000 },
|
|
561
572
|
});
|
|
562
|
-
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;
|
|
563
574
|
this.mc = createChildMonitoringContext({
|
|
564
575
|
logger: this.logger,
|
|
565
576
|
namespace: "ContainerRuntime",
|
|
@@ -569,13 +580,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
569
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)
|
|
570
581
|
// compression.
|
|
571
582
|
const compressionOptions = {
|
|
572
|
-
minimumBatchSizeInBytes: this.
|
|
583
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
573
584
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
574
585
|
: Number.POSITIVE_INFINITY,
|
|
575
586
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
576
587
|
};
|
|
577
588
|
this.innerDeltaManager = deltaManager;
|
|
578
|
-
this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
579
589
|
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
580
590
|
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
581
591
|
this.submitFn = submitFn;
|
|
@@ -649,15 +659,36 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
649
659
|
}, this.mc.logger);
|
|
650
660
|
const opSplitter = new OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
651
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;
|
|
652
688
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
653
689
|
if (this.summaryConfiguration.state === "enabled") {
|
|
654
690
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
655
691
|
}
|
|
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
692
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
662
693
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
663
694
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -673,7 +704,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
673
704
|
else {
|
|
674
705
|
this._flushMode = runtimeOptions.flushMode;
|
|
675
706
|
}
|
|
676
|
-
const pendingRuntimeState = pendingLocalState;
|
|
677
707
|
if (context.attachState === AttachState.Attached) {
|
|
678
708
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
679
709
|
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
@@ -729,7 +759,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
729
759
|
const envelope2 = this.createNewSignalEnvelope(envelope1.address, type, envelope1.contents);
|
|
730
760
|
return this.submitSignalFn(envelope2, targetClientId);
|
|
731
761
|
};
|
|
732
|
-
|
|
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);
|
|
733
770
|
this.blobManager = new BlobManager({
|
|
734
771
|
routeContext: this.handleContext,
|
|
735
772
|
snapshot: blobManagerSnapshot,
|
|
@@ -742,26 +779,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
742
779
|
});
|
|
743
780
|
}
|
|
744
781
|
},
|
|
745
|
-
blobRequested: (blobPath) => this.garbageCollector.nodeUpdated(
|
|
782
|
+
blobRequested: (blobPath) => this.garbageCollector.nodeUpdated({
|
|
783
|
+
node: { type: "Blob", path: blobPath },
|
|
784
|
+
reason: "Loaded",
|
|
785
|
+
}),
|
|
746
786
|
isBlobDeleted: (blobPath) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
747
787
|
runtime: this,
|
|
748
788
|
stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
|
|
749
789
|
closeContainer: (error) => this.closeFn(error),
|
|
750
790
|
});
|
|
751
791
|
this.scheduleManager = new ScheduleManager(this.innerDeltaManager, this, () => this.clientId, createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }));
|
|
752
|
-
this.pendingStateManager = new PendingStateManager({
|
|
753
|
-
applyStashedOp: this.applyStashedOp.bind(this),
|
|
754
|
-
clientId: () => this.clientId,
|
|
755
|
-
close: this.closeFn,
|
|
756
|
-
connected: () => this.connected,
|
|
757
|
-
reSubmit: (message) => {
|
|
758
|
-
this.reSubmit(message);
|
|
759
|
-
this.flush();
|
|
760
|
-
},
|
|
761
|
-
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
762
|
-
isActiveConnection: () => this.innerDeltaManager.active,
|
|
763
|
-
isAttached: () => this.attachState !== AttachState.Detached,
|
|
764
|
-
}, pendingRuntimeState?.pending, this.logger);
|
|
765
792
|
const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
|
|
766
793
|
const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
|
|
767
794
|
this.outbox = new Outbox({
|
|
@@ -790,12 +817,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
790
817
|
this._quorum.on("removeMember", (clientId) => {
|
|
791
818
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
792
819
|
});
|
|
793
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
794
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
|
+
}
|
|
795
841
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
796
842
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
797
|
-
this.validateSummaryBeforeUpload =
|
|
798
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
799
843
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
800
844
|
this.dirtyContainer =
|
|
801
845
|
this.attachState !== AttachState.Attached || this.hasPendingMessages();
|
|
@@ -868,9 +912,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
868
912
|
options: JSON.stringify(runtimeOptions),
|
|
869
913
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
870
914
|
idCompressorMode: this.idCompressorMode,
|
|
915
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
871
916
|
featureGates: JSON.stringify({
|
|
872
917
|
...featureGatesForTelemetry,
|
|
873
|
-
disableOpReentryCheck,
|
|
874
918
|
disableChunking,
|
|
875
919
|
disableAttachReorder: this.disableAttachReorder,
|
|
876
920
|
disablePartialFlush,
|
|
@@ -878,6 +922,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
878
922
|
}),
|
|
879
923
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
880
924
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
925
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
881
926
|
});
|
|
882
927
|
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
|
|
883
928
|
BindBatchTracker(this, this.logger);
|
|
@@ -893,6 +938,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
893
938
|
this.skipSavedCompressorOps = pendingRuntimeState?.pendingIdCompressorState !== undefined;
|
|
894
939
|
}
|
|
895
940
|
onSchemaChange(schema) {
|
|
941
|
+
this.logger.sendTelemetryEvent({
|
|
942
|
+
eventName: "SchemaChangeAccept",
|
|
943
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
944
|
+
});
|
|
896
945
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
897
946
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
898
947
|
// this is not done.
|
|
@@ -925,9 +974,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
925
974
|
async initializeBaseState() {
|
|
926
975
|
if (this.idCompressorMode === "on" ||
|
|
927
976
|
(this.idCompressorMode === "delayed" && this.connected)) {
|
|
977
|
+
this._idCompressor = await this.createIdCompressor();
|
|
928
978
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
929
979
|
assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
930
|
-
this._idCompressor = await this.createIdCompressor();
|
|
931
980
|
}
|
|
932
981
|
await this.garbageCollector.initializeBaseState();
|
|
933
982
|
}
|
|
@@ -1225,6 +1274,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1225
1274
|
this.emitDirtyDocumentEvent = false;
|
|
1226
1275
|
let newState;
|
|
1227
1276
|
try {
|
|
1277
|
+
this.submitIdAllocationOpIfNeeded(true);
|
|
1228
1278
|
// replay the ops
|
|
1229
1279
|
this.pendingStateManager.replayPendingStates();
|
|
1230
1280
|
}
|
|
@@ -1272,8 +1322,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1272
1322
|
return;
|
|
1273
1323
|
case ContainerMessageType.BlobAttach:
|
|
1274
1324
|
return;
|
|
1275
|
-
case ContainerMessageType.ChunkedOp:
|
|
1276
|
-
throw new Error("chunkedOp not expected here");
|
|
1277
1325
|
case ContainerMessageType.Rejoin:
|
|
1278
1326
|
throw new Error("rejoin not expected here");
|
|
1279
1327
|
case ContainerMessageType.GC:
|
|
@@ -1285,7 +1333,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1285
1333
|
// e.g. if an app rolled back its container version
|
|
1286
1334
|
const compatBehavior = opContents.compatDetails?.behavior;
|
|
1287
1335
|
if (!compatBehaviorAllowsMessageType(opContents.type, compatBehavior)) {
|
|
1288
|
-
const error = DataProcessingError.create("Stashed runtime message of
|
|
1336
|
+
const error = DataProcessingError.create("Stashed runtime message of unexpected type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1289
1337
|
messageDetails: JSON.stringify({
|
|
1290
1338
|
type: opContents.type,
|
|
1291
1339
|
compatBehavior,
|
|
@@ -1305,12 +1353,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1305
1353
|
this._loadIdCompressor === undefined) {
|
|
1306
1354
|
this._loadIdCompressor = this.createIdCompressor()
|
|
1307
1355
|
.then((compressor) => {
|
|
1308
|
-
this._idCompressor = compressor;
|
|
1309
1356
|
// Finalize any ranges we received while the compressor was turned off.
|
|
1310
|
-
|
|
1311
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
1312
|
-
}
|
|
1357
|
+
const ops = this.pendingIdCompressorOps;
|
|
1313
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;
|
|
1314
1364
|
})
|
|
1315
1365
|
.catch((error) => {
|
|
1316
1366
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -1320,6 +1370,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1320
1370
|
return this._loadIdCompressor;
|
|
1321
1371
|
}
|
|
1322
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 */);
|
|
1323
1377
|
if (connected && this.idCompressorMode === "delayed") {
|
|
1324
1378
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1325
1379
|
this.loadIdCompressor();
|
|
@@ -1401,9 +1455,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1401
1455
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
1402
1456
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
1403
1457
|
const messageCopy = { ...messageArg };
|
|
1458
|
+
const savedOp = messageCopy.metadata?.savedOp;
|
|
1404
1459
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1405
|
-
|
|
1406
|
-
|
|
1460
|
+
const msg = modernRuntimeMessage
|
|
1461
|
+
? {
|
|
1407
1462
|
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
1408
1463
|
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
1409
1464
|
// the result messages will be so. In the end modern bool being true only directs to
|
|
@@ -1411,12 +1466,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1411
1466
|
message: message,
|
|
1412
1467
|
local,
|
|
1413
1468
|
modernRuntimeMessage,
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
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));
|
|
1420
1479
|
}
|
|
1421
1480
|
}
|
|
1422
1481
|
/**
|
|
@@ -1424,6 +1483,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1424
1483
|
*/
|
|
1425
1484
|
processCore(messageWithContext) {
|
|
1426
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
|
+
}
|
|
1427
1493
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1428
1494
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1429
1495
|
// messages once a batch has been fully processed.
|
|
@@ -1434,7 +1500,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1434
1500
|
// These calls should be made for all but chunked ops:
|
|
1435
1501
|
// 1) this.pendingStateManager.processPendingLocalMessage() below
|
|
1436
1502
|
// 2) this.resetReconnectCount() below
|
|
1437
|
-
assert(message.type !== ContainerMessageType.ChunkedOp,
|
|
1503
|
+
assert(message.type !== ContainerMessageType.ChunkedOp, 0x93b /* we should never get here with chunked ops */);
|
|
1438
1504
|
let localOpMetadata;
|
|
1439
1505
|
if (local && messageWithContext.modernRuntimeMessage) {
|
|
1440
1506
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(messageWithContext.message);
|
|
@@ -1481,17 +1547,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1481
1547
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
1482
1548
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
1483
1549
|
// thus we need to process all the ops.
|
|
1484
|
-
if (!(this.skipSavedCompressorOps &&
|
|
1485
|
-
messageWithContext.message.metadata?.savedOp ===
|
|
1486
|
-
true)) {
|
|
1550
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
1487
1551
|
const range = messageWithContext.message.contents;
|
|
1488
1552
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
1489
1553
|
// put it in a pending queue and delay finalization.
|
|
1490
1554
|
if (this._idCompressor === undefined) {
|
|
1491
|
-
assert(this.idCompressorMode !== undefined,
|
|
1555
|
+
assert(this.idCompressorMode !== undefined, 0x93c /* id compressor should be enabled */);
|
|
1492
1556
|
this.pendingIdCompressorOps.push(range);
|
|
1493
1557
|
}
|
|
1494
1558
|
else {
|
|
1559
|
+
assert(this.pendingIdCompressorOps.length === 0, 0x979 /* there should be no pending ops! */);
|
|
1495
1560
|
this._idCompressor.finalizeCreationRange(range);
|
|
1496
1561
|
}
|
|
1497
1562
|
}
|
|
@@ -1502,7 +1567,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1502
1567
|
case ContainerMessageType.ChunkedOp:
|
|
1503
1568
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
1504
1569
|
// Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
|
|
1505
|
-
assert(false,
|
|
1570
|
+
assert(false, 0x93d /* should not even get here */);
|
|
1506
1571
|
case ContainerMessageType.Rejoin:
|
|
1507
1572
|
break;
|
|
1508
1573
|
case ContainerMessageType.DocumentSchemaChange:
|
|
@@ -1612,9 +1677,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1612
1677
|
let checkpoint;
|
|
1613
1678
|
let result;
|
|
1614
1679
|
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.
|
|
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.
|
|
1618
1683
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1619
1684
|
}
|
|
1620
1685
|
try {
|
|
@@ -1677,7 +1742,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1677
1742
|
if (channel.entryPoint === undefined) {
|
|
1678
1743
|
throw new UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
|
|
1679
1744
|
}
|
|
1680
|
-
this.garbageCollector.nodeUpdated(
|
|
1745
|
+
this.garbageCollector.nodeUpdated({
|
|
1746
|
+
node: { type: "DataStore", path: `/${internalId}` },
|
|
1747
|
+
reason: "Loaded",
|
|
1748
|
+
packagePath: context.packagePath,
|
|
1749
|
+
});
|
|
1681
1750
|
return channel.entryPoint;
|
|
1682
1751
|
}
|
|
1683
1752
|
createDetachedDataStore(pkg, loadingGroupId) {
|
|
@@ -1801,7 +1870,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1801
1870
|
// We can finalize any allocated IDs since we're the only client
|
|
1802
1871
|
const idRange = this._idCompressor?.takeNextCreationRange();
|
|
1803
1872
|
if (idRange !== undefined) {
|
|
1804
|
-
assert(idRange.ids === undefined || idRange.ids.firstGenCount === 1,
|
|
1873
|
+
assert(idRange.ids === undefined || idRange.ids.firstGenCount === 1, 0x93e /* No other ranges should be taken while container is detached. */);
|
|
1805
1874
|
this._idCompressor?.finalizeCreationRange(idRange);
|
|
1806
1875
|
}
|
|
1807
1876
|
const summarizeResult = this.channelCollection.getAttachSummary(telemetryContext);
|
|
@@ -1848,7 +1917,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1848
1917
|
return { stats, summary };
|
|
1849
1918
|
}
|
|
1850
1919
|
finally {
|
|
1851
|
-
|
|
1920
|
+
summaryLogger.sendTelemetryEvent({
|
|
1852
1921
|
eventName: "SummarizeTelemetry",
|
|
1853
1922
|
details: telemetryContext.serialize(),
|
|
1854
1923
|
});
|
|
@@ -2011,10 +2080,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2011
2080
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
2012
2081
|
// use it for all events logged during this summary.
|
|
2013
2082
|
const summaryNumber = this.nextSummaryNumber;
|
|
2083
|
+
let summaryRefSeqNum;
|
|
2014
2084
|
const summaryNumberLogger = createChildLogger({
|
|
2015
2085
|
logger: summaryLogger,
|
|
2016
2086
|
properties: {
|
|
2017
|
-
all: {
|
|
2087
|
+
all: {
|
|
2088
|
+
summaryNumber,
|
|
2089
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
2090
|
+
},
|
|
2018
2091
|
},
|
|
2019
2092
|
});
|
|
2020
2093
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
@@ -2028,7 +2101,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2028
2101
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
2029
2102
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
2030
2103
|
// saved within the timeout, check if it should be failed or can continue.
|
|
2031
|
-
if (this.
|
|
2104
|
+
if (this.isDirty) {
|
|
2032
2105
|
const countBefore = this.pendingMessagesCount;
|
|
2033
2106
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
2034
2107
|
const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
|
|
@@ -2061,7 +2134,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2061
2134
|
}
|
|
2062
2135
|
const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
|
|
2063
2136
|
const shouldValidatePreSummaryState = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState") === true;
|
|
2064
|
-
let summaryRefSeqNum;
|
|
2065
2137
|
try {
|
|
2066
2138
|
await this.deltaManager.inbound.pause();
|
|
2067
2139
|
if (shouldPauseInboundSignal) {
|
|
@@ -2072,9 +2144,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2072
2144
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
2073
2145
|
const lastAck = this.summaryCollection.latestAck;
|
|
2074
2146
|
const startSummaryResult = this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger, latestSummaryRefSeqNum);
|
|
2147
|
+
/**
|
|
2148
|
+
* This was added to validate that the summarizer node tree has the same reference sequence number from the
|
|
2149
|
+
* top running summarizer down to the lowest summarizer node.
|
|
2150
|
+
*
|
|
2151
|
+
* The order of mismatch numbers goes (validate sequence number)-(node sequence number).
|
|
2152
|
+
* Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
|
|
2153
|
+
* summarizer nodes.
|
|
2154
|
+
*/
|
|
2075
2155
|
if (startSummaryResult.invalidNodes > 0 ||
|
|
2076
2156
|
startSummaryResult.mismatchNumbers.size > 0) {
|
|
2077
|
-
summaryLogger.
|
|
2157
|
+
summaryLogger.sendTelemetryEvent({
|
|
2078
2158
|
eventName: "LatestSummaryRefSeqNumMismatch",
|
|
2079
2159
|
details: {
|
|
2080
2160
|
...startSummaryResult,
|
|
@@ -2086,7 +2166,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2086
2166
|
stage: "base",
|
|
2087
2167
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2088
2168
|
minimumSequenceNumber,
|
|
2089
|
-
error: `Summarizer node state inconsistent with summarizer state
|
|
2169
|
+
error: new LoggingError(`Summarizer node state inconsistent with summarizer state.`),
|
|
2090
2170
|
};
|
|
2091
2171
|
}
|
|
2092
2172
|
}
|
|
@@ -2129,7 +2209,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2129
2209
|
stage: "base",
|
|
2130
2210
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2131
2211
|
minimumSequenceNumber,
|
|
2132
|
-
error: continueResult.error,
|
|
2212
|
+
error: new LoggingError(continueResult.error),
|
|
2133
2213
|
};
|
|
2134
2214
|
}
|
|
2135
2215
|
const trace = Trace.start();
|
|
@@ -2146,6 +2226,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2146
2226
|
});
|
|
2147
2227
|
}
|
|
2148
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 });
|
|
2149
2241
|
return {
|
|
2150
2242
|
stage: "base",
|
|
2151
2243
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
@@ -2153,24 +2245,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2153
2245
|
error,
|
|
2154
2246
|
};
|
|
2155
2247
|
}
|
|
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
|
-
}
|
|
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;
|
|
2174
2253
|
}
|
|
2175
2254
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
2176
2255
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -2203,7 +2282,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2203
2282
|
};
|
|
2204
2283
|
continueResult = checkContinue();
|
|
2205
2284
|
if (!continueResult.continue) {
|
|
2206
|
-
return {
|
|
2285
|
+
return {
|
|
2286
|
+
stage: "generate",
|
|
2287
|
+
...generateSummaryData,
|
|
2288
|
+
error: new LoggingError(continueResult.error),
|
|
2289
|
+
};
|
|
2207
2290
|
}
|
|
2208
2291
|
const summaryContext = lastAck === undefined
|
|
2209
2292
|
? {
|
|
@@ -2221,7 +2304,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2221
2304
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
2222
2305
|
}
|
|
2223
2306
|
catch (error) {
|
|
2224
|
-
return {
|
|
2307
|
+
return {
|
|
2308
|
+
stage: "generate",
|
|
2309
|
+
...generateSummaryData,
|
|
2310
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2311
|
+
};
|
|
2225
2312
|
}
|
|
2226
2313
|
const parent = summaryContext.ackHandle;
|
|
2227
2314
|
const summaryMessage = {
|
|
@@ -2238,14 +2325,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2238
2325
|
};
|
|
2239
2326
|
continueResult = checkContinue();
|
|
2240
2327
|
if (!continueResult.continue) {
|
|
2241
|
-
return {
|
|
2328
|
+
return {
|
|
2329
|
+
stage: "upload",
|
|
2330
|
+
...uploadData,
|
|
2331
|
+
error: new LoggingError(continueResult.error),
|
|
2332
|
+
};
|
|
2242
2333
|
}
|
|
2243
2334
|
let clientSequenceNumber;
|
|
2244
2335
|
try {
|
|
2245
2336
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
2246
2337
|
}
|
|
2247
2338
|
catch (error) {
|
|
2248
|
-
return {
|
|
2339
|
+
return {
|
|
2340
|
+
stage: "upload",
|
|
2341
|
+
...uploadData,
|
|
2342
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2343
|
+
};
|
|
2249
2344
|
}
|
|
2250
2345
|
const submitData = {
|
|
2251
2346
|
stage: "submit",
|
|
@@ -2254,11 +2349,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2254
2349
|
submitOpDuration: trace.trace().duration,
|
|
2255
2350
|
};
|
|
2256
2351
|
try {
|
|
2257
|
-
|
|
2258
|
-
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
2352
|
+
this.summarizerNode.completeSummary(handle);
|
|
2259
2353
|
}
|
|
2260
2354
|
catch (error) {
|
|
2261
|
-
return {
|
|
2355
|
+
return {
|
|
2356
|
+
stage: "upload",
|
|
2357
|
+
...uploadData,
|
|
2358
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
2359
|
+
};
|
|
2262
2360
|
}
|
|
2263
2361
|
return submitData;
|
|
2264
2362
|
}
|
|
@@ -2347,9 +2445,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2347
2445
|
this.verifyNotClosed();
|
|
2348
2446
|
return this.blobManager.createBlob(blob, signal);
|
|
2349
2447
|
}
|
|
2350
|
-
submitIdAllocationOpIfNeeded() {
|
|
2448
|
+
submitIdAllocationOpIfNeeded(resubmitOutstandingRanges = false) {
|
|
2351
2449
|
if (this._idCompressor) {
|
|
2352
|
-
const idRange =
|
|
2450
|
+
const idRange = resubmitOutstandingRanges
|
|
2451
|
+
? this.idCompressor?.takeUnfinalizedCreationRange()
|
|
2452
|
+
: this._idCompressor.takeNextCreationRange();
|
|
2353
2453
|
// Don't include the idRange if there weren't any Ids allocated
|
|
2354
2454
|
if (idRange?.ids !== undefined) {
|
|
2355
2455
|
const idAllocationMessage = {
|
|
@@ -2366,11 +2466,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2366
2466
|
}
|
|
2367
2467
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
2368
2468
|
this.verifyNotClosed();
|
|
2369
|
-
this.verifyCanSubmitOps();
|
|
2370
2469
|
// There should be no ops in detached container state!
|
|
2371
2470
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
2372
2471
|
assert(metadata === undefined ||
|
|
2373
|
-
containerRuntimeMessage.type === ContainerMessageType.BlobAttach,
|
|
2472
|
+
containerRuntimeMessage.type === ContainerMessageType.BlobAttach, 0x93f /* metadata */);
|
|
2374
2473
|
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
2375
2474
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
2376
2475
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
@@ -2402,6 +2501,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2402
2501
|
// on this callback to do actual sending.
|
|
2403
2502
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
2404
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
|
+
});
|
|
2405
2512
|
const msg = {
|
|
2406
2513
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
2407
2514
|
contents,
|
|
@@ -2411,32 +2518,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2411
2518
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2412
2519
|
});
|
|
2413
2520
|
}
|
|
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) {
|
|
2521
|
+
if (type === ContainerMessageType.BlobAttach) {
|
|
2440
2522
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
2441
2523
|
this.outbox.submitBlobAttach(message);
|
|
2442
2524
|
}
|
|
@@ -2511,32 +2593,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2511
2593
|
throw new Error("Runtime is closed");
|
|
2512
2594
|
}
|
|
2513
2595
|
}
|
|
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
2596
|
reSubmitBatch(batch) {
|
|
2541
2597
|
this.orderSequentially(() => {
|
|
2542
2598
|
for (const message of batch) {
|
|
@@ -2568,11 +2624,15 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2568
2624
|
this.channelCollection.reSubmit(message.type, message.contents, localOpMetadata);
|
|
2569
2625
|
break;
|
|
2570
2626
|
case ContainerMessageType.IdAllocation: {
|
|
2571
|
-
|
|
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.
|
|
2572
2634
|
break;
|
|
2573
2635
|
}
|
|
2574
|
-
case ContainerMessageType.ChunkedOp:
|
|
2575
|
-
throw new Error(`chunkedOp not expected here`);
|
|
2576
2636
|
case ContainerMessageType.BlobAttach:
|
|
2577
2637
|
this.blobManager.reSubmit(opMetadata);
|
|
2578
2638
|
break;
|
|
@@ -2599,7 +2659,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2599
2659
|
});
|
|
2600
2660
|
}
|
|
2601
2661
|
else {
|
|
2602
|
-
const error = DataProcessingError.create("Resubmitting runtime message of
|
|
2662
|
+
const error = DataProcessingError.create("Resubmitting runtime message of unexpected type", "reSubmitCore", undefined /* sequencedMessage */, {
|
|
2603
2663
|
messageDetails: JSON.stringify({
|
|
2604
2664
|
type: message.type,
|
|
2605
2665
|
compatBehavior,
|
|
@@ -2666,7 +2726,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2666
2726
|
await this.closeStaleSummarizer();
|
|
2667
2727
|
return {
|
|
2668
2728
|
stage: "base",
|
|
2669
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
2729
|
+
error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
2670
2730
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2671
2731
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
2672
2732
|
};
|
|
@@ -2710,16 +2770,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2710
2770
|
}
|
|
2711
2771
|
this.imminentClosure || (this.imminentClosure = props?.notifyImminentClosure ?? false);
|
|
2712
2772
|
const getSyncState = (pendingAttachmentBlobs) => {
|
|
2713
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
2714
|
-
|
|
2715
|
-
return; // no pending state to save
|
|
2716
|
-
}
|
|
2773
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
2774
|
+
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
2717
2775
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
2718
2776
|
return {
|
|
2719
2777
|
pending,
|
|
2720
2778
|
pendingIdCompressorState,
|
|
2721
2779
|
pendingAttachmentBlobs,
|
|
2722
|
-
sessionExpiryTimerStarted
|
|
2780
|
+
sessionExpiryTimerStarted,
|
|
2723
2781
|
};
|
|
2724
2782
|
};
|
|
2725
2783
|
const perfEvent = {
|
|
@@ -2789,7 +2847,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2789
2847
|
}
|
|
2790
2848
|
}
|
|
2791
2849
|
get groupedBatchingEnabled() {
|
|
2792
|
-
return this.
|
|
2850
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
2793
2851
|
}
|
|
2794
2852
|
}
|
|
2795
2853
|
//# sourceMappingURL=containerRuntime.js.map
|