@fluidframework/container-runtime 2.13.0 → 2.21.0
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/.eslintrc.cjs +71 -5
- package/CHANGELOG.md +84 -0
- package/api-report/container-runtime.legacy.alpha.api.md +38 -232
- package/dist/batchTracker.d.ts +1 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManager.d.ts +5 -1
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +30 -13
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts +1 -0
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.js +7 -5
- package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
- package/dist/channelCollection.d.ts +23 -12
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +85 -53
- 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 +10 -6
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerHandleContext.d.ts +1 -1
- package/dist/containerHandleContext.d.ts.map +1 -1
- package/dist/containerHandleContext.js.map +1 -1
- package/dist/containerRuntime.d.ts +87 -94
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +312 -226
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +7 -3
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +8 -4
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +41 -25
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +47 -29
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +6 -2
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -2
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.d.ts +1 -1
- package/dist/dataStoreRegistry.d.ts.map +1 -1
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts +1 -17
- package/dist/deltaManagerProxies.d.ts.map +1 -1
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/deltaScheduler.d.ts +9 -6
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +95 -89
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +21 -7
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +48 -19
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts +11 -0
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +5 -2
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +218 -70
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +40 -13
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +6 -2
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +14 -7
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcReferenceGraphAlgorithm.js.map +1 -1
- package/dist/gc/gcSummaryDefinitions.d.ts +18 -6
- package/dist/gc/gcSummaryDefinitions.d.ts.map +1 -1
- package/dist/gc/gcSummaryDefinitions.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +2 -1
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +33 -11
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +35 -17
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.d.ts +42 -13
- package/dist/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +27 -9
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +1 -0
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -1
- package/dist/gc/index.js.map +1 -1
- package/dist/inboundBatchAggregator.d.ts +34 -0
- package/dist/inboundBatchAggregator.d.ts.map +1 -0
- package/dist/inboundBatchAggregator.js +185 -0
- package/dist/inboundBatchAggregator.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layerCompatState.d.ts +19 -0
- package/dist/layerCompatState.d.ts.map +1 -0
- package/dist/layerCompatState.js +64 -0
- package/dist/layerCompatState.js.map +1 -0
- package/dist/legacy.d.ts +0 -4
- package/dist/messageTypes.d.ts +14 -5
- package/dist/messageTypes.d.ts.map +1 -1
- package/dist/messageTypes.js.map +1 -1
- package/dist/metadata.d.ts +12 -4
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +6 -2
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +9 -3
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +3 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.d.ts +9 -3
- package/dist/opLifecycle/duplicateBatchDetector.d.ts.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.js +11 -5
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +3 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +13 -19
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +6 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +16 -8
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +1 -2
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +9 -6
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +13 -10
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +16 -11
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +4 -4
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +17 -16
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +9 -3
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +3 -1
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/package.json +2 -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 +22 -11
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +24 -15
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +7 -0
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +8 -4
- 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 +94 -31
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +28 -16
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.d.ts +1 -0
- package/dist/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +7 -2
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +17 -6
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +48 -19
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +10 -5
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +26 -11
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +1 -0
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.d.ts +6 -2
- package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summarizerHeuristics.js +13 -5
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/index.d.ts.map +1 -1
- package/dist/summary/summarizerNode/index.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +24 -8
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +45 -36
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +48 -16
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +3 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +13 -5
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +15 -7
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +253 -135
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +3 -4
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +10 -8
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +28 -9
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +3 -2
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +9 -3
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +22 -9
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +8 -4
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +20 -9
- package/dist/summary/summaryManager.js.map +1 -1
- package/dist/throttler.d.ts +26 -10
- package/dist/throttler.d.ts.map +1 -1
- package/dist/throttler.js +12 -4
- package/dist/throttler.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +2 -2
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +5 -1
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +30 -13
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts +1 -0
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.js +7 -5
- package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
- package/lib/channelCollection.d.ts +23 -12
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +88 -54
- 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 +11 -7
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerHandleContext.d.ts +1 -1
- package/lib/containerHandleContext.d.ts.map +1 -1
- package/lib/containerHandleContext.js.map +1 -1
- package/lib/containerRuntime.d.ts +87 -94
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +319 -228
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +7 -3
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +8 -4
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +41 -25
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +47 -29
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +6 -2
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -2
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.d.ts +1 -1
- package/lib/dataStoreRegistry.d.ts.map +1 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts +1 -17
- package/lib/deltaManagerProxies.d.ts.map +1 -1
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/deltaScheduler.d.ts +9 -6
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +96 -90
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +21 -7
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +51 -20
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts +11 -0
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +4 -2
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +218 -70
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +40 -13
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +6 -2
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +14 -7
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcReferenceGraphAlgorithm.js.map +1 -1
- package/lib/gc/gcSummaryDefinitions.d.ts +18 -6
- package/lib/gc/gcSummaryDefinitions.d.ts.map +1 -1
- package/lib/gc/gcSummaryDefinitions.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +2 -1
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +33 -11
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +38 -18
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.d.ts +42 -13
- package/lib/gc/gcUnreferencedStateTracker.d.ts.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +27 -9
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +1 -0
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +1 -0
- package/lib/gc/index.js.map +1 -1
- package/lib/inboundBatchAggregator.d.ts +34 -0
- package/lib/inboundBatchAggregator.d.ts.map +1 -0
- package/lib/inboundBatchAggregator.js +181 -0
- package/lib/inboundBatchAggregator.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/layerCompatState.d.ts +19 -0
- package/lib/layerCompatState.d.ts.map +1 -0
- package/lib/layerCompatState.js +60 -0
- package/lib/layerCompatState.js.map +1 -0
- package/lib/legacy.d.ts +0 -4
- package/lib/messageTypes.d.ts +14 -5
- package/lib/messageTypes.d.ts.map +1 -1
- package/lib/messageTypes.js.map +1 -1
- package/lib/metadata.d.ts +12 -4
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js +6 -2
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +9 -3
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +3 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.d.ts +9 -3
- package/lib/opLifecycle/duplicateBatchDetector.d.ts.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.js +11 -5
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +3 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +14 -20
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +6 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +17 -9
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +1 -2
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +10 -7
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +13 -10
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +16 -11
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +4 -4
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +17 -16
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +9 -3
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +3 -1
- package/lib/opLifecycle/remoteMessageProcessor.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 +22 -11
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +25 -16
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +7 -0
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +8 -4
- 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 +94 -31
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +28 -16
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.d.ts +1 -0
- package/lib/summary/runWhileConnectedCoordinator.d.ts.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +7 -2
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +17 -6
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +48 -19
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +10 -5
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +26 -11
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +1 -0
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.d.ts +6 -2
- package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summarizerHeuristics.js +13 -5
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/index.d.ts.map +1 -1
- package/lib/summary/summarizerNode/index.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +24 -8
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +45 -36
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +48 -16
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +3 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +13 -5
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +15 -7
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +253 -135
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +3 -4
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +10 -8
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +28 -9
- 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 +9 -3
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +22 -9
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +8 -4
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +20 -9
- package/lib/summary/summaryManager.js.map +1 -1
- package/lib/throttler.d.ts +26 -10
- package/lib/throttler.d.ts.map +1 -1
- package/lib/throttler.js +12 -4
- package/lib/throttler.js.map +1 -1
- package/package.json +22 -31
- package/src/batchTracker.ts +34 -36
- package/src/blobManager/blobManager.ts +54 -33
- package/src/blobManager/blobManagerSnapSum.ts +10 -10
- package/src/channelCollection.ts +108 -82
- package/src/connectionTelemetry.ts +43 -19
- package/src/containerHandleContext.ts +2 -2
- package/src/containerRuntime.ts +492 -364
- package/src/dataStore.ts +17 -9
- package/src/dataStoreContext.ts +94 -73
- package/src/dataStoreContexts.ts +17 -12
- package/src/dataStoreRegistry.ts +1 -1
- package/src/deltaManagerProxies.ts +5 -5
- package/src/deltaScheduler.ts +24 -18
- package/src/gc/garbageCollection.ts +89 -40
- package/src/gc/gcConfigs.ts +13 -5
- package/src/gc/gcDefinitions.ts +224 -70
- package/src/gc/gcHelpers.ts +22 -11
- package/src/gc/gcReferenceGraphAlgorithm.ts +1 -1
- package/src/gc/gcSummaryDefinitions.ts +18 -6
- package/src/gc/gcSummaryStateTracker.ts +7 -3
- package/src/gc/gcTelemetry.ts +73 -30
- package/src/gc/gcUnreferencedStateTracker.ts +40 -16
- package/src/gc/index.ts +1 -0
- package/src/{scheduleManager.ts → inboundBatchAggregator.ts} +55 -122
- package/src/index.ts +0 -3
- package/src/layerCompatState.ts +75 -0
- package/src/messageTypes.ts +16 -5
- package/src/metadata.ts +12 -4
- package/src/opLifecycle/README.md +43 -34
- package/src/opLifecycle/batchManager.ts +12 -6
- package/src/opLifecycle/duplicateBatchDetector.ts +12 -6
- package/src/opLifecycle/opCompressor.ts +22 -25
- package/src/opLifecycle/opDecompressor.ts +23 -11
- package/src/opLifecycle/opGroupingManager.ts +16 -11
- package/src/opLifecycle/opSplitter.ts +24 -18
- package/src/opLifecycle/outbox.ts +35 -33
- package/src/opLifecycle/remoteMessageProcessor.ts +13 -5
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +49 -26
- package/src/summary/documentSchema.ts +41 -22
- package/src/summary/index.ts +0 -3
- package/src/summary/orderedClientElection.ts +114 -49
- package/src/summary/runWhileConnectedCoordinator.ts +12 -3
- package/src/summary/runningSummarizer.ts +79 -36
- package/src/summary/summarizer.ts +51 -25
- package/src/summary/summarizerClientElection.ts +4 -2
- package/src/summary/summarizerHeuristics.ts +23 -12
- package/src/summary/summarizerNode/index.ts +1 -0
- package/src/summary/summarizerNode/summarizerNode.ts +54 -43
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +48 -16
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +25 -15
- package/src/summary/summarizerTypes.ts +253 -139
- package/src/summary/summaryCollection.ts +41 -31
- package/src/summary/summaryFormat.ts +34 -13
- package/src/summary/summaryGenerator.ts +39 -18
- package/src/summary/summaryManager.ts +36 -24
- package/src/throttler.ts +23 -11
- package/container-runtime.test-files.tar +0 -0
- package/dist/scheduleManager.d.ts +0 -28
- package/dist/scheduleManager.d.ts.map +0 -1
- package/dist/scheduleManager.js +0 -233
- package/dist/scheduleManager.js.map +0 -1
- package/lib/scheduleManager.d.ts +0 -28
- package/lib/scheduleManager.d.ts.map +0 -1
- package/lib/scheduleManager.js +0 -229
- package/lib/scheduleManager.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAuB,MAAM,gBAAgB,CAAC;AA2BtE,kHAAkH;AAClH,MAAM,UAAU,eAAe,CAAC,gBAAwB,EAAE,aAAqB;IAC9E,OAAO,GAAG,gBAAgB,KAAK,aAAa,GAAG,CAAC;AACjD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAClC,8BAAgE;IAEhE,IAAI,iBAAiB,IAAI,8BAA8B,EAAE,CAAC;QACzD,MAAM,cAAc,GAAoB,8BAA8B,CAAC;QACvE,OAAO,CACN,eAAe,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,OAAO;YACnD,eAAe,CACd,cAAc,CAAC,SAAS,CAAC,QAAQ,EACjC,cAAc,CAAC,SAAS,CAAC,aAAa,CACtC,CACD,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAmB,8BAA8B,CAAC;IAClE,OAAO,UAAU,CAAC,OAAO,IAAI,eAAe,CAAC,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;AAC7F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB;;GAEG;AACH,MAAM,OAAO,YAAY;IAKxB,IAAW,MAAM;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;IACjC,CAAC;IACD,IAAW,kBAAkB;QAC5B,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC9B,CAAC;IAED,IAAW,eAAe;QACzB,OAAO;YACN,uBAAuB,EAAE,IAAI,CAAC,uBAAuB;YACrD,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;SAC/C,CAAC;IACH,CAAC;IAED,IAAY,uBAAuB;QAClC,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;YACpC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAC5E,CAAC;IAQD,YAA4B,OAA6B;QAA7B,YAAO,GAAP,OAAO,CAAsB;QA9BjD,iBAAY,GAAmB,EAAE,CAAC;QAClC,qBAAgB,GAAG,CAAC,CAAC;QACrB,oBAAe,GAAG,KAAK,CAAC;IA4B4B,CAAC;IAEtD,IAAI,CACV,OAAqB,EACrB,SAAkB,EAClB,2BAAoC;QAEpC,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC;QAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QACzC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,SAAS,CAAC;QAEzD,2DAA2D;QAC3D,iEAAiE;QACjE,sGAAsG;QACtG,iGAAiG;QACjG,gFAAgF;QAChF,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,OAAO,CAAC;QAE7D,IAAI,iBAAiB,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACjD,OAAO,KAAK,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,oBAAoB,GAAG,2BAA2B,CAAC;QACzD,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,WAAW,CAAC;QACpC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,IAAW,KAAK;QACf,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACI,QAAQ,CAAC,OAAiB;QAChC,MAAM,KAAK,GAAW;YACrB,QAAQ,EAAE,IAAI,CAAC,YAAY;YAC3B,kBAAkB,EAAE,IAAI,CAAC,gBAAgB;YACzC,uBAAuB,EAAE,IAAI,CAAC,uBAAuB;YACrD,eAAe,EAAE,IAAI,CAAC,eAAe;SACrC,CAAC;QAEF,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,oBAAoB,GAAG,SAAS,CAAC;QACtC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAE7B,OAAO,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACI,UAAU;QAChB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAC5C,OAAO;YACN,QAAQ,EAAE,CAAC,OAAwC,EAAE,EAAE;gBACtD,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,GAAG,UAAU,GAAI,CAAC;oBACzD,CAAC,EAAE,CAAC;oBACJ,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;oBACrC,IAAI,CAAC,gBAAgB,IAAI,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC;oBACvD,OAAO,CAAC,OAAO,CAAC,CAAC;gBAClB,CAAC;gBAED,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,UAAU,CAAC;YACvC,CAAC;SACD,CAAC;IACH,CAAC;CACD;AAED,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,OAAiB,EAAU,EAAE;IACrE,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAE3C,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,CACL,QAAQ,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAC/C,KAAK,CAAC,8BAA8B,CACpC,CAAC;IAEF,MAAM,aAAa,GAA4B,QAAQ,CAAC,QAAQ,IAAI,EAAE,CAAC;IACvE,MAAM,YAAY,GAA4B,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;IAErE,2GAA2G;IAC3G,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,aAAa,CAAC,KAAK,GAAG,IAAI,CAAC;QAC3B,YAAY,CAAC,KAAK,GAAG,KAAK,CAAC;QAC3B,QAAQ,CAAC,QAAQ,GAAG,aAAa,CAAC;QAClC,OAAO,CAAC,QAAQ,GAAG,YAAY,CAAC;IACjC,CAAC;IAED,mFAAmF;IACnF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC3B,aAAa,CAAC,OAAO,GAAG,OAAO,CAAC;QAChC,QAAQ,CAAC,QAAQ,GAAG,aAAa,CAAC;IACnC,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAU,EAAE;IAC3D,OAAO,KAAK,CAAC,kBAAkB,GAAG,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;AACtE,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CACnC,OAA6B,EAC7B,YAAkC,EACxB,EAAE;IACZ,OAAO,CACN,CAAC,OAAO,CAAC,uBAAuB,KAAK,SAAS;QAC7C,YAAY,CAAC,uBAAuB,KAAK,SAAS;QAClD,OAAO,CAAC,uBAAuB,KAAK,YAAY,CAAC,uBAAuB,CAAC;QAC1E,CAAC,OAAO,CAAC,oBAAoB,KAAK,SAAS;YAC1C,YAAY,CAAC,oBAAoB,KAAK,SAAS;YAC/C,OAAO,CAAC,oBAAoB,KAAK,YAAY,CAAC,oBAAoB,CAAC,CACpE,CAAC;AACH,CAAC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\n\nimport { ICompressionRuntimeOptions } from \"../containerRuntime.js\";\nimport { asBatchMetadata, type IBatchMetadata } from \"../metadata.js\";\nimport type { IPendingMessage } from \"../pendingStateManager.js\";\n\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions.js\";\nimport type { BatchStartInfo } from \"./remoteMessageProcessor.js\";\n\nexport interface IBatchManagerOptions {\n\treadonly hardLimit: number;\n\treadonly compressionOptions?: ICompressionRuntimeOptions;\n\n\t/**\n\t * If true, the outbox is allowed to rebase the batch during flushing.\n\t */\n\treadonly canRebase: boolean;\n\n\t/** If true, don't compare batchID of incoming batches to this. e.g. ID Allocation Batch IDs should be ignored */\n\treadonly ignoreBatchId?: boolean;\n}\n\nexport interface BatchSequenceNumbers {\n\treferenceSequenceNumber?: number;\n\tclientSequenceNumber?: number;\n}\n\n/** Type alias for the batchId stored in batch metadata */\nexport type BatchId = string;\n\n/** Compose original client ID and client sequence number into BatchId to stamp on the message during reconnect */\nexport function generateBatchId(originalClientId: string, batchStartCsn: number): BatchId {\n\treturn `${originalClientId}_[${batchStartCsn}]`;\n}\n\n/**\n * Get the effective batch ID for the input argument.\n * Supports either an IPendingMessage or BatchStartInfo.\n * If the batch ID is explicitly present, return it.\n * Otherwise, generate a new batch ID using the client ID and batch start CSN.\n */\nexport function getEffectiveBatchId(\n\tpendingMessageOrBatchStartInfo: IPendingMessage | BatchStartInfo,\n): string {\n\tif (\"localOpMetadata\" in pendingMessageOrBatchStartInfo) {\n\t\tconst pendingMessage: IPendingMessage = pendingMessageOrBatchStartInfo;\n\t\treturn (\n\t\t\tasBatchMetadata(pendingMessage.opMetadata)?.batchId ??\n\t\t\tgenerateBatchId(\n\t\t\t\tpendingMessage.batchInfo.clientId,\n\t\t\t\tpendingMessage.batchInfo.batchStartCsn,\n\t\t\t)\n\t\t);\n\t}\n\n\tconst batchStart: BatchStartInfo = pendingMessageOrBatchStartInfo;\n\treturn batchStart.batchId ?? generateBatchId(batchStart.clientId, batchStart.batchStartCsn);\n}\n\n/**\n * Estimated size of the stringification overhead for an op accumulated\n * from runtime to loader to the service.\n */\nconst opOverhead = 200;\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n\tprivate pendingBatch: BatchMessage[] = [];\n\tprivate batchContentSize = 0;\n\tprivate hasReentrantOps = false;\n\n\tpublic get length() {\n\t\treturn this.pendingBatch.length;\n\t}\n\tpublic get contentSizeInBytes() {\n\t\treturn this.batchContentSize;\n\t}\n\n\tpublic get sequenceNumbers(): BatchSequenceNumbers {\n\t\treturn {\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\tclientSequenceNumber: this.clientSequenceNumber,\n\t\t};\n\t}\n\n\tprivate get referenceSequenceNumber(): number | undefined {\n\t\treturn this.pendingBatch.length === 0\n\t\t\t? undefined\n\t\t\t: this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;\n\t}\n\n\t/**\n\t * The last-processed CSN when this batch started.\n\t * This is used to ensure that while the batch is open, no incoming ops are processed.\n\t */\n\tprivate clientSequenceNumber: number | undefined;\n\n\tconstructor(public readonly options: IBatchManagerOptions) {}\n\n\tpublic push(\n\t\tmessage: BatchMessage,\n\t\treentrant: boolean,\n\t\tcurrentClientSequenceNumber?: number,\n\t): boolean {\n\t\tconst contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n\t\tconst opCount = this.pendingBatch.length;\n\t\tthis.hasReentrantOps = this.hasReentrantOps || reentrant;\n\n\t\t// Attempt to estimate batch size, aka socket message size.\n\t\t// Each op has pretty large envelope, estimating to be 200 bytes.\n\t\t// Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n\t\t// Not taking it into account, as compression work should help there - compressed payload will be\n\t\t// initially stored as base64, and that requires only 2 extra escape characters.\n\t\tconst socketMessageSize = contentSize + opOverhead * opCount;\n\n\t\tif (socketMessageSize >= this.options.hardLimit) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.pendingBatch.length === 0) {\n\t\t\tthis.clientSequenceNumber = currentClientSequenceNumber;\n\t\t}\n\n\t\tthis.batchContentSize = contentSize;\n\t\tthis.pendingBatch.push(message);\n\t\treturn true;\n\t}\n\n\tpublic get empty() {\n\t\treturn this.pendingBatch.length === 0;\n\t}\n\n\t/**\n\t * Gets the pending batch and clears state for the next batch.\n\t */\n\tpublic popBatch(batchId?: BatchId): IBatch {\n\t\tconst batch: IBatch = {\n\t\t\tmessages: this.pendingBatch,\n\t\t\tcontentSizeInBytes: this.batchContentSize,\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\thasReentrantOps: this.hasReentrantOps,\n\t\t};\n\n\t\tthis.pendingBatch = [];\n\t\tthis.batchContentSize = 0;\n\t\tthis.clientSequenceNumber = undefined;\n\t\tthis.hasReentrantOps = false;\n\n\t\treturn addBatchMetadata(batch, batchId);\n\t}\n\n\t/**\n\t * Capture the pending state at this point\n\t */\n\tpublic checkpoint(): IBatchCheckpoint {\n\t\tconst startPoint = this.pendingBatch.length;\n\t\treturn {\n\t\t\trollback: (process: (message: BatchMessage) => void) => {\n\t\t\t\tfor (let i = this.pendingBatch.length; i > startPoint; ) {\n\t\t\t\t\ti--;\n\t\t\t\t\tconst message = this.pendingBatch[i];\n\t\t\t\t\tthis.batchContentSize -= message.contents?.length ?? 0;\n\t\t\t\t\tprocess(message);\n\t\t\t\t}\n\n\t\t\t\tthis.pendingBatch.length = startPoint;\n\t\t\t},\n\t\t};\n\t}\n}\n\nconst addBatchMetadata = (batch: IBatch, batchId?: BatchId): IBatch => {\n\tconst batchEnd = batch.messages.length - 1;\n\n\tconst firstMsg = batch.messages[0];\n\tconst lastMsg = batch.messages[batchEnd];\n\tassert(\n\t\tfirstMsg !== undefined && lastMsg !== undefined,\n\t\t0x9d1 /* expected non-empty batch */,\n\t);\n\n\tconst firstMetadata: Partial<IBatchMetadata> = firstMsg.metadata ?? {};\n\tconst lastMetadata: Partial<IBatchMetadata> = lastMsg.metadata ?? {};\n\n\t// Multi-message batches: mark the first and last messages with the \"batch\" flag indicating batch start/end\n\tif (batch.messages.length > 1) {\n\t\tfirstMetadata.batch = true;\n\t\tlastMetadata.batch = false;\n\t\tfirstMsg.metadata = firstMetadata;\n\t\tlastMsg.metadata = lastMetadata;\n\t}\n\n\t// If batchId is provided (e.g. in case of resubmit): stamp it on the first message\n\tif (batchId !== undefined) {\n\t\tfirstMetadata.batchId = batchId;\n\t\tfirstMsg.metadata = firstMetadata;\n\t}\n\n\treturn batch;\n};\n\n/**\n * Estimates the real size in bytes on the socket for a given batch. It assumes that\n * the envelope size (and the size of an empty op) is 200 bytes, taking into account\n * extra overhead from stringification.\n *\n * @param batch - the batch to inspect\n * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire\n */\nexport const estimateSocketSize = (batch: IBatch): number => {\n\treturn batch.contentSizeInBytes + opOverhead * batch.messages.length;\n};\n\nexport const sequenceNumbersMatch = (\n\tseqNums: BatchSequenceNumbers,\n\totherSeqNums: BatchSequenceNumbers,\n): boolean => {\n\treturn (\n\t\t(seqNums.referenceSequenceNumber === undefined ||\n\t\t\totherSeqNums.referenceSequenceNumber === undefined ||\n\t\t\tseqNums.referenceSequenceNumber === otherSeqNums.referenceSequenceNumber) &&\n\t\t(seqNums.clientSequenceNumber === undefined ||\n\t\t\totherSeqNums.clientSequenceNumber === undefined ||\n\t\t\tseqNums.clientSequenceNumber === otherSeqNums.clientSequenceNumber)\n\t);\n};\n"]}
|
|
1
|
+
{"version":3,"file":"batchManager.js","sourceRoot":"","sources":["../../src/opLifecycle/batchManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\n\nimport { ICompressionRuntimeOptions } from \"../containerRuntime.js\";\nimport { asBatchMetadata, type IBatchMetadata } from \"../metadata.js\";\nimport type { IPendingMessage } from \"../pendingStateManager.js\";\n\nimport { BatchMessage, IBatch, IBatchCheckpoint } from \"./definitions.js\";\nimport type { BatchStartInfo } from \"./remoteMessageProcessor.js\";\n\nexport interface IBatchManagerOptions {\n\treadonly hardLimit: number;\n\treadonly compressionOptions?: ICompressionRuntimeOptions;\n\n\t/**\n\t * If true, the outbox is allowed to rebase the batch during flushing.\n\t */\n\treadonly canRebase: boolean;\n\n\t/**\n\t * If true, don't compare batchID of incoming batches to this. e.g. ID Allocation Batch IDs should be ignored\n\t */\n\treadonly ignoreBatchId?: boolean;\n}\n\nexport interface BatchSequenceNumbers {\n\treferenceSequenceNumber?: number;\n\tclientSequenceNumber?: number;\n}\n\n/**\n * Type alias for the batchId stored in batch metadata\n */\nexport type BatchId = string;\n\n/**\n * Compose original client ID and client sequence number into BatchId to stamp on the message during reconnect\n */\nexport function generateBatchId(originalClientId: string, batchStartCsn: number): BatchId {\n\treturn `${originalClientId}_[${batchStartCsn}]`;\n}\n\n/**\n * Get the effective batch ID for the input argument.\n * Supports either an IPendingMessage or BatchStartInfo.\n * If the batch ID is explicitly present, return it.\n * Otherwise, generate a new batch ID using the client ID and batch start CSN.\n */\nexport function getEffectiveBatchId(\n\tpendingMessageOrBatchStartInfo: IPendingMessage | BatchStartInfo,\n): string {\n\tif (\"localOpMetadata\" in pendingMessageOrBatchStartInfo) {\n\t\tconst pendingMessage: IPendingMessage = pendingMessageOrBatchStartInfo;\n\t\treturn (\n\t\t\tasBatchMetadata(pendingMessage.opMetadata)?.batchId ??\n\t\t\tgenerateBatchId(\n\t\t\t\tpendingMessage.batchInfo.clientId,\n\t\t\t\tpendingMessage.batchInfo.batchStartCsn,\n\t\t\t)\n\t\t);\n\t}\n\n\tconst batchStart: BatchStartInfo = pendingMessageOrBatchStartInfo;\n\treturn batchStart.batchId ?? generateBatchId(batchStart.clientId, batchStart.batchStartCsn);\n}\n\n/**\n * Estimated size of the stringification overhead for an op accumulated\n * from runtime to loader to the service.\n */\nconst opOverhead = 200;\n\n/**\n * Helper class that manages partial batch & rollback.\n */\nexport class BatchManager {\n\tprivate pendingBatch: BatchMessage[] = [];\n\tprivate batchContentSize = 0;\n\tprivate hasReentrantOps = false;\n\n\tpublic get length(): number {\n\t\treturn this.pendingBatch.length;\n\t}\n\tpublic get contentSizeInBytes(): number {\n\t\treturn this.batchContentSize;\n\t}\n\n\tpublic get sequenceNumbers(): BatchSequenceNumbers {\n\t\treturn {\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\tclientSequenceNumber: this.clientSequenceNumber,\n\t\t};\n\t}\n\n\tprivate get referenceSequenceNumber(): number | undefined {\n\t\treturn this.pendingBatch.length === 0\n\t\t\t? undefined\n\t\t\t: this.pendingBatch[this.pendingBatch.length - 1].referenceSequenceNumber;\n\t}\n\n\t/**\n\t * The last-processed CSN when this batch started.\n\t * This is used to ensure that while the batch is open, no incoming ops are processed.\n\t */\n\tprivate clientSequenceNumber: number | undefined;\n\n\tconstructor(public readonly options: IBatchManagerOptions) {}\n\n\tpublic push(\n\t\tmessage: BatchMessage,\n\t\treentrant: boolean,\n\t\tcurrentClientSequenceNumber?: number,\n\t): boolean {\n\t\tconst contentSize = this.batchContentSize + (message.contents?.length ?? 0);\n\t\tconst opCount = this.pendingBatch.length;\n\t\tthis.hasReentrantOps = this.hasReentrantOps || reentrant;\n\n\t\t// Attempt to estimate batch size, aka socket message size.\n\t\t// Each op has pretty large envelope, estimating to be 200 bytes.\n\t\t// Also content will be strigified, and that adds a lot of overhead due to a lot of escape characters.\n\t\t// Not taking it into account, as compression work should help there - compressed payload will be\n\t\t// initially stored as base64, and that requires only 2 extra escape characters.\n\t\tconst socketMessageSize = contentSize + opOverhead * opCount;\n\n\t\tif (socketMessageSize >= this.options.hardLimit) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (this.pendingBatch.length === 0) {\n\t\t\tthis.clientSequenceNumber = currentClientSequenceNumber;\n\t\t}\n\n\t\tthis.batchContentSize = contentSize;\n\t\tthis.pendingBatch.push(message);\n\t\treturn true;\n\t}\n\n\tpublic get empty(): boolean {\n\t\treturn this.pendingBatch.length === 0;\n\t}\n\n\t/**\n\t * Gets the pending batch and clears state for the next batch.\n\t */\n\tpublic popBatch(batchId?: BatchId): IBatch {\n\t\tconst batch: IBatch = {\n\t\t\tmessages: this.pendingBatch,\n\t\t\tcontentSizeInBytes: this.batchContentSize,\n\t\t\treferenceSequenceNumber: this.referenceSequenceNumber,\n\t\t\thasReentrantOps: this.hasReentrantOps,\n\t\t};\n\n\t\tthis.pendingBatch = [];\n\t\tthis.batchContentSize = 0;\n\t\tthis.clientSequenceNumber = undefined;\n\t\tthis.hasReentrantOps = false;\n\n\t\treturn addBatchMetadata(batch, batchId);\n\t}\n\n\t/**\n\t * Capture the pending state at this point\n\t */\n\tpublic checkpoint(): IBatchCheckpoint {\n\t\tconst startPoint = this.pendingBatch.length;\n\t\treturn {\n\t\t\trollback: (process: (message: BatchMessage) => void) => {\n\t\t\t\tfor (let i = this.pendingBatch.length; i > startPoint; ) {\n\t\t\t\t\ti--;\n\t\t\t\t\tconst message = this.pendingBatch[i];\n\t\t\t\t\tthis.batchContentSize -= message.contents?.length ?? 0;\n\t\t\t\t\tprocess(message);\n\t\t\t\t}\n\n\t\t\t\tthis.pendingBatch.length = startPoint;\n\t\t\t},\n\t\t};\n\t}\n}\n\nconst addBatchMetadata = (batch: IBatch, batchId?: BatchId): IBatch => {\n\tconst batchEnd = batch.messages.length - 1;\n\n\tconst firstMsg = batch.messages[0];\n\tconst lastMsg = batch.messages[batchEnd];\n\tassert(\n\t\tfirstMsg !== undefined && lastMsg !== undefined,\n\t\t0x9d1 /* expected non-empty batch */,\n\t);\n\n\tconst firstMetadata: Partial<IBatchMetadata> = firstMsg.metadata ?? {};\n\tconst lastMetadata: Partial<IBatchMetadata> = lastMsg.metadata ?? {};\n\n\t// Multi-message batches: mark the first and last messages with the \"batch\" flag indicating batch start/end\n\tif (batch.messages.length > 1) {\n\t\tfirstMetadata.batch = true;\n\t\tlastMetadata.batch = false;\n\t\tfirstMsg.metadata = firstMetadata;\n\t\tlastMsg.metadata = lastMetadata;\n\t}\n\n\t// If batchId is provided (e.g. in case of resubmit): stamp it on the first message\n\tif (batchId !== undefined) {\n\t\tfirstMetadata.batchId = batchId;\n\t\tfirstMsg.metadata = firstMetadata;\n\t}\n\n\treturn batch;\n};\n\n/**\n * Estimates the real size in bytes on the socket for a given batch. It assumes that\n * the envelope size (and the size of an empty op) is 200 bytes, taking into account\n * extra overhead from stringification.\n *\n * @param batch - the batch to inspect\n * @returns An estimate of the payload size in bytes which will be produced when the batch is sent over the wire\n */\nexport const estimateSocketSize = (batch: IBatch): number => {\n\treturn batch.contentSizeInBytes + opOverhead * batch.messages.length;\n};\n\nexport const sequenceNumbersMatch = (\n\tseqNums: BatchSequenceNumbers,\n\totherSeqNums: BatchSequenceNumbers,\n): boolean => {\n\treturn (\n\t\t(seqNums.referenceSequenceNumber === undefined ||\n\t\t\totherSeqNums.referenceSequenceNumber === undefined ||\n\t\t\tseqNums.referenceSequenceNumber === otherSeqNums.referenceSequenceNumber) &&\n\t\t(seqNums.clientSequenceNumber === undefined ||\n\t\t\totherSeqNums.clientSequenceNumber === undefined ||\n\t\t\tseqNums.clientSequenceNumber === otherSeqNums.clientSequenceNumber)\n\t);\n};\n"]}
|
|
@@ -8,11 +8,17 @@ import { type BatchStartInfo } from "./remoteMessageProcessor.js";
|
|
|
8
8
|
* This class tracks recent batchIds we've seen, and checks incoming batches for duplicates.
|
|
9
9
|
*/
|
|
10
10
|
export declare class DuplicateBatchDetector {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* All batchIds we've seen recently enough (based on MSN) that we need to watch for duplicates
|
|
13
|
+
*/
|
|
12
14
|
private readonly batchIdsAll;
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* We map from sequenceNumber to batchId to find which ones we can stop tracking as MSN advances
|
|
17
|
+
*/
|
|
14
18
|
private readonly batchIdsBySeqNum;
|
|
15
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Initialize from snapshot data if provided - otherwise initialize empty
|
|
21
|
+
*/
|
|
16
22
|
constructor(batchIdsFromSnapshot: [number, string][] | undefined);
|
|
17
23
|
/**
|
|
18
24
|
* Records this batch's batchId, and checks if it's a duplicate of a batch we've already seen.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicateBatchDetector.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/duplicateBatchDetector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAGtF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE;;GAEG;AACH,qBAAa,sBAAsB;IAClC
|
|
1
|
+
{"version":3,"file":"duplicateBatchDetector.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/duplicateBatchDetector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8CAA8C,CAAC;AAGtF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE;;GAEG;AACH,qBAAa,sBAAsB;IAClC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAEjD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA6B;IAE9D;;OAEG;gBACS,oBAAoB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,SAAS;IAShE;;;;;OAKG;IACI,mBAAmB,CACzB,UAAU,EAAE,cAAc,GACxB;QAAE,SAAS,EAAE,IAAI,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,SAAS,EAAE,KAAK,CAAA;KAAE;IAqC1E;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IASxB;;;;;OAKG;IACI,4BAA4B,CAClC,gBAAgB,CAAC,EAAE,iBAAiB,GAClC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,SAAS;CAajC"}
|
|
@@ -8,11 +8,17 @@ import { getEffectiveBatchId } from "./batchManager.js";
|
|
|
8
8
|
* This class tracks recent batchIds we've seen, and checks incoming batches for duplicates.
|
|
9
9
|
*/
|
|
10
10
|
export class DuplicateBatchDetector {
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Initialize from snapshot data if provided - otherwise initialize empty
|
|
13
|
+
*/
|
|
12
14
|
constructor(batchIdsFromSnapshot) {
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* All batchIds we've seen recently enough (based on MSN) that we need to watch for duplicates
|
|
17
|
+
*/
|
|
14
18
|
this.batchIdsAll = new Set();
|
|
15
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* We map from sequenceNumber to batchId to find which ones we can stop tracking as MSN advances
|
|
21
|
+
*/
|
|
16
22
|
this.batchIdsBySeqNum = new Map();
|
|
17
23
|
if (batchIdsFromSnapshot) {
|
|
18
24
|
this.batchIdsBySeqNum = new Map(batchIdsFromSnapshot);
|
|
@@ -60,12 +66,12 @@ export class DuplicateBatchDetector {
|
|
|
60
66
|
* since the batch start has been processed by all clients, and local batches are deduped and the forked client would close.
|
|
61
67
|
*/
|
|
62
68
|
clearOldBatchIds(msn) {
|
|
63
|
-
this.batchIdsBySeqNum
|
|
69
|
+
for (const [sequenceNumber, batchId] of this.batchIdsBySeqNum) {
|
|
64
70
|
if (sequenceNumber < msn) {
|
|
65
71
|
this.batchIdsBySeqNum.delete(sequenceNumber);
|
|
66
72
|
this.batchIdsAll.delete(batchId);
|
|
67
73
|
}
|
|
68
|
-
}
|
|
74
|
+
}
|
|
69
75
|
}
|
|
70
76
|
/**
|
|
71
77
|
* Returns a snapshot of the state of the detector which can be included in a summary
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicateBatchDetector.js","sourceRoot":"","sources":["../../src/opLifecycle/duplicateBatchDetector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD;;GAEG;AACH,MAAM,OAAO,sBAAsB;
|
|
1
|
+
{"version":3,"file":"duplicateBatchDetector.js","sourceRoot":"","sources":["../../src/opLifecycle/duplicateBatchDetector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD;;GAEG;AACH,MAAM,OAAO,sBAAsB;IAWlC;;OAEG;IACH,YAAY,oBAAoD;QAbhE;;WAEG;QACc,gBAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QAEjD;;WAEG;QACc,qBAAgB,GAAG,IAAI,GAAG,EAAkB,CAAC;QAM7D,IAAI,oBAAoB,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,GAAG,IAAI,GAAG,CAAC,oBAAoB,CAAC,CAAC;YACtD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC;gBACtD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACI,mBAAmB,CACzB,UAA0B;QAE1B,MAAM,EAAE,cAAc,EAAE,qBAAqB,EAAE,GAAG,UAAU,CAAC,UAAU,CAAC;QAExE,+GAA+G;QAC/G,6IAA6I;QAC7I,IAAI,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC;QAE7C,6EAA6E;QAC7E,oGAAoG;QACpG,4EAA4E;QAC5E,4GAA4G;QAC5G,MAAM,OAAO,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;QAEhD,2EAA2E;QAC3E,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACnC,KAAK,MAAM,CAAC,mBAAmB,EAAE,YAAY,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC;gBACnF,IAAI,YAAY,KAAK,OAAO,EAAE,CAAC;oBAC9B,OAAO;wBACN,SAAS,EAAE,IAAI;wBACf,mBAAmB;qBACnB,CAAC;gBACH,CAAC;YACF,CAAC;YACD,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,0DAA0D,CAAC,CAAC;QACjF,CAAC;QAED,kFAAkF;QAClF,MAAM,CACL,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,cAAc,CAAC,EAC1C,KAAK,CAAC,wDAAwD,CAC9D,CAAC;QACF,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAE9B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACK,gBAAgB,CAAC,GAAW;QACnC,KAAK,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC/D,IAAI,cAAc,GAAG,GAAG,EAAE,CAAC;gBAC1B,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;gBAC7C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAClC,CAAC;QACF,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACI,4BAA4B,CAClC,gBAAoC;QAEpC,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,gBAAgB,EAAE,GAAG,CACpB,+BAA+B,EAC/B,kBAAkB,EAClB,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAC1B,CAAC;QAEF,OAAO,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { ITelemetryContext } from \"@fluidframework/runtime-definitions/internal\";\n\nimport { getEffectiveBatchId } from \"./batchManager.js\";\nimport { type BatchStartInfo } from \"./remoteMessageProcessor.js\";\n\n/**\n * This class tracks recent batchIds we've seen, and checks incoming batches for duplicates.\n */\nexport class DuplicateBatchDetector {\n\t/**\n\t * All batchIds we've seen recently enough (based on MSN) that we need to watch for duplicates\n\t */\n\tprivate readonly batchIdsAll = new Set<string>();\n\n\t/**\n\t * We map from sequenceNumber to batchId to find which ones we can stop tracking as MSN advances\n\t */\n\tprivate readonly batchIdsBySeqNum = new Map<number, string>();\n\n\t/**\n\t * Initialize from snapshot data if provided - otherwise initialize empty\n\t */\n\tconstructor(batchIdsFromSnapshot: [number, string][] | undefined) {\n\t\tif (batchIdsFromSnapshot) {\n\t\t\tthis.batchIdsBySeqNum = new Map(batchIdsFromSnapshot);\n\t\t\tfor (const batchId of this.batchIdsBySeqNum.values()) {\n\t\t\t\tthis.batchIdsAll.add(batchId);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Records this batch's batchId, and checks if it's a duplicate of a batch we've already seen.\n\t * If it's a duplicate, also return the sequence number of the other batch for logging.\n\t *\n\t * @remarks - We also use the minimumSequenceNumber to clear out old batchIds that are no longer at risk for duplicates.\n\t */\n\tpublic processInboundBatch(\n\t\tbatchStart: BatchStartInfo,\n\t): { duplicate: true; otherSequenceNumber: number } | { duplicate: false } {\n\t\tconst { sequenceNumber, minimumSequenceNumber } = batchStart.keyMessage;\n\n\t\t// Glance at this batch's MSN. Any batchIds we're tracking with a lower sequence number are now safe to forget.\n\t\t// Why? Because any other client holding the same batch locally would have seen the earlier batch and closed before submitting its duplicate.\n\t\tthis.clearOldBatchIds(minimumSequenceNumber);\n\n\t\t// getEffectiveBatchId is only needed in the SUPER rare/surprising case where\n\t\t// the original batch (not resubmitted, so no batchId) arrives in parallel with a resubmitted batch.\n\t\t// In the presence of typical network conditions, this would not be possible\n\t\t// (the original batch should roundtrip WAY before another container could rehydrate, connect, and resubmit)\n\t\tconst batchId = getEffectiveBatchId(batchStart);\n\n\t\t// Check this batch against the tracked batchIds to see if it's a duplicate\n\t\tif (this.batchIdsAll.has(batchId)) {\n\t\t\tfor (const [otherSequenceNumber, otherBatchId] of this.batchIdsBySeqNum.entries()) {\n\t\t\t\tif (otherBatchId === batchId) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tduplicate: true,\n\t\t\t\t\t\totherSequenceNumber,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\tassert(false, 0xa34 /* Should have found the batchId in batchIdBySeqNum map */);\n\t\t}\n\n\t\t// Now we know it's not a duplicate, so add it to the tracked batchIds and return.\n\t\tassert(\n\t\t\t!this.batchIdsBySeqNum.has(sequenceNumber),\n\t\t\t0xa35 /* batchIdsAll and batchIdsBySeqNum should be in sync */,\n\t\t);\n\t\tthis.batchIdsBySeqNum.set(sequenceNumber, batchId);\n\t\tthis.batchIdsAll.add(batchId);\n\n\t\treturn { duplicate: false };\n\t}\n\n\t/**\n\t * Batches that started before the MSN are not at risk for a sequenced duplicate to arrive,\n\t * since the batch start has been processed by all clients, and local batches are deduped and the forked client would close.\n\t */\n\tprivate clearOldBatchIds(msn: number): void {\n\t\tfor (const [sequenceNumber, batchId] of this.batchIdsBySeqNum) {\n\t\t\tif (sequenceNumber < msn) {\n\t\t\t\tthis.batchIdsBySeqNum.delete(sequenceNumber);\n\t\t\t\tthis.batchIdsAll.delete(batchId);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns a snapshot of the state of the detector which can be included in a summary\n\t * and used to \"rehydrate\" this class when loading from a snapshot.\n\t *\n\t * @returns - A serializable object representing the state of the detector, or undefined if there is nothing to save.\n\t */\n\tpublic getRecentBatchInfoForSummary(\n\t\ttelemetryContext?: ITelemetryContext,\n\t): [number, string][] | undefined {\n\t\tif (this.batchIdsBySeqNum.size === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\ttelemetryContext?.set(\n\t\t\t\"fluid_DuplicateBatchDetector_\",\n\t\t\t\"recentBatchCount\",\n\t\t\tthis.batchIdsBySeqNum.size,\n\t\t);\n\n\t\treturn [...this.batchIdsBySeqNum.entries()];\n\t}\n}\n"]}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { ITelemetryBaseLogger } from "@fluidframework/core-interfaces";
|
|
6
|
-
import { IBatch } from "./definitions.js";
|
|
6
|
+
import { BatchMessage, IBatch } from "./definitions.js";
|
|
7
7
|
/**
|
|
8
8
|
* Compresses batches of ops. It generates a single compressed op that contains
|
|
9
9
|
* the contents of each op in the batch. It then submits empty ops for each original
|
|
@@ -16,10 +16,11 @@ export declare class OpCompressor {
|
|
|
16
16
|
* Combines the contents of the batch into a single JSON string and compresses it, putting
|
|
17
17
|
* the resulting string as the first message of the batch. The rest of the messages are
|
|
18
18
|
* empty placeholders to reserve sequence numbers.
|
|
19
|
+
* This should only take a single message batch and compress it.
|
|
19
20
|
* @param batch - The batch to compress
|
|
20
21
|
* @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders
|
|
21
22
|
*/
|
|
22
|
-
compressBatch(batch: IBatch): IBatch
|
|
23
|
+
compressBatch(batch: IBatch): IBatch<[BatchMessage]>;
|
|
23
24
|
/**
|
|
24
25
|
* Combine the batch's content strings into a single JSON string (a serialized array)
|
|
25
26
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"opCompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAYvE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExD;;;;GAIG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;gBAEjC,MAAM,EAAE,oBAAoB;IAIxC;;;;;;;OAOG;IACI,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAyC3D;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAuB9B"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { IsoBuffer } from "@fluid-internal/client-utils";
|
|
6
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
-
import { UsageError, createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
7
|
+
import { UsageError, createChildLogger, } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
import { compress } from "lz4js";
|
|
9
9
|
import { CompressionAlgorithms } from "../containerRuntime.js";
|
|
10
10
|
import { estimateSocketSize } from "./batchManager.js";
|
|
@@ -21,31 +21,25 @@ export class OpCompressor {
|
|
|
21
21
|
* Combines the contents of the batch into a single JSON string and compresses it, putting
|
|
22
22
|
* the resulting string as the first message of the batch. The rest of the messages are
|
|
23
23
|
* empty placeholders to reserve sequence numbers.
|
|
24
|
+
* This should only take a single message batch and compress it.
|
|
24
25
|
* @param batch - The batch to compress
|
|
25
26
|
* @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders
|
|
26
27
|
*/
|
|
27
28
|
compressBatch(batch) {
|
|
28
|
-
assert(batch.contentSizeInBytes > 0 && batch.messages.length
|
|
29
|
+
assert(batch.contentSizeInBytes > 0 && batch.messages.length === 1, 0x5a4 /* Batch should not be empty and should contain a single message */);
|
|
29
30
|
const compressionStart = Date.now();
|
|
30
31
|
const contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));
|
|
31
32
|
const compressedContents = compress(contentsAsBuffer);
|
|
32
33
|
const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
|
|
33
34
|
const duration = Date.now() - compressionStart;
|
|
34
|
-
const messages = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (const message of batch.messages.slice(1)) {
|
|
43
|
-
messages.push({
|
|
44
|
-
localOpMetadata: message.localOpMetadata,
|
|
45
|
-
metadata: message.metadata,
|
|
46
|
-
referenceSequenceNumber: message.referenceSequenceNumber,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
35
|
+
const messages = [
|
|
36
|
+
{
|
|
37
|
+
...batch.messages[0],
|
|
38
|
+
contents: JSON.stringify({ packedContents: compressedContent }),
|
|
39
|
+
metadata: batch.messages[0].metadata,
|
|
40
|
+
compression: CompressionAlgorithms.lz4,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
49
43
|
const compressedBatch = {
|
|
50
44
|
contentSizeInBytes: compressedContent.length,
|
|
51
45
|
messages,
|
|
@@ -71,8 +65,8 @@ export class OpCompressor {
|
|
|
71
65
|
// Yields a valid JSON array, since each message.contents is already serialized to JSON
|
|
72
66
|
return `[${batch.messages.map(({ contents }) => contents).join(",")}]`;
|
|
73
67
|
}
|
|
74
|
-
catch (
|
|
75
|
-
if (
|
|
68
|
+
catch (newError) {
|
|
69
|
+
if (newError.message === "Invalid string length") {
|
|
76
70
|
// This is how JSON.stringify signals that
|
|
77
71
|
// the content size exceeds its capacity
|
|
78
72
|
const error = new UsageError("Payload too large");
|
|
@@ -83,7 +77,7 @@ export class OpCompressor {
|
|
|
83
77
|
}, error);
|
|
84
78
|
throw error;
|
|
85
79
|
}
|
|
86
|
-
throw
|
|
80
|
+
throw newError;
|
|
87
81
|
}
|
|
88
82
|
}
|
|
89
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAC7D,OAAO,
|
|
1
|
+
{"version":3,"file":"opCompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opCompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IsoBuffer } from \"@fluid-internal/client-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport {\n\tUsageError,\n\tcreateChildLogger,\n\ttype ITelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { compress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\n\nimport { estimateSocketSize } from \"./batchManager.js\";\nimport { BatchMessage, IBatch } from \"./definitions.js\";\n\n/**\n * Compresses batches of ops. It generates a single compressed op that contains\n * the contents of each op in the batch. It then submits empty ops for each original\n * op to reserve sequence numbers.\n */\nexport class OpCompressor {\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpCompressor\" });\n\t}\n\n\t/**\n\t * Combines the contents of the batch into a single JSON string and compresses it, putting\n\t * the resulting string as the first message of the batch. The rest of the messages are\n\t * empty placeholders to reserve sequence numbers.\n\t * This should only take a single message batch and compress it.\n\t * @param batch - The batch to compress\n\t * @returns A batch of the same length as the input batch, containing a single compressed message followed by empty placeholders\n\t */\n\tpublic compressBatch(batch: IBatch): IBatch<[BatchMessage]> {\n\t\tassert(\n\t\t\tbatch.contentSizeInBytes > 0 && batch.messages.length === 1,\n\t\t\t0x5a4 /* Batch should not be empty and should contain a single message */,\n\t\t);\n\n\t\tconst compressionStart = Date.now();\n\t\tconst contentsAsBuffer = new TextEncoder().encode(this.serializeBatchContents(batch));\n\t\tconst compressedContents = compress(contentsAsBuffer);\n\t\tconst compressedContent = IsoBuffer.from(compressedContents).toString(\"base64\");\n\t\tconst duration = Date.now() - compressionStart;\n\n\t\tconst messages: [BatchMessage] = [\n\t\t\t{\n\t\t\t\t...batch.messages[0],\n\t\t\t\tcontents: JSON.stringify({ packedContents: compressedContent }),\n\t\t\t\tmetadata: batch.messages[0].metadata,\n\t\t\t\tcompression: CompressionAlgorithms.lz4,\n\t\t\t},\n\t\t];\n\n\t\tconst compressedBatch = {\n\t\t\tcontentSizeInBytes: compressedContent.length,\n\t\t\tmessages,\n\t\t\treferenceSequenceNumber: batch.referenceSequenceNumber,\n\t\t};\n\n\t\tif (batch.contentSizeInBytes > 200000) {\n\t\t\tthis.logger.sendPerformanceEvent({\n\t\t\t\teventName: \"CompressedBatch\",\n\t\t\t\tduration,\n\t\t\t\tsizeBeforeCompression: batch.contentSizeInBytes,\n\t\t\t\tsizeAfterCompression: compressedBatch.contentSizeInBytes,\n\t\t\t\topCount: compressedBatch.messages.length,\n\t\t\t\tsocketSize: estimateSocketSize(compressedBatch),\n\t\t\t});\n\t\t}\n\n\t\treturn compressedBatch;\n\t}\n\n\t/**\n\t * Combine the batch's content strings into a single JSON string (a serialized array)\n\t */\n\tprivate serializeBatchContents(batch: IBatch): string {\n\t\ttry {\n\t\t\t// Yields a valid JSON array, since each message.contents is already serialized to JSON\n\t\t\treturn `[${batch.messages.map(({ contents }) => contents).join(\",\")}]`;\n\t\t} catch (newError: unknown) {\n\t\t\tif ((newError as Partial<Error>).message === \"Invalid string length\") {\n\t\t\t\t// This is how JSON.stringify signals that\n\t\t\t\t// the content size exceeds its capacity\n\t\t\t\tconst error = new UsageError(\"Payload too large\");\n\t\t\t\tthis.logger.sendErrorEvent(\n\t\t\t\t\t{\n\t\t\t\t\t\teventName: \"BatchTooLarge\",\n\t\t\t\t\t\tsize: batch.contentSizeInBytes,\n\t\t\t\t\t\tlength: batch.messages.length,\n\t\t\t\t\t},\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow newError;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -11,6 +11,9 @@ import { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/in
|
|
|
11
11
|
* 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set
|
|
12
12
|
* 3. The final message of a batch will have batch metadata set to false
|
|
13
13
|
* 4. An individually compressed op will have undefined batch metadata and compression set to true
|
|
14
|
+
*
|
|
15
|
+
* Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages
|
|
16
|
+
* because we need that functionality for back compat.
|
|
14
17
|
*/
|
|
15
18
|
export declare class OpDecompressor {
|
|
16
19
|
private activeBatch;
|
|
@@ -20,7 +23,9 @@ export declare class OpDecompressor {
|
|
|
20
23
|
constructor(logger: ITelemetryBaseLogger);
|
|
21
24
|
isCompressedMessage(message: ISequencedDocumentMessage): boolean;
|
|
22
25
|
get currentlyUnrolling(): boolean;
|
|
23
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Is the decompressed and stored batch only comprised of a single message
|
|
28
|
+
*/
|
|
24
29
|
private isSingleMessageBatch;
|
|
25
30
|
/**
|
|
26
31
|
* Decompress the given compressed message and store it to be subsequently unrolled.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"opDecompressor.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAiBxF;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,WAAW,CAAS;IAG5B,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;gBAEjC,MAAM,EAAE,oBAAoB;IAIjC,mBAAmB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO;IA2CvE,IAAW,kBAAkB,IAAI,OAAO,CAEvC;IAED;;OAEG;IACH,OAAO,CAAC,oBAAoB,CAAS;IAErC;;;OAGG;IACI,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI;IA8BnE;;;OAGG;IACI,MAAM,CAAC,OAAO,EAAE,yBAAyB,GAAG,yBAAyB;CAmC5E"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { IsoBuffer, Uint8ArrayToString } from "@fluid-internal/client-utils";
|
|
6
6
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
7
|
-
import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
7
|
+
import { createChildLogger, } from "@fluidframework/telemetry-utils/internal";
|
|
8
8
|
import { decompress } from "lz4js";
|
|
9
9
|
import { CompressionAlgorithms } from "../containerRuntime.js";
|
|
10
10
|
/**
|
|
@@ -14,12 +14,17 @@ import { CompressionAlgorithms } from "../containerRuntime.js";
|
|
|
14
14
|
* 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set
|
|
15
15
|
* 3. The final message of a batch will have batch metadata set to false
|
|
16
16
|
* 4. An individually compressed op will have undefined batch metadata and compression set to true
|
|
17
|
+
*
|
|
18
|
+
* Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages
|
|
19
|
+
* because we need that functionality for back compat.
|
|
17
20
|
*/
|
|
18
21
|
export class OpDecompressor {
|
|
19
22
|
constructor(logger) {
|
|
20
23
|
this.activeBatch = false;
|
|
21
24
|
this.processedCount = 0;
|
|
22
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Is the decompressed and stored batch only comprised of a single message
|
|
27
|
+
*/
|
|
23
28
|
this.isSingleMessageBatch = false;
|
|
24
29
|
this.logger = createChildLogger({ logger, namespace: "OpDecompressor" });
|
|
25
30
|
}
|
|
@@ -54,7 +59,7 @@ export class OpDecompressor {
|
|
|
54
59
|
return true;
|
|
55
60
|
}
|
|
56
61
|
}
|
|
57
|
-
catch
|
|
62
|
+
catch {
|
|
58
63
|
return false;
|
|
59
64
|
}
|
|
60
65
|
return false;
|
|
@@ -81,8 +86,8 @@ export class OpDecompressor {
|
|
|
81
86
|
const contents = IsoBuffer.from(message.contents.packedContents, "base64");
|
|
82
87
|
const decompressedMessage = decompress(contents);
|
|
83
88
|
const intoString = Uint8ArrayToString(decompressedMessage);
|
|
84
|
-
|
|
85
|
-
this.rootMessageContents =
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
90
|
+
this.rootMessageContents = JSON.parse(intoString);
|
|
86
91
|
}
|
|
87
92
|
/**
|
|
88
93
|
* Unroll the next message from the decompressed content provided to {@link decompressAndStore}
|
|
@@ -91,10 +96,13 @@ export class OpDecompressor {
|
|
|
91
96
|
unroll(message) {
|
|
92
97
|
assert(this.currentlyUnrolling, 0x942 /* not currently unrolling */);
|
|
93
98
|
assert(this.rootMessageContents !== undefined, 0x943 /* missing rootMessageContents */);
|
|
94
|
-
assert(
|
|
99
|
+
assert(
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
101
|
+
this.rootMessageContents.length > this.processedCount, 0x944 /* no more content to unroll */);
|
|
95
102
|
const batchMetadata = message.metadata?.batch;
|
|
96
103
|
if (batchMetadata === false || this.isSingleMessageBatch) {
|
|
97
104
|
// End of compressed batch
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
98
106
|
const returnMessage = newMessage(message, this.rootMessageContents[this.processedCount]);
|
|
99
107
|
this.activeBatch = false;
|
|
100
108
|
this.isSingleMessageBatch = false;
|
|
@@ -104,11 +112,13 @@ export class OpDecompressor {
|
|
|
104
112
|
}
|
|
105
113
|
else if (batchMetadata === true) {
|
|
106
114
|
// Start of compressed batch
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
107
116
|
return newMessage(message, this.rootMessageContents[this.processedCount++]);
|
|
108
117
|
}
|
|
109
118
|
assert(batchMetadata === undefined, 0x945 /* invalid batch metadata */);
|
|
110
119
|
assert(message.contents === undefined, 0x512 /* Expecting empty message */);
|
|
111
120
|
// Continuation of compressed batch
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
112
122
|
return newMessage(message, this.rootMessageContents[this.processedCount++]);
|
|
113
123
|
}
|
|
114
124
|
}
|
|
@@ -118,8 +128,6 @@ const newMessage = (originalMessage, contents) => ({
|
|
|
118
128
|
contents,
|
|
119
129
|
compression: undefined,
|
|
120
130
|
// TODO: It should already be the case that we're not modifying any metadata, not clear if/why this shallow clone should be required.
|
|
121
|
-
metadata: originalMessage.metadata === undefined
|
|
122
|
-
? undefined
|
|
123
|
-
: { ...originalMessage.metadata },
|
|
131
|
+
metadata: originalMessage.metadata === undefined ? undefined : { ...originalMessage.metadata },
|
|
124
132
|
});
|
|
125
133
|
//# sourceMappingURL=opDecompressor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IsoBuffer, Uint8ArrayToString } from \"@fluid-internal/client-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport { createChildLogger } from \"@fluidframework/telemetry-utils/internal\";\nimport { decompress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\nimport { IBatchMetadata } from \"../metadata.js\";\n\n/**\n * Compression makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IPackedContentsContents {\n\tpackedContents: string;\n}\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n */\nexport class OpDecompressor {\n\tprivate activeBatch = false;\n\tprivate rootMessageContents: any | undefined;\n\tprivate processedCount = 0;\n\tprivate readonly logger;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpDecompressor\" });\n\t}\n\n\tpublic isCompressedMessage(message: ISequencedDocumentMessage): boolean {\n\t\tif (message.compression === CompressionAlgorithms.lz4) {\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Back-compat self healing mechanism for ADO:3538, as loaders from\n\t\t * version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not\n\t\t * support adding the proper compression metadata to compressed messages submitted\n\t\t * by the runtime. Should be removed after the loader reaches sufficient saturation\n\t\t * for a version greater or equal than client_v2.0.0-internal.2.2.0.\n\t\t *\n\t\t * The condition holds true for compressed messages, regardless of metadata. We are ultimately\n\t\t * looking for a message with a single property `packedContents` inside `contents`, of type 'string'\n\t\t * with a base64 encoded value.\n\t\t */\n\t\ttry {\n\t\t\tif (\n\t\t\t\tmessage.contents !== null &&\n\t\t\t\ttypeof message.contents === \"object\" &&\n\t\t\t\tObject.keys(message.contents).length === 1 &&\n\t\t\t\ttypeof (message.contents as { packedContents?: unknown }).packedContents ===\n\t\t\t\t\t\"string\" &&\n\t\t\t\t(message.contents as IPackedContentsContents).packedContents.length > 0 &&\n\t\t\t\tIsoBuffer.from(\n\t\t\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\t\t\"base64\",\n\t\t\t\t).toString(\"base64\") === (message.contents as IPackedContentsContents).packedContents\n\t\t\t) {\n\t\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LegacyCompression\",\n\t\t\t\t\ttype: message.type,\n\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch (err) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic get currentlyUnrolling() {\n\t\treturn this.activeBatch;\n\t}\n\n\t/** Is the decompressed and stored batch only comprised of a single message */\n\tprivate isSingleMessageBatch = false;\n\n\t/**\n\t * Decompress the given compressed message and store it to be subsequently unrolled.\n\t * The stored message will be of type `any[]` where each element represents a message's `contents`\n\t */\n\tpublic decompressAndStore(message: ISequencedDocumentMessage): void {\n\t\tassert(\n\t\t\tmessage.compression === undefined || message.compression === CompressionAlgorithms.lz4,\n\t\t\t0x511 /* Only lz4 compression is supported */,\n\t\t);\n\t\tassert(\n\t\t\tthis.isCompressedMessage(message),\n\t\t\t0x940 /* provided message should be compressed */,\n\t\t);\n\n\t\tassert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n\t\tthis.activeBatch = true;\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\t\tif (batchMetadata === undefined) {\n\t\t\tthis.isSingleMessageBatch = true;\n\t\t} else {\n\t\t\tassert(batchMetadata === true, 0x941 /* invalid batch metadata */);\n\t\t}\n\n\t\tconst contents = IsoBuffer.from(\n\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\"base64\",\n\t\t);\n\t\tconst decompressedMessage = decompress(contents);\n\t\tconst intoString = Uint8ArrayToString(decompressedMessage);\n\t\tconst asObj = JSON.parse(intoString);\n\t\tthis.rootMessageContents = asObj;\n\t}\n\n\t/**\n\t * Unroll the next message from the decompressed content provided to {@link decompressAndStore}\n\t * @returns the unrolled `ISequencedDocumentMessage`\n\t */\n\tpublic unroll(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n\t\tassert(this.currentlyUnrolling, 0x942 /* not currently unrolling */);\n\t\tassert(this.rootMessageContents !== undefined, 0x943 /* missing rootMessageContents */);\n\t\tassert(\n\t\t\tthis.rootMessageContents.length > this.processedCount,\n\t\t\t0x944 /* no more content to unroll */,\n\t\t);\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\n\t\tif (batchMetadata === false || this.isSingleMessageBatch) {\n\t\t\t// End of compressed batch\n\t\t\tconst returnMessage = newMessage(message, this.rootMessageContents[this.processedCount]);\n\n\t\t\tthis.activeBatch = false;\n\t\t\tthis.isSingleMessageBatch = false;\n\t\t\tthis.rootMessageContents = undefined;\n\t\t\tthis.processedCount = 0;\n\n\t\t\treturn returnMessage;\n\t\t} else if (batchMetadata === true) {\n\t\t\t// Start of compressed batch\n\t\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t\t}\n\n\t\tassert(batchMetadata === undefined, 0x945 /* invalid batch metadata */);\n\t\tassert(message.contents === undefined, 0x512 /* Expecting empty message */);\n\n\t\t// Continuation of compressed batch\n\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t}\n}\n\n// We should not be mutating the input message nor its metadata\nconst newMessage = (\n\toriginalMessage: ISequencedDocumentMessage,\n\tcontents: any,\n): ISequencedDocumentMessage => ({\n\t...originalMessage,\n\tcontents,\n\tcompression: undefined,\n\t// TODO: It should already be the case that we're not modifying any metadata, not clear if/why this shallow clone should be required.\n\n\tmetadata:\n\t\toriginalMessage.metadata === undefined\n\t\t\t? undefined\n\t\t\t: { ...(originalMessage.metadata as any) },\n});\n"]}
|
|
1
|
+
{"version":3,"file":"opDecompressor.js","sourceRoot":"","sources":["../../src/opLifecycle/opDecompressor.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { IsoBuffer, Uint8ArrayToString } from \"@fluid-internal/client-utils\";\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype ITelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\nimport { decompress } from \"lz4js\";\n\nimport { CompressionAlgorithms } from \"../containerRuntime.js\";\nimport { IBatchMetadata } from \"../metadata.js\";\n\n/**\n * Compression makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IPackedContentsContents {\n\tpackedContents: string;\n}\n\n/**\n * State machine that \"unrolls\" contents of compressed batches of ops after decompressing them.\n * This class relies on some implicit contracts defined below:\n * 1. A compressed batch will have its first message with batch metadata set to true and compressed set to true\n * 2. Messages in the middle of a compressed batch will have neither batch metadata nor the compression property set\n * 3. The final message of a batch will have batch metadata set to false\n * 4. An individually compressed op will have undefined batch metadata and compression set to true\n *\n * Compressed batches from current code are always a single message but this class needs to handle a legacy compressed batch with multiple messages\n * because we need that functionality for back compat.\n */\nexport class OpDecompressor {\n\tprivate activeBatch = false;\n\t// TODO: better typing\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tprivate rootMessageContents: any | undefined;\n\tprivate processedCount = 0;\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tconstructor(logger: ITelemetryBaseLogger) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpDecompressor\" });\n\t}\n\n\tpublic isCompressedMessage(message: ISequencedDocumentMessage): boolean {\n\t\tif (message.compression === CompressionAlgorithms.lz4) {\n\t\t\treturn true;\n\t\t}\n\n\t\t/**\n\t\t * Back-compat self healing mechanism for ADO:3538, as loaders from\n\t\t * version client_v2.0.0-internal.1.2.0 to client_v2.0.0-internal.2.2.0 do not\n\t\t * support adding the proper compression metadata to compressed messages submitted\n\t\t * by the runtime. Should be removed after the loader reaches sufficient saturation\n\t\t * for a version greater or equal than client_v2.0.0-internal.2.2.0.\n\t\t *\n\t\t * The condition holds true for compressed messages, regardless of metadata. We are ultimately\n\t\t * looking for a message with a single property `packedContents` inside `contents`, of type 'string'\n\t\t * with a base64 encoded value.\n\t\t */\n\t\ttry {\n\t\t\tif (\n\t\t\t\tmessage.contents !== null &&\n\t\t\t\ttypeof message.contents === \"object\" &&\n\t\t\t\tObject.keys(message.contents).length === 1 &&\n\t\t\t\ttypeof (message.contents as { packedContents?: unknown }).packedContents ===\n\t\t\t\t\t\"string\" &&\n\t\t\t\t(message.contents as IPackedContentsContents).packedContents.length > 0 &&\n\t\t\t\tIsoBuffer.from(\n\t\t\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\t\t\"base64\",\n\t\t\t\t).toString(\"base64\") === (message.contents as IPackedContentsContents).packedContents\n\t\t\t) {\n\t\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LegacyCompression\",\n\t\t\t\t\ttype: message.type,\n\t\t\t\t\tbatch: (message.metadata as IBatchMetadata | undefined)?.batch,\n\t\t\t\t});\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic get currentlyUnrolling(): boolean {\n\t\treturn this.activeBatch;\n\t}\n\n\t/**\n\t * Is the decompressed and stored batch only comprised of a single message\n\t */\n\tprivate isSingleMessageBatch = false;\n\n\t/**\n\t * Decompress the given compressed message and store it to be subsequently unrolled.\n\t * The stored message will be of type `any[]` where each element represents a message's `contents`\n\t */\n\tpublic decompressAndStore(message: ISequencedDocumentMessage): void {\n\t\tassert(\n\t\t\tmessage.compression === undefined || message.compression === CompressionAlgorithms.lz4,\n\t\t\t0x511 /* Only lz4 compression is supported */,\n\t\t);\n\t\tassert(\n\t\t\tthis.isCompressedMessage(message),\n\t\t\t0x940 /* provided message should be compressed */,\n\t\t);\n\n\t\tassert(this.activeBatch === false, 0x4b8 /* shouldn't have multiple active batches */);\n\t\tthis.activeBatch = true;\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\t\tif (batchMetadata === undefined) {\n\t\t\tthis.isSingleMessageBatch = true;\n\t\t} else {\n\t\t\tassert(batchMetadata === true, 0x941 /* invalid batch metadata */);\n\t\t}\n\n\t\tconst contents = IsoBuffer.from(\n\t\t\t(message.contents as IPackedContentsContents).packedContents,\n\t\t\t\"base64\",\n\t\t);\n\t\tconst decompressedMessage = decompress(contents);\n\t\tconst intoString = Uint8ArrayToString(decompressedMessage);\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tthis.rootMessageContents = JSON.parse(intoString);\n\t}\n\n\t/**\n\t * Unroll the next message from the decompressed content provided to {@link decompressAndStore}\n\t * @returns the unrolled `ISequencedDocumentMessage`\n\t */\n\tpublic unroll(message: ISequencedDocumentMessage): ISequencedDocumentMessage {\n\t\tassert(this.currentlyUnrolling, 0x942 /* not currently unrolling */);\n\t\tassert(this.rootMessageContents !== undefined, 0x943 /* missing rootMessageContents */);\n\t\tassert(\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tthis.rootMessageContents.length > this.processedCount,\n\t\t\t0x944 /* no more content to unroll */,\n\t\t);\n\n\t\tconst batchMetadata = (message.metadata as IBatchMetadata | undefined)?.batch;\n\n\t\tif (batchMetadata === false || this.isSingleMessageBatch) {\n\t\t\t// End of compressed batch\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\tconst returnMessage = newMessage(message, this.rootMessageContents[this.processedCount]);\n\n\t\t\tthis.activeBatch = false;\n\t\t\tthis.isSingleMessageBatch = false;\n\t\t\tthis.rootMessageContents = undefined;\n\t\t\tthis.processedCount = 0;\n\n\t\t\treturn returnMessage;\n\t\t} else if (batchMetadata === true) {\n\t\t\t// Start of compressed batch\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t\t}\n\n\t\tassert(batchMetadata === undefined, 0x945 /* invalid batch metadata */);\n\t\tassert(message.contents === undefined, 0x512 /* Expecting empty message */);\n\n\t\t// Continuation of compressed batch\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\treturn newMessage(message, this.rootMessageContents[this.processedCount++]);\n\t}\n}\n\n// We should not be mutating the input message nor its metadata\nconst newMessage = (\n\toriginalMessage: ISequencedDocumentMessage,\n\tcontents: unknown,\n): ISequencedDocumentMessage => ({\n\t...originalMessage,\n\tcontents,\n\tcompression: undefined,\n\t// TODO: It should already be the case that we're not modifying any metadata, not clear if/why this shallow clone should be required.\n\n\tmetadata:\n\t\toriginalMessage.metadata === undefined ? undefined : { ...originalMessage.metadata },\n});\n"]}
|
|
@@ -8,8 +8,6 @@ import { IBatch, type BatchMessage } from "./definitions.js";
|
|
|
8
8
|
export declare function isGroupedBatch(op: ISequencedDocumentMessage): boolean;
|
|
9
9
|
export interface OpGroupingManagerConfig {
|
|
10
10
|
readonly groupedBatchingEnabled: boolean;
|
|
11
|
-
readonly opCountThreshold: number;
|
|
12
|
-
readonly reentrantBatchGroupingEnabled: boolean;
|
|
13
11
|
}
|
|
14
12
|
export declare class OpGroupingManager {
|
|
15
13
|
private readonly config;
|
|
@@ -34,5 +32,6 @@ export declare class OpGroupingManager {
|
|
|
34
32
|
groupBatch(batch: IBatch): IBatch<[BatchMessage]>;
|
|
35
33
|
ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[];
|
|
36
34
|
shouldGroup(batch: IBatch): boolean;
|
|
35
|
+
groupedBatchingEnabled(): boolean;
|
|
37
36
|
}
|
|
38
37
|
//# sourceMappingURL=opGroupingManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opGroupingManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"opGroupingManager.d.ts","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,6CAA6C,CAAC;AAMxF,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAuB7D,wBAAgB,cAAc,CAAC,EAAE,EAAE,yBAAyB,GAAG,OAAO,CAErE;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CACzC;AAED,qBAAa,iBAAiB;IAK5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJxB,MAAM,CAAC,QAAQ,CAAC,cAAc,kBAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;gBAG3B,MAAM,EAAE,uBAAuB,EAChD,MAAM,EAAE,oBAAoB;IAK7B;;;;;;OAMG;IACI,uBAAuB,CAC7B,mBAAmB,EAAE,MAAM,EAC3B,uBAAuB,EAAE,MAAM,GAC7B,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAwBzB;;;;;;OAMG;IACI,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IA6CjD,SAAS,CAAC,EAAE,EAAE,yBAAyB,GAAG,yBAAyB,EAAE;IAcrE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAUnC,sBAAsB,IAAI,OAAO;CAGxC"}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
6
|
-
import { createChildLogger } from "@fluidframework/telemetry-utils/internal";
|
|
6
|
+
import { createChildLogger, } from "@fluidframework/telemetry-utils/internal";
|
|
7
7
|
function isGroupContents(opContents) {
|
|
8
|
-
return opContents?.type ===
|
|
8
|
+
return (opContents?.type ===
|
|
9
|
+
OpGroupingManager.groupedBatchOp);
|
|
9
10
|
}
|
|
10
11
|
export function isGroupedBatch(op) {
|
|
11
12
|
return isGroupContents(op.contents);
|
|
@@ -54,7 +55,6 @@ export class OpGroupingManager {
|
|
|
54
55
|
this.logger.sendTelemetryEvent({
|
|
55
56
|
eventName: "GroupLargeBatch",
|
|
56
57
|
length: batch.messages.length,
|
|
57
|
-
threshold: this.config.opCountThreshold,
|
|
58
58
|
reentrant: batch.hasReentrantOps,
|
|
59
59
|
referenceSequenceNumber: batch.messages[0].referenceSequenceNumber,
|
|
60
60
|
});
|
|
@@ -106,11 +106,14 @@ export class OpGroupingManager {
|
|
|
106
106
|
return (
|
|
107
107
|
// Grouped batching must be enabled
|
|
108
108
|
this.config.groupedBatchingEnabled &&
|
|
109
|
-
// The number of ops in the batch must
|
|
109
|
+
// The number of ops in the batch must be 2 or more
|
|
110
110
|
// or be empty (to allow for empty batches to be grouped)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
batch.messages.length !== 1
|
|
112
|
+
// Support for reentrant batches will be on by default
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
groupedBatchingEnabled() {
|
|
116
|
+
return this.config.groupedBatchingEnabled;
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
OpGroupingManager.groupedBatchOp = "groupedBatch";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D,OAAO,
|
|
1
|
+
{"version":3,"file":"opGroupingManager.js","sourceRoot":"","sources":["../../src/opLifecycle/opGroupingManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAE7D,OAAO,EACN,iBAAiB,GAEjB,MAAM,0CAA0C,CAAC;AAkBlD,SAAS,eAAe,CAAC,UAAmB;IAC3C,OAAO,CACL,UAAoD,EAAE,IAAI;QAC3D,iBAAiB,CAAC,cAAc,CAChC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAA6B;IAC3D,OAAO,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC;AAMD,MAAM,OAAO,iBAAiB;IAI7B,YACkB,MAA+B,EAChD,MAA4B;QADX,WAAM,GAAN,MAAM,CAAyB;QAGhD,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED;;;;;;OAMG;IACI,uBAAuB,CAC7B,mBAA2B,EAC3B,uBAA+B;QAE/B,MAAM,CACL,IAAI,CAAC,MAAM,CAAC,sBAAsB,EAClC,KAAK,CAAC,yEAAyE,CAC/E,CAAC;QACF,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC;YACxC,IAAI,EAAE,iBAAiB,CAAC,cAAc;YACtC,QAAQ,EAAE,EAAE;SACZ,CAAC,CAAC;QAEH,OAAO;YACN,kBAAkB,EAAE,CAAC;YACrB,QAAQ,EAAE;gBACT;oBACC,QAAQ,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE;oBAC1C,eAAe,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;oBACrC,uBAAuB;oBACvB,QAAQ,EAAE,iBAAiB;iBAC3B;aACD;YACD,uBAAuB;SACvB,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACI,UAAU,CAAC,KAAa;QAC9B,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAE7E,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;gBAC9B,SAAS,EAAE,iBAAiB;gBAC5B,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM;gBAC7B,SAAS,EAAE,KAAK,CAAC,eAAe;gBAChC,uBAAuB,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,uBAAuB;aAClE,CAAC,CAAC;QACJ,CAAC;QACD,kEAAkE;QAClE,IAAI,cAAc,CAAC;QACnB,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACtC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACtB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;gBAC7D,IAAI,OAAO,EAAE,CAAC;oBACb,cAAc,GAAG,OAAO,CAAC;gBAC1B,CAAC;gBACD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,KAAK,CAAC,oCAAoC,CAAC,CAAC;YACpF,CAAC;QACF,CAAC;QAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC;YACxC,IAAI,EAAE,iBAAiB,CAAC,cAAc;YACtC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAC3D,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACnF,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,WAAW;aAChC,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,MAAM,YAAY,GAA2B;YAC5C,GAAG,KAAK;YACR,QAAQ,EAAE;gBACT;oBACC,QAAQ,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE;oBACrC,uBAAuB,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,uBAAuB;oBAClE,QAAQ,EAAE,iBAAiB;iBAC3B;aACD;SACD,CAAC;QACF,OAAO,YAAY,CAAC;IACrB,CAAC;IAEM,SAAS,CAAC,EAA6B;QAC7C,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACnF,MAAM,QAAQ,GAAiC,EAAE,CAAC,QAAQ,CAAC;QAE3D,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,OAAO,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YAC7C,GAAG,EAAE;YACL,oBAAoB,EAAE,OAAO,EAAE;YAC/B,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,WAAW,EAAE,UAAU,CAAC,WAAW;SACnC,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,WAAW,CAAC,KAAa;QAC/B,OAAO;QACN,mCAAmC;QACnC,IAAI,CAAC,MAAM,CAAC,sBAAsB;YAClC,mDAAmD;YACnD,yDAAyD;YACzD,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAC3B,sDAAsD;SACtD,CAAC;IACH,CAAC;IACM,sBAAsB;QAC5B,OAAO,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC;IAC3C,CAAC;;AA1He,gCAAc,GAAG,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryBaseLogger } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISequencedDocumentMessage } from \"@fluidframework/driver-definitions/internal\";\nimport {\n\tcreateChildLogger,\n\ttype ITelemetryLoggerExt,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { IBatch, type BatchMessage } from \"./definitions.js\";\n\n/**\n * Grouping makes assumptions about the shape of message contents. This interface codifies those assumptions, but does not validate them.\n */\ninterface IGroupedBatchMessageContents {\n\ttype: typeof OpGroupingManager.groupedBatchOp;\n\tcontents: IGroupedMessage[];\n}\n\ninterface IGroupedMessage {\n\tcontents?: unknown;\n\tmetadata?: Record<string, unknown>;\n\tcompression?: string;\n}\n\nfunction isGroupContents(opContents: unknown): opContents is IGroupedBatchMessageContents {\n\treturn (\n\t\t(opContents as Partial<IGroupedBatchMessageContents>)?.type ===\n\t\tOpGroupingManager.groupedBatchOp\n\t);\n}\n\nexport function isGroupedBatch(op: ISequencedDocumentMessage): boolean {\n\treturn isGroupContents(op.contents);\n}\n\nexport interface OpGroupingManagerConfig {\n\treadonly groupedBatchingEnabled: boolean;\n}\n\nexport class OpGroupingManager {\n\tstatic readonly groupedBatchOp = \"groupedBatch\";\n\tprivate readonly logger: ITelemetryLoggerExt;\n\n\tconstructor(\n\t\tprivate readonly config: OpGroupingManagerConfig,\n\t\tlogger: ITelemetryBaseLogger,\n\t) {\n\t\tthis.logger = createChildLogger({ logger, namespace: \"OpGroupingManager\" });\n\t}\n\n\t/**\n\t * Creates a new batch with a single message of type \"groupedBatch\" and empty contents.\n\t * This is needed as a placeholder if a batch becomes empty on resubmit, but we are tracking batch IDs.\n\t * @param resubmittingBatchId - batch ID of the resubmitting batch\n\t * @param referenceSequenceNumber - reference sequence number\n\t * @returns - IBatch containing a single empty Grouped Batch op\n\t */\n\tpublic createEmptyGroupedBatch(\n\t\tresubmittingBatchId: string,\n\t\treferenceSequenceNumber: number,\n\t): IBatch<[BatchMessage]> {\n\t\tassert(\n\t\t\tthis.config.groupedBatchingEnabled,\n\t\t\t0xa00 /* cannot create empty grouped batch when grouped batching is disabled */,\n\t\t);\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: [],\n\t\t});\n\n\t\treturn {\n\t\t\tcontentSizeInBytes: 0,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: { batchId: resubmittingBatchId },\n\t\t\t\t\tlocalOpMetadata: { emptyBatch: true },\n\t\t\t\t\treferenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t\treferenceSequenceNumber,\n\t\t};\n\t}\n\n\t/**\n\t * Converts the given batch into a \"grouped batch\" - a batch with a single message of type \"groupedBatch\",\n\t * with contents being an array of the original batch's messages.\n\t *\n\t * @remarks - Remember that a BatchMessage has its content JSON serialized, so the incoming batch message contents\n\t * must be parsed first, and then the type and contents mentioned above are hidden in that JSON serialization.\n\t */\n\tpublic groupBatch(batch: IBatch): IBatch<[BatchMessage]> {\n\t\tassert(this.shouldGroup(batch), 0x946 /* cannot group the provided batch */);\n\n\t\tif (batch.messages.length >= 1000) {\n\t\t\tthis.logger.sendTelemetryEvent({\n\t\t\t\teventName: \"GroupLargeBatch\",\n\t\t\t\tlength: batch.messages.length,\n\t\t\t\treentrant: batch.hasReentrantOps,\n\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t});\n\t\t}\n\t\t// We expect this will be on the first message, if present at all.\n\t\tlet groupedBatchId;\n\t\tfor (const message of batch.messages) {\n\t\t\tif (message.metadata) {\n\t\t\t\tconst { batch: _batch, batchId, ...rest } = message.metadata;\n\t\t\t\tif (batchId) {\n\t\t\t\t\tgroupedBatchId = batchId;\n\t\t\t\t}\n\t\t\t\tassert(Object.keys(rest).length === 0, 0x5dd /* cannot group ops with metadata */);\n\t\t\t}\n\t\t}\n\n\t\tconst serializedContent = JSON.stringify({\n\t\t\ttype: OpGroupingManager.groupedBatchOp,\n\t\t\tcontents: batch.messages.map<IGroupedMessage>((message) => ({\n\t\t\t\tcontents: message.contents === undefined ? undefined : JSON.parse(message.contents),\n\t\t\t\tmetadata: message.metadata,\n\t\t\t\tcompression: message.compression,\n\t\t\t})),\n\t\t});\n\n\t\tconst groupedBatch: IBatch<[BatchMessage]> = {\n\t\t\t...batch,\n\t\t\tmessages: [\n\t\t\t\t{\n\t\t\t\t\tmetadata: { batchId: groupedBatchId },\n\t\t\t\t\treferenceSequenceNumber: batch.messages[0].referenceSequenceNumber,\n\t\t\t\t\tcontents: serializedContent,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t\treturn groupedBatch;\n\t}\n\n\tpublic ungroupOp(op: ISequencedDocumentMessage): ISequencedDocumentMessage[] {\n\t\tassert(isGroupContents(op.contents), 0x947 /* can only ungroup a grouped batch */);\n\t\tconst contents: IGroupedBatchMessageContents = op.contents;\n\n\t\tlet fakeCsn = 1;\n\t\treturn contents.contents.map((subMessage) => ({\n\t\t\t...op,\n\t\t\tclientSequenceNumber: fakeCsn++,\n\t\t\tcontents: subMessage.contents,\n\t\t\tmetadata: subMessage.metadata,\n\t\t\tcompression: subMessage.compression,\n\t\t}));\n\t}\n\n\tpublic shouldGroup(batch: IBatch): boolean {\n\t\treturn (\n\t\t\t// Grouped batching must be enabled\n\t\t\tthis.config.groupedBatchingEnabled &&\n\t\t\t// The number of ops in the batch must be 2 or more\n\t\t\t// or be empty (to allow for empty batches to be grouped)\n\t\t\tbatch.messages.length !== 1\n\t\t\t// Support for reentrant batches will be on by default\n\t\t);\n\t}\n\tpublic groupedBatchingEnabled(): boolean {\n\t\treturn this.config.groupedBatchingEnabled;\n\t}\n}\n"]}
|