@fluidframework/container-runtime 2.0.0-rc.4.0.6 → 2.0.0-rc.5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/api-extractor/api-extractor-lint-bundle.json +5 -0
- package/api-extractor/api-extractor-lint-legacy.cjs.json +5 -0
- package/api-extractor/api-extractor-lint-legacy.esm.json +5 -0
- package/api-extractor/api-extractor-lint-public.cjs.json +5 -0
- package/api-extractor/api-extractor-lint-public.esm.json +5 -0
- package/api-extractor.json +1 -1
- package/api-report/{container-runtime.api.md → container-runtime.alpha.api.md} +33 -516
- package/api-report/container-runtime.beta.api.md +73 -0
- package/api-report/container-runtime.public.api.md +73 -0
- package/biome.jsonc +4 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +3 -20
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +1 -35
- package/dist/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +21 -12
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +113 -110
- 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 +14 -14
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerHandleContext.d.ts +2 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +17 -34
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +109 -145
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +30 -44
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +26 -40
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts +4 -3
- package/dist/deltaManagerProxies.d.ts.map +1 -1
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/deltaScheduler.d.ts +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +1 -3
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +5 -5
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +29 -30
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +12 -31
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +10 -38
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +1 -5
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +2 -3
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
- package/dist/gc/gcSummaryDefinitions.d.ts +1 -1
- package/dist/gc/gcSummaryDefinitions.d.ts.map +1 -1
- package/dist/gc/gcSummaryDefinitions.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts +2 -35
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +9 -62
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +2 -2
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +3 -9
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +1 -1
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +1 -3
- 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.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/messageTypes.d.ts +6 -22
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +2 -2
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +11 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +13 -2
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +1 -2
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +10 -3
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +7 -0
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +4 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +13 -8
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +10 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +13 -18
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +2 -2
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.d.ts +1 -1
- package/dist/opProperties.d.ts.map +1 -1
- package/dist/opProperties.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +15 -5
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/storageServiceWithAttachBlobs.js.map +1 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +2 -3
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/index.d.ts +2 -2
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +4 -2
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +35 -13
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +1 -6
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +23 -110
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +1 -1
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +4 -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 +2 -2
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +2 -3
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +16 -16
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -1
- 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 +2 -3
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +5 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +17 -29
- 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 +11 -11
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +2 -3
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +2 -2
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +7 -8
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +27 -24
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +1 -2
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +3 -1
- package/dist/throttler.js.map +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +3 -20
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +2 -36
- package/lib/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +21 -12
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +114 -112
- 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 +2 -2
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.d.ts +2 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +17 -34
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +106 -142
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +30 -44
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +29 -43
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts +4 -3
- package/lib/deltaManagerProxies.d.ts.map +1 -1
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/deltaScheduler.d.ts +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +1 -3
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +5 -5
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +30 -31
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +13 -32
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +10 -38
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +0 -4
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +2 -3
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcReferenceGraphAlgorithm.js.map +1 -1
- package/lib/gc/gcSummaryDefinitions.d.ts +1 -1
- package/lib/gc/gcSummaryDefinitions.d.ts.map +1 -1
- package/lib/gc/gcSummaryDefinitions.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts +2 -35
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +4 -57
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +2 -2
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +3 -9
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +1 -1
- 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.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/messageTypes.d.ts +6 -22
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +2 -2
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +11 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +13 -2
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +1 -2
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +10 -3
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +7 -0
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +4 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +13 -8
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +10 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +13 -18
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.d.ts +1 -1
- package/lib/opProperties.d.ts.map +1 -1
- package/lib/opProperties.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +15 -5
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/storageServiceWithAttachBlobs.js.map +1 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +2 -3
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/index.d.ts +2 -2
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +4 -2
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +35 -13
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +1 -6
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +22 -109
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +1 -1
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +4 -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 +1 -1
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +2 -3
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +14 -14
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -1
- 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 +2 -3
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +5 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +17 -29
- 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 +3 -3
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +2 -3
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +2 -2
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +7 -8
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +26 -23
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +1 -2
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +3 -1
- package/lib/throttler.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +92 -28
- package/src/batchTracker.ts +5 -2
- package/src/blobManager.ts +21 -61
- package/src/channelCollection.ts +179 -186
- package/src/connectionTelemetry.ts +18 -12
- package/src/containerHandleContext.ts +2 -1
- package/src/containerRuntime.ts +166 -214
- package/src/dataStore.ts +2 -1
- package/src/dataStoreContext.ts +79 -98
- package/src/dataStoreContexts.ts +7 -2
- package/src/deltaManagerProxies.ts +15 -6
- package/src/deltaScheduler.ts +5 -4
- package/src/gc/garbageCollection.md +0 -8
- package/src/gc/garbageCollection.ts +66 -57
- package/src/gc/gcConfigs.ts +15 -37
- package/src/gc/gcDefinitions.ts +20 -39
- package/src/gc/gcHelpers.ts +8 -4
- package/src/gc/gcSummaryDefinitions.ts +1 -1
- package/src/gc/gcSummaryStateTracker.ts +11 -74
- package/src/gc/gcTelemetry.ts +4 -11
- package/src/gc/index.ts +0 -2
- package/src/index.ts +1 -2
- package/src/messageTypes.ts +8 -24
- package/src/opLifecycle/README.md +120 -160
- package/src/opLifecycle/definitions.ts +2 -2
- package/src/opLifecycle/index.ts +5 -1
- package/src/opLifecycle/opCompressor.ts +13 -2
- package/src/opLifecycle/opDecompressor.ts +3 -7
- package/src/opLifecycle/opGroupingManager.ts +12 -8
- package/src/opLifecycle/opSplitter.ts +22 -11
- package/src/opLifecycle/outbox.ts +14 -32
- package/src/opLifecycle/remoteMessageProcessor.ts +4 -1
- package/src/opProperties.ts +2 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +20 -7
- package/src/scheduleManager.ts +4 -1
- package/src/summary/documentSchema.ts +4 -7
- package/src/summary/images/appTree.png +0 -0
- package/src/summary/images/protocolAndAppTree.png +0 -0
- package/src/summary/images/summaryTree.png +0 -0
- package/src/summary/index.ts +5 -2
- package/src/summary/orderedClientElection.ts +100 -22
- package/src/summary/runningSummarizer.ts +54 -124
- package/src/summary/summarizer.ts +5 -2
- package/src/summary/summarizerClientElection.ts +4 -2
- package/src/summary/summarizerNode/summarizerNode.ts +23 -30
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +9 -3
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +11 -11
- package/src/summary/summarizerTypes.ts +32 -33
- package/src/summary/summaryCollection.ts +6 -3
- package/src/summary/summaryFormat.ts +18 -10
- package/src/summary/summaryFormats.md +160 -0
- package/src/summary/summaryGenerator.ts +47 -30
- package/src/summary/summaryManager.ts +6 -9
- package/src/throttler.ts +3 -1
- package/tsconfig.json +2 -0
- package/tsdoc.json +4 -0
|
@@ -2,26 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Table of contents
|
|
4
4
|
|
|
5
|
-
- [
|
|
6
|
-
- [
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [How
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
5
|
+
- [Configs and feature gates for solving the 1MB limit.](#configs-and-feature-gates-for-solving-the-1mb-limit)
|
|
6
|
+
- [Table of contents](#table-of-contents)
|
|
7
|
+
- [Introduction](#introduction)
|
|
8
|
+
- [How batching works](#how-batching-works)
|
|
9
|
+
- [Compression](#compression)
|
|
10
|
+
- [Grouped batching](#grouped-batching)
|
|
11
|
+
- [Changes in op semantics](#changes-in-op-semantics)
|
|
12
|
+
- [Chunking for compression](#chunking-for-compression)
|
|
13
|
+
- [Disabling in case of emergency](#disabling-in-case-of-emergency)
|
|
14
|
+
- [Configuration](#configuration)
|
|
15
|
+
- [Note about performance and latency](#note-about-performance-and-latency)
|
|
16
|
+
- [How it works](#how-it-works)
|
|
17
|
+
- [How it works (Grouped Batching disabled)](#how-it-works-grouped-batching-disabled)
|
|
18
|
+
- [How the overall op flow works](#how-the-overall-op-flow-works)
|
|
19
|
+
- [Outbound](#outbound)
|
|
20
|
+
- [Inbound](#inbound)
|
|
19
21
|
|
|
20
22
|
## Introduction
|
|
21
23
|
|
|
22
24
|
There is a current limitation regarding the size of the payload a Fluid client can send and receive. [The limit is 1MB per payload](https://github.com/microsoft/FluidFramework/issues/9023) and it is currently enforced explicitly with the `BatchTooLarge` error which closes the container.
|
|
23
25
|
|
|
24
|
-
There are
|
|
26
|
+
There are three features which can be used to work around this size limit: "grouped batching", "batch compression", and "compressed batch chunking". This document describes how to enable/disable them, along with a brief description of how they work. The features are enabled by default.
|
|
25
27
|
|
|
26
28
|
By default, the runtime is configured with a max batch size of `716800` bytes, which is lower than the 1MB limit. The reason for the lower value is to account for possible overhead from the op envelope and metadata.
|
|
27
29
|
|
|
@@ -29,7 +31,7 @@ By default, the runtime is configured with a max batch size of `716800` bytes, w
|
|
|
29
31
|
|
|
30
32
|
Batching in the context of Fluid ops is a way in which the framework accumulates and applies ops. A batch is a group of ops accumulated within a single JS turn, which will be broadcasted in the same order to all the other connected clients and applied synchronously. Additional logic and validation ensure that batches are never interleaved, nested or interrupted and they are processed in isolation without interleaving of ops from other clients.
|
|
31
33
|
|
|
32
|
-
The way batches are formed is governed by the `FlushMode` setting of the `
|
|
34
|
+
The way batches are formed is governed by the `FlushMode` setting of the `IContainerRuntimeOptions` and it is immutable for the entire lifetime of the runtime and subsequently the container.
|
|
33
35
|
|
|
34
36
|
```
|
|
35
37
|
export enum FlushMode {
|
|
@@ -66,47 +68,30 @@ As `FlushMode.TurnBased` accumulates ops, it is the most vulnerable to run into
|
|
|
66
68
|
|
|
67
69
|
Compression is relevant for both `FlushMode.TurnBased` and `FlushMode.Immediate` as it only targets the contents of the ops and not the number of ops in a batch. Compression is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
The `IContainerRuntimeOptions.enableGroupedBatching` option has been added to the container runtime layer and is **off by default**. This option will group all batch messages under a new "grouped" message to be sent to the service. Upon receiving this new "grouped" message, the batch messages will be extracted and given the sequence number of the parent "grouped" message.
|
|
72
|
-
|
|
73
|
-
The purpose for enabling grouped batching on top of compression is that regular compression won't include the empty messages in the chunks. Thus, if we have batches with many messages (i.e. more than 4k), we will go over the batch size limit just on empty op envelopes alone.
|
|
74
|
-
|
|
75
|
-
See [below](#how-grouped-batching-works) for an example.
|
|
76
|
-
|
|
77
|
-
### Risks
|
|
71
|
+
Compressing a batch yields a batch with the same number of messages. It compresses all the content, shifting the compressed payload into the first op,
|
|
72
|
+
leaving the rest of the batch's messages as empty placeholders to reserve sequence numbers for the compressed messages.
|
|
78
73
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
This option will change a couple of expectations around message structure and runtime layer expectations. Only enable this option after testing
|
|
82
|
-
and verifying that the following expectation changes won't have any effects:
|
|
74
|
+
## Grouped batching
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
- messages within the same batch will have the same sequence number
|
|
86
|
-
- client sequence numbers on batch messages can only be used to order messages with the same sequenceNumber
|
|
87
|
-
- requires all ops to be processed by runtime layer (version "2.0.0-internal.1.2.0" or later https://github.com/microsoft/FluidFramework/pull/11832)
|
|
76
|
+
With Grouped Batching enabled (it's on by default), all batch messages are combined under a single "grouped" message _before compression_. Upon receiving this new "grouped" message, the batch messages will be extracted, and they each will be given the same sequence number - that of the parent "grouped" message.
|
|
88
77
|
|
|
89
|
-
|
|
78
|
+
The purpose for enabling grouped batching before compression is to eliminate the empty placeholder messages in the chunks. These empty messages are not free to transmit, can trigger service throttling, and in extreme cases can _still_ result in a batch too large (from empty op envelopes alone).
|
|
90
79
|
|
|
91
|
-
|
|
80
|
+
Grouped batching is only relevant for `FlushMode.TurnBased`, since `OpGroupingManagerConfig.opCountThreshold` defaults to 2. Grouped batching is opaque to the server and implementations of the Fluid protocol do not need to alter their behavior to support this client feature.
|
|
92
81
|
|
|
93
|
-
|
|
82
|
+
Grouped Batching can be disabled by setting `IContainerRuntimeOptions.enableGroupedBatching` to `false`.
|
|
94
83
|
|
|
95
|
-
|
|
84
|
+
See [below](#how-grouped-batching-works) for an example.
|
|
96
85
|
|
|
97
|
-
|
|
86
|
+
### Changes in op semantics
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
const runtimeOptions: IContainerRuntimeOptions = {
|
|
101
|
-
(...)
|
|
102
|
-
enableGroupedBatching: true,
|
|
103
|
-
(...)
|
|
104
|
-
}
|
|
105
|
-
```
|
|
88
|
+
Grouped Batching changed a couple of expectations around message structure and runtime layer expectations. Specifically:
|
|
106
89
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
90
|
+
- Batch messages observed at the runtime layer no longer match messages seen at the loader layer (i.e. grouped form at loader layer, ungrouped form at runtime layer)
|
|
91
|
+
- Once the ContainerRuntime ungroups the batch, the client sequence numbers on the resulting messages can only be used to order messages with that batch (having the same sequenceNumber)
|
|
92
|
+
- Messages within the same batch now all share the same sequence number
|
|
93
|
+
- All ops in a batch must also have the same reference sequence number to ensure eventualy consistency of the model. The runtime will "rebase" ops in a batch with different ref sequence number to satisfy that requirement.
|
|
94
|
+
- What causes ops in a single JS turn (and thus in a batch) to have different reference sequence number? "Op reentrancy", where changes are made to a DDS inside a DDS 'onChanged' event handler.
|
|
110
95
|
|
|
111
96
|
## Chunking for compression
|
|
112
97
|
|
|
@@ -120,34 +105,15 @@ Chunking is relevant for both `FlushMode.TurnBased` and `FlushMode.Immediate` as
|
|
|
120
105
|
|
|
121
106
|
## Disabling in case of emergency
|
|
122
107
|
|
|
123
|
-
|
|
108
|
+
Compression and Chunking configuration can be overridden via feature gates to force-disable them:
|
|
124
109
|
|
|
125
110
|
- `Fluid.ContainerRuntime.CompressionDisabled` - if set to true, will disable compression (this has a side effect of also disabling chunking, as chunking is invoked only for compressed payloads).
|
|
126
|
-
- `Fluid.ContainerRuntime.DisableGroupedBatching` - if set to true, will disable grouped batching.
|
|
127
111
|
- `Fluid.ContainerRuntime.CompressionChunkingDisabled` - if set to true, will disable chunking for compression.
|
|
128
112
|
|
|
129
|
-
##
|
|
130
|
-
|
|
131
|
-
By default, the runtime is configured with the following values related to compression and chunking:
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
const runtimeOptions: IContainerRuntimeOptions = {
|
|
135
|
-
compressionOptions: {
|
|
136
|
-
minimumBatchSizeInBytes: 614400,
|
|
137
|
-
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
138
|
-
},
|
|
139
|
-
chunkSizeInBytes: 204800,
|
|
140
|
-
maxBatchSizeInBytes: 716800,
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
To enable grouped batching, use the following property:
|
|
113
|
+
## Configuration
|
|
145
114
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
enableGroupedBatching: true,
|
|
149
|
-
}
|
|
150
|
-
```
|
|
115
|
+
These features are configured via `IContainerRuntimeOptions`, passed to the `ContainerRuntime.loadRuntime` function.
|
|
116
|
+
Default values are specified in code in [containerRuntime.ts](../containerRuntime.ts).
|
|
151
117
|
|
|
152
118
|
## Note about performance and latency
|
|
153
119
|
|
|
@@ -157,86 +123,7 @@ In general, compression offers a trade-off between higher compute costs, lower b
|
|
|
157
123
|
|
|
158
124
|
## How it works
|
|
159
125
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
If we have a batch with a size larger than the configured minimum required for compression (in the example let’s say it’s 850 bytes), as following:
|
|
163
|
-
|
|
164
|
-
```
|
|
165
|
-
+-----------+-----------+-----------+-----------+
|
|
166
|
-
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
167
|
-
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
168
|
-
| Size: 100 | Size: 150 | Size: 200 | Size: 400 |
|
|
169
|
-
+-----------+-----------+-----------+-----------+
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
The total size of the batch is 850 bytes. The client which needs to send the batch would compress the batch to a smaller size (200 bytes) and will send a new batch like the following:
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
+--------------------+-----------+-----------+-----------+
|
|
176
|
-
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
177
|
-
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
178
|
-
| Size: 200 | Size: 0 | Size: 0 | Size: 0 |
|
|
179
|
-
| Compression: 'lz4' | | | |
|
|
180
|
-
+--------------------+-----------+-----------+-----------+
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
The first op in the batch is the only one with content (which is opaque due to it being compressed), the rest of the ops serve only to reserve the sequence numbers so that the state machine which rebuilds the original batch on the receiving client can reconstruct the original batch.
|
|
184
|
-
|
|
185
|
-
When the batch is received by a client, it will detect the first op as being compressed, it will decompress it and store it in memory. For each empty op subsequently received, it will fetch the uncompressed content from memory and rebuild the original ops. The original ops are then processed by the runtime and applied accordingly.
|
|
186
|
-
So, compression virtualizes the batch.
|
|
187
|
-
|
|
188
|
-
After compression, the first op in the batch can exceed 1MB, therefore it would still be rejected. In this case, another layer of virtualization is added after compression (and before decompression, symmetrically on the receiving end).
|
|
189
|
-
|
|
190
|
-
The first op in the compressed batch can be chunked into smaller ops which can be sent outside the original batch. However, to conveniently maintain the batch semantics, the last chunk (the chunk which triggers rebuilding the original op) is the first op in the new batch.
|
|
191
|
-
|
|
192
|
-
To illustrate, let’s take the large batch below:
|
|
193
|
-
|
|
194
|
-
```
|
|
195
|
-
+--------------------+-----------+-----------+-----------+
|
|
196
|
-
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
197
|
-
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
198
|
-
| Size: 900 | Size: 0 | Size: 0 | Size: 0 |
|
|
199
|
-
| Compression: 'lz4' | | | |
|
|
200
|
-
+--------------------+-----------+-----------+-----------+
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
This will produce the following batches:
|
|
204
|
-
|
|
205
|
-
```
|
|
206
|
-
+-----------+
|
|
207
|
-
| Chunk 1/3 |
|
|
208
|
-
| SeqNum: 1 |
|
|
209
|
-
| Size: 300 |
|
|
210
|
-
+-----------+
|
|
211
|
-
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
```
|
|
215
|
-
+-----------+
|
|
216
|
-
| Chunk 2/3 |
|
|
217
|
-
| SeqNum: 2 |
|
|
218
|
-
| Size: 300 |
|
|
219
|
-
+-----------+
|
|
220
|
-
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
```
|
|
224
|
-
+-----------+-----------+-----------+-----------+
|
|
225
|
-
| Chunk 3/3 | Op 2 | Op 3 | Op 4 |
|
|
226
|
-
| SeqNum: 3 | SeqNum: 4 | SeqNum: 5 | SeqNum: 6 |
|
|
227
|
-
| Size: 300 | Size: 0 | Size: 0 | Size: 0 |
|
|
228
|
-
+-----------+-----------+-----------+-----------+
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
The first 2 chunks are sent in their own batches, while the last chunk is the first op in the last batch which contains the ops reserving the required sequence numbers.
|
|
232
|
-
|
|
233
|
-
Notice that the sequence numbers don’t matter here, as all ops will be based off the same reference sequence number, so the sequence number will be recalculated for all, without additional work.
|
|
234
|
-
|
|
235
|
-
Additionally, as compression preserves the original uncompressed batch layout in terms of the number of ops by using empty ops to reserve the sequence numbers, this ensures that the clients will always receive the exact count of ops to rebuild the uncompressed batch sequentially.
|
|
236
|
-
|
|
237
|
-
On the receiving end, the client will accumulate chunks 1 and 2 and keep them in memory. When chunk 3 is received, the original large, decompressed op will be rebuilt, and the runtime will then process the batch as if it is a compressed batch.
|
|
238
|
-
|
|
239
|
-
## How grouped batching works
|
|
126
|
+
Virtualization works as an intermediate step in the Runtime layer, as the closest step to sending/receiving ops via the Loader layer.
|
|
240
127
|
|
|
241
128
|
Given the following baseline batch:
|
|
242
129
|
|
|
@@ -262,9 +149,9 @@ Compressed batch:
|
|
|
262
149
|
|
|
263
150
|
```
|
|
264
151
|
+-------------------------------------------------------------------------------------------------------------------------+
|
|
265
|
-
| Op 1
|
|
266
|
-
| Compression: 'lz4'
|
|
267
|
-
|
|
|
152
|
+
| Op 1 Logical +------------------------------------------------------------------------------------+ |
|
|
153
|
+
| Compression: 'lz4' Contents: | Type: "groupedBatch" | |
|
|
154
|
+
| Compressed buffer: "wxyz" | +----------------+---------------+---------------+---------------+---------------+ | |
|
|
268
155
|
| | | Op 1 | Op 2 | Op 3 | Op 4 | Op 5 | | |
|
|
269
156
|
| | | Contents: "a" | Contents: "b" | Contents: "c" | Contents: "d" | Contents: "e" | | |
|
|
270
157
|
| | +----------------+---------------+---------------+---------------+---------------+ | |
|
|
@@ -278,7 +165,7 @@ Can produce the following chunks:
|
|
|
278
165
|
+------------------------------------------------+
|
|
279
166
|
| Chunk 1/2 Contents: +---------------------+ |
|
|
280
167
|
| | +-----------------+ | |
|
|
281
|
-
| | | Contents: "
|
|
168
|
+
| | | Contents: "wx" | | |
|
|
282
169
|
| | +-----------------+ | |
|
|
283
170
|
| +---------------------+ |
|
|
284
171
|
+------------------------------------------------+
|
|
@@ -288,16 +175,16 @@ Can produce the following chunks:
|
|
|
288
175
|
+-----------------------------------------------+
|
|
289
176
|
| Chunk 2/2 Contents: +--------------------+ |
|
|
290
177
|
| | +----------------+ | |
|
|
291
|
-
| | | Contents: "
|
|
178
|
+
| | | Contents: "yz" | | |
|
|
292
179
|
| | +----------------+ | |
|
|
293
180
|
| +--------------------+ |
|
|
294
181
|
+-----------------------------------------------+
|
|
295
182
|
```
|
|
296
183
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
-
|
|
300
|
-
-
|
|
184
|
+
The chunks are sent to service to be sequenced, and broadcast to all clients.
|
|
185
|
+
|
|
186
|
+
- On the receiving end, the client will accumulate chunks 1 through n-1 and keep them in memory (in this example, it's just Chunk 1).
|
|
187
|
+
- When the final chunk is received, the original large, decompressed op will be rebuilt, and the runtime will then process the batch as if it is a compressed batch.
|
|
301
188
|
|
|
302
189
|
Decompressed batch:
|
|
303
190
|
|
|
@@ -321,6 +208,79 @@ Ungrouped batch:
|
|
|
321
208
|
+-----------------+-----------------+-----------------+-----------------+-----------------+
|
|
322
209
|
```
|
|
323
210
|
|
|
211
|
+
## How it works (Grouped Batching disabled)
|
|
212
|
+
|
|
213
|
+
If we have a batch with a size larger than the configured minimum required for compression (in the example let’s say it’s 850 bytes), as following:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
+-----------+-----------+-----------+-----------+
|
|
217
|
+
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
218
|
+
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
219
|
+
| Size: 100 | Size: 150 | Size: 200 | Size: 400 |
|
|
220
|
+
+-----------+-----------+-----------+-----------+
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The total size of the batch is 850 bytes. The client which needs to send the batch would compress the batch to a smaller size (200 bytes) and will send a new batch like the following:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
+--------------------+-----------+-----------+-----------+
|
|
227
|
+
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
228
|
+
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
229
|
+
| Size: 200 | Size: 0 | Size: 0 | Size: 0 |
|
|
230
|
+
| Compression: 'lz4' | | | |
|
|
231
|
+
+--------------------+-----------+-----------+-----------+
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The first op in the batch is the only one with content (which is opaque due to it being compressed), the rest of the ops serve only to reserve the sequence numbers so that the state machine which rebuilds the original batch on the receiving client can reconstruct the original batch.
|
|
235
|
+
|
|
236
|
+
When the batch is received by a client, it will detect the first op as being compressed, it will decompress it and store it in memory. For each empty op subsequently received, it will fetch the uncompressed content from memory and rebuild the original ops. The original ops are then processed by the runtime and applied accordingly.
|
|
237
|
+
So, compression virtualizes the batch.
|
|
238
|
+
|
|
239
|
+
After compression, the first op in the batch can exceed 1MB, therefore it would still be rejected. In this case, another layer of virtualization is added after compression (and before decompression, symmetrically on the receiving end).
|
|
240
|
+
|
|
241
|
+
The first op in the compressed batch can be chunked into smaller ops which can be sent outside the original batch. However, to conveniently maintain the batch semantics, the last chunk (the chunk which triggers rebuilding the original op) is the first op in the new batch.
|
|
242
|
+
|
|
243
|
+
To illustrate, let’s take the large batch below:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
+--------------------+-----------+-----------+-----------+
|
|
247
|
+
| Op 1 | Op 2 | Op 3 | Op 4 |
|
|
248
|
+
| SeqNum: 1 | SeqNum: 2 | SeqNum: 3 | SeqNum: 4 |
|
|
249
|
+
| Size: 900 | Size: 0 | Size: 0 | Size: 0 |
|
|
250
|
+
| Compression: 'lz4' | | | |
|
|
251
|
+
+--------------------+-----------+-----------+-----------+
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This will produce the following batches:
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
+-----------+
|
|
258
|
+
| Chunk 1/3 |
|
|
259
|
+
| SeqNum: 1 |
|
|
260
|
+
| Size: 300 |
|
|
261
|
+
+-----------+
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
+-----------+
|
|
267
|
+
| Chunk 2/3 |
|
|
268
|
+
| SeqNum: 2 |
|
|
269
|
+
| Size: 300 |
|
|
270
|
+
+-----------+
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
+-----------+-----------+-----------+-----------+
|
|
276
|
+
| Chunk 3/3 | Op 2 | Op 3 | Op 4 |
|
|
277
|
+
| SeqNum: 3 | SeqNum: 4 | SeqNum: 5 | SeqNum: 6 |
|
|
278
|
+
| Size: 300 | Size: 0 | Size: 0 | Size: 0 |
|
|
279
|
+
+-----------+-----------+-----------+-----------+
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The first 2 chunks are sent in their own batches, while the last chunk is the first op in the last batch which contains the ops reserving the required sequence numbers.
|
|
283
|
+
|
|
324
284
|
## How the overall op flow works
|
|
325
285
|
|
|
326
286
|
### Outbound
|
|
@@ -19,7 +19,7 @@ export type BatchMessage = IBatchMessage & {
|
|
|
19
19
|
/**
|
|
20
20
|
* Batch interface used internally by the runtime.
|
|
21
21
|
*/
|
|
22
|
-
export interface IBatch {
|
|
22
|
+
export interface IBatch<TMessages extends BatchMessage[] = BatchMessage[]> {
|
|
23
23
|
/**
|
|
24
24
|
* Sum of the in-memory content sizes of all messages in the batch.
|
|
25
25
|
* If the batch is compressed, this number reflects the post-compression size.
|
|
@@ -28,7 +28,7 @@ export interface IBatch {
|
|
|
28
28
|
/**
|
|
29
29
|
* All the messages in the batch
|
|
30
30
|
*/
|
|
31
|
-
readonly content:
|
|
31
|
+
readonly content: TMessages;
|
|
32
32
|
/**
|
|
33
33
|
* The reference sequence number for the batch
|
|
34
34
|
*/
|
package/src/opLifecycle/index.ts
CHANGED
|
@@ -10,4 +10,8 @@ export { OpCompressor } from "./opCompressor.js";
|
|
|
10
10
|
export { OpDecompressor } from "./opDecompressor.js";
|
|
11
11
|
export { OpSplitter, splitOp, isChunkedMessage } from "./opSplitter.js";
|
|
12
12
|
export { RemoteMessageProcessor, unpackRuntimeMessage } from "./remoteMessageProcessor.js";
|
|
13
|
-
export {
|
|
13
|
+
export {
|
|
14
|
+
OpGroupingManager,
|
|
15
|
+
OpGroupingManagerConfig,
|
|
16
|
+
isGroupedBatch,
|
|
17
|
+
} from "./opGroupingManager.js";
|
|
@@ -26,6 +26,13 @@ export class OpCompressor {
|
|
|
26
26
|
this.logger = createChildLogger({ logger, namespace: "OpCompressor" });
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Combines the contents of the batch into a single JSON string and compresses it, putting
|
|
31
|
+
* the resulting string as the first message of the batch. The rest of the messages are
|
|
32
|
+
* empty placeholders to reserve sequence numbers.
|
|
33
|
+
* @param batch - The batch to compress
|
|
34
|
+
* @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders
|
|
35
|
+
*/
|
|
29
36
|
public compressBatch(batch: IBatch): IBatch {
|
|
30
37
|
assert(
|
|
31
38
|
batch.contentSizeInBytes > 0 && batch.content.length > 0,
|
|
@@ -33,7 +40,7 @@ export class OpCompressor {
|
|
|
33
40
|
);
|
|
34
41
|
|
|
35
42
|
const compressionStart = Date.now();
|
|
36
|
-
const contentsAsBuffer = new TextEncoder().encode(this.
|
|
43
|
+
const contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));
|
|
37
44
|
const compressedContents = compress(contentsAsBuffer);
|
|
38
45
|
const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
|
|
39
46
|
const duration = Date.now() - compressionStart;
|
|
@@ -75,8 +82,12 @@ export class OpCompressor {
|
|
|
75
82
|
return compressedBatch;
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Combine the batch's content strings into a single JSON string (a serialized array)
|
|
87
|
+
*/
|
|
88
|
+
private serializeBatchContents(batch: IBatch): string {
|
|
79
89
|
try {
|
|
90
|
+
// Yields a valid JSON array, since each message.contents is already serialized to JSON
|
|
80
91
|
return `[${batch.content.map((message) => message.contents).join(",")}]`;
|
|
81
92
|
} catch (e: any) {
|
|
82
93
|
if (e.message === "Invalid string length") {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { IsoBuffer, Uint8ArrayToString } from "@fluid-internal/client-utils";
|
|
7
7
|
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
8
8
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
9
|
-
import { ISequencedDocumentMessage } from "@fluidframework/
|
|
9
|
+
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
10
10
|
import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
11
11
|
import { decompress } from "lz4js";
|
|
12
12
|
|
|
@@ -65,8 +65,7 @@ export class OpDecompressor {
|
|
|
65
65
|
IsoBuffer.from(
|
|
66
66
|
(message.contents as IPackedContentsContents).packedContents,
|
|
67
67
|
"base64",
|
|
68
|
-
).toString("base64") ===
|
|
69
|
-
(message.contents as IPackedContentsContents).packedContents
|
|
68
|
+
).toString("base64") === (message.contents as IPackedContentsContents).packedContents
|
|
70
69
|
) {
|
|
71
70
|
this.logger.sendTelemetryEvent({
|
|
72
71
|
eventName: "LegacyCompression",
|
|
@@ -139,10 +138,7 @@ export class OpDecompressor {
|
|
|
139
138
|
|
|
140
139
|
if (batchMetadata === false || this.isSingleMessageBatch) {
|
|
141
140
|
// End of compressed batch
|
|
142
|
-
const returnMessage = newMessage(
|
|
143
|
-
message,
|
|
144
|
-
this.rootMessageContents[this.processedCount],
|
|
145
|
-
);
|
|
141
|
+
const returnMessage = newMessage(message, this.rootMessageContents[this.processedCount]);
|
|
146
142
|
|
|
147
143
|
this.activeBatch = false;
|
|
148
144
|
this.isSingleMessageBatch = false;
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
7
7
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
8
|
-
import { ISequencedDocumentMessage } from "@fluidframework/
|
|
8
|
+
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
9
9
|
import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
10
10
|
|
|
11
|
-
import { IBatch } from "./definitions.js";
|
|
11
|
+
import { IBatch, type BatchMessage } from "./definitions.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.
|
|
@@ -49,7 +49,14 @@ export class OpGroupingManager {
|
|
|
49
49
|
this.logger = createChildLogger({ logger, namespace: "OpGroupingManager" });
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Converts the given batch into a "grouped batch" - a batch with a single message of type "groupedBatch",
|
|
54
|
+
* with contents being an array of the original batch's messages.
|
|
55
|
+
*
|
|
56
|
+
* @remarks - Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents
|
|
57
|
+
* must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.
|
|
58
|
+
*/
|
|
59
|
+
public groupBatch(batch: IBatch): IBatch<[BatchMessage]> {
|
|
53
60
|
assert(this.shouldGroup(batch), 0x946 /* cannot group the provided batch */);
|
|
54
61
|
|
|
55
62
|
if (batch.content.length >= 1000) {
|
|
@@ -66,10 +73,7 @@ export class OpGroupingManager {
|
|
|
66
73
|
if (message.metadata) {
|
|
67
74
|
const keys = Object.keys(message.metadata);
|
|
68
75
|
assert(keys.length < 2, 0x5dd /* cannot group ops with metadata */);
|
|
69
|
-
assert(
|
|
70
|
-
keys.length === 0 || keys[0] === "batch",
|
|
71
|
-
0x5de /* unexpected op metadata */,
|
|
72
|
-
);
|
|
76
|
+
assert(keys.length === 0 || keys[0] === "batch", 0x5de /* unexpected op metadata */);
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
|
|
@@ -82,7 +86,7 @@ export class OpGroupingManager {
|
|
|
82
86
|
})),
|
|
83
87
|
});
|
|
84
88
|
|
|
85
|
-
const groupedBatch: IBatch = {
|
|
89
|
+
const groupedBatch: IBatch<[BatchMessage]> = {
|
|
86
90
|
...batch,
|
|
87
91
|
content: [
|
|
88
92
|
{
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { IBatchMessage } from "@fluidframework/container-definitions/internal";
|
|
7
7
|
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
8
8
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
9
|
-
import { ISequencedDocumentMessage } from "@fluidframework/
|
|
9
|
+
import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal";
|
|
10
10
|
import {
|
|
11
11
|
DataCorruptionError,
|
|
12
12
|
createChildLogger,
|
|
@@ -53,7 +53,9 @@ export class OpSplitter {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
public get isBatchChunkingEnabled(): boolean {
|
|
56
|
-
return
|
|
56
|
+
return (
|
|
57
|
+
this.chunkSizeInBytes < Number.POSITIVE_INFINITY && this.submitBatchFn !== undefined
|
|
58
|
+
);
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
public get chunks(): ReadonlyMap<string, string[]> {
|
|
@@ -111,8 +113,10 @@ export class OpSplitter {
|
|
|
111
113
|
* To illustrate, if the input is `[largeOp, emptyOp, emptyOp]`, `largeOp` will be split into `[chunk1, chunk2, chunk3, chunk4]`.
|
|
112
114
|
* `chunk1`, `chunk2` and `chunk3` will be sent individually and `[chunk4, emptyOp, emptyOp]` will be returned.
|
|
113
115
|
*
|
|
116
|
+
* @remarks - A side effect here is that 1 or more chunks are queued immediately for sending in next JS turn.
|
|
117
|
+
*
|
|
114
118
|
* @param batch - the compressed batch which needs to be processed
|
|
115
|
-
* @returns A new adjusted batch which can be sent over the wire
|
|
119
|
+
* @returns A new adjusted batch (last chunk + empty placeholders) which can be sent over the wire
|
|
116
120
|
*/
|
|
117
121
|
public splitFirstBatchMessage(batch: IBatch): IBatch {
|
|
118
122
|
assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
|
|
@@ -203,16 +207,19 @@ export class OpSplitter {
|
|
|
203
207
|
const serializedContent = this.chunkMap.get(clientId)!.join("");
|
|
204
208
|
this.clearPartialChunks(clientId);
|
|
205
209
|
|
|
206
|
-
|
|
207
|
-
|
|
210
|
+
// The final/complete message will contain the data from all the chunks.
|
|
211
|
+
// It will have the sequenceNumber of the last chunk
|
|
212
|
+
const completeMessage = { ...message };
|
|
213
|
+
completeMessage.contents =
|
|
214
|
+
serializedContent === "" ? undefined : JSON.parse(serializedContent);
|
|
208
215
|
// back-compat with 1.x builds
|
|
209
216
|
// This is only required / present for non-compressed, chunked ops
|
|
210
217
|
// For compressed ops, we have op grouping enabled, and type of each op is preserved within compressed content.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
218
|
+
completeMessage.type = (chunkedContent as any).originalType;
|
|
219
|
+
completeMessage.metadata = chunkedContent.originalMetadata;
|
|
220
|
+
completeMessage.compression = chunkedContent.originalCompression;
|
|
214
221
|
return {
|
|
215
|
-
message:
|
|
222
|
+
message: completeMessage,
|
|
216
223
|
isFinalChunk: true,
|
|
217
224
|
};
|
|
218
225
|
}
|
|
@@ -267,7 +274,8 @@ export const splitOp = (
|
|
|
267
274
|
);
|
|
268
275
|
|
|
269
276
|
const contentLength = op.contents.length;
|
|
270
|
-
const chunkCount =
|
|
277
|
+
const chunkCount =
|
|
278
|
+
Math.floor((contentLength - 1) / chunkSizeInBytes) + 1 + (extraOp ? 1 : 0);
|
|
271
279
|
let offset = 0;
|
|
272
280
|
for (let chunkId = 1; chunkId <= chunkCount; chunkId++) {
|
|
273
281
|
const chunk: IChunkedOp = {
|
|
@@ -301,7 +309,10 @@ export const splitOp = (
|
|
|
301
309
|
);
|
|
302
310
|
}
|
|
303
311
|
|
|
304
|
-
assert(
|
|
312
|
+
assert(
|
|
313
|
+
offset >= contentLength,
|
|
314
|
+
0x58c /* Content offset equal or larger than content length */,
|
|
315
|
+
);
|
|
305
316
|
assert(chunks.length === chunkCount, 0x5a5 /* Expected number of chunks */);
|
|
306
317
|
return chunks;
|
|
307
318
|
};
|