@fluidframework/container-runtime 2.0.0-dev.5.3.2.178189 → 2.0.0-dev.6.4.0.191457
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -0
- package/README.md +4 -3
- package/dist/batchTracker.d.ts +3 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +6 -5
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +10 -16
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +184 -172
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +25 -16
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +161 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +697 -449
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +15 -7
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -2
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +83 -87
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +1 -2
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +8 -9
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.js +2 -2
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +22 -6
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +123 -81
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaManagerProxyBase.d.ts +35 -0
- package/dist/deltaManagerProxyBase.d.ts.map +1 -0
- package/dist/deltaManagerProxyBase.js +77 -0
- package/dist/deltaManagerProxyBase.js.map +1 -0
- package/dist/deltaManagerSummarizerProxy.d.ts +1 -1
- package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +4 -2
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +10 -10
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.d.ts +14 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +21 -0
- package/dist/error.js.map +1 -0
- package/dist/gc/garbageCollection.d.ts +10 -9
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +61 -53
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +18 -14
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +17 -4
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +14 -15
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +0 -8
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +11 -24
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts +4 -7
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +19 -58
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +45 -35
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +4 -4
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -5
- package/dist/gc/index.js.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.d.ts +8 -30
- package/dist/id-compressor/appendOnlySortedMap.d.ts.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.js +26 -68
- package/dist/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/finalSpace.d.ts +29 -0
- package/dist/id-compressor/finalSpace.d.ts.map +1 -0
- package/dist/id-compressor/finalSpace.js +62 -0
- package/dist/id-compressor/finalSpace.js.map +1 -0
- package/dist/id-compressor/idCompressor.d.ts +25 -250
- package/dist/id-compressor/idCompressor.d.ts.map +1 -1
- package/dist/id-compressor/idCompressor.js +390 -1153
- package/dist/id-compressor/idCompressor.js.map +1 -1
- package/dist/id-compressor/identifiers.d.ts +32 -0
- package/dist/id-compressor/identifiers.d.ts.map +1 -0
- package/dist/id-compressor/identifiers.js +15 -0
- package/dist/id-compressor/identifiers.js.map +1 -0
- package/dist/id-compressor/index.d.ts +5 -6
- package/dist/id-compressor/index.d.ts.map +1 -1
- package/dist/id-compressor/index.js +20 -26
- package/dist/id-compressor/index.js.map +1 -1
- package/dist/id-compressor/persistanceUtilities.d.ts +22 -0
- package/dist/id-compressor/persistanceUtilities.d.ts.map +1 -0
- package/dist/id-compressor/persistanceUtilities.js +43 -0
- package/dist/id-compressor/persistanceUtilities.js.map +1 -0
- package/dist/id-compressor/sessionSpaceNormalizer.d.ts +46 -0
- package/dist/id-compressor/sessionSpaceNormalizer.d.ts.map +1 -0
- package/dist/id-compressor/sessionSpaceNormalizer.js +80 -0
- package/dist/id-compressor/sessionSpaceNormalizer.js.map +1 -0
- package/dist/id-compressor/sessions.d.ts +115 -0
- package/dist/id-compressor/sessions.d.ts.map +1 -0
- package/dist/id-compressor/sessions.js +305 -0
- package/dist/id-compressor/sessions.js.map +1 -0
- package/dist/id-compressor/utilities.d.ts +49 -0
- package/dist/id-compressor/utilities.d.ts.map +1 -0
- package/dist/id-compressor/utilities.js +166 -0
- package/dist/id-compressor/utilities.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.js +10 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +2 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +12 -7
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +2 -2
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +23 -20
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +19 -9
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +2 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +22 -19
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +7 -5
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +22 -31
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +6 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +19 -7
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.js +1 -2
- package/dist/opProperties.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +18 -8
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +73 -51
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +36 -32
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/index.d.ts +3 -3
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +2 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +3 -3
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +26 -27
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +3 -3
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +31 -10
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +271 -139
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +8 -7
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +79 -78
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts +2 -2
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +7 -11
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +10 -14
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/index.d.ts +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 +10 -19
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +57 -130
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +6 -30
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -4
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -14
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +15 -101
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +38 -25
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -3
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +12 -13
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +3 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +6 -4
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +10 -4
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +106 -57
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +8 -8
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +38 -28
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +3 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +5 -4
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +10 -16
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +159 -147
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +15 -6
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +161 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +651 -402
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +13 -5
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -2
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +47 -51
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +1 -2
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +3 -4
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.js +1 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +22 -6
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +107 -65
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaManagerProxyBase.d.ts +35 -0
- package/lib/deltaManagerProxyBase.d.ts.map +1 -0
- package/lib/deltaManagerProxyBase.js +73 -0
- package/lib/deltaManagerProxyBase.js.map +1 -0
- package/lib/deltaManagerSummarizerProxy.d.ts +1 -1
- package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +3 -1
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +7 -7
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.d.ts +14 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +17 -0
- package/lib/error.js.map +1 -0
- package/lib/gc/garbageCollection.d.ts +10 -9
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +61 -53
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +16 -12
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +17 -4
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +13 -14
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +0 -8
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +5 -17
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts +4 -7
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +20 -59
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +46 -36
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +2 -2
- package/lib/gc/index.js.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.d.ts +8 -30
- package/lib/id-compressor/appendOnlySortedMap.d.ts.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.js +25 -66
- package/lib/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/finalSpace.d.ts +29 -0
- package/lib/id-compressor/finalSpace.d.ts.map +1 -0
- package/lib/id-compressor/finalSpace.js +58 -0
- package/lib/id-compressor/finalSpace.js.map +1 -0
- package/lib/id-compressor/idCompressor.d.ts +25 -250
- package/lib/id-compressor/idCompressor.d.ts.map +1 -1
- package/lib/id-compressor/idCompressor.js +385 -1142
- package/lib/id-compressor/idCompressor.js.map +1 -1
- package/lib/id-compressor/identifiers.d.ts +32 -0
- package/lib/id-compressor/identifiers.d.ts.map +1 -0
- package/lib/id-compressor/identifiers.js +11 -0
- package/lib/id-compressor/identifiers.js.map +1 -0
- package/lib/id-compressor/index.d.ts +5 -6
- package/lib/id-compressor/index.d.ts.map +1 -1
- package/lib/id-compressor/index.js +5 -6
- package/lib/id-compressor/index.js.map +1 -1
- package/lib/id-compressor/persistanceUtilities.d.ts +22 -0
- package/lib/id-compressor/persistanceUtilities.d.ts.map +1 -0
- package/lib/id-compressor/persistanceUtilities.js +34 -0
- package/lib/id-compressor/persistanceUtilities.js.map +1 -0
- package/lib/id-compressor/sessionSpaceNormalizer.d.ts +46 -0
- package/lib/id-compressor/sessionSpaceNormalizer.d.ts.map +1 -0
- package/lib/id-compressor/sessionSpaceNormalizer.js +76 -0
- package/lib/id-compressor/sessionSpaceNormalizer.js.map +1 -0
- package/lib/id-compressor/sessions.d.ts +115 -0
- package/lib/id-compressor/sessions.d.ts.map +1 -0
- package/lib/id-compressor/sessions.js +290 -0
- package/lib/id-compressor/sessions.js.map +1 -0
- package/lib/id-compressor/utilities.d.ts +49 -0
- package/lib/id-compressor/utilities.d.ts.map +1 -0
- package/lib/id-compressor/utilities.js +148 -0
- package/lib/id-compressor/utilities.js.map +1 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.js +10 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +2 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +10 -5
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +2 -2
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +15 -12
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +17 -7
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +2 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +13 -10
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +7 -5
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +13 -22
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +6 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +20 -8
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.js +1 -2
- package/lib/opProperties.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +18 -8
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +62 -40
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +18 -14
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/index.d.ts +3 -3
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +3 -3
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +21 -22
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +31 -10
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +265 -133
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +8 -7
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +75 -74
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts +2 -2
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +6 -10
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +9 -13
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/index.d.ts +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 +10 -19
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +42 -115
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +6 -30
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -4
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -14
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +14 -100
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +38 -25
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -3
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +5 -6
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +3 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +5 -3
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +10 -4
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +100 -51
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +8 -8
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +35 -25
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +28 -31
- package/src/batchTracker.ts +7 -5
- package/src/blobManager.ts +188 -166
- package/src/connectionTelemetry.ts +9 -4
- package/src/containerRuntime.ts +806 -441
- package/src/dataStore.ts +12 -4
- package/src/dataStoreContext.ts +39 -43
- package/src/dataStoreContexts.ts +4 -6
- package/src/dataStoreRegistry.ts +1 -1
- package/src/dataStores.ts +114 -80
- package/src/deltaManagerProxyBase.ts +111 -0
- package/src/deltaManagerSummarizerProxy.ts +4 -1
- package/src/deltaScheduler.ts +7 -11
- package/src/error.ts +18 -0
- package/src/gc/garbageCollection.md +53 -5
- package/src/gc/garbageCollection.ts +58 -52
- package/src/gc/gcConfigs.ts +4 -2
- package/src/gc/gcDefinitions.ts +17 -20
- package/src/gc/gcEarlyAdoption.md +145 -0
- package/src/gc/gcHelpers.ts +1 -12
- package/src/gc/gcSummaryStateTracker.ts +19 -65
- package/src/gc/gcTelemetry.ts +14 -12
- package/src/gc/gcUnreferencedStateTracker.ts +1 -1
- package/src/gc/index.ts +2 -4
- package/src/id-compressor/appendOnlySortedMap.ts +26 -87
- package/src/id-compressor/finalSpace.ts +67 -0
- package/src/id-compressor/idCompressor.ts +458 -1682
- package/src/id-compressor/identifiers.ts +42 -0
- package/src/id-compressor/index.ts +11 -20
- package/src/id-compressor/persistanceUtilities.ts +58 -0
- package/src/id-compressor/sessionSpaceNormalizer.ts +83 -0
- package/src/id-compressor/sessions.ts +405 -0
- package/src/id-compressor/utilities.ts +187 -0
- package/src/index.ts +7 -2
- package/src/opLifecycle/opCompressor.ts +6 -5
- package/src/opLifecycle/opDecompressor.ts +6 -4
- package/src/opLifecycle/opGroupingManager.ts +9 -6
- package/src/opLifecycle/opSplitter.ts +7 -6
- package/src/opLifecycle/outbox.ts +23 -32
- package/src/opLifecycle/remoteMessageProcessor.ts +27 -8
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +72 -42
- package/src/scheduleManager.ts +7 -5
- package/src/summary/index.ts +4 -3
- package/src/summary/orderedClientElection.ts +10 -6
- package/src/summary/runWhileConnectedCoordinator.ts +1 -1
- package/src/summary/runningSummarizer.ts +291 -163
- package/src/summary/summarizer.ts +27 -16
- package/src/summary/summarizerClientElection.ts +2 -2
- package/src/summary/summarizerHeuristics.ts +1 -1
- package/src/summary/summarizerNode/index.ts +1 -2
- package/src/summary/summarizerNode/summarizerNode.ts +36 -160
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +7 -38
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +11 -130
- package/src/summary/summarizerTypes.ts +40 -25
- package/src/summary/summaryCollection.ts +3 -3
- package/src/summary/summaryFormat.ts +4 -1
- package/src/summary/summaryGenerator.ts +52 -52
- package/src/summary/summaryManager.ts +44 -17
- package/dist/id-compressor/idRange.d.ts +0 -11
- package/dist/id-compressor/idRange.d.ts.map +0 -1
- package/dist/id-compressor/idRange.js +0 -29
- package/dist/id-compressor/idRange.js.map +0 -1
- package/dist/id-compressor/numericUuid.d.ts +0 -59
- package/dist/id-compressor/numericUuid.d.ts.map +0 -1
- package/dist/id-compressor/numericUuid.js +0 -325
- package/dist/id-compressor/numericUuid.js.map +0 -1
- package/dist/id-compressor/sessionIdNormalizer.d.ts +0 -138
- package/dist/id-compressor/sessionIdNormalizer.d.ts.map +0 -1
- package/dist/id-compressor/sessionIdNormalizer.js +0 -488
- package/dist/id-compressor/sessionIdNormalizer.js.map +0 -1
- package/dist/id-compressor/utils.d.ts +0 -57
- package/dist/id-compressor/utils.d.ts.map +0 -1
- package/dist/id-compressor/utils.js +0 -90
- package/dist/id-compressor/utils.js.map +0 -1
- package/dist/id-compressor/uuidUtilities.d.ts +0 -28
- package/dist/id-compressor/uuidUtilities.d.ts.map +0 -1
- package/dist/id-compressor/uuidUtilities.js +0 -104
- package/dist/id-compressor/uuidUtilities.js.map +0 -1
- package/lib/id-compressor/idRange.d.ts +0 -11
- package/lib/id-compressor/idRange.d.ts.map +0 -1
- package/lib/id-compressor/idRange.js +0 -25
- package/lib/id-compressor/idRange.js.map +0 -1
- package/lib/id-compressor/numericUuid.d.ts +0 -59
- package/lib/id-compressor/numericUuid.d.ts.map +0 -1
- package/lib/id-compressor/numericUuid.js +0 -315
- package/lib/id-compressor/numericUuid.js.map +0 -1
- package/lib/id-compressor/sessionIdNormalizer.d.ts +0 -138
- package/lib/id-compressor/sessionIdNormalizer.d.ts.map +0 -1
- package/lib/id-compressor/sessionIdNormalizer.js +0 -484
- package/lib/id-compressor/sessionIdNormalizer.js.map +0 -1
- package/lib/id-compressor/utils.d.ts +0 -57
- package/lib/id-compressor/utils.d.ts.map +0 -1
- package/lib/id-compressor/utils.js +0 -79
- package/lib/id-compressor/utils.js.map +0 -1
- package/lib/id-compressor/uuidUtilities.d.ts +0 -28
- package/lib/id-compressor/uuidUtilities.d.ts.map +0 -1
- package/lib/id-compressor/uuidUtilities.js +0 -96
- package/lib/id-compressor/uuidUtilities.js.map +0 -1
- package/src/id-compressor/idRange.ts +0 -35
- package/src/id-compressor/numericUuid.ts +0 -383
- package/src/id-compressor/sessionIdNormalizer.ts +0 -609
- package/src/id-compressor/utils.ts +0 -114
- package/src/id-compressor/uuidUtilities.ts +0 -120
package/src/containerRuntime.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from "@fluidframework/core-interfaces";
|
|
15
15
|
import {
|
|
16
16
|
IAudience,
|
|
17
|
+
IBatchMessage,
|
|
17
18
|
IContainerContext,
|
|
18
19
|
IDeltaManager,
|
|
19
20
|
IRuntime,
|
|
@@ -26,23 +27,22 @@ import {
|
|
|
26
27
|
IContainerRuntime,
|
|
27
28
|
IContainerRuntimeEvents,
|
|
28
29
|
} from "@fluidframework/container-runtime-definitions";
|
|
30
|
+
import { assert, delay, LazyPromise } from "@fluidframework/core-utils";
|
|
31
|
+
import { Trace, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
29
32
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} from "@fluidframework/common-utils";
|
|
36
|
-
import { LazyPromise } from "@fluidframework/core-utils";
|
|
37
|
-
import {
|
|
38
|
-
ChildLogger,
|
|
33
|
+
createChildLogger,
|
|
34
|
+
createChildMonitoringContext,
|
|
35
|
+
DataCorruptionError,
|
|
36
|
+
DataProcessingError,
|
|
37
|
+
GenericError,
|
|
39
38
|
raiseConnectedEvent,
|
|
40
39
|
PerformanceEvent,
|
|
40
|
+
// eslint-disable-next-line import/no-deprecated
|
|
41
41
|
TaggedLoggerAdapter,
|
|
42
42
|
MonitoringContext,
|
|
43
|
-
loggerToMonitoringContext,
|
|
44
43
|
wrapError,
|
|
45
44
|
ITelemetryLoggerExt,
|
|
45
|
+
UsageError,
|
|
46
46
|
} from "@fluidframework/telemetry-utils";
|
|
47
47
|
import {
|
|
48
48
|
DriverHeader,
|
|
@@ -51,12 +51,6 @@ import {
|
|
|
51
51
|
ISummaryContext,
|
|
52
52
|
} from "@fluidframework/driver-definitions";
|
|
53
53
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
54
|
-
import {
|
|
55
|
-
DataCorruptionError,
|
|
56
|
-
DataProcessingError,
|
|
57
|
-
GenericError,
|
|
58
|
-
UsageError,
|
|
59
|
-
} from "@fluidframework/container-utils";
|
|
60
54
|
import {
|
|
61
55
|
IClientDetails,
|
|
62
56
|
IDocumentMessage,
|
|
@@ -132,7 +126,6 @@ import {
|
|
|
132
126
|
IContainerRuntimeMetadata,
|
|
133
127
|
ICreateContainerMetadata,
|
|
134
128
|
idCompressorBlobName,
|
|
135
|
-
IFetchSnapshotResult,
|
|
136
129
|
IRootSummarizerNodeWithGC,
|
|
137
130
|
ISummaryMetadataMessage,
|
|
138
131
|
metadataBlobName,
|
|
@@ -156,6 +149,12 @@ import {
|
|
|
156
149
|
RunWhileConnectedCoordinator,
|
|
157
150
|
IGenerateSummaryTreeResult,
|
|
158
151
|
RetriableSummaryError,
|
|
152
|
+
IOnDemandSummarizeOptions,
|
|
153
|
+
ISummarizeResults,
|
|
154
|
+
IEnqueueSummarizeOptions,
|
|
155
|
+
EnqueueSummarizeResult,
|
|
156
|
+
ISummarizerEvents,
|
|
157
|
+
IBaseSummarizeResult,
|
|
159
158
|
} from "./summary";
|
|
160
159
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
161
160
|
import {
|
|
@@ -173,6 +172,7 @@ import { BindBatchTracker } from "./batchTracker";
|
|
|
173
172
|
import { ScheduleManager } from "./scheduleManager";
|
|
174
173
|
import {
|
|
175
174
|
BatchMessage,
|
|
175
|
+
IBatch,
|
|
176
176
|
IBatchCheckpoint,
|
|
177
177
|
OpCompressor,
|
|
178
178
|
OpDecompressor,
|
|
@@ -212,11 +212,64 @@ export enum ContainerMessageType {
|
|
|
212
212
|
IdAllocation = "idAllocation",
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
/**
|
|
216
|
+
* How should an older client handle an unrecognized remote op type?
|
|
217
|
+
*
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
export type CompatModeBehavior =
|
|
221
|
+
/** Ignore the op. It won't be persisted if this client summarizes */
|
|
222
|
+
| "Ignore"
|
|
223
|
+
/** Fail processing immediately. (The container will close) */
|
|
224
|
+
| "FailToProcess";
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* All the info an older client would need to know how to handle an unrecognized remote op type
|
|
228
|
+
*
|
|
229
|
+
* @internal
|
|
230
|
+
*/
|
|
231
|
+
export interface IContainerRuntimeMessageCompatDetails {
|
|
232
|
+
/** How should an older client handle an unrecognized remote op type? */
|
|
233
|
+
behavior: CompatModeBehavior;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Utility to implement compat behaviors given an unknown message type
|
|
238
|
+
* The parameters are typed to support compile-time enforcement of handling all known types/behaviors
|
|
239
|
+
*
|
|
240
|
+
* @param _unknownContainerRuntimeMessageType - Typed as never, to ensure all known types have been
|
|
241
|
+
* handled before calling this function (e.g. in a switch statement).
|
|
242
|
+
* @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
|
|
243
|
+
*/
|
|
244
|
+
function compatBehaviorAllowsMessageType(
|
|
245
|
+
_unknownContainerRuntimeMessageType: never,
|
|
246
|
+
compatBehavior: "Ignore" | "FailToProcess" | undefined,
|
|
247
|
+
): boolean {
|
|
248
|
+
// undefined defaults to same behavior as "FailToProcess"
|
|
249
|
+
return compatBehavior === "Ignore";
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The unpacked runtime message / details to be handled or dispatched by the ContainerRuntime
|
|
254
|
+
*
|
|
255
|
+
* IMPORTANT: when creating one to be serialized, set the properties in the order they appear here.
|
|
256
|
+
* This way stringified values can be compared.
|
|
257
|
+
*/
|
|
215
258
|
export interface ContainerRuntimeMessage {
|
|
216
|
-
|
|
259
|
+
/** Type of the op, within the ContainerRuntime's domain */
|
|
217
260
|
type: ContainerMessageType;
|
|
261
|
+
/** Domain-specific contents, interpreted according to the type */
|
|
262
|
+
contents: any;
|
|
263
|
+
/** Info describing how to handle this op in case the type is unrecognized (default: fail to process) */
|
|
264
|
+
compatDetails?: IContainerRuntimeMessageCompatDetails;
|
|
218
265
|
}
|
|
219
266
|
|
|
267
|
+
/**
|
|
268
|
+
* An unpacked ISequencedDocumentMessage with the inner ContainerRuntimeMessage type/contents/etc
|
|
269
|
+
* promoted up to the outer object
|
|
270
|
+
*/
|
|
271
|
+
export type SequencedContainerRuntimeMessage = ISequencedDocumentMessage & ContainerRuntimeMessage;
|
|
272
|
+
|
|
220
273
|
export interface ISummaryBaseConfiguration {
|
|
221
274
|
/**
|
|
222
275
|
* Delay before first attempt to spawn summarizing container.
|
|
@@ -443,14 +496,6 @@ export interface IContainerRuntimeOptions {
|
|
|
443
496
|
readonly enableGroupedBatching?: boolean;
|
|
444
497
|
}
|
|
445
498
|
|
|
446
|
-
/**
|
|
447
|
-
* The summary tree returned by the root node. It adds state relevant to the root of the tree.
|
|
448
|
-
*/
|
|
449
|
-
export interface IRootSummaryTreeWithStats extends ISummaryTreeWithStats {
|
|
450
|
-
/** The garbage collection stats if GC ran, undefined otherwise. */
|
|
451
|
-
gcStats?: IGCStats;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
499
|
/**
|
|
455
500
|
* Accepted header keys for requests coming to the runtime.
|
|
456
501
|
*/
|
|
@@ -463,9 +508,13 @@ export enum RuntimeHeaders {
|
|
|
463
508
|
|
|
464
509
|
/** True if a tombstoned object should be returned without erroring */
|
|
465
510
|
export const AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
511
|
+
/** [IRRELEVANT IF throwOnInactiveLoad OPTION NOT SET] True if an inactive object should be returned without erroring */
|
|
512
|
+
export const AllowInactiveRequestHeaderKey = "allowInactive"; // Belongs in the enum above, but avoiding the breaking change
|
|
466
513
|
|
|
467
514
|
/** Tombstone error responses will have this header set to true */
|
|
468
515
|
export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
516
|
+
/** Inactive error responses will have this header set to true */
|
|
517
|
+
export const InactiveResponseHeaderKey = "isInactive";
|
|
469
518
|
|
|
470
519
|
/**
|
|
471
520
|
* The full set of parsed header data that may be found on Runtime requests
|
|
@@ -506,7 +555,7 @@ interface OldContainerContextWithLogger extends Omit<IContainerContext, "taggedL
|
|
|
506
555
|
* instantiated runtime in a new instance of the container, so it can load to the
|
|
507
556
|
* same state
|
|
508
557
|
*/
|
|
509
|
-
interface IPendingRuntimeState {
|
|
558
|
+
export interface IPendingRuntimeState {
|
|
510
559
|
/**
|
|
511
560
|
* Pending ops from PendingStateManager
|
|
512
561
|
*/
|
|
@@ -535,12 +584,17 @@ const defaultCompressionConfig = {
|
|
|
535
584
|
|
|
536
585
|
const defaultChunkSizeInBytes = 204800;
|
|
537
586
|
|
|
587
|
+
/** The default time to wait for pending ops to be processed during summarization */
|
|
588
|
+
export const defaultPendingOpsWaitTimeoutMs = 1000;
|
|
589
|
+
/** The default time to delay a summarization retry attempt when there are pending ops */
|
|
590
|
+
export const defaultPendingOpsRetryDelayMs = 1000;
|
|
591
|
+
|
|
538
592
|
/**
|
|
539
593
|
* Instead of refreshing from latest because we do not have 100% confidence in the state
|
|
540
594
|
* of the current system, we should close the summarizer and let it recover.
|
|
541
595
|
* This delay's goal is to prevent tight restart loops
|
|
542
596
|
*/
|
|
543
|
-
const defaultCloseSummarizerDelayMs =
|
|
597
|
+
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
544
598
|
|
|
545
599
|
/**
|
|
546
600
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
@@ -582,14 +636,41 @@ export function getDeviceSpec() {
|
|
|
582
636
|
return {};
|
|
583
637
|
}
|
|
584
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
|
|
641
|
+
* Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
|
|
642
|
+
* we can provide a partially-applied function to keep those items private to the ContainerRuntime.
|
|
643
|
+
*/
|
|
644
|
+
export const makeLegacySendBatchFn =
|
|
645
|
+
(
|
|
646
|
+
submitFn: (type: MessageType, contents: any, batch: boolean, appData?: any) => number,
|
|
647
|
+
deltaManager: Pick<IDeltaManager<unknown, unknown>, "flush">,
|
|
648
|
+
) =>
|
|
649
|
+
(batch: IBatch) => {
|
|
650
|
+
for (const message of batch.content) {
|
|
651
|
+
submitFn(
|
|
652
|
+
MessageType.Operation,
|
|
653
|
+
// For back-compat (submitFn only works on deserialized content)
|
|
654
|
+
message.contents === undefined ? undefined : JSON.parse(message.contents),
|
|
655
|
+
true, // batch
|
|
656
|
+
message.metadata,
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
deltaManager.flush();
|
|
661
|
+
};
|
|
662
|
+
|
|
585
663
|
/**
|
|
586
664
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
587
665
|
* It will define the store level mappings.
|
|
588
666
|
*/
|
|
589
667
|
export class ContainerRuntime
|
|
590
|
-
extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
668
|
+
extends TypedEventEmitter<IContainerRuntimeEvents & ISummarizerEvents>
|
|
591
669
|
implements IContainerRuntime, IRuntime, ISummarizerRuntime, ISummarizerInternalsProvider
|
|
592
670
|
{
|
|
671
|
+
/**
|
|
672
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
673
|
+
*/
|
|
593
674
|
public get IFluidRouter() {
|
|
594
675
|
return this;
|
|
595
676
|
}
|
|
@@ -648,10 +729,10 @@ export class ContainerRuntime
|
|
|
648
729
|
context: IContainerContext;
|
|
649
730
|
registryEntries: NamedFluidDataStoreRegistryEntries;
|
|
650
731
|
existing: boolean;
|
|
651
|
-
requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
|
|
652
732
|
runtimeOptions?: IContainerRuntimeOptions;
|
|
653
733
|
containerScope?: FluidObject;
|
|
654
734
|
containerRuntimeCtor?: typeof ContainerRuntime;
|
|
735
|
+
requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
|
|
655
736
|
initializeEntryPoint?: (containerRuntime: IContainerRuntime) => Promise<FluidObject>;
|
|
656
737
|
}): Promise<ContainerRuntime> {
|
|
657
738
|
const {
|
|
@@ -662,18 +743,32 @@ export class ContainerRuntime
|
|
|
662
743
|
runtimeOptions = {},
|
|
663
744
|
containerScope = {},
|
|
664
745
|
containerRuntimeCtor = ContainerRuntime,
|
|
665
|
-
initializeEntryPoint,
|
|
666
746
|
} = params;
|
|
667
747
|
|
|
748
|
+
const initializeEntryPoint =
|
|
749
|
+
params.initializeEntryPoint ??
|
|
750
|
+
(async (containerRuntime: IContainerRuntime) => ({
|
|
751
|
+
get IFluidRouter() {
|
|
752
|
+
return this;
|
|
753
|
+
},
|
|
754
|
+
async request(req) {
|
|
755
|
+
return containerRuntime.request(req);
|
|
756
|
+
},
|
|
757
|
+
}));
|
|
758
|
+
|
|
668
759
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
669
760
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
670
761
|
const backCompatContext: IContainerContext | OldContainerContextWithLogger = context;
|
|
671
762
|
const passLogger =
|
|
672
763
|
backCompatContext.taggedLogger ??
|
|
764
|
+
// eslint-disable-next-line import/no-deprecated
|
|
673
765
|
new TaggedLoggerAdapter((backCompatContext as OldContainerContextWithLogger).logger);
|
|
674
|
-
const logger =
|
|
675
|
-
|
|
676
|
-
|
|
766
|
+
const logger = createChildLogger({
|
|
767
|
+
logger: passLogger,
|
|
768
|
+
properties: {
|
|
769
|
+
all: {
|
|
770
|
+
runtimeVersion: pkgVersion,
|
|
771
|
+
},
|
|
677
772
|
},
|
|
678
773
|
});
|
|
679
774
|
|
|
@@ -714,8 +809,6 @@ export class ContainerRuntime
|
|
|
714
809
|
tryFetchBlob<SerializedIdCompressorWithNoSession>(idCompressorBlobName),
|
|
715
810
|
]);
|
|
716
811
|
|
|
717
|
-
const loadExisting = existing === true || context.existing === true;
|
|
718
|
-
|
|
719
812
|
// read snapshot blobs needed for BlobManager to load
|
|
720
813
|
const blobManagerSnapshot = await BlobManager.load(
|
|
721
814
|
context.baseSnapshot?.trees[blobsTreeName],
|
|
@@ -750,9 +843,7 @@ export class ContainerRuntime
|
|
|
750
843
|
if (loadSequenceNumberVerification === "log") {
|
|
751
844
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
752
845
|
} else {
|
|
753
|
-
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
754
846
|
context.closeFn(error);
|
|
755
|
-
context.disposeFn?.(error);
|
|
756
847
|
}
|
|
757
848
|
}
|
|
758
849
|
}
|
|
@@ -765,7 +856,7 @@ export class ContainerRuntime
|
|
|
765
856
|
idCompressor =
|
|
766
857
|
serializedIdCompressor !== undefined
|
|
767
858
|
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
768
|
-
:
|
|
859
|
+
: IdCompressor.create(logger);
|
|
769
860
|
}
|
|
770
861
|
|
|
771
862
|
const runtime = new containerRuntimeCtor(
|
|
@@ -789,7 +880,7 @@ export class ContainerRuntime
|
|
|
789
880
|
},
|
|
790
881
|
containerScope,
|
|
791
882
|
logger,
|
|
792
|
-
|
|
883
|
+
existing,
|
|
793
884
|
blobManagerSnapshot,
|
|
794
885
|
context.storage,
|
|
795
886
|
idCompressor,
|
|
@@ -798,6 +889,7 @@ export class ContainerRuntime
|
|
|
798
889
|
initializeEntryPoint,
|
|
799
890
|
);
|
|
800
891
|
|
|
892
|
+
await runtime.blobManager.processStashedChanges();
|
|
801
893
|
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
802
894
|
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
803
895
|
await runtime.pendingStateManager.applyStashedOpsAt(0);
|
|
@@ -808,49 +900,49 @@ export class ContainerRuntime
|
|
|
808
900
|
return runtime;
|
|
809
901
|
}
|
|
810
902
|
|
|
811
|
-
public
|
|
812
|
-
return this.context.options;
|
|
813
|
-
}
|
|
903
|
+
public readonly options: ILoaderOptions;
|
|
814
904
|
|
|
905
|
+
private readonly _getClientId: () => string | undefined;
|
|
815
906
|
public get clientId(): string | undefined {
|
|
816
|
-
return this.
|
|
907
|
+
return this._getClientId();
|
|
817
908
|
}
|
|
818
909
|
|
|
819
|
-
public
|
|
820
|
-
return this.context.clientDetails;
|
|
821
|
-
}
|
|
910
|
+
public readonly clientDetails: IClientDetails;
|
|
822
911
|
|
|
823
912
|
public get storage(): IDocumentStorageService {
|
|
824
913
|
return this._storage;
|
|
825
914
|
}
|
|
826
915
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
public get disposeFn(): (error?: ICriticalContainerError) => void {
|
|
838
|
-
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
839
|
-
return this.context.disposeFn ?? this.context.closeFn;
|
|
916
|
+
/** @deprecated - The functionality is no longer exposed publicly */
|
|
917
|
+
public get reSubmitFn() {
|
|
918
|
+
return (
|
|
919
|
+
type: ContainerMessageType,
|
|
920
|
+
contents: any,
|
|
921
|
+
localOpMetadata: unknown,
|
|
922
|
+
opMetadata: Record<string, unknown> | undefined,
|
|
923
|
+
) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
|
|
924
|
+
// Note: compatDetails is not included in this deprecated API
|
|
840
925
|
}
|
|
841
926
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
927
|
+
private readonly submitFn: (
|
|
928
|
+
type: MessageType,
|
|
929
|
+
contents: any,
|
|
930
|
+
batch: boolean,
|
|
931
|
+
appData?: any,
|
|
932
|
+
) => number;
|
|
933
|
+
/**
|
|
934
|
+
* Although current IContainerContext guarantees submitBatchFn, it is not available on older loaders.
|
|
935
|
+
*/
|
|
936
|
+
private readonly submitBatchFn:
|
|
937
|
+
| ((batch: IBatchMessage[], referenceSequenceNumber?: number) => number)
|
|
938
|
+
| undefined;
|
|
939
|
+
private readonly submitSummaryFn: (
|
|
940
|
+
summaryOp: ISummaryContent,
|
|
941
|
+
referenceSequenceNumber?: number,
|
|
942
|
+
) => number;
|
|
943
|
+
private readonly submitSignalFn: (contents: any) => void;
|
|
944
|
+
public readonly disposeFn: (error?: ICriticalContainerError) => void;
|
|
945
|
+
public readonly closeFn: (error?: ICriticalContainerError) => void;
|
|
854
946
|
|
|
855
947
|
public get flushMode(): FlushMode {
|
|
856
948
|
return this._flushMode;
|
|
@@ -864,8 +956,9 @@ export class ContainerRuntime
|
|
|
864
956
|
return this.registry;
|
|
865
957
|
}
|
|
866
958
|
|
|
959
|
+
private readonly _getAttachState: () => AttachState;
|
|
867
960
|
public get attachState(): AttachState {
|
|
868
|
-
return this.
|
|
961
|
+
return this._getAttachState();
|
|
869
962
|
}
|
|
870
963
|
|
|
871
964
|
public idCompressor: (IIdCompressor & IIdCompressorCore) | undefined;
|
|
@@ -964,7 +1057,6 @@ export class ContainerRuntime
|
|
|
964
1057
|
private emitDirtyDocumentEvent = true;
|
|
965
1058
|
private readonly enableOpReentryCheck: boolean;
|
|
966
1059
|
private readonly disableAttachReorder: boolean | undefined;
|
|
967
|
-
private readonly summaryStateUpdateMethod: string | undefined;
|
|
968
1060
|
private readonly closeSummarizerDelayMs: number;
|
|
969
1061
|
/**
|
|
970
1062
|
* If true, summary generated is validate before uploading it to the server. With single commit summaries,
|
|
@@ -974,7 +1066,7 @@ export class ContainerRuntime
|
|
|
974
1066
|
private readonly validateSummaryBeforeUpload: boolean;
|
|
975
1067
|
|
|
976
1068
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
977
|
-
private _perfSignalData: IPerfSignalReport = {
|
|
1069
|
+
private readonly _perfSignalData: IPerfSignalReport = {
|
|
978
1070
|
signalsLost: 0,
|
|
979
1071
|
signalSequenceNumber: 0,
|
|
980
1072
|
signalTimestamp: 0,
|
|
@@ -1056,11 +1148,21 @@ export class ContainerRuntime
|
|
|
1056
1148
|
*/
|
|
1057
1149
|
private readonly idCompressorEnabled: boolean;
|
|
1058
1150
|
|
|
1151
|
+
/**
|
|
1152
|
+
* Whether this client is the summarizer client itself (type is summarizerClientType)
|
|
1153
|
+
*/
|
|
1154
|
+
private readonly isSummarizerClient: boolean;
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* The id of the version used to initially load this runtime, or undefined if it's newly created.
|
|
1158
|
+
*/
|
|
1159
|
+
private readonly loadedFromVersionId: string | undefined;
|
|
1160
|
+
|
|
1059
1161
|
/**
|
|
1060
1162
|
* @internal
|
|
1061
1163
|
*/
|
|
1062
1164
|
protected constructor(
|
|
1063
|
-
|
|
1165
|
+
context: IContainerContext,
|
|
1064
1166
|
private readonly registry: IFluidDataStoreRegistry,
|
|
1065
1167
|
metadata: IContainerRuntimeMetadata | undefined,
|
|
1066
1168
|
electedSummarizerData: ISerializedElection | undefined,
|
|
@@ -1087,10 +1189,64 @@ export class ContainerRuntime
|
|
|
1087
1189
|
) {
|
|
1088
1190
|
super();
|
|
1089
1191
|
|
|
1090
|
-
|
|
1091
|
-
|
|
1192
|
+
const {
|
|
1193
|
+
options,
|
|
1194
|
+
clientDetails,
|
|
1195
|
+
connected,
|
|
1196
|
+
baseSnapshot,
|
|
1197
|
+
submitFn,
|
|
1198
|
+
submitBatchFn,
|
|
1199
|
+
submitSummaryFn,
|
|
1200
|
+
submitSignalFn,
|
|
1201
|
+
disposeFn,
|
|
1202
|
+
closeFn,
|
|
1203
|
+
deltaManager,
|
|
1204
|
+
quorum,
|
|
1205
|
+
audience,
|
|
1206
|
+
loader,
|
|
1207
|
+
pendingLocalState,
|
|
1208
|
+
supportedFeatures,
|
|
1209
|
+
} = context;
|
|
1210
|
+
|
|
1211
|
+
this.innerDeltaManager = deltaManager;
|
|
1212
|
+
this.deltaManager = new DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
1213
|
+
|
|
1214
|
+
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
1215
|
+
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
1216
|
+
this.submitFn = submitFn;
|
|
1217
|
+
this.submitBatchFn = submitBatchFn;
|
|
1218
|
+
this.submitSummaryFn = submitSummaryFn;
|
|
1219
|
+
this.submitSignalFn = submitSignalFn;
|
|
1220
|
+
|
|
1221
|
+
this.options = options;
|
|
1222
|
+
this.clientDetails = clientDetails;
|
|
1223
|
+
this.isSummarizerClient = this.clientDetails.type === summarizerClientType;
|
|
1224
|
+
this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
|
|
1225
|
+
this._getClientId = () => context.clientId;
|
|
1226
|
+
this._getAttachState = () => context.attachState;
|
|
1227
|
+
this.getAbsoluteUrl = async (relativeUrl: string) => {
|
|
1228
|
+
if (context.getAbsoluteUrl === undefined) {
|
|
1229
|
+
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
1230
|
+
}
|
|
1231
|
+
if (this.attachState !== AttachState.Attached) {
|
|
1232
|
+
return undefined;
|
|
1233
|
+
}
|
|
1234
|
+
return context.getAbsoluteUrl(relativeUrl);
|
|
1235
|
+
};
|
|
1236
|
+
// TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
|
|
1237
|
+
// customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
|
|
1238
|
+
this.on("dirty", () => context.updateDirtyContainerState(true));
|
|
1239
|
+
this.on("saved", () => context.updateDirtyContainerState(false));
|
|
1240
|
+
|
|
1241
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
1242
|
+
this.disposeFn = disposeFn ?? closeFn;
|
|
1243
|
+
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
1244
|
+
this.closeFn = this.isSummarizerClient ? this.disposeFn : closeFn;
|
|
1092
1245
|
|
|
1093
|
-
this.mc =
|
|
1246
|
+
this.mc = createChildMonitoringContext({
|
|
1247
|
+
logger: this.logger,
|
|
1248
|
+
namespace: "ContainerRuntime",
|
|
1249
|
+
});
|
|
1094
1250
|
|
|
1095
1251
|
let loadSummaryNumber: number;
|
|
1096
1252
|
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
@@ -1122,7 +1278,9 @@ export class ContainerRuntime
|
|
|
1122
1278
|
|
|
1123
1279
|
this.messageAtLastSummary = metadata?.message;
|
|
1124
1280
|
|
|
1125
|
-
|
|
1281
|
+
// Note that we only need to pull the *initial* connected state from the context.
|
|
1282
|
+
// Later updates come through calls to setConnectionState.
|
|
1283
|
+
this._connected = connected;
|
|
1126
1284
|
|
|
1127
1285
|
this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(
|
|
1128
1286
|
metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */,
|
|
@@ -1151,7 +1309,7 @@ export class ContainerRuntime
|
|
|
1151
1309
|
|
|
1152
1310
|
const opSplitter = new OpSplitter(
|
|
1153
1311
|
chunks,
|
|
1154
|
-
this.
|
|
1312
|
+
this.submitBatchFn,
|
|
1155
1313
|
disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
|
|
1156
1314
|
runtimeOptions.maxBatchSizeInBytes,
|
|
1157
1315
|
this.mc.logger,
|
|
@@ -1192,7 +1350,7 @@ export class ContainerRuntime
|
|
|
1192
1350
|
|
|
1193
1351
|
if (
|
|
1194
1352
|
runtimeOptions.flushMode === (FlushModeExperimental.Async as unknown as FlushMode) &&
|
|
1195
|
-
|
|
1353
|
+
supportedFeatures?.get("referenceSequenceNumbers") !== true
|
|
1196
1354
|
) {
|
|
1197
1355
|
// The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
|
|
1198
1356
|
this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
|
|
@@ -1201,7 +1359,7 @@ export class ContainerRuntime
|
|
|
1201
1359
|
this._flushMode = runtimeOptions.flushMode;
|
|
1202
1360
|
}
|
|
1203
1361
|
|
|
1204
|
-
const pendingRuntimeState =
|
|
1362
|
+
const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
|
|
1205
1363
|
|
|
1206
1364
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1207
1365
|
if (
|
|
@@ -1217,12 +1375,12 @@ export class ContainerRuntime
|
|
|
1217
1375
|
this.garbageCollector = GarbageCollector.create({
|
|
1218
1376
|
runtime: this,
|
|
1219
1377
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
1220
|
-
baseSnapshot
|
|
1378
|
+
baseSnapshot,
|
|
1221
1379
|
baseLogger: this.mc.logger,
|
|
1222
1380
|
existing,
|
|
1223
1381
|
metadata,
|
|
1224
1382
|
createContainerMetadata: this.createContainerMetadata,
|
|
1225
|
-
isSummarizerClient: this.
|
|
1383
|
+
isSummarizerClient: this.isSummarizerClient,
|
|
1226
1384
|
getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1227
1385
|
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
1228
1386
|
readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
@@ -1233,14 +1391,14 @@ export class ContainerRuntime
|
|
|
1233
1391
|
|
|
1234
1392
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
1235
1393
|
this.summarizerNode = createRootSummarizerNodeWithGC(
|
|
1236
|
-
|
|
1394
|
+
createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
|
|
1237
1395
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
1238
1396
|
async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
|
|
1239
1397
|
this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
1240
1398
|
// Latest change sequence number, no changes since summary applied yet
|
|
1241
1399
|
loadedFromSequenceNumber,
|
|
1242
1400
|
// Summary reference sequence number, undefined if no summary yet
|
|
1243
|
-
|
|
1401
|
+
baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined,
|
|
1244
1402
|
{
|
|
1245
1403
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
1246
1404
|
// a summary with an older protocol state.
|
|
@@ -1257,14 +1415,14 @@ export class ContainerRuntime
|
|
|
1257
1415
|
async () => this.garbageCollector.getBaseGCDetails(),
|
|
1258
1416
|
);
|
|
1259
1417
|
|
|
1260
|
-
if (
|
|
1261
|
-
this.summarizerNode.updateBaseSummaryState(
|
|
1418
|
+
if (baseSnapshot) {
|
|
1419
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
1262
1420
|
}
|
|
1263
1421
|
|
|
1264
1422
|
this.dataStores = new DataStores(
|
|
1265
|
-
getSummaryForDatastores(
|
|
1423
|
+
getSummaryForDatastores(baseSnapshot, metadata),
|
|
1266
1424
|
this,
|
|
1267
|
-
(attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg),
|
|
1425
|
+
(attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }),
|
|
1268
1426
|
(id: string, createParam: CreateChildSummarizerNodeParam) =>
|
|
1269
1427
|
(
|
|
1270
1428
|
summarizeInternal: SummarizeInternalFn,
|
|
@@ -1291,10 +1449,14 @@ export class ContainerRuntime
|
|
|
1291
1449
|
() => this.storage,
|
|
1292
1450
|
(localId: string, blobId?: string) => {
|
|
1293
1451
|
if (!this.disposed) {
|
|
1294
|
-
this.submit(
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1452
|
+
this.submit(
|
|
1453
|
+
{ type: ContainerMessageType.BlobAttach, contents: undefined },
|
|
1454
|
+
undefined,
|
|
1455
|
+
{
|
|
1456
|
+
localId,
|
|
1457
|
+
blobId,
|
|
1458
|
+
},
|
|
1459
|
+
);
|
|
1298
1460
|
}
|
|
1299
1461
|
},
|
|
1300
1462
|
(blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
|
|
@@ -1305,10 +1467,10 @@ export class ContainerRuntime
|
|
|
1305
1467
|
);
|
|
1306
1468
|
|
|
1307
1469
|
this.scheduleManager = new ScheduleManager(
|
|
1308
|
-
|
|
1470
|
+
this.innerDeltaManager,
|
|
1309
1471
|
this,
|
|
1310
1472
|
() => this.clientId,
|
|
1311
|
-
|
|
1473
|
+
createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
|
|
1312
1474
|
);
|
|
1313
1475
|
|
|
1314
1476
|
this.pendingStateManager = new PendingStateManager(
|
|
@@ -1319,8 +1481,10 @@ export class ContainerRuntime
|
|
|
1319
1481
|
connected: () => this.connected,
|
|
1320
1482
|
reSubmit: this.reSubmit.bind(this),
|
|
1321
1483
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1484
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1322
1485
|
},
|
|
1323
1486
|
pendingRuntimeState?.pending,
|
|
1487
|
+
this.logger,
|
|
1324
1488
|
);
|
|
1325
1489
|
|
|
1326
1490
|
const disableCompression = this.mc.config.getBoolean(
|
|
@@ -1337,10 +1501,14 @@ export class ContainerRuntime
|
|
|
1337
1501
|
const disablePartialFlush = this.mc.config.getBoolean(
|
|
1338
1502
|
"Fluid.ContainerRuntime.DisablePartialFlush",
|
|
1339
1503
|
);
|
|
1504
|
+
|
|
1505
|
+
const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
|
|
1506
|
+
|
|
1340
1507
|
this.outbox = new Outbox({
|
|
1341
1508
|
shouldSend: () => this.canSendOps(),
|
|
1342
1509
|
pendingStateManager: this.pendingStateManager,
|
|
1343
|
-
|
|
1510
|
+
submitBatchFn: this.submitBatchFn,
|
|
1511
|
+
legacySendBatchFn,
|
|
1344
1512
|
compressor: new OpCompressor(this.mc.logger),
|
|
1345
1513
|
splitter: opSplitter,
|
|
1346
1514
|
config: {
|
|
@@ -1360,41 +1528,43 @@ export class ContainerRuntime
|
|
|
1360
1528
|
closeContainer: this.closeFn,
|
|
1361
1529
|
});
|
|
1362
1530
|
|
|
1363
|
-
this.
|
|
1531
|
+
this._quorum = quorum;
|
|
1532
|
+
this._quorum.on("removeMember", (clientId: string) => {
|
|
1364
1533
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
1365
1534
|
});
|
|
1366
1535
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1536
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1537
|
+
this._audience = audience!;
|
|
1538
|
+
|
|
1370
1539
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
1371
1540
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
1372
1541
|
);
|
|
1373
1542
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
1374
1543
|
this.validateSummaryBeforeUpload =
|
|
1375
|
-
this.mc.config.getBoolean("Fluid.
|
|
1376
|
-
false;
|
|
1544
|
+
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
1377
1545
|
|
|
1378
1546
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1379
1547
|
|
|
1380
1548
|
this.dirtyContainer =
|
|
1381
|
-
this.
|
|
1382
|
-
|
|
1383
|
-
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
1549
|
+
this.attachState !== AttachState.Attached || this.hasPendingMessages();
|
|
1550
|
+
context.updateDirtyContainerState(this.dirtyContainer);
|
|
1384
1551
|
|
|
1385
1552
|
if (this.summariesDisabled) {
|
|
1386
1553
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
1387
1554
|
} else {
|
|
1388
|
-
const orderedClientLogger =
|
|
1555
|
+
const orderedClientLogger = createChildLogger({
|
|
1556
|
+
logger: this.logger,
|
|
1557
|
+
namespace: "OrderedClientElection",
|
|
1558
|
+
});
|
|
1389
1559
|
const orderedClientCollection = new OrderedClientCollection(
|
|
1390
1560
|
orderedClientLogger,
|
|
1391
|
-
this.
|
|
1392
|
-
this.
|
|
1561
|
+
this.innerDeltaManager,
|
|
1562
|
+
this._quorum,
|
|
1393
1563
|
);
|
|
1394
1564
|
const orderedClientElectionForSummarizer = new OrderedClientElection(
|
|
1395
1565
|
orderedClientLogger,
|
|
1396
1566
|
orderedClientCollection,
|
|
1397
|
-
electedSummarizerData ?? this.
|
|
1567
|
+
electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber,
|
|
1398
1568
|
SummarizerClientElection.isClientEligible,
|
|
1399
1569
|
);
|
|
1400
1570
|
|
|
@@ -1405,7 +1575,7 @@ export class ContainerRuntime
|
|
|
1405
1575
|
this.maxOpsSinceLastSummary,
|
|
1406
1576
|
);
|
|
1407
1577
|
|
|
1408
|
-
if (this.
|
|
1578
|
+
if (this.isSummarizerClient) {
|
|
1409
1579
|
this._summarizer = new Summarizer(
|
|
1410
1580
|
this /* ISummarizerRuntime */,
|
|
1411
1581
|
() => this.summaryConfiguration,
|
|
@@ -1420,19 +1590,19 @@ export class ContainerRuntime
|
|
|
1420
1590
|
() => this.innerDeltaManager.active,
|
|
1421
1591
|
),
|
|
1422
1592
|
);
|
|
1423
|
-
} else if (
|
|
1424
|
-
SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)
|
|
1425
|
-
) {
|
|
1593
|
+
} else if (SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
|
|
1426
1594
|
// Only create a SummaryManager and SummarizerClientElection
|
|
1427
1595
|
// if summaries are enabled and we are not the summarizer client.
|
|
1428
1596
|
const defaultAction = () => {
|
|
1429
1597
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
1430
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1598
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1431
1599
|
// unregister default to no log on every op after falling behind
|
|
1432
1600
|
// and register summary ack handler to re-register this handler
|
|
1433
1601
|
// after successful summary
|
|
1434
1602
|
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
1435
|
-
this.logger.sendTelemetryEvent({
|
|
1603
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1604
|
+
eventName: "SummaryStatus:CaughtUp",
|
|
1605
|
+
});
|
|
1436
1606
|
// we've caught up, so re-register the default action to monitor for
|
|
1437
1607
|
// falling behind, and unregister ourself
|
|
1438
1608
|
this.summaryCollection.on("default", defaultAction);
|
|
@@ -1449,7 +1619,7 @@ export class ContainerRuntime
|
|
|
1449
1619
|
this, // IConnectedState
|
|
1450
1620
|
this.summaryCollection,
|
|
1451
1621
|
this.logger,
|
|
1452
|
-
this.formRequestSummarizerFn(
|
|
1622
|
+
this.formRequestSummarizerFn(loader),
|
|
1453
1623
|
new Throttler(
|
|
1454
1624
|
60 * 1000, // 60 sec delay window
|
|
1455
1625
|
30 * 1000, // 30 sec max delay
|
|
@@ -1461,6 +1631,9 @@ export class ContainerRuntime
|
|
|
1461
1631
|
},
|
|
1462
1632
|
this.heuristicsDisabled,
|
|
1463
1633
|
);
|
|
1634
|
+
this.summaryManager.on("summarize", (eventProps) => {
|
|
1635
|
+
this.emit("summarize", eventProps);
|
|
1636
|
+
});
|
|
1464
1637
|
this.summaryManager.start();
|
|
1465
1638
|
}
|
|
1466
1639
|
}
|
|
@@ -1500,7 +1673,7 @@ export class ContainerRuntime
|
|
|
1500
1673
|
...getDeviceSpec(),
|
|
1501
1674
|
});
|
|
1502
1675
|
|
|
1503
|
-
this.logger.sendTelemetryEvent({
|
|
1676
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1504
1677
|
eventName: "ContainerLoadStats",
|
|
1505
1678
|
...this.createContainerMetadata,
|
|
1506
1679
|
...this.dataStores.containerLoadStats,
|
|
@@ -1516,18 +1689,17 @@ export class ContainerRuntime
|
|
|
1516
1689
|
disableAttachReorder: this.disableAttachReorder,
|
|
1517
1690
|
disablePartialFlush,
|
|
1518
1691
|
idCompressorEnabled: this.idCompressorEnabled,
|
|
1519
|
-
summaryStateUpdateMethod: this.summaryStateUpdateMethod,
|
|
1520
1692
|
closeSummarizerDelayOverride,
|
|
1521
1693
|
}),
|
|
1522
1694
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1523
1695
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1524
1696
|
});
|
|
1525
1697
|
|
|
1526
|
-
ReportOpPerfTelemetry(this.
|
|
1698
|
+
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this.logger);
|
|
1527
1699
|
BindBatchTracker(this, this.logger);
|
|
1528
1700
|
|
|
1529
1701
|
this.entryPoint = new LazyPromise(async () => {
|
|
1530
|
-
if (this.
|
|
1702
|
+
if (this.isSummarizerClient) {
|
|
1531
1703
|
assert(
|
|
1532
1704
|
this._summarizer !== undefined,
|
|
1533
1705
|
0x5bf /* Summarizer object is undefined in a summarizer client */,
|
|
@@ -1551,7 +1723,7 @@ export class ContainerRuntime
|
|
|
1551
1723
|
}
|
|
1552
1724
|
this._disposed = true;
|
|
1553
1725
|
|
|
1554
|
-
this.logger.sendTelemetryEvent(
|
|
1726
|
+
this.mc.logger.sendTelemetryEvent(
|
|
1555
1727
|
{
|
|
1556
1728
|
eventName: "ContainerRuntimeDisposed",
|
|
1557
1729
|
isDirty: this.isDirty,
|
|
@@ -1575,6 +1747,7 @@ export class ContainerRuntime
|
|
|
1575
1747
|
/**
|
|
1576
1748
|
* Notifies this object about the request made to the container.
|
|
1577
1749
|
* @param request - Request made to the handler.
|
|
1750
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
1578
1751
|
*/
|
|
1579
1752
|
public async request(request: IRequest): Promise<IResponse> {
|
|
1580
1753
|
try {
|
|
@@ -1632,7 +1805,7 @@ export class ContainerRuntime
|
|
|
1632
1805
|
subRequest.url.startsWith("/"),
|
|
1633
1806
|
0x126 /* "Expected createSubRequest url to include a leading slash" */,
|
|
1634
1807
|
);
|
|
1635
|
-
return dataStore.
|
|
1808
|
+
return dataStore.request(subRequest);
|
|
1636
1809
|
}
|
|
1637
1810
|
|
|
1638
1811
|
return create404Response(request);
|
|
@@ -1680,6 +1853,7 @@ export class ContainerRuntime
|
|
|
1680
1853
|
dataStoreContext.packagePath,
|
|
1681
1854
|
request?.headers,
|
|
1682
1855
|
);
|
|
1856
|
+
|
|
1683
1857
|
return dataStoreChannel;
|
|
1684
1858
|
}
|
|
1685
1859
|
|
|
@@ -1774,7 +1948,7 @@ export class ContainerRuntime
|
|
|
1774
1948
|
this.mc.logger.sendTelemetryEvent({
|
|
1775
1949
|
eventName: "ReconnectsWithNoProgress",
|
|
1776
1950
|
attempts: this.consecutiveReconnects,
|
|
1777
|
-
pendingMessages: this.
|
|
1951
|
+
pendingMessages: this.pendingMessagesCount,
|
|
1778
1952
|
});
|
|
1779
1953
|
}
|
|
1780
1954
|
|
|
@@ -1841,19 +2015,17 @@ export class ContainerRuntime
|
|
|
1841
2015
|
* Parse an op's type and actual content from given serialized content
|
|
1842
2016
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
1843
2017
|
*/
|
|
1844
|
-
private parseOpContent(serializedContent?: string): {
|
|
1845
|
-
type: ContainerMessageType;
|
|
1846
|
-
contents: unknown;
|
|
1847
|
-
} {
|
|
2018
|
+
private parseOpContent(serializedContent?: string): ContainerRuntimeMessage {
|
|
1848
2019
|
assert(serializedContent !== undefined, 0x6d5 /* content must be defined */);
|
|
1849
|
-
const
|
|
1850
|
-
|
|
1851
|
-
|
|
2020
|
+
const { type, contents, compatDetails }: ContainerRuntimeMessage =
|
|
2021
|
+
JSON.parse(serializedContent);
|
|
2022
|
+
assert(type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
2023
|
+
return { type, contents, compatDetails };
|
|
1852
2024
|
}
|
|
1853
2025
|
|
|
1854
2026
|
private async applyStashedOp(op: string): Promise<unknown> {
|
|
1855
2027
|
// Need to parse from string for back-compat
|
|
1856
|
-
const { type, contents } = this.parseOpContent(op);
|
|
2028
|
+
const { type, contents, compatDetails } = this.parseOpContent(op);
|
|
1857
2029
|
switch (type) {
|
|
1858
2030
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1859
2031
|
return this.dataStores.applyStashedOp(contents as IEnvelope);
|
|
@@ -1872,8 +2044,27 @@ export class ContainerRuntime
|
|
|
1872
2044
|
throw new Error("chunkedOp not expected here");
|
|
1873
2045
|
case ContainerMessageType.Rejoin:
|
|
1874
2046
|
throw new Error("rejoin not expected here");
|
|
1875
|
-
default:
|
|
1876
|
-
|
|
2047
|
+
default: {
|
|
2048
|
+
// This should be extremely rare for stashed ops.
|
|
2049
|
+
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
2050
|
+
// e.g. if an app rolled back its container version
|
|
2051
|
+
const compatBehavior = compatDetails?.behavior;
|
|
2052
|
+
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
2053
|
+
const error = DataProcessingError.create(
|
|
2054
|
+
"Stashed runtime message of unknown type",
|
|
2055
|
+
"applyStashedOp",
|
|
2056
|
+
undefined /* sequencedMessage */,
|
|
2057
|
+
{
|
|
2058
|
+
messageDetails: JSON.stringify({
|
|
2059
|
+
type,
|
|
2060
|
+
compatBehavior,
|
|
2061
|
+
}),
|
|
2062
|
+
},
|
|
2063
|
+
);
|
|
2064
|
+
this.closeFn(error);
|
|
2065
|
+
throw error;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1877
2068
|
}
|
|
1878
2069
|
}
|
|
1879
2070
|
|
|
@@ -1887,32 +2078,6 @@ export class ContainerRuntime
|
|
|
1887
2078
|
return;
|
|
1888
2079
|
}
|
|
1889
2080
|
|
|
1890
|
-
// If attachment blobs were added while disconnected, we need to delay
|
|
1891
|
-
// propagation of the "connected" event until we have uploaded them to
|
|
1892
|
-
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1893
|
-
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
1894
|
-
const connecting =
|
|
1895
|
-
connected && !this._connected && !this.innerDeltaManager.readOnlyInfo.readonly;
|
|
1896
|
-
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
1897
|
-
assert(
|
|
1898
|
-
!this.delayConnectClientId,
|
|
1899
|
-
0x392 /* Connect event delay must be canceled before subsequent connect event */,
|
|
1900
|
-
);
|
|
1901
|
-
assert(!!clientId, 0x393 /* Must have clientId when connecting */);
|
|
1902
|
-
this.delayConnectClientId = clientId;
|
|
1903
|
-
this.blobManager.onConnected().then(
|
|
1904
|
-
() => {
|
|
1905
|
-
// make sure we didn't reconnect before the promise resolved
|
|
1906
|
-
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1907
|
-
this.delayConnectClientId = undefined;
|
|
1908
|
-
this.setConnectionStateCore(connected, clientId);
|
|
1909
|
-
}
|
|
1910
|
-
},
|
|
1911
|
-
(error) => this.closeFn(error),
|
|
1912
|
-
);
|
|
1913
|
-
return;
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
2081
|
this.setConnectionStateCore(connected, clientId);
|
|
1917
2082
|
}
|
|
1918
2083
|
|
|
@@ -1926,6 +2091,14 @@ export class ContainerRuntime
|
|
|
1926
2091
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1927
2092
|
const changeOfState = this._connected !== connected;
|
|
1928
2093
|
const reconnection = changeOfState && !connected;
|
|
2094
|
+
|
|
2095
|
+
// We need to flush the ops currently collected by Outbox to preserve original order.
|
|
2096
|
+
// This flush NEEDS to happen before we set the ContainerRuntime to "connected".
|
|
2097
|
+
// We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
|
|
2098
|
+
if (changeOfState && connected) {
|
|
2099
|
+
this.flush();
|
|
2100
|
+
}
|
|
2101
|
+
|
|
1929
2102
|
this._connected = connected;
|
|
1930
2103
|
|
|
1931
2104
|
if (!connected) {
|
|
@@ -1952,7 +2125,7 @@ export class ContainerRuntime
|
|
|
1952
2125
|
{
|
|
1953
2126
|
dataLoss: 1,
|
|
1954
2127
|
attempts: this.consecutiveReconnects,
|
|
1955
|
-
pendingMessages: this.
|
|
2128
|
+
pendingMessages: this.pendingMessagesCount,
|
|
1956
2129
|
},
|
|
1957
2130
|
),
|
|
1958
2131
|
);
|
|
@@ -1977,24 +2150,30 @@ export class ContainerRuntime
|
|
|
1977
2150
|
public process(messageArg: ISequencedDocumentMessage, local: boolean) {
|
|
1978
2151
|
this.verifyNotClosed();
|
|
1979
2152
|
|
|
1980
|
-
// Whether or not the message
|
|
2153
|
+
// Whether or not the message appears to be a runtime message from an up-to-date client.
|
|
1981
2154
|
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
1982
2155
|
// or something different, like a system message.
|
|
1983
|
-
const
|
|
2156
|
+
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
1984
2157
|
|
|
1985
2158
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1986
2159
|
const messageCopy = { ...messageArg };
|
|
1987
2160
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1988
|
-
this.processCore(message, local,
|
|
2161
|
+
this.processCore(message, local, modernRuntimeMessage);
|
|
1989
2162
|
}
|
|
1990
2163
|
}
|
|
1991
2164
|
|
|
1992
2165
|
private _processedClientSequenceNumber: number | undefined;
|
|
1993
2166
|
|
|
2167
|
+
/**
|
|
2168
|
+
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
2169
|
+
* @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
|
|
2170
|
+
* @param local - Did this client send the op?
|
|
2171
|
+
* @param modernRuntimeMessage - Does this appear like a current ContainerRuntimeMessage?
|
|
2172
|
+
*/
|
|
1994
2173
|
private processCore(
|
|
1995
|
-
message: ISequencedDocumentMessage,
|
|
2174
|
+
message: ISequencedDocumentMessage | SequencedContainerRuntimeMessage,
|
|
1996
2175
|
local: boolean,
|
|
1997
|
-
|
|
2176
|
+
modernRuntimeMessage: boolean,
|
|
1998
2177
|
) {
|
|
1999
2178
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
2000
2179
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
@@ -2005,8 +2184,10 @@ export class ContainerRuntime
|
|
|
2005
2184
|
|
|
2006
2185
|
try {
|
|
2007
2186
|
let localOpMetadata: unknown;
|
|
2008
|
-
if (local &&
|
|
2009
|
-
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2187
|
+
if (local && modernRuntimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
2188
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2189
|
+
message as SequencedContainerRuntimeMessage,
|
|
2190
|
+
);
|
|
2010
2191
|
}
|
|
2011
2192
|
|
|
2012
2193
|
// If there are no more pending messages after processing a local message,
|
|
@@ -2015,53 +2196,14 @@ export class ContainerRuntime
|
|
|
2015
2196
|
this.updateDocumentDirtyState(false);
|
|
2016
2197
|
}
|
|
2017
2198
|
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
this.processAliasMessage(message, localOpMetadata, local);
|
|
2025
|
-
break;
|
|
2026
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
2027
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
2028
|
-
break;
|
|
2029
|
-
case ContainerMessageType.BlobAttach:
|
|
2030
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
2031
|
-
break;
|
|
2032
|
-
case ContainerMessageType.IdAllocation:
|
|
2033
|
-
assert(
|
|
2034
|
-
this.idCompressor !== undefined,
|
|
2035
|
-
0x67c /* IdCompressor should be defined if enabled */,
|
|
2036
|
-
);
|
|
2037
|
-
this.idCompressor.finalizeCreationRange(message.contents as IdCreationRange);
|
|
2038
|
-
break;
|
|
2039
|
-
case ContainerMessageType.ChunkedOp:
|
|
2040
|
-
case ContainerMessageType.Rejoin:
|
|
2041
|
-
break;
|
|
2042
|
-
default:
|
|
2043
|
-
if (runtimeMessage) {
|
|
2044
|
-
const error = DataProcessingError.create(
|
|
2045
|
-
// Former assert 0x3ce
|
|
2046
|
-
"Runtime message of unknown type",
|
|
2047
|
-
"OpProcessing",
|
|
2048
|
-
message,
|
|
2049
|
-
{
|
|
2050
|
-
local,
|
|
2051
|
-
type: message.type,
|
|
2052
|
-
contentType: typeof message.contents,
|
|
2053
|
-
batch: (message.metadata as IBatchMetadata | undefined)?.batch,
|
|
2054
|
-
compression: message.compression,
|
|
2055
|
-
},
|
|
2056
|
-
);
|
|
2057
|
-
this.closeFn(error);
|
|
2058
|
-
throw error;
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2199
|
+
this.validateAndProcessRuntimeMessage(
|
|
2200
|
+
message,
|
|
2201
|
+
localOpMetadata,
|
|
2202
|
+
local,
|
|
2203
|
+
modernRuntimeMessage,
|
|
2204
|
+
);
|
|
2061
2205
|
|
|
2062
|
-
|
|
2063
|
-
this.emit("op", message, runtimeMessage);
|
|
2064
|
-
}
|
|
2206
|
+
this.emit("op", message, modernRuntimeMessage);
|
|
2065
2207
|
|
|
2066
2208
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2067
2209
|
|
|
@@ -2076,13 +2218,74 @@ export class ContainerRuntime
|
|
|
2076
2218
|
throw e;
|
|
2077
2219
|
}
|
|
2078
2220
|
}
|
|
2079
|
-
|
|
2080
|
-
|
|
2221
|
+
/**
|
|
2222
|
+
* Assuming the given message is also a ContainerRuntimeMessage,
|
|
2223
|
+
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
2224
|
+
* Throws a DataProcessingError if the message doesn't conform to the ContainerRuntimeMessage type.
|
|
2225
|
+
*/
|
|
2226
|
+
private validateAndProcessRuntimeMessage(
|
|
2081
2227
|
message: ISequencedDocumentMessage,
|
|
2082
2228
|
localOpMetadata: unknown,
|
|
2083
2229
|
local: boolean,
|
|
2084
|
-
|
|
2085
|
-
|
|
2230
|
+
expectRuntimeMessageType: boolean,
|
|
2231
|
+
): asserts message is SequencedContainerRuntimeMessage {
|
|
2232
|
+
// Optimistically extract ContainerRuntimeMessage-specific props from the message
|
|
2233
|
+
const { type: maybeContainerMessageType, compatDetails } =
|
|
2234
|
+
message as ContainerRuntimeMessage;
|
|
2235
|
+
|
|
2236
|
+
switch (maybeContainerMessageType) {
|
|
2237
|
+
case ContainerMessageType.Attach:
|
|
2238
|
+
this.dataStores.processAttachMessage(message, local);
|
|
2239
|
+
break;
|
|
2240
|
+
case ContainerMessageType.Alias:
|
|
2241
|
+
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
2242
|
+
break;
|
|
2243
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
2244
|
+
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
2245
|
+
break;
|
|
2246
|
+
case ContainerMessageType.BlobAttach:
|
|
2247
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
2248
|
+
break;
|
|
2249
|
+
case ContainerMessageType.IdAllocation:
|
|
2250
|
+
assert(
|
|
2251
|
+
this.idCompressor !== undefined,
|
|
2252
|
+
0x67c /* IdCompressor should be defined if enabled */,
|
|
2253
|
+
);
|
|
2254
|
+
this.idCompressor.finalizeCreationRange(message.contents as IdCreationRange);
|
|
2255
|
+
break;
|
|
2256
|
+
case ContainerMessageType.ChunkedOp:
|
|
2257
|
+
case ContainerMessageType.Rejoin:
|
|
2258
|
+
break;
|
|
2259
|
+
default: {
|
|
2260
|
+
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
2261
|
+
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
2262
|
+
if (!expectRuntimeMessageType) {
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
const compatBehavior = compatDetails?.behavior;
|
|
2267
|
+
if (!compatBehaviorAllowsMessageType(maybeContainerMessageType, compatBehavior)) {
|
|
2268
|
+
const error = DataProcessingError.create(
|
|
2269
|
+
// Former assert 0x3ce
|
|
2270
|
+
"Runtime message of unknown type",
|
|
2271
|
+
"OpProcessing",
|
|
2272
|
+
message,
|
|
2273
|
+
{
|
|
2274
|
+
local,
|
|
2275
|
+
messageDetails: JSON.stringify({
|
|
2276
|
+
type: message.type,
|
|
2277
|
+
contentType: typeof message.contents,
|
|
2278
|
+
compatBehavior,
|
|
2279
|
+
batch: (message.metadata as IBatchMetadata | undefined)?.batch,
|
|
2280
|
+
compression: message.compression,
|
|
2281
|
+
}),
|
|
2282
|
+
},
|
|
2283
|
+
);
|
|
2284
|
+
this.closeFn(error);
|
|
2285
|
+
throw error;
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2086
2289
|
}
|
|
2087
2290
|
|
|
2088
2291
|
/**
|
|
@@ -2091,7 +2294,7 @@ export class ContainerRuntime
|
|
|
2091
2294
|
*/
|
|
2092
2295
|
private sendSignalTelemetryEvent(clientSignalSequenceNumber: number) {
|
|
2093
2296
|
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
2094
|
-
this.logger.sendPerformanceEvent({
|
|
2297
|
+
this.mc.logger.sendPerformanceEvent({
|
|
2095
2298
|
eventName: "SignalLatency",
|
|
2096
2299
|
duration,
|
|
2097
2300
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -2119,7 +2322,7 @@ export class ContainerRuntime
|
|
|
2119
2322
|
) {
|
|
2120
2323
|
this._perfSignalData.signalsLost++;
|
|
2121
2324
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
2122
|
-
this.logger.sendErrorEvent({
|
|
2325
|
+
this.mc.logger.sendErrorEvent({
|
|
2123
2326
|
eventName: "SignalLost",
|
|
2124
2327
|
type: envelope.contents.type,
|
|
2125
2328
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -2147,6 +2350,12 @@ export class ContainerRuntime
|
|
|
2147
2350
|
this.dataStores.processSignal(envelope.address, transformed, local);
|
|
2148
2351
|
}
|
|
2149
2352
|
|
|
2353
|
+
/**
|
|
2354
|
+
* Returns the runtime of the data store.
|
|
2355
|
+
* @param id - Id supplied during creating the data store.
|
|
2356
|
+
* @param wait - True if you want to wait for it.
|
|
2357
|
+
* @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
2358
|
+
*/
|
|
2150
2359
|
public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
|
|
2151
2360
|
return this.getRootDataStoreChannel(id, wait);
|
|
2152
2361
|
}
|
|
@@ -2222,15 +2431,30 @@ export class ContainerRuntime
|
|
|
2222
2431
|
return result;
|
|
2223
2432
|
}
|
|
2224
2433
|
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2434
|
+
/**
|
|
2435
|
+
* Returns the aliased data store's entryPoint, given the alias.
|
|
2436
|
+
* @param alias - The alias for the data store.
|
|
2437
|
+
* @returns The data store's entry point ({@link @fluidframework/core-interfaces#IFluidHandle}) if it exists and is aliased.
|
|
2438
|
+
* Returns undefined if no data store has been assigned the given alias.
|
|
2439
|
+
*/
|
|
2440
|
+
public async getAliasedDataStoreEntryPoint(
|
|
2441
|
+
alias: string,
|
|
2442
|
+
): Promise<IFluidHandle<FluidObject> | undefined> {
|
|
2443
|
+
await this.dataStores.waitIfPendingAlias(alias);
|
|
2444
|
+
const internalId = this.internalId(alias);
|
|
2445
|
+
const context = await this.dataStores.getDataStoreIfAvailable(internalId, { wait: false });
|
|
2446
|
+
// If the data store is not available or not an alias, return undefined.
|
|
2447
|
+
if (context === undefined || !(await context.isRoot())) {
|
|
2448
|
+
return undefined;
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
const channel = await context.realize();
|
|
2452
|
+
if (channel.entryPoint === undefined) {
|
|
2453
|
+
throw new UsageError(
|
|
2454
|
+
"entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint",
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2457
|
+
return channel.entryPoint;
|
|
2234
2458
|
}
|
|
2235
2459
|
|
|
2236
2460
|
public createDetachedRootDataStore(
|
|
@@ -2247,25 +2471,37 @@ export class ContainerRuntime
|
|
|
2247
2471
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
2248
2472
|
}
|
|
2249
2473
|
|
|
2474
|
+
public async createDataStore(pkg: string | string[]): Promise<IDataStore> {
|
|
2475
|
+
const id = uuid();
|
|
2476
|
+
return channelToDataStore(
|
|
2477
|
+
await this.dataStores
|
|
2478
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id)
|
|
2479
|
+
.realize(),
|
|
2480
|
+
id,
|
|
2481
|
+
this,
|
|
2482
|
+
this.dataStores,
|
|
2483
|
+
this.mc.logger,
|
|
2484
|
+
);
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
/**
|
|
2488
|
+
* @deprecated 0.16 Issue #1537, #3631
|
|
2489
|
+
* @internal
|
|
2490
|
+
*/
|
|
2250
2491
|
public async _createDataStoreWithProps(
|
|
2251
2492
|
pkg: string | string[],
|
|
2252
2493
|
props?: any,
|
|
2253
2494
|
id = uuid(),
|
|
2254
2495
|
): Promise<IDataStore> {
|
|
2255
|
-
|
|
2256
|
-
.
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
props?: any,
|
|
2265
|
-
): Promise<IFluidDataStoreChannel> {
|
|
2266
|
-
return this.dataStores
|
|
2267
|
-
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
2268
|
-
.realize();
|
|
2496
|
+
return channelToDataStore(
|
|
2497
|
+
await this.dataStores
|
|
2498
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
2499
|
+
.realize(),
|
|
2500
|
+
id,
|
|
2501
|
+
this,
|
|
2502
|
+
this.dataStores,
|
|
2503
|
+
this.mc.logger,
|
|
2504
|
+
);
|
|
2269
2505
|
}
|
|
2270
2506
|
|
|
2271
2507
|
private canSendOps() {
|
|
@@ -2281,13 +2517,14 @@ export class ContainerRuntime
|
|
|
2281
2517
|
return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
2282
2518
|
}
|
|
2283
2519
|
|
|
2520
|
+
private readonly _quorum: IQuorumClients;
|
|
2284
2521
|
public getQuorum(): IQuorumClients {
|
|
2285
|
-
return this.
|
|
2522
|
+
return this._quorum;
|
|
2286
2523
|
}
|
|
2287
2524
|
|
|
2525
|
+
private readonly _audience: IAudience;
|
|
2288
2526
|
public getAudience(): IAudience {
|
|
2289
|
-
|
|
2290
|
-
return this.context.audience!;
|
|
2527
|
+
return this._audience;
|
|
2291
2528
|
}
|
|
2292
2529
|
|
|
2293
2530
|
/**
|
|
@@ -2298,7 +2535,7 @@ export class ContainerRuntime
|
|
|
2298
2535
|
return this.dirtyContainer;
|
|
2299
2536
|
}
|
|
2300
2537
|
|
|
2301
|
-
private isContainerMessageDirtyable(type
|
|
2538
|
+
private isContainerMessageDirtyable({ type, contents }: ContainerRuntimeMessage) {
|
|
2302
2539
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
2303
2540
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
2304
2541
|
if (type === ContainerMessageType.Attach) {
|
|
@@ -2347,12 +2584,12 @@ export class ContainerRuntime
|
|
|
2347
2584
|
public submitSignal(type: string, content: any) {
|
|
2348
2585
|
this.verifyNotClosed();
|
|
2349
2586
|
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
2350
|
-
return this.
|
|
2587
|
+
return this.submitSignalFn(envelope);
|
|
2351
2588
|
}
|
|
2352
2589
|
|
|
2353
2590
|
public submitDataStoreSignal(address: string, type: string, content: any) {
|
|
2354
2591
|
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
2355
|
-
return this.
|
|
2592
|
+
return this.submitSignalFn(envelope);
|
|
2356
2593
|
}
|
|
2357
2594
|
|
|
2358
2595
|
public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
|
|
@@ -2404,15 +2641,7 @@ export class ContainerRuntime
|
|
|
2404
2641
|
return summarizeResult.summary;
|
|
2405
2642
|
}
|
|
2406
2643
|
|
|
2407
|
-
public
|
|
2408
|
-
if (this.context.getAbsoluteUrl === undefined) {
|
|
2409
|
-
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
2410
|
-
}
|
|
2411
|
-
if (this.attachState !== AttachState.Attached) {
|
|
2412
|
-
return undefined;
|
|
2413
|
-
}
|
|
2414
|
-
return this.context.getAbsoluteUrl(relativeUrl);
|
|
2415
|
-
}
|
|
2644
|
+
public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>;
|
|
2416
2645
|
|
|
2417
2646
|
private async summarizeInternal(
|
|
2418
2647
|
fullTree: boolean,
|
|
@@ -2453,7 +2682,7 @@ export class ContainerRuntime
|
|
|
2453
2682
|
fullGC?: boolean;
|
|
2454
2683
|
/** True to run GC sweep phase after the mark phase */
|
|
2455
2684
|
runSweep?: boolean;
|
|
2456
|
-
}): Promise<
|
|
2685
|
+
}): Promise<ISummaryTreeWithStats> {
|
|
2457
2686
|
this.verifyNotClosed();
|
|
2458
2687
|
|
|
2459
2688
|
const {
|
|
@@ -2476,9 +2705,8 @@ export class ContainerRuntime
|
|
|
2476
2705
|
});
|
|
2477
2706
|
|
|
2478
2707
|
try {
|
|
2479
|
-
let gcStats: IGCStats | undefined;
|
|
2480
2708
|
if (runGC) {
|
|
2481
|
-
|
|
2709
|
+
await this.collectGarbage(
|
|
2482
2710
|
{ logger: summaryLogger, runSweep, fullGC },
|
|
2483
2711
|
telemetryContext,
|
|
2484
2712
|
);
|
|
@@ -2495,9 +2723,9 @@ export class ContainerRuntime
|
|
|
2495
2723
|
0x12f /* "Container Runtime's summarize should always return a tree" */,
|
|
2496
2724
|
);
|
|
2497
2725
|
|
|
2498
|
-
return { stats, summary
|
|
2726
|
+
return { stats, summary };
|
|
2499
2727
|
} finally {
|
|
2500
|
-
this.logger.sendTelemetryEvent({
|
|
2728
|
+
this.mc.logger.sendTelemetryEvent({
|
|
2501
2729
|
eventName: "SummarizeTelemetry",
|
|
2502
2730
|
details: telemetryContext.serialize(),
|
|
2503
2731
|
});
|
|
@@ -2569,7 +2797,7 @@ export class ContainerRuntime
|
|
|
2569
2797
|
/**
|
|
2570
2798
|
* After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
|
|
2571
2799
|
* @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
|
|
2572
|
-
* @returns
|
|
2800
|
+
* @returns The routes of nodes that were deleted.
|
|
2573
2801
|
*/
|
|
2574
2802
|
public deleteSweepReadyNodes(sweepReadyRoutes: string[]): string[] {
|
|
2575
2803
|
const { dataStoreRoutes, blobManagerRoutes } =
|
|
@@ -2640,7 +2868,7 @@ export class ContainerRuntime
|
|
|
2640
2868
|
/**
|
|
2641
2869
|
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
2642
2870
|
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
2643
|
-
* @returns
|
|
2871
|
+
* @returns Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
2644
2872
|
* for data stores.
|
|
2645
2873
|
*/
|
|
2646
2874
|
private getDataStoreAndBlobManagerRoutes(routes: string[]) {
|
|
@@ -2696,12 +2924,15 @@ export class ContainerRuntime
|
|
|
2696
2924
|
* @param options - options controlling how the summary is generated or submitted
|
|
2697
2925
|
*/
|
|
2698
2926
|
public async submitSummary(options: ISubmitSummaryOptions): Promise<SubmitSummaryResult> {
|
|
2699
|
-
const { fullTree = false, refreshLatestAck, summaryLogger } = options;
|
|
2927
|
+
const { fullTree = false, finalAttempt = false, refreshLatestAck, summaryLogger } = options;
|
|
2700
2928
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
2701
2929
|
// use it for all events logged during this summary.
|
|
2702
2930
|
const summaryNumber = this.nextSummaryNumber;
|
|
2703
|
-
const summaryNumberLogger =
|
|
2704
|
-
|
|
2931
|
+
const summaryNumberLogger = createChildLogger({
|
|
2932
|
+
logger: summaryLogger,
|
|
2933
|
+
properties: {
|
|
2934
|
+
all: { summaryNumber },
|
|
2935
|
+
},
|
|
2705
2936
|
});
|
|
2706
2937
|
|
|
2707
2938
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
@@ -2709,7 +2940,10 @@ export class ContainerRuntime
|
|
|
2709
2940
|
let latestSnapshotVersionId: string | undefined;
|
|
2710
2941
|
if (refreshLatestAck) {
|
|
2711
2942
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
|
|
2712
|
-
|
|
2943
|
+
createChildLogger({
|
|
2944
|
+
logger: summaryNumberLogger,
|
|
2945
|
+
properties: { all: { safeSummary: true } },
|
|
2946
|
+
}),
|
|
2713
2947
|
);
|
|
2714
2948
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2715
2949
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
@@ -2718,6 +2952,50 @@ export class ContainerRuntime
|
|
|
2718
2952
|
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
2719
2953
|
}
|
|
2720
2954
|
|
|
2955
|
+
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
2956
|
+
// incorrect. So, wait for the container to be saved with a timeout. If the container is not saved
|
|
2957
|
+
// within the timeout, check if it should be failed or can continue.
|
|
2958
|
+
if (this.validateSummaryBeforeUpload && this.hasPendingMessages()) {
|
|
2959
|
+
const countBefore = this.pendingMessagesCount;
|
|
2960
|
+
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
2961
|
+
const pendingOpsTimeout =
|
|
2962
|
+
this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
|
|
2963
|
+
defaultPendingOpsWaitTimeoutMs;
|
|
2964
|
+
await new Promise<void>((resolve, reject) => {
|
|
2965
|
+
const timeoutId = setTimeout(() => resolve(), pendingOpsTimeout);
|
|
2966
|
+
this.once("saved", () => {
|
|
2967
|
+
clearTimeout(timeoutId);
|
|
2968
|
+
resolve();
|
|
2969
|
+
});
|
|
2970
|
+
this.once("dispose", () => {
|
|
2971
|
+
clearTimeout(timeoutId);
|
|
2972
|
+
reject(new Error("Runtime is disposed while summarizing"));
|
|
2973
|
+
});
|
|
2974
|
+
});
|
|
2975
|
+
|
|
2976
|
+
// Log that there are pending ops while summarizing. This will help us gather data on how often this
|
|
2977
|
+
// happens, whether we attempted to wait for these ops to be acked and what was the result.
|
|
2978
|
+
summaryNumberLogger.sendTelemetryEvent({
|
|
2979
|
+
eventName: "PendingOpsWhileSummarizing",
|
|
2980
|
+
saved: this.hasPendingMessages() ? false : true,
|
|
2981
|
+
timeout: pendingOpsTimeout,
|
|
2982
|
+
countBefore,
|
|
2983
|
+
countAfter: this.pendingMessagesCount,
|
|
2984
|
+
});
|
|
2985
|
+
|
|
2986
|
+
// There could still be pending ops. Check if summary should fail or continue.
|
|
2987
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
2988
|
+
summaryNumberLogger,
|
|
2989
|
+
this.deltaManager.lastSequenceNumber,
|
|
2990
|
+
this.deltaManager.minimumSequenceNumber,
|
|
2991
|
+
finalAttempt,
|
|
2992
|
+
true /* beforeSummaryGeneration */,
|
|
2993
|
+
);
|
|
2994
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
2995
|
+
return pendingMessagesFailResult;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2721
2999
|
const shouldPauseInboundSignal =
|
|
2722
3000
|
this.mc.config.getBoolean(
|
|
2723
3001
|
"Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause",
|
|
@@ -2788,7 +3066,7 @@ export class ContainerRuntime
|
|
|
2788
3066
|
}
|
|
2789
3067
|
|
|
2790
3068
|
const trace = Trace.start();
|
|
2791
|
-
let summarizeResult:
|
|
3069
|
+
let summarizeResult: ISummaryTreeWithStats;
|
|
2792
3070
|
// If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
|
|
2793
3071
|
// state of all the nodes.
|
|
2794
3072
|
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
@@ -2807,6 +3085,38 @@ export class ContainerRuntime
|
|
|
2807
3085
|
error,
|
|
2808
3086
|
};
|
|
2809
3087
|
}
|
|
3088
|
+
|
|
3089
|
+
// If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
|
|
3090
|
+
if (this.validateSummaryBeforeUpload) {
|
|
3091
|
+
// Validate that the summaries generated by summarize nodes is correct.
|
|
3092
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
3093
|
+
if (!validateResult.success) {
|
|
3094
|
+
const { success, ...loggingProps } = validateResult;
|
|
3095
|
+
const error = new RetriableSummaryError(
|
|
3096
|
+
validateResult.reason,
|
|
3097
|
+
validateResult.retryAfterSeconds,
|
|
3098
|
+
{ ...loggingProps },
|
|
3099
|
+
);
|
|
3100
|
+
return {
|
|
3101
|
+
stage: "base",
|
|
3102
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
3103
|
+
minimumSequenceNumber,
|
|
3104
|
+
error,
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
3109
|
+
summaryNumberLogger,
|
|
3110
|
+
summaryRefSeqNum,
|
|
3111
|
+
minimumSequenceNumber,
|
|
3112
|
+
finalAttempt,
|
|
3113
|
+
false /* beforeSummaryGeneration */,
|
|
3114
|
+
);
|
|
3115
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
3116
|
+
return pendingMessagesFailResult;
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
|
|
2810
3120
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
2811
3121
|
|
|
2812
3122
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -2843,21 +3153,6 @@ export class ContainerRuntime
|
|
|
2843
3153
|
forcedFullTree,
|
|
2844
3154
|
} as const;
|
|
2845
3155
|
|
|
2846
|
-
// If validateSummaryBeforeUpload is true, validate that the summary generated by the summarizer nodes is
|
|
2847
|
-
// correct before this summary is uploaded.
|
|
2848
|
-
if (this.validateSummaryBeforeUpload) {
|
|
2849
|
-
const validateResult = this.summarizerNode.validateSummary();
|
|
2850
|
-
if (!validateResult.success) {
|
|
2851
|
-
const { success, ...loggingProps } = validateResult;
|
|
2852
|
-
const error = new RetriableSummaryError(
|
|
2853
|
-
validateResult.reason,
|
|
2854
|
-
validateResult.retryAfterSeconds,
|
|
2855
|
-
{ ...loggingProps },
|
|
2856
|
-
);
|
|
2857
|
-
return { stage: "base", ...generateSummaryData, error };
|
|
2858
|
-
}
|
|
2859
|
-
}
|
|
2860
|
-
|
|
2861
3156
|
continueResult = checkContinue();
|
|
2862
3157
|
if (!continueResult.continue) {
|
|
2863
3158
|
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
@@ -2881,7 +3176,7 @@ export class ContainerRuntime
|
|
|
2881
3176
|
} else if (lastAck === undefined) {
|
|
2882
3177
|
summaryContext = {
|
|
2883
3178
|
proposalHandle: undefined,
|
|
2884
|
-
ackHandle: this.
|
|
3179
|
+
ackHandle: this.loadedFromVersionId,
|
|
2885
3180
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2886
3181
|
};
|
|
2887
3182
|
} else {
|
|
@@ -2960,8 +3255,78 @@ export class ContainerRuntime
|
|
|
2960
3255
|
}
|
|
2961
3256
|
}
|
|
2962
3257
|
|
|
3258
|
+
/**
|
|
3259
|
+
* This helper is called during summarization. If there are pending ops, it will return a failed summarize result
|
|
3260
|
+
* (IBaseSummarizeResult) unless this is the final summarize attempt and SkipFailingIncorrectSummary option is set.
|
|
3261
|
+
* @param logger - The logger to be used for sending telemetry.
|
|
3262
|
+
* @param referenceSequenceNumber - The reference sequence number of the summary attempt.
|
|
3263
|
+
* @param minimumSequenceNumber - The minimum sequence number of the summary attempt.
|
|
3264
|
+
* @param finalAttempt - Whether this is the final summary attempt.
|
|
3265
|
+
* @param beforeSummaryGeneration - Whether this is called before summary generation or after.
|
|
3266
|
+
* @returns failed summarize result (IBaseSummarizeResult) if summary should be failed, undefined otherwise.
|
|
3267
|
+
*/
|
|
3268
|
+
private async shouldFailSummaryOnPendingOps(
|
|
3269
|
+
logger: ITelemetryLoggerExt,
|
|
3270
|
+
referenceSequenceNumber: number,
|
|
3271
|
+
minimumSequenceNumber: number,
|
|
3272
|
+
finalAttempt: boolean,
|
|
3273
|
+
beforeSummaryGeneration: boolean,
|
|
3274
|
+
): Promise<IBaseSummarizeResult | undefined> {
|
|
3275
|
+
if (!this.hasPendingMessages()) {
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
// If "SkipFailingIncorrectSummary" option is true, don't fail the summary in the last attempt.
|
|
3280
|
+
// This is a fallback to make progress in documents where there are consistently pending ops in
|
|
3281
|
+
// the summarizer.
|
|
3282
|
+
if (
|
|
3283
|
+
finalAttempt &&
|
|
3284
|
+
this.mc.config.getBoolean("Fluid.Summarizer.SkipFailingIncorrectSummary")
|
|
3285
|
+
) {
|
|
3286
|
+
const error = DataProcessingError.create(
|
|
3287
|
+
"Pending ops during summarization",
|
|
3288
|
+
"submitSummary",
|
|
3289
|
+
undefined,
|
|
3290
|
+
{ pendingMessages: this.pendingMessagesCount },
|
|
3291
|
+
);
|
|
3292
|
+
logger.sendErrorEvent(
|
|
3293
|
+
{
|
|
3294
|
+
eventName: "SkipFailingIncorrectSummary",
|
|
3295
|
+
referenceSequenceNumber,
|
|
3296
|
+
minimumSequenceNumber,
|
|
3297
|
+
beforeGenerate: beforeSummaryGeneration,
|
|
3298
|
+
},
|
|
3299
|
+
error,
|
|
3300
|
+
);
|
|
3301
|
+
} else {
|
|
3302
|
+
// The retry delay when there are pending ops can be overridden via config so that we can adjust it
|
|
3303
|
+
// based on telemetry while we decide on a stable number.
|
|
3304
|
+
const retryDelayMs =
|
|
3305
|
+
this.mc.config.getNumber("Fluid.Summarizer.PendingOpsRetryDelayMs") ??
|
|
3306
|
+
defaultPendingOpsRetryDelayMs;
|
|
3307
|
+
const error = new RetriableSummaryError(
|
|
3308
|
+
"PendingOpsWhileSummarizing",
|
|
3309
|
+
retryDelayMs / 1000,
|
|
3310
|
+
{
|
|
3311
|
+
count: this.pendingMessagesCount,
|
|
3312
|
+
beforeGenerate: beforeSummaryGeneration,
|
|
3313
|
+
},
|
|
3314
|
+
);
|
|
3315
|
+
return {
|
|
3316
|
+
stage: "base",
|
|
3317
|
+
referenceSequenceNumber,
|
|
3318
|
+
minimumSequenceNumber,
|
|
3319
|
+
error,
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
private get pendingMessagesCount(): number {
|
|
3325
|
+
return this.pendingStateManager.pendingMessagesCount + this.outbox.messageCount;
|
|
3326
|
+
}
|
|
3327
|
+
|
|
2963
3328
|
private hasPendingMessages() {
|
|
2964
|
-
return this.
|
|
3329
|
+
return this.pendingMessagesCount !== 0;
|
|
2965
3330
|
}
|
|
2966
3331
|
|
|
2967
3332
|
private updateDocumentDirtyState(dirty: boolean) {
|
|
@@ -2982,7 +3347,6 @@ export class ContainerRuntime
|
|
|
2982
3347
|
this.dirtyContainer = dirty;
|
|
2983
3348
|
if (this.emitDirtyDocumentEvent) {
|
|
2984
3349
|
this.emit(dirty ? "dirty" : "saved");
|
|
2985
|
-
this.context.updateDirtyContainerState(dirty);
|
|
2986
3350
|
}
|
|
2987
3351
|
}
|
|
2988
3352
|
|
|
@@ -2995,7 +3359,10 @@ export class ContainerRuntime
|
|
|
2995
3359
|
address: id,
|
|
2996
3360
|
contents,
|
|
2997
3361
|
};
|
|
2998
|
-
this.submit(
|
|
3362
|
+
this.submit(
|
|
3363
|
+
{ type: ContainerMessageType.FluidDataStoreOp, contents: envelope },
|
|
3364
|
+
localOpMetadata,
|
|
3365
|
+
);
|
|
2999
3366
|
}
|
|
3000
3367
|
|
|
3001
3368
|
public submitDataStoreAliasOp(contents: any, localOpMetadata: unknown): void {
|
|
@@ -3004,12 +3371,15 @@ export class ContainerRuntime
|
|
|
3004
3371
|
throw new UsageError("malformedDataStoreAliasMessage");
|
|
3005
3372
|
}
|
|
3006
3373
|
|
|
3007
|
-
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
3374
|
+
this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
3008
3375
|
}
|
|
3009
3376
|
|
|
3010
|
-
public async uploadBlob(
|
|
3377
|
+
public async uploadBlob(
|
|
3378
|
+
blob: ArrayBufferLike,
|
|
3379
|
+
signal?: AbortSignal,
|
|
3380
|
+
): Promise<IFluidHandle<ArrayBufferLike>> {
|
|
3011
3381
|
this.verifyNotClosed();
|
|
3012
|
-
return this.blobManager.createBlob(blob);
|
|
3382
|
+
return this.blobManager.createBlob(blob, signal);
|
|
3013
3383
|
}
|
|
3014
3384
|
|
|
3015
3385
|
private maybeSubmitIdAllocationOp(type: ContainerMessageType) {
|
|
@@ -3023,7 +3393,7 @@ export class ContainerRuntime
|
|
|
3023
3393
|
);
|
|
3024
3394
|
idRange = this.idCompressor.takeNextCreationRange();
|
|
3025
3395
|
// Don't include the idRange if there weren't any Ids allocated
|
|
3026
|
-
idRange = idRange?.ids
|
|
3396
|
+
idRange = idRange?.ids !== undefined ? idRange : undefined;
|
|
3027
3397
|
}
|
|
3028
3398
|
|
|
3029
3399
|
if (idRange !== undefined) {
|
|
@@ -3047,8 +3417,7 @@ export class ContainerRuntime
|
|
|
3047
3417
|
}
|
|
3048
3418
|
|
|
3049
3419
|
private submit(
|
|
3050
|
-
|
|
3051
|
-
contents: any,
|
|
3420
|
+
containerRuntimeMessage: ContainerRuntimeMessage,
|
|
3052
3421
|
localOpMetadata: unknown = undefined,
|
|
3053
3422
|
metadata: Record<string, unknown> | undefined = undefined,
|
|
3054
3423
|
): void {
|
|
@@ -3061,17 +3430,18 @@ export class ContainerRuntime
|
|
|
3061
3430
|
0x132 /* "sending ops in detached container" */,
|
|
3062
3431
|
);
|
|
3063
3432
|
|
|
3064
|
-
const serializedContent = JSON.stringify(
|
|
3433
|
+
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
3065
3434
|
|
|
3066
3435
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
3067
3436
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
3068
3437
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
3069
|
-
this.logger.sendTelemetryEvent({
|
|
3438
|
+
this.mc.logger.sendTelemetryEvent({
|
|
3070
3439
|
eventName: "SubmitOpInReadonly",
|
|
3071
3440
|
connected: this.connected,
|
|
3072
3441
|
});
|
|
3073
3442
|
}
|
|
3074
3443
|
|
|
3444
|
+
const type = containerRuntimeMessage.type;
|
|
3075
3445
|
const message: BatchMessage = {
|
|
3076
3446
|
contents: serializedContent,
|
|
3077
3447
|
type,
|
|
@@ -3130,7 +3500,7 @@ export class ContainerRuntime
|
|
|
3130
3500
|
throw error;
|
|
3131
3501
|
}
|
|
3132
3502
|
|
|
3133
|
-
if (this.isContainerMessageDirtyable(
|
|
3503
|
+
if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
|
|
3134
3504
|
this.updateDocumentDirtyState(true);
|
|
3135
3505
|
}
|
|
3136
3506
|
}
|
|
@@ -3186,9 +3556,9 @@ export class ContainerRuntime
|
|
|
3186
3556
|
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
3187
3557
|
|
|
3188
3558
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
3189
|
-
return this.
|
|
3190
|
-
? this.
|
|
3191
|
-
: this.
|
|
3559
|
+
return this.submitSummaryFn !== undefined
|
|
3560
|
+
? this.submitSummaryFn(contents, referenceSequenceNumber)
|
|
3561
|
+
: this.submitFn(MessageType.Summarize, contents, false);
|
|
3192
3562
|
}
|
|
3193
3563
|
|
|
3194
3564
|
/**
|
|
@@ -3243,38 +3613,38 @@ export class ContainerRuntime
|
|
|
3243
3613
|
|
|
3244
3614
|
private reSubmit(message: IPendingBatchMessage) {
|
|
3245
3615
|
// Need to parse from string for back-compat
|
|
3246
|
-
const
|
|
3247
|
-
this.reSubmitCore(
|
|
3616
|
+
const containerRuntimeMessage = this.parseOpContent(message.content);
|
|
3617
|
+
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
3248
3618
|
}
|
|
3249
3619
|
|
|
3250
3620
|
/**
|
|
3251
3621
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
3252
3622
|
* reconnect and there are pending messages.
|
|
3253
|
-
* @param
|
|
3623
|
+
* @param message - The original ContainerRuntimeMessage.
|
|
3254
3624
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
3255
3625
|
*/
|
|
3256
3626
|
private reSubmitCore(
|
|
3257
|
-
|
|
3258
|
-
content: any,
|
|
3627
|
+
message: ContainerRuntimeMessage,
|
|
3259
3628
|
localOpMetadata: unknown,
|
|
3260
3629
|
opMetadata: Record<string, unknown> | undefined,
|
|
3261
3630
|
) {
|
|
3262
|
-
|
|
3631
|
+
const contents = message.contents;
|
|
3632
|
+
switch (message.type) {
|
|
3263
3633
|
case ContainerMessageType.FluidDataStoreOp:
|
|
3264
3634
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
3265
3635
|
// and trigger resubmission on it.
|
|
3266
|
-
this.dataStores.resubmitDataStoreOp(
|
|
3636
|
+
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
3267
3637
|
break;
|
|
3268
3638
|
case ContainerMessageType.Attach:
|
|
3269
3639
|
case ContainerMessageType.Alias:
|
|
3270
|
-
this.submit(
|
|
3640
|
+
this.submit(message, localOpMetadata);
|
|
3271
3641
|
break;
|
|
3272
3642
|
case ContainerMessageType.IdAllocation:
|
|
3273
3643
|
// Remove the stashedState from the op if it's a stashed op
|
|
3274
|
-
if (
|
|
3275
|
-
delete
|
|
3644
|
+
if (contents.stashedState !== undefined) {
|
|
3645
|
+
delete contents.stashedState;
|
|
3276
3646
|
}
|
|
3277
|
-
this.submit(
|
|
3647
|
+
this.submit(message, localOpMetadata);
|
|
3278
3648
|
break;
|
|
3279
3649
|
case ContainerMessageType.ChunkedOp:
|
|
3280
3650
|
throw new Error(`chunkedOp not expected here`);
|
|
@@ -3282,10 +3652,33 @@ export class ContainerRuntime
|
|
|
3282
3652
|
this.blobManager.reSubmit(opMetadata);
|
|
3283
3653
|
break;
|
|
3284
3654
|
case ContainerMessageType.Rejoin:
|
|
3285
|
-
this.submit(
|
|
3655
|
+
this.submit(message);
|
|
3286
3656
|
break;
|
|
3287
|
-
default:
|
|
3288
|
-
|
|
3657
|
+
default: {
|
|
3658
|
+
// This case should be very rare - it would imply an op was stashed from a
|
|
3659
|
+
// future version of runtime code and now is being applied on an older version
|
|
3660
|
+
const compatBehavior = message.compatDetails?.behavior;
|
|
3661
|
+
if (compatBehaviorAllowsMessageType(message.type, compatBehavior)) {
|
|
3662
|
+
this.logger.sendTelemetryEvent({
|
|
3663
|
+
eventName: "resubmitUnrecognizedMessageTypeAllowed",
|
|
3664
|
+
messageDetails: { type: message.type, compatBehavior },
|
|
3665
|
+
});
|
|
3666
|
+
} else {
|
|
3667
|
+
const error = DataProcessingError.create(
|
|
3668
|
+
"Resubmitting runtime message of unknown type",
|
|
3669
|
+
"reSubmitCore",
|
|
3670
|
+
undefined /* sequencedMessage */,
|
|
3671
|
+
{
|
|
3672
|
+
messageDetails: JSON.stringify({
|
|
3673
|
+
type: message.type,
|
|
3674
|
+
compatBehavior,
|
|
3675
|
+
}),
|
|
3676
|
+
},
|
|
3677
|
+
);
|
|
3678
|
+
this.closeFn(error);
|
|
3679
|
+
throw error;
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3289
3682
|
}
|
|
3290
3683
|
}
|
|
3291
3684
|
|
|
@@ -3299,6 +3692,7 @@ export class ContainerRuntime
|
|
|
3299
3692
|
this.dataStores.rollbackDataStoreOp(contents as IEnvelope, localOpMetadata);
|
|
3300
3693
|
break;
|
|
3301
3694
|
default:
|
|
3695
|
+
// Don't check message.compatDetails because this is for rolling back a local op so the type will be known
|
|
3302
3696
|
throw new Error(`Can't rollback ${type}`);
|
|
3303
3697
|
}
|
|
3304
3698
|
}
|
|
@@ -3326,12 +3720,22 @@ export class ContainerRuntime
|
|
|
3326
3720
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
3327
3721
|
public async refreshLatestSummaryAck(options: IRefreshSummaryAckOptions) {
|
|
3328
3722
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
3723
|
+
// proposalHandle is always passed from RunningSummarizer.
|
|
3724
|
+
assert(proposalHandle !== undefined, 0x766 /* proposalHandle should be available */);
|
|
3329
3725
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3726
|
+
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3727
|
+
proposalHandle,
|
|
3728
|
+
summaryRefSeq,
|
|
3729
|
+
);
|
|
3730
|
+
|
|
3731
|
+
/**
|
|
3732
|
+
* When refreshing a summary ack, this check indicates a new ack of a summary that is newer than the
|
|
3733
|
+
* current summary that is tracked, but this summarizer runtime did not produce/track that summary. Thus
|
|
3734
|
+
* it needs to refresh its state. Today refresh is done by fetching the latest snapshot to update the cache
|
|
3735
|
+
* and then close as the current main client is likely to be re-elected as the parent summarizer again.
|
|
3736
|
+
*/
|
|
3737
|
+
if (!result.isSummaryTracked && result.isSummaryNewer) {
|
|
3738
|
+
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
3335
3739
|
summaryLogger,
|
|
3336
3740
|
{
|
|
3337
3741
|
eventName: "RefreshLatestSummaryAckFetch",
|
|
@@ -3339,27 +3743,9 @@ export class ContainerRuntime
|
|
|
3339
3743
|
targetSequenceNumber: summaryRefSeq,
|
|
3340
3744
|
},
|
|
3341
3745
|
readAndParseBlob,
|
|
3746
|
+
null,
|
|
3342
3747
|
);
|
|
3343
3748
|
|
|
3344
|
-
/**
|
|
3345
|
-
* back-compat - Older loaders and drivers (pre 2.0.0-internal.1.4) don't have fetchSource as a param in the
|
|
3346
|
-
* getVersions API. So, they will not fetch the latest snapshot from network in the previous fetch call. For
|
|
3347
|
-
* these scenarios, fetch the snapshot corresponding to the ack handle to have the same behavior before the
|
|
3348
|
-
* change that started fetching latest snapshot always.
|
|
3349
|
-
*/
|
|
3350
|
-
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
3351
|
-
fetchResult = await this.fetchSnapshotFromStorage(
|
|
3352
|
-
summaryLogger,
|
|
3353
|
-
{
|
|
3354
|
-
eventName: "RefreshLatestSummaryAckFetchBackCompat",
|
|
3355
|
-
ackHandle,
|
|
3356
|
-
targetSequenceNumber: summaryRefSeq,
|
|
3357
|
-
},
|
|
3358
|
-
readAndParseBlob,
|
|
3359
|
-
ackHandle,
|
|
3360
|
-
);
|
|
3361
|
-
}
|
|
3362
|
-
|
|
3363
3749
|
/**
|
|
3364
3750
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
3365
3751
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -3381,33 +3767,16 @@ export class ContainerRuntime
|
|
|
3381
3767
|
fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
3382
3768
|
},
|
|
3383
3769
|
);
|
|
3384
|
-
this.
|
|
3770
|
+
this.disposeFn(error);
|
|
3385
3771
|
throw error;
|
|
3386
3772
|
}
|
|
3387
3773
|
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
fetchResult.latestSnapshotRefSeq,
|
|
3392
|
-
summaryLogger,
|
|
3393
|
-
);
|
|
3394
|
-
|
|
3395
|
-
return {
|
|
3396
|
-
snapshotTree: fetchResult.snapshotTree,
|
|
3397
|
-
snapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
3398
|
-
};
|
|
3399
|
-
};
|
|
3400
|
-
|
|
3401
|
-
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3402
|
-
proposalHandle,
|
|
3403
|
-
summaryRefSeq,
|
|
3404
|
-
fetchLatestSnapshot,
|
|
3405
|
-
readAndParseBlob,
|
|
3406
|
-
summaryLogger,
|
|
3407
|
-
);
|
|
3774
|
+
await this.closeStaleSummarizer("RefreshLatestSummaryAckFetch");
|
|
3775
|
+
return;
|
|
3776
|
+
}
|
|
3408
3777
|
|
|
3409
3778
|
// Notify the garbage collector so it can update its latest summary state.
|
|
3410
|
-
await this.garbageCollector.refreshLatestSummary(
|
|
3779
|
+
await this.garbageCollector.refreshLatestSummary(result);
|
|
3411
3780
|
}
|
|
3412
3781
|
|
|
3413
3782
|
/**
|
|
@@ -3420,51 +3789,49 @@ export class ContainerRuntime
|
|
|
3420
3789
|
summaryLogger: ITelemetryLoggerExt,
|
|
3421
3790
|
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined }> {
|
|
3422
3791
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3423
|
-
const {
|
|
3424
|
-
await this.fetchLatestSnapshotFromStorage(
|
|
3425
|
-
summaryLogger,
|
|
3426
|
-
{
|
|
3427
|
-
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
3428
|
-
},
|
|
3429
|
-
readAndParseBlob,
|
|
3430
|
-
);
|
|
3431
|
-
const fetchLatestSnapshot: IFetchSnapshotResult = {
|
|
3432
|
-
snapshotTree,
|
|
3433
|
-
snapshotRefSeq: latestSnapshotRefSeq,
|
|
3434
|
-
};
|
|
3435
|
-
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3436
|
-
undefined /* proposalHandle */,
|
|
3437
|
-
latestSnapshotRefSeq,
|
|
3438
|
-
async () => fetchLatestSnapshot,
|
|
3439
|
-
readAndParseBlob,
|
|
3792
|
+
const { versionId, latestSnapshotRefSeq } = await this.fetchSnapshotFromStorage(
|
|
3440
3793
|
summaryLogger,
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
await this.garbageCollector.refreshLatestSummary(
|
|
3445
|
-
undefined /* proposalHandle */,
|
|
3446
|
-
result,
|
|
3794
|
+
{
|
|
3795
|
+
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
3796
|
+
},
|
|
3447
3797
|
readAndParseBlob,
|
|
3798
|
+
null,
|
|
3448
3799
|
);
|
|
3449
3800
|
|
|
3801
|
+
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
3802
|
+
|
|
3450
3803
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
3451
3804
|
}
|
|
3452
3805
|
|
|
3453
|
-
private async
|
|
3454
|
-
logger
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3806
|
+
private async closeStaleSummarizer(codePath: string): Promise<void> {
|
|
3807
|
+
this.mc.logger.sendTelemetryEvent(
|
|
3808
|
+
{
|
|
3809
|
+
eventName: "ClosingSummarizerOnSummaryStale",
|
|
3810
|
+
codePath,
|
|
3811
|
+
message: "Stopping fetch from storage",
|
|
3812
|
+
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
3813
|
+
},
|
|
3814
|
+
new GenericError("Restarting summarizer instead of refreshing"),
|
|
3815
|
+
);
|
|
3816
|
+
|
|
3817
|
+
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
3818
|
+
await delay(this.closeSummarizerDelayMs);
|
|
3819
|
+
this._summarizer?.stop("latestSummaryStateStale");
|
|
3820
|
+
this.disposeFn();
|
|
3459
3821
|
}
|
|
3460
3822
|
|
|
3823
|
+
/**
|
|
3824
|
+
* Downloads snapshot from storage with the given versionId or latest if versionId is null.
|
|
3825
|
+
* By default, it also closes the container after downloading the snapshot. However, this may be
|
|
3826
|
+
* overridden via options.
|
|
3827
|
+
*/
|
|
3461
3828
|
private async fetchSnapshotFromStorage(
|
|
3462
3829
|
logger: ITelemetryLoggerExt,
|
|
3463
3830
|
event: ITelemetryGenericEvent,
|
|
3464
3831
|
readAndParseBlob: ReadAndParseBlob,
|
|
3465
3832
|
versionId: string | null,
|
|
3466
3833
|
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
|
|
3467
|
-
|
|
3834
|
+
return PerformanceEvent.timedExecAsync(
|
|
3468
3835
|
logger,
|
|
3469
3836
|
event,
|
|
3470
3837
|
async (perfEvent: {
|
|
@@ -3510,77 +3877,75 @@ export class ContainerRuntime
|
|
|
3510
3877
|
};
|
|
3511
3878
|
},
|
|
3512
3879
|
);
|
|
3513
|
-
|
|
3514
|
-
// We choose to close the summarizer after the snapshot cache is updated to avoid
|
|
3515
|
-
// situations which the main client (which is likely to be re-elected as the leader again)
|
|
3516
|
-
// loads the summarizer from cache.
|
|
3517
|
-
if (this.summaryStateUpdateMethod === "restart") {
|
|
3518
|
-
const error = new GenericError("Restarting summarizer instead of refreshing");
|
|
3519
|
-
|
|
3520
|
-
this.mc.logger.sendTelemetryEvent(
|
|
3521
|
-
{
|
|
3522
|
-
...event,
|
|
3523
|
-
eventName: "ClosingSummarizerOnSummaryStale",
|
|
3524
|
-
codePath: event.eventName,
|
|
3525
|
-
message: "Stopping fetch from storage",
|
|
3526
|
-
versionId: versionId != null ? versionId : undefined,
|
|
3527
|
-
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
3528
|
-
},
|
|
3529
|
-
error,
|
|
3530
|
-
);
|
|
3531
|
-
|
|
3532
|
-
// Delay 10 seconds before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
3533
|
-
await delay(this.closeSummarizerDelayMs);
|
|
3534
|
-
this._summarizer?.stop("latestSummaryStateStale");
|
|
3535
|
-
this.closeFn();
|
|
3536
|
-
throw error;
|
|
3537
|
-
}
|
|
3538
|
-
|
|
3539
|
-
return snapshotResults;
|
|
3540
3880
|
}
|
|
3541
3881
|
|
|
3542
3882
|
public notifyAttaching() {} // do nothing (deprecated method)
|
|
3543
3883
|
|
|
3544
|
-
public getPendingLocalState(
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3884
|
+
public async getPendingLocalState(props?: {
|
|
3885
|
+
notifyImminentClosure: boolean;
|
|
3886
|
+
}): Promise<unknown> {
|
|
3887
|
+
return PerformanceEvent.timedExecAsync(
|
|
3888
|
+
this.mc.logger,
|
|
3889
|
+
{
|
|
3890
|
+
eventName: "getPendingLocalState",
|
|
3891
|
+
notifyImminentClosure: props?.notifyImminentClosure,
|
|
3892
|
+
},
|
|
3893
|
+
async (event) => {
|
|
3894
|
+
this.verifyNotClosed();
|
|
3895
|
+
const waitBlobsToAttach = props?.notifyImminentClosure;
|
|
3896
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
3897
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
3898
|
+
}
|
|
3899
|
+
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(
|
|
3900
|
+
waitBlobsToAttach,
|
|
3901
|
+
);
|
|
3902
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
3903
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
3904
|
+
return; // no pending state to save
|
|
3905
|
+
}
|
|
3906
|
+
// Flush pending batch.
|
|
3907
|
+
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
3908
|
+
// to close current batch.
|
|
3909
|
+
this.flush();
|
|
3552
3910
|
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3911
|
+
const pendingState: IPendingRuntimeState = {
|
|
3912
|
+
pending,
|
|
3913
|
+
pendingAttachmentBlobs,
|
|
3914
|
+
};
|
|
3915
|
+
event.end({
|
|
3916
|
+
attachmentBlobsSize: Object.keys(pendingAttachmentBlobs ?? {}).length,
|
|
3917
|
+
pendingOpsSize: pending?.pendingStates.length,
|
|
3918
|
+
});
|
|
3919
|
+
return pendingState;
|
|
3920
|
+
},
|
|
3921
|
+
);
|
|
3557
3922
|
}
|
|
3558
3923
|
|
|
3559
|
-
public
|
|
3560
|
-
if (this.
|
|
3561
|
-
return this.summarizer.summarizeOnDemand(
|
|
3924
|
+
public summarizeOnDemand(options: IOnDemandSummarizeOptions): ISummarizeResults {
|
|
3925
|
+
if (this.isSummarizerClient) {
|
|
3926
|
+
return this.summarizer.summarizeOnDemand(options);
|
|
3562
3927
|
} else if (this.summaryManager !== undefined) {
|
|
3563
|
-
return this.summaryManager.summarizeOnDemand(
|
|
3928
|
+
return this.summaryManager.summarizeOnDemand(options);
|
|
3564
3929
|
} else {
|
|
3565
3930
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
3566
3931
|
// disableSummaries is turned on. We are throwing instead of returning a failure here,
|
|
3567
3932
|
// because it is a misuse of the API rather than an expected failure.
|
|
3568
3933
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
3569
3934
|
}
|
|
3570
|
-
}
|
|
3935
|
+
}
|
|
3571
3936
|
|
|
3572
|
-
public
|
|
3573
|
-
if (this.
|
|
3574
|
-
return this.summarizer.enqueueSummarize(
|
|
3937
|
+
public enqueueSummarize(options: IEnqueueSummarizeOptions): EnqueueSummarizeResult {
|
|
3938
|
+
if (this.isSummarizerClient) {
|
|
3939
|
+
return this.summarizer.enqueueSummarize(options);
|
|
3575
3940
|
} else if (this.summaryManager !== undefined) {
|
|
3576
|
-
return this.summaryManager.enqueueSummarize(
|
|
3941
|
+
return this.summaryManager.enqueueSummarize(options);
|
|
3577
3942
|
} else {
|
|
3578
3943
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
3579
3944
|
// generateSummaries is turned off. We are throwing instead of returning a failure here,
|
|
3580
3945
|
// because it is a misuse of the API rather than an expected failure.
|
|
3581
3946
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
3582
3947
|
}
|
|
3583
|
-
}
|
|
3948
|
+
}
|
|
3584
3949
|
|
|
3585
3950
|
/**
|
|
3586
3951
|
* * Forms a function that will request a Summarizer.
|