@fluidframework/container-runtime 2.0.0-dev.5.2.0.169897 → 2.0.0-dev.6.4.0.191258
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 +147 -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 +15 -18
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +212 -171
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +33 -17
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +172 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +722 -425
- 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 +4 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +87 -90
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +10 -10
- 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 +23 -7
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +125 -82
- 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 +65 -56
- 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 -5
- 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 +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/metadata.d.ts +18 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +7 -0
- package/dist/metadata.js.map +1 -0
- package/dist/opLifecycle/batchManager.d.ts +2 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +15 -7
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +11 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.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 +30 -21
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +19 -13
- 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 +24 -19
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +39 -6
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +138 -61
- 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 +22 -8
- 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 +25 -10
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +101 -64
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +43 -33
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/index.d.ts +4 -4
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +3 -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 +40 -23
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +144 -149
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +25 -29
- 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 +21 -16
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +74 -123
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +44 -24
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -2
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +16 -13
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +4 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +8 -5
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +21 -6
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +117 -54
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +8 -7
- 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 +15 -18
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +187 -146
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +23 -7
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +172 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +678 -380
- 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 +4 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +49 -52
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +3 -3
- 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 +23 -7
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +107 -64
- 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 -52
- 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 -5
- 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 +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/metadata.d.ts +18 -0
- package/lib/metadata.d.ts.map +1 -0
- package/lib/metadata.js +6 -0
- package/lib/metadata.js.map +1 -0
- package/lib/opLifecycle/batchManager.d.ts +2 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +15 -7
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +11 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +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 +22 -13
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +17 -11
- 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 +15 -10
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +39 -6
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +132 -56
- 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 +23 -9
- 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 +25 -10
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +90 -53
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +25 -15
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/index.d.ts +4 -4
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +2 -2
- 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 +40 -23
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +132 -137
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +25 -29
- 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 +21 -16
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +70 -119
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +44 -24
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -2
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +9 -6
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +4 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +7 -4
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +21 -6
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +109 -47
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +8 -7
- 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 +30 -32
- package/src/batchTracker.ts +7 -5
- package/src/blobManager.ts +235 -172
- package/src/connectionTelemetry.ts +19 -5
- package/src/containerRuntime.ts +853 -431
- package/src/dataStore.ts +12 -4
- package/src/dataStoreContext.ts +49 -46
- package/src/dataStoreContexts.ts +4 -4
- package/src/dataStoreRegistry.ts +1 -1
- package/src/dataStores.ts +119 -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 -51
- package/src/gc/gcConfigs.ts +4 -2
- package/src/gc/gcDefinitions.ts +17 -21
- 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 +15 -13
- 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 +9 -2
- package/src/metadata.ts +19 -0
- package/src/opLifecycle/README.md +20 -0
- package/src/opLifecycle/batchManager.ts +9 -1
- package/src/opLifecycle/definitions.ts +11 -0
- package/src/opLifecycle/index.ts +1 -1
- package/src/opLifecycle/opCompressor.ts +6 -5
- package/src/opLifecycle/opDecompressor.ts +47 -17
- package/src/opLifecycle/opGroupingManager.ts +18 -8
- package/src/opLifecycle/opSplitter.ts +10 -7
- package/src/opLifecycle/outbox.ts +177 -72
- package/src/opLifecycle/remoteMessageProcessor.ts +32 -9
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +123 -78
- package/src/scheduleManager.ts +22 -11
- package/src/summary/index.ts +7 -4
- 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 +2 -2
- package/src/summary/summarizerNode/summarizerNode.ts +142 -184
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +27 -35
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +72 -148
- package/src/summary/summarizerTypes.ts +49 -24
- package/src/summary/summaryCollection.ts +9 -4
- package/src/summary/summaryFormat.ts +9 -2
- package/src/summary/summaryGenerator.ts +72 -49
- package/src/summary/summaryManager.ts +44 -16
- 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 -30
- package/dist/id-compressor/uuidUtilities.d.ts.map +0 -1
- package/dist/id-compressor/uuidUtilities.js +0 -106
- 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 -30
- package/lib/id-compressor/uuidUtilities.d.ts.map +0 -1
- package/lib/id-compressor/uuidUtilities.js +0 -98
- 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 -123
package/src/containerRuntime.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import { ITelemetryBaseLogger, ITelemetryGenericEvent } from "@fluidframework/common-definitions";
|
|
6
5
|
import {
|
|
6
|
+
ITelemetryBaseLogger,
|
|
7
|
+
ITelemetryGenericEvent,
|
|
7
8
|
FluidObject,
|
|
8
9
|
IFluidHandle,
|
|
9
10
|
IFluidHandleContext,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
} from "@fluidframework/core-interfaces";
|
|
14
15
|
import {
|
|
15
16
|
IAudience,
|
|
17
|
+
IBatchMessage,
|
|
16
18
|
IContainerContext,
|
|
17
19
|
IDeltaManager,
|
|
18
20
|
IRuntime,
|
|
@@ -25,23 +27,22 @@ import {
|
|
|
25
27
|
IContainerRuntime,
|
|
26
28
|
IContainerRuntimeEvents,
|
|
27
29
|
} from "@fluidframework/container-runtime-definitions";
|
|
30
|
+
import { assert, delay, LazyPromise } from "@fluidframework/core-utils";
|
|
31
|
+
import { Trace, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
28
32
|
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
unreachableCase,
|
|
35
|
-
} from "@fluidframework/common-utils";
|
|
36
|
-
import {
|
|
37
|
-
ChildLogger,
|
|
33
|
+
createChildLogger,
|
|
34
|
+
createChildMonitoringContext,
|
|
35
|
+
DataCorruptionError,
|
|
36
|
+
DataProcessingError,
|
|
37
|
+
GenericError,
|
|
38
38
|
raiseConnectedEvent,
|
|
39
39
|
PerformanceEvent,
|
|
40
|
+
// eslint-disable-next-line import/no-deprecated
|
|
40
41
|
TaggedLoggerAdapter,
|
|
41
42
|
MonitoringContext,
|
|
42
|
-
loggerToMonitoringContext,
|
|
43
43
|
wrapError,
|
|
44
44
|
ITelemetryLoggerExt,
|
|
45
|
+
UsageError,
|
|
45
46
|
} from "@fluidframework/telemetry-utils";
|
|
46
47
|
import {
|
|
47
48
|
DriverHeader,
|
|
@@ -50,12 +51,6 @@ import {
|
|
|
50
51
|
ISummaryContext,
|
|
51
52
|
} from "@fluidframework/driver-definitions";
|
|
52
53
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
53
|
-
import {
|
|
54
|
-
DataCorruptionError,
|
|
55
|
-
DataProcessingError,
|
|
56
|
-
GenericError,
|
|
57
|
-
UsageError,
|
|
58
|
-
} from "@fluidframework/container-utils";
|
|
59
54
|
import {
|
|
60
55
|
IClientDetails,
|
|
61
56
|
IDocumentMessage,
|
|
@@ -113,7 +108,11 @@ import { v4 as uuid } from "uuid";
|
|
|
113
108
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
114
109
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
115
110
|
import { ReportOpPerfTelemetry, IPerfSignalReport } from "./connectionTelemetry";
|
|
116
|
-
import {
|
|
111
|
+
import {
|
|
112
|
+
IPendingBatchMessage,
|
|
113
|
+
IPendingLocalState,
|
|
114
|
+
PendingStateManager,
|
|
115
|
+
} from "./pendingStateManager";
|
|
117
116
|
import { pkgVersion } from "./packageVersion";
|
|
118
117
|
import { BlobManager, IBlobManagerLoadInfo, IPendingBlobs } from "./blobManager";
|
|
119
118
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
@@ -127,7 +126,6 @@ import {
|
|
|
127
126
|
IContainerRuntimeMetadata,
|
|
128
127
|
ICreateContainerMetadata,
|
|
129
128
|
idCompressorBlobName,
|
|
130
|
-
IFetchSnapshotResult,
|
|
131
129
|
IRootSummarizerNodeWithGC,
|
|
132
130
|
ISummaryMetadataMessage,
|
|
133
131
|
metadataBlobName,
|
|
@@ -149,6 +147,14 @@ import {
|
|
|
149
147
|
ISummarizerRuntime,
|
|
150
148
|
IRefreshSummaryAckOptions,
|
|
151
149
|
RunWhileConnectedCoordinator,
|
|
150
|
+
IGenerateSummaryTreeResult,
|
|
151
|
+
RetriableSummaryError,
|
|
152
|
+
IOnDemandSummarizeOptions,
|
|
153
|
+
ISummarizeResults,
|
|
154
|
+
IEnqueueSummarizeOptions,
|
|
155
|
+
EnqueueSummarizeResult,
|
|
156
|
+
ISummarizerEvents,
|
|
157
|
+
IBaseSummarizeResult,
|
|
152
158
|
} from "./summary";
|
|
153
159
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
154
160
|
import {
|
|
@@ -166,6 +172,7 @@ import { BindBatchTracker } from "./batchTracker";
|
|
|
166
172
|
import { ScheduleManager } from "./scheduleManager";
|
|
167
173
|
import {
|
|
168
174
|
BatchMessage,
|
|
175
|
+
IBatch,
|
|
169
176
|
IBatchCheckpoint,
|
|
170
177
|
OpCompressor,
|
|
171
178
|
OpDecompressor,
|
|
@@ -173,8 +180,10 @@ import {
|
|
|
173
180
|
OpSplitter,
|
|
174
181
|
RemoteMessageProcessor,
|
|
175
182
|
OpGroupingManager,
|
|
183
|
+
getLongStack,
|
|
176
184
|
} from "./opLifecycle";
|
|
177
185
|
import { DeltaManagerSummarizerProxy } from "./deltaManagerSummarizerProxy";
|
|
186
|
+
import { IBatchMetadata } from "./metadata";
|
|
178
187
|
|
|
179
188
|
export enum ContainerMessageType {
|
|
180
189
|
// An op to be delivered to store
|
|
@@ -203,11 +212,64 @@ export enum ContainerMessageType {
|
|
|
203
212
|
IdAllocation = "idAllocation",
|
|
204
213
|
}
|
|
205
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
|
+
*/
|
|
206
258
|
export interface ContainerRuntimeMessage {
|
|
207
|
-
|
|
259
|
+
/** Type of the op, within the ContainerRuntime's domain */
|
|
208
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;
|
|
209
265
|
}
|
|
210
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
|
+
|
|
211
273
|
export interface ISummaryBaseConfiguration {
|
|
212
274
|
/**
|
|
213
275
|
* Delay before first attempt to spawn summarizing container.
|
|
@@ -434,14 +496,6 @@ export interface IContainerRuntimeOptions {
|
|
|
434
496
|
readonly enableGroupedBatching?: boolean;
|
|
435
497
|
}
|
|
436
498
|
|
|
437
|
-
/**
|
|
438
|
-
* The summary tree returned by the root node. It adds state relevant to the root of the tree.
|
|
439
|
-
*/
|
|
440
|
-
export interface IRootSummaryTreeWithStats extends ISummaryTreeWithStats {
|
|
441
|
-
/** The garbage collection stats if GC ran, undefined otherwise. */
|
|
442
|
-
gcStats?: IGCStats;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
499
|
/**
|
|
446
500
|
* Accepted header keys for requests coming to the runtime.
|
|
447
501
|
*/
|
|
@@ -454,9 +508,13 @@ export enum RuntimeHeaders {
|
|
|
454
508
|
|
|
455
509
|
/** True if a tombstoned object should be returned without erroring */
|
|
456
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
|
|
457
513
|
|
|
458
514
|
/** Tombstone error responses will have this header set to true */
|
|
459
515
|
export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
516
|
+
/** Inactive error responses will have this header set to true */
|
|
517
|
+
export const InactiveResponseHeaderKey = "isInactive";
|
|
460
518
|
|
|
461
519
|
/**
|
|
462
520
|
* The full set of parsed header data that may be found on Runtime requests
|
|
@@ -497,7 +555,7 @@ interface OldContainerContextWithLogger extends Omit<IContainerContext, "taggedL
|
|
|
497
555
|
* instantiated runtime in a new instance of the container, so it can load to the
|
|
498
556
|
* same state
|
|
499
557
|
*/
|
|
500
|
-
interface IPendingRuntimeState {
|
|
558
|
+
export interface IPendingRuntimeState {
|
|
501
559
|
/**
|
|
502
560
|
* Pending ops from PendingStateManager
|
|
503
561
|
*/
|
|
@@ -526,12 +584,17 @@ const defaultCompressionConfig = {
|
|
|
526
584
|
|
|
527
585
|
const defaultChunkSizeInBytes = 204800;
|
|
528
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
|
+
|
|
529
592
|
/**
|
|
530
593
|
* Instead of refreshing from latest because we do not have 100% confidence in the state
|
|
531
594
|
* of the current system, we should close the summarizer and let it recover.
|
|
532
595
|
* This delay's goal is to prevent tight restart loops
|
|
533
596
|
*/
|
|
534
|
-
const defaultCloseSummarizerDelayMs =
|
|
597
|
+
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
535
598
|
|
|
536
599
|
/**
|
|
537
600
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
@@ -573,14 +636,41 @@ export function getDeviceSpec() {
|
|
|
573
636
|
return {};
|
|
574
637
|
}
|
|
575
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
|
+
|
|
576
663
|
/**
|
|
577
664
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
578
665
|
* It will define the store level mappings.
|
|
579
666
|
*/
|
|
580
667
|
export class ContainerRuntime
|
|
581
|
-
extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
668
|
+
extends TypedEventEmitter<IContainerRuntimeEvents & ISummarizerEvents>
|
|
582
669
|
implements IContainerRuntime, IRuntime, ISummarizerRuntime, ISummarizerInternalsProvider
|
|
583
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
|
+
*/
|
|
584
674
|
public get IFluidRouter() {
|
|
585
675
|
return this;
|
|
586
676
|
}
|
|
@@ -639,10 +729,10 @@ export class ContainerRuntime
|
|
|
639
729
|
context: IContainerContext;
|
|
640
730
|
registryEntries: NamedFluidDataStoreRegistryEntries;
|
|
641
731
|
existing: boolean;
|
|
642
|
-
requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
|
|
643
732
|
runtimeOptions?: IContainerRuntimeOptions;
|
|
644
733
|
containerScope?: FluidObject;
|
|
645
734
|
containerRuntimeCtor?: typeof ContainerRuntime;
|
|
735
|
+
requestHandler?: (request: IRequest, runtime: IContainerRuntime) => Promise<IResponse>;
|
|
646
736
|
initializeEntryPoint?: (containerRuntime: IContainerRuntime) => Promise<FluidObject>;
|
|
647
737
|
}): Promise<ContainerRuntime> {
|
|
648
738
|
const {
|
|
@@ -653,18 +743,32 @@ export class ContainerRuntime
|
|
|
653
743
|
runtimeOptions = {},
|
|
654
744
|
containerScope = {},
|
|
655
745
|
containerRuntimeCtor = ContainerRuntime,
|
|
656
|
-
initializeEntryPoint,
|
|
657
746
|
} = params;
|
|
658
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
|
+
|
|
659
759
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
660
760
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
661
761
|
const backCompatContext: IContainerContext | OldContainerContextWithLogger = context;
|
|
662
762
|
const passLogger =
|
|
663
763
|
backCompatContext.taggedLogger ??
|
|
764
|
+
// eslint-disable-next-line import/no-deprecated
|
|
664
765
|
new TaggedLoggerAdapter((backCompatContext as OldContainerContextWithLogger).logger);
|
|
665
|
-
const logger =
|
|
666
|
-
|
|
667
|
-
|
|
766
|
+
const logger = createChildLogger({
|
|
767
|
+
logger: passLogger,
|
|
768
|
+
properties: {
|
|
769
|
+
all: {
|
|
770
|
+
runtimeVersion: pkgVersion,
|
|
771
|
+
},
|
|
668
772
|
},
|
|
669
773
|
});
|
|
670
774
|
|
|
@@ -705,8 +809,6 @@ export class ContainerRuntime
|
|
|
705
809
|
tryFetchBlob<SerializedIdCompressorWithNoSession>(idCompressorBlobName),
|
|
706
810
|
]);
|
|
707
811
|
|
|
708
|
-
const loadExisting = existing === true || context.existing === true;
|
|
709
|
-
|
|
710
812
|
// read snapshot blobs needed for BlobManager to load
|
|
711
813
|
const blobManagerSnapshot = await BlobManager.load(
|
|
712
814
|
context.baseSnapshot?.trees[blobsTreeName],
|
|
@@ -741,9 +843,7 @@ export class ContainerRuntime
|
|
|
741
843
|
if (loadSequenceNumberVerification === "log") {
|
|
742
844
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
743
845
|
} else {
|
|
744
|
-
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
745
846
|
context.closeFn(error);
|
|
746
|
-
context.disposeFn?.(error);
|
|
747
847
|
}
|
|
748
848
|
}
|
|
749
849
|
}
|
|
@@ -756,7 +856,7 @@ export class ContainerRuntime
|
|
|
756
856
|
idCompressor =
|
|
757
857
|
serializedIdCompressor !== undefined
|
|
758
858
|
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
759
|
-
:
|
|
859
|
+
: IdCompressor.create(logger);
|
|
760
860
|
}
|
|
761
861
|
|
|
762
862
|
const runtime = new containerRuntimeCtor(
|
|
@@ -780,7 +880,7 @@ export class ContainerRuntime
|
|
|
780
880
|
},
|
|
781
881
|
containerScope,
|
|
782
882
|
logger,
|
|
783
|
-
|
|
883
|
+
existing,
|
|
784
884
|
blobManagerSnapshot,
|
|
785
885
|
context.storage,
|
|
786
886
|
idCompressor,
|
|
@@ -789,6 +889,7 @@ export class ContainerRuntime
|
|
|
789
889
|
initializeEntryPoint,
|
|
790
890
|
);
|
|
791
891
|
|
|
892
|
+
await runtime.blobManager.processStashedChanges();
|
|
792
893
|
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
793
894
|
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
794
895
|
await runtime.pendingStateManager.applyStashedOpsAt(0);
|
|
@@ -799,44 +900,49 @@ export class ContainerRuntime
|
|
|
799
900
|
return runtime;
|
|
800
901
|
}
|
|
801
902
|
|
|
802
|
-
public
|
|
803
|
-
return this.context.options;
|
|
804
|
-
}
|
|
903
|
+
public readonly options: ILoaderOptions;
|
|
805
904
|
|
|
905
|
+
private readonly _getClientId: () => string | undefined;
|
|
806
906
|
public get clientId(): string | undefined {
|
|
807
|
-
return this.
|
|
907
|
+
return this._getClientId();
|
|
808
908
|
}
|
|
809
909
|
|
|
810
|
-
public
|
|
811
|
-
return this.context.clientDetails;
|
|
812
|
-
}
|
|
910
|
+
public readonly clientDetails: IClientDetails;
|
|
813
911
|
|
|
814
912
|
public get storage(): IDocumentStorageService {
|
|
815
913
|
return this._storage;
|
|
816
914
|
}
|
|
817
915
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
|
826
925
|
}
|
|
827
926
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
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;
|
|
840
946
|
|
|
841
947
|
public get flushMode(): FlushMode {
|
|
842
948
|
return this._flushMode;
|
|
@@ -850,8 +956,9 @@ export class ContainerRuntime
|
|
|
850
956
|
return this.registry;
|
|
851
957
|
}
|
|
852
958
|
|
|
959
|
+
private readonly _getAttachState: () => AttachState;
|
|
853
960
|
public get attachState(): AttachState {
|
|
854
|
-
return this.
|
|
961
|
+
return this._getAttachState();
|
|
855
962
|
}
|
|
856
963
|
|
|
857
964
|
public idCompressor: (IIdCompressor & IIdCompressorCore) | undefined;
|
|
@@ -950,11 +1057,16 @@ export class ContainerRuntime
|
|
|
950
1057
|
private emitDirtyDocumentEvent = true;
|
|
951
1058
|
private readonly enableOpReentryCheck: boolean;
|
|
952
1059
|
private readonly disableAttachReorder: boolean | undefined;
|
|
953
|
-
private readonly summaryStateUpdateMethod: string | undefined;
|
|
954
1060
|
private readonly closeSummarizerDelayMs: number;
|
|
1061
|
+
/**
|
|
1062
|
+
* If true, summary generated is validate before uploading it to the server. With single commit summaries,
|
|
1063
|
+
* summaries will be accepted once uploaded, so they should be validated before upload. However, this can
|
|
1064
|
+
* currently be controlled via a feature flag as its a new functionality.
|
|
1065
|
+
*/
|
|
1066
|
+
private readonly validateSummaryBeforeUpload: boolean;
|
|
955
1067
|
|
|
956
1068
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
957
|
-
private _perfSignalData: IPerfSignalReport = {
|
|
1069
|
+
private readonly _perfSignalData: IPerfSignalReport = {
|
|
958
1070
|
signalsLost: 0,
|
|
959
1071
|
signalSequenceNumber: 0,
|
|
960
1072
|
signalTimestamp: 0,
|
|
@@ -1036,11 +1148,21 @@ export class ContainerRuntime
|
|
|
1036
1148
|
*/
|
|
1037
1149
|
private readonly idCompressorEnabled: boolean;
|
|
1038
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
|
+
|
|
1039
1161
|
/**
|
|
1040
1162
|
* @internal
|
|
1041
1163
|
*/
|
|
1042
1164
|
protected constructor(
|
|
1043
|
-
|
|
1165
|
+
context: IContainerContext,
|
|
1044
1166
|
private readonly registry: IFluidDataStoreRegistry,
|
|
1045
1167
|
metadata: IContainerRuntimeMetadata | undefined,
|
|
1046
1168
|
electedSummarizerData: ISerializedElection | undefined,
|
|
@@ -1067,10 +1189,64 @@ export class ContainerRuntime
|
|
|
1067
1189
|
) {
|
|
1068
1190
|
super();
|
|
1069
1191
|
|
|
1070
|
-
|
|
1071
|
-
|
|
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;
|
|
1072
1245
|
|
|
1073
|
-
this.mc =
|
|
1246
|
+
this.mc = createChildMonitoringContext({
|
|
1247
|
+
logger: this.logger,
|
|
1248
|
+
namespace: "ContainerRuntime",
|
|
1249
|
+
});
|
|
1074
1250
|
|
|
1075
1251
|
let loadSummaryNumber: number;
|
|
1076
1252
|
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
@@ -1102,7 +1278,9 @@ export class ContainerRuntime
|
|
|
1102
1278
|
|
|
1103
1279
|
this.messageAtLastSummary = metadata?.message;
|
|
1104
1280
|
|
|
1105
|
-
|
|
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;
|
|
1106
1284
|
|
|
1107
1285
|
this.gcTombstoneEnforcementAllowed = shouldAllowGcTombstoneEnforcement(
|
|
1108
1286
|
metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */,
|
|
@@ -1131,7 +1309,7 @@ export class ContainerRuntime
|
|
|
1131
1309
|
|
|
1132
1310
|
const opSplitter = new OpSplitter(
|
|
1133
1311
|
chunks,
|
|
1134
|
-
this.
|
|
1312
|
+
this.submitBatchFn,
|
|
1135
1313
|
disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes,
|
|
1136
1314
|
runtimeOptions.maxBatchSizeInBytes,
|
|
1137
1315
|
this.mc.logger,
|
|
@@ -1172,7 +1350,7 @@ export class ContainerRuntime
|
|
|
1172
1350
|
|
|
1173
1351
|
if (
|
|
1174
1352
|
runtimeOptions.flushMode === (FlushModeExperimental.Async as unknown as FlushMode) &&
|
|
1175
|
-
|
|
1353
|
+
supportedFeatures?.get("referenceSequenceNumbers") !== true
|
|
1176
1354
|
) {
|
|
1177
1355
|
// The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
|
|
1178
1356
|
this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
|
|
@@ -1181,7 +1359,7 @@ export class ContainerRuntime
|
|
|
1181
1359
|
this._flushMode = runtimeOptions.flushMode;
|
|
1182
1360
|
}
|
|
1183
1361
|
|
|
1184
|
-
const pendingRuntimeState =
|
|
1362
|
+
const pendingRuntimeState = pendingLocalState as IPendingRuntimeState | undefined;
|
|
1185
1363
|
|
|
1186
1364
|
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
1187
1365
|
if (
|
|
@@ -1197,16 +1375,15 @@ export class ContainerRuntime
|
|
|
1197
1375
|
this.garbageCollector = GarbageCollector.create({
|
|
1198
1376
|
runtime: this,
|
|
1199
1377
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
1200
|
-
baseSnapshot
|
|
1378
|
+
baseSnapshot,
|
|
1201
1379
|
baseLogger: this.mc.logger,
|
|
1202
1380
|
existing,
|
|
1203
1381
|
metadata,
|
|
1204
1382
|
createContainerMetadata: this.createContainerMetadata,
|
|
1205
|
-
isSummarizerClient: this.
|
|
1383
|
+
isSummarizerClient: this.isSummarizerClient,
|
|
1206
1384
|
getNodePackagePath: async (nodePath: string) => this.getGCNodePackagePath(nodePath),
|
|
1207
1385
|
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
1208
1386
|
readAndParseBlob: async <T>(id: string) => readAndParse<T>(this.storage, id),
|
|
1209
|
-
getContainerDiagnosticId: () => this.context.id,
|
|
1210
1387
|
// GC runs in summarizer client and needs access to the real (non-proxy) active information. The proxy
|
|
1211
1388
|
// delta manager would always return false for summarizer client.
|
|
1212
1389
|
activeConnection: () => this.innerDeltaManager.active,
|
|
@@ -1214,14 +1391,14 @@ export class ContainerRuntime
|
|
|
1214
1391
|
|
|
1215
1392
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
1216
1393
|
this.summarizerNode = createRootSummarizerNodeWithGC(
|
|
1217
|
-
|
|
1394
|
+
createChildLogger({ logger: this.logger, namespace: "SummarizerNode" }),
|
|
1218
1395
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
1219
1396
|
async (fullTree: boolean, trackState: boolean, telemetryContext?: ITelemetryContext) =>
|
|
1220
1397
|
this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
1221
1398
|
// Latest change sequence number, no changes since summary applied yet
|
|
1222
1399
|
loadedFromSequenceNumber,
|
|
1223
1400
|
// Summary reference sequence number, undefined if no summary yet
|
|
1224
|
-
|
|
1401
|
+
baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined,
|
|
1225
1402
|
{
|
|
1226
1403
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
1227
1404
|
// a summary with an older protocol state.
|
|
@@ -1238,14 +1415,14 @@ export class ContainerRuntime
|
|
|
1238
1415
|
async () => this.garbageCollector.getBaseGCDetails(),
|
|
1239
1416
|
);
|
|
1240
1417
|
|
|
1241
|
-
if (
|
|
1242
|
-
this.summarizerNode.updateBaseSummaryState(
|
|
1418
|
+
if (baseSnapshot) {
|
|
1419
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
1243
1420
|
}
|
|
1244
1421
|
|
|
1245
1422
|
this.dataStores = new DataStores(
|
|
1246
|
-
getSummaryForDatastores(
|
|
1423
|
+
getSummaryForDatastores(baseSnapshot, metadata),
|
|
1247
1424
|
this,
|
|
1248
|
-
(attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg),
|
|
1425
|
+
(attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }),
|
|
1249
1426
|
(id: string, createParam: CreateChildSummarizerNodeParam) =>
|
|
1250
1427
|
(
|
|
1251
1428
|
summarizeInternal: SummarizeInternalFn,
|
|
@@ -1272,10 +1449,14 @@ export class ContainerRuntime
|
|
|
1272
1449
|
() => this.storage,
|
|
1273
1450
|
(localId: string, blobId?: string) => {
|
|
1274
1451
|
if (!this.disposed) {
|
|
1275
|
-
this.submit(
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1452
|
+
this.submit(
|
|
1453
|
+
{ type: ContainerMessageType.BlobAttach, contents: undefined },
|
|
1454
|
+
undefined,
|
|
1455
|
+
{
|
|
1456
|
+
localId,
|
|
1457
|
+
blobId,
|
|
1458
|
+
},
|
|
1459
|
+
);
|
|
1279
1460
|
}
|
|
1280
1461
|
},
|
|
1281
1462
|
(blobPath: string) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"),
|
|
@@ -1286,10 +1467,10 @@ export class ContainerRuntime
|
|
|
1286
1467
|
);
|
|
1287
1468
|
|
|
1288
1469
|
this.scheduleManager = new ScheduleManager(
|
|
1289
|
-
|
|
1470
|
+
this.innerDeltaManager,
|
|
1290
1471
|
this,
|
|
1291
1472
|
() => this.clientId,
|
|
1292
|
-
|
|
1473
|
+
createChildLogger({ logger: this.logger, namespace: "ScheduleManager" }),
|
|
1293
1474
|
);
|
|
1294
1475
|
|
|
1295
1476
|
this.pendingStateManager = new PendingStateManager(
|
|
@@ -1299,9 +1480,11 @@ export class ContainerRuntime
|
|
|
1299
1480
|
close: this.closeFn,
|
|
1300
1481
|
connected: () => this.connected,
|
|
1301
1482
|
reSubmit: this.reSubmit.bind(this),
|
|
1302
|
-
|
|
1483
|
+
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
1484
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
1303
1485
|
},
|
|
1304
1486
|
pendingRuntimeState?.pending,
|
|
1487
|
+
this.logger,
|
|
1305
1488
|
);
|
|
1306
1489
|
|
|
1307
1490
|
const disableCompression = this.mc.config.getBoolean(
|
|
@@ -1318,16 +1501,21 @@ export class ContainerRuntime
|
|
|
1318
1501
|
const disablePartialFlush = this.mc.config.getBoolean(
|
|
1319
1502
|
"Fluid.ContainerRuntime.DisablePartialFlush",
|
|
1320
1503
|
);
|
|
1504
|
+
|
|
1505
|
+
const legacySendBatchFn = makeLegacySendBatchFn(this.submitFn, this.innerDeltaManager);
|
|
1506
|
+
|
|
1321
1507
|
this.outbox = new Outbox({
|
|
1322
1508
|
shouldSend: () => this.canSendOps(),
|
|
1323
1509
|
pendingStateManager: this.pendingStateManager,
|
|
1324
|
-
|
|
1510
|
+
submitBatchFn: this.submitBatchFn,
|
|
1511
|
+
legacySendBatchFn,
|
|
1325
1512
|
compressor: new OpCompressor(this.mc.logger),
|
|
1326
1513
|
splitter: opSplitter,
|
|
1327
1514
|
config: {
|
|
1328
1515
|
compressionOptions,
|
|
1329
1516
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1330
1517
|
disablePartialFlush: disablePartialFlush === true,
|
|
1518
|
+
enableGroupedBatching: this.groupedBatchingEnabled,
|
|
1331
1519
|
},
|
|
1332
1520
|
logger: this.mc.logger,
|
|
1333
1521
|
groupingManager: opGroupingManager,
|
|
@@ -1335,40 +1523,48 @@ export class ContainerRuntime
|
|
|
1335
1523
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1336
1524
|
clientSequenceNumber: this._processedClientSequenceNumber,
|
|
1337
1525
|
}),
|
|
1526
|
+
reSubmit: this.reSubmit.bind(this),
|
|
1527
|
+
opReentrancy: () => this.ensureNoDataModelChangesCalls > 0,
|
|
1528
|
+
closeContainer: this.closeFn,
|
|
1338
1529
|
});
|
|
1339
1530
|
|
|
1340
|
-
this.
|
|
1531
|
+
this._quorum = quorum;
|
|
1532
|
+
this._quorum.on("removeMember", (clientId: string) => {
|
|
1341
1533
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
1342
1534
|
});
|
|
1343
1535
|
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1536
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1537
|
+
this._audience = audience!;
|
|
1538
|
+
|
|
1347
1539
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
1348
1540
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
1349
1541
|
);
|
|
1350
1542
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
1543
|
+
this.validateSummaryBeforeUpload =
|
|
1544
|
+
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
1351
1545
|
|
|
1352
1546
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1353
1547
|
|
|
1354
1548
|
this.dirtyContainer =
|
|
1355
|
-
this.
|
|
1356
|
-
|
|
1357
|
-
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
1549
|
+
this.attachState !== AttachState.Attached || this.hasPendingMessages();
|
|
1550
|
+
context.updateDirtyContainerState(this.dirtyContainer);
|
|
1358
1551
|
|
|
1359
1552
|
if (this.summariesDisabled) {
|
|
1360
1553
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
1361
1554
|
} else {
|
|
1362
|
-
const orderedClientLogger =
|
|
1555
|
+
const orderedClientLogger = createChildLogger({
|
|
1556
|
+
logger: this.logger,
|
|
1557
|
+
namespace: "OrderedClientElection",
|
|
1558
|
+
});
|
|
1363
1559
|
const orderedClientCollection = new OrderedClientCollection(
|
|
1364
1560
|
orderedClientLogger,
|
|
1365
|
-
this.
|
|
1366
|
-
this.
|
|
1561
|
+
this.innerDeltaManager,
|
|
1562
|
+
this._quorum,
|
|
1367
1563
|
);
|
|
1368
1564
|
const orderedClientElectionForSummarizer = new OrderedClientElection(
|
|
1369
1565
|
orderedClientLogger,
|
|
1370
1566
|
orderedClientCollection,
|
|
1371
|
-
electedSummarizerData ?? this.
|
|
1567
|
+
electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber,
|
|
1372
1568
|
SummarizerClientElection.isClientEligible,
|
|
1373
1569
|
);
|
|
1374
1570
|
|
|
@@ -1379,7 +1575,7 @@ export class ContainerRuntime
|
|
|
1379
1575
|
this.maxOpsSinceLastSummary,
|
|
1380
1576
|
);
|
|
1381
1577
|
|
|
1382
|
-
if (this.
|
|
1578
|
+
if (this.isSummarizerClient) {
|
|
1383
1579
|
this._summarizer = new Summarizer(
|
|
1384
1580
|
this /* ISummarizerRuntime */,
|
|
1385
1581
|
() => this.summaryConfiguration,
|
|
@@ -1394,19 +1590,19 @@ export class ContainerRuntime
|
|
|
1394
1590
|
() => this.innerDeltaManager.active,
|
|
1395
1591
|
),
|
|
1396
1592
|
);
|
|
1397
|
-
} else if (
|
|
1398
|
-
SummarizerClientElection.clientDetailsPermitElection(this.context.clientDetails)
|
|
1399
|
-
) {
|
|
1593
|
+
} else if (SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
|
|
1400
1594
|
// Only create a SummaryManager and SummarizerClientElection
|
|
1401
1595
|
// if summaries are enabled and we are not the summarizer client.
|
|
1402
1596
|
const defaultAction = () => {
|
|
1403
1597
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
1404
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1598
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
1405
1599
|
// unregister default to no log on every op after falling behind
|
|
1406
1600
|
// and register summary ack handler to re-register this handler
|
|
1407
1601
|
// after successful summary
|
|
1408
1602
|
this.summaryCollection.once(MessageType.SummaryAck, () => {
|
|
1409
|
-
this.logger.sendTelemetryEvent({
|
|
1603
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1604
|
+
eventName: "SummaryStatus:CaughtUp",
|
|
1605
|
+
});
|
|
1410
1606
|
// we've caught up, so re-register the default action to monitor for
|
|
1411
1607
|
// falling behind, and unregister ourself
|
|
1412
1608
|
this.summaryCollection.on("default", defaultAction);
|
|
@@ -1423,7 +1619,7 @@ export class ContainerRuntime
|
|
|
1423
1619
|
this, // IConnectedState
|
|
1424
1620
|
this.summaryCollection,
|
|
1425
1621
|
this.logger,
|
|
1426
|
-
this.formRequestSummarizerFn(
|
|
1622
|
+
this.formRequestSummarizerFn(loader),
|
|
1427
1623
|
new Throttler(
|
|
1428
1624
|
60 * 1000, // 60 sec delay window
|
|
1429
1625
|
30 * 1000, // 30 sec max delay
|
|
@@ -1435,6 +1631,9 @@ export class ContainerRuntime
|
|
|
1435
1631
|
},
|
|
1436
1632
|
this.heuristicsDisabled,
|
|
1437
1633
|
);
|
|
1634
|
+
this.summaryManager.on("summarize", (eventProps) => {
|
|
1635
|
+
this.emit("summarize", eventProps);
|
|
1636
|
+
});
|
|
1438
1637
|
this.summaryManager.start();
|
|
1439
1638
|
}
|
|
1440
1639
|
}
|
|
@@ -1474,7 +1673,7 @@ export class ContainerRuntime
|
|
|
1474
1673
|
...getDeviceSpec(),
|
|
1475
1674
|
});
|
|
1476
1675
|
|
|
1477
|
-
this.logger.sendTelemetryEvent({
|
|
1676
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1478
1677
|
eventName: "ContainerLoadStats",
|
|
1479
1678
|
...this.createContainerMetadata,
|
|
1480
1679
|
...this.dataStores.containerLoadStats,
|
|
@@ -1490,18 +1689,17 @@ export class ContainerRuntime
|
|
|
1490
1689
|
disableAttachReorder: this.disableAttachReorder,
|
|
1491
1690
|
disablePartialFlush,
|
|
1492
1691
|
idCompressorEnabled: this.idCompressorEnabled,
|
|
1493
|
-
summaryStateUpdateMethod: this.summaryStateUpdateMethod,
|
|
1494
1692
|
closeSummarizerDelayOverride,
|
|
1495
1693
|
}),
|
|
1496
1694
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1497
1695
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1498
1696
|
});
|
|
1499
1697
|
|
|
1500
|
-
ReportOpPerfTelemetry(this.
|
|
1698
|
+
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this.logger);
|
|
1501
1699
|
BindBatchTracker(this, this.logger);
|
|
1502
1700
|
|
|
1503
1701
|
this.entryPoint = new LazyPromise(async () => {
|
|
1504
|
-
if (this.
|
|
1702
|
+
if (this.isSummarizerClient) {
|
|
1505
1703
|
assert(
|
|
1506
1704
|
this._summarizer !== undefined,
|
|
1507
1705
|
0x5bf /* Summarizer object is undefined in a summarizer client */,
|
|
@@ -1525,7 +1723,7 @@ export class ContainerRuntime
|
|
|
1525
1723
|
}
|
|
1526
1724
|
this._disposed = true;
|
|
1527
1725
|
|
|
1528
|
-
this.logger.sendTelemetryEvent(
|
|
1726
|
+
this.mc.logger.sendTelemetryEvent(
|
|
1529
1727
|
{
|
|
1530
1728
|
eventName: "ContainerRuntimeDisposed",
|
|
1531
1729
|
isDirty: this.isDirty,
|
|
@@ -1549,6 +1747,7 @@ export class ContainerRuntime
|
|
|
1549
1747
|
/**
|
|
1550
1748
|
* Notifies this object about the request made to the container.
|
|
1551
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
|
|
1552
1751
|
*/
|
|
1553
1752
|
public async request(request: IRequest): Promise<IResponse> {
|
|
1554
1753
|
try {
|
|
@@ -1606,7 +1805,7 @@ export class ContainerRuntime
|
|
|
1606
1805
|
subRequest.url.startsWith("/"),
|
|
1607
1806
|
0x126 /* "Expected createSubRequest url to include a leading slash" */,
|
|
1608
1807
|
);
|
|
1609
|
-
return dataStore.
|
|
1808
|
+
return dataStore.request(subRequest);
|
|
1610
1809
|
}
|
|
1611
1810
|
|
|
1612
1811
|
return create404Response(request);
|
|
@@ -1654,6 +1853,7 @@ export class ContainerRuntime
|
|
|
1654
1853
|
dataStoreContext.packagePath,
|
|
1655
1854
|
request?.headers,
|
|
1656
1855
|
);
|
|
1856
|
+
|
|
1657
1857
|
return dataStoreChannel;
|
|
1658
1858
|
}
|
|
1659
1859
|
|
|
@@ -1748,7 +1948,7 @@ export class ContainerRuntime
|
|
|
1748
1948
|
this.mc.logger.sendTelemetryEvent({
|
|
1749
1949
|
eventName: "ReconnectsWithNoProgress",
|
|
1750
1950
|
attempts: this.consecutiveReconnects,
|
|
1751
|
-
pendingMessages: this.
|
|
1951
|
+
pendingMessages: this.pendingMessagesCount,
|
|
1752
1952
|
});
|
|
1753
1953
|
}
|
|
1754
1954
|
|
|
@@ -1815,19 +2015,17 @@ export class ContainerRuntime
|
|
|
1815
2015
|
* Parse an op's type and actual content from given serialized content
|
|
1816
2016
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
1817
2017
|
*/
|
|
1818
|
-
private parseOpContent(serializedContent?: string): {
|
|
1819
|
-
type: ContainerMessageType;
|
|
1820
|
-
contents: unknown;
|
|
1821
|
-
} {
|
|
2018
|
+
private parseOpContent(serializedContent?: string): ContainerRuntimeMessage {
|
|
1822
2019
|
assert(serializedContent !== undefined, 0x6d5 /* content must be defined */);
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
2020
|
+
const { type, contents, compatDetails }: ContainerRuntimeMessage =
|
|
2021
|
+
JSON.parse(serializedContent);
|
|
2022
|
+
assert(type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
2023
|
+
return { type, contents, compatDetails };
|
|
1826
2024
|
}
|
|
1827
2025
|
|
|
1828
2026
|
private async applyStashedOp(op: string): Promise<unknown> {
|
|
1829
2027
|
// Need to parse from string for back-compat
|
|
1830
|
-
const { type, contents } = this.parseOpContent(op);
|
|
2028
|
+
const { type, contents, compatDetails } = this.parseOpContent(op);
|
|
1831
2029
|
switch (type) {
|
|
1832
2030
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1833
2031
|
return this.dataStores.applyStashedOp(contents as IEnvelope);
|
|
@@ -1846,8 +2044,27 @@ export class ContainerRuntime
|
|
|
1846
2044
|
throw new Error("chunkedOp not expected here");
|
|
1847
2045
|
case ContainerMessageType.Rejoin:
|
|
1848
2046
|
throw new Error("rejoin not expected here");
|
|
1849
|
-
default:
|
|
1850
|
-
|
|
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
|
+
}
|
|
1851
2068
|
}
|
|
1852
2069
|
}
|
|
1853
2070
|
|
|
@@ -1861,32 +2078,6 @@ export class ContainerRuntime
|
|
|
1861
2078
|
return;
|
|
1862
2079
|
}
|
|
1863
2080
|
|
|
1864
|
-
// If attachment blobs were added while disconnected, we need to delay
|
|
1865
|
-
// propagation of the "connected" event until we have uploaded them to
|
|
1866
|
-
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1867
|
-
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
1868
|
-
const connecting =
|
|
1869
|
-
connected && !this._connected && !this.innerDeltaManager.readOnlyInfo.readonly;
|
|
1870
|
-
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
1871
|
-
assert(
|
|
1872
|
-
!this.delayConnectClientId,
|
|
1873
|
-
0x392 /* Connect event delay must be canceled before subsequent connect event */,
|
|
1874
|
-
);
|
|
1875
|
-
assert(!!clientId, 0x393 /* Must have clientId when connecting */);
|
|
1876
|
-
this.delayConnectClientId = clientId;
|
|
1877
|
-
this.blobManager.onConnected().then(
|
|
1878
|
-
() => {
|
|
1879
|
-
// make sure we didn't reconnect before the promise resolved
|
|
1880
|
-
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1881
|
-
this.delayConnectClientId = undefined;
|
|
1882
|
-
this.setConnectionStateCore(connected, clientId);
|
|
1883
|
-
}
|
|
1884
|
-
},
|
|
1885
|
-
(error) => this.closeFn(error),
|
|
1886
|
-
);
|
|
1887
|
-
return;
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
2081
|
this.setConnectionStateCore(connected, clientId);
|
|
1891
2082
|
}
|
|
1892
2083
|
|
|
@@ -1900,6 +2091,14 @@ export class ContainerRuntime
|
|
|
1900
2091
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1901
2092
|
const changeOfState = this._connected !== connected;
|
|
1902
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
|
+
|
|
1903
2102
|
this._connected = connected;
|
|
1904
2103
|
|
|
1905
2104
|
if (!connected) {
|
|
@@ -1926,7 +2125,7 @@ export class ContainerRuntime
|
|
|
1926
2125
|
{
|
|
1927
2126
|
dataLoss: 1,
|
|
1928
2127
|
attempts: this.consecutiveReconnects,
|
|
1929
|
-
pendingMessages: this.
|
|
2128
|
+
pendingMessages: this.pendingMessagesCount,
|
|
1930
2129
|
},
|
|
1931
2130
|
),
|
|
1932
2131
|
);
|
|
@@ -1951,24 +2150,30 @@ export class ContainerRuntime
|
|
|
1951
2150
|
public process(messageArg: ISequencedDocumentMessage, local: boolean) {
|
|
1952
2151
|
this.verifyNotClosed();
|
|
1953
2152
|
|
|
1954
|
-
// Whether or not the message
|
|
2153
|
+
// Whether or not the message appears to be a runtime message from an up-to-date client.
|
|
1955
2154
|
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
1956
2155
|
// or something different, like a system message.
|
|
1957
|
-
const
|
|
2156
|
+
const modernRuntimeMessage = messageArg.type === MessageType.Operation;
|
|
1958
2157
|
|
|
1959
2158
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1960
2159
|
const messageCopy = { ...messageArg };
|
|
1961
2160
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1962
|
-
this.processCore(message, local,
|
|
2161
|
+
this.processCore(message, local, modernRuntimeMessage);
|
|
1963
2162
|
}
|
|
1964
2163
|
}
|
|
1965
2164
|
|
|
1966
2165
|
private _processedClientSequenceNumber: number | undefined;
|
|
1967
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
|
+
*/
|
|
1968
2173
|
private processCore(
|
|
1969
|
-
message: ISequencedDocumentMessage,
|
|
2174
|
+
message: ISequencedDocumentMessage | SequencedContainerRuntimeMessage,
|
|
1970
2175
|
local: boolean,
|
|
1971
|
-
|
|
2176
|
+
modernRuntimeMessage: boolean,
|
|
1972
2177
|
) {
|
|
1973
2178
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1974
2179
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
@@ -1979,8 +2184,10 @@ export class ContainerRuntime
|
|
|
1979
2184
|
|
|
1980
2185
|
try {
|
|
1981
2186
|
let localOpMetadata: unknown;
|
|
1982
|
-
if (local &&
|
|
1983
|
-
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2187
|
+
if (local && modernRuntimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
2188
|
+
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(
|
|
2189
|
+
message as SequencedContainerRuntimeMessage,
|
|
2190
|
+
);
|
|
1984
2191
|
}
|
|
1985
2192
|
|
|
1986
2193
|
// If there are no more pending messages after processing a local message,
|
|
@@ -1989,53 +2196,14 @@ export class ContainerRuntime
|
|
|
1989
2196
|
this.updateDocumentDirtyState(false);
|
|
1990
2197
|
}
|
|
1991
2198
|
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
this.processAliasMessage(message, localOpMetadata, local);
|
|
1999
|
-
break;
|
|
2000
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
2001
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
2002
|
-
break;
|
|
2003
|
-
case ContainerMessageType.BlobAttach:
|
|
2004
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
2005
|
-
break;
|
|
2006
|
-
case ContainerMessageType.IdAllocation:
|
|
2007
|
-
assert(
|
|
2008
|
-
this.idCompressor !== undefined,
|
|
2009
|
-
0x67c /* IdCompressor should be defined if enabled */,
|
|
2010
|
-
);
|
|
2011
|
-
this.idCompressor.finalizeCreationRange(message.contents as IdCreationRange);
|
|
2012
|
-
break;
|
|
2013
|
-
case ContainerMessageType.ChunkedOp:
|
|
2014
|
-
case ContainerMessageType.Rejoin:
|
|
2015
|
-
break;
|
|
2016
|
-
default:
|
|
2017
|
-
if (runtimeMessage) {
|
|
2018
|
-
const error = DataProcessingError.create(
|
|
2019
|
-
// Former assert 0x3ce
|
|
2020
|
-
"Runtime message of unknown type",
|
|
2021
|
-
"OpProcessing",
|
|
2022
|
-
message,
|
|
2023
|
-
{
|
|
2024
|
-
local,
|
|
2025
|
-
type: message.type,
|
|
2026
|
-
contentType: typeof message.contents,
|
|
2027
|
-
batch: message.metadata?.batch,
|
|
2028
|
-
compression: message.compression,
|
|
2029
|
-
},
|
|
2030
|
-
);
|
|
2031
|
-
this.closeFn(error);
|
|
2032
|
-
throw error;
|
|
2033
|
-
}
|
|
2034
|
-
}
|
|
2199
|
+
this.validateAndProcessRuntimeMessage(
|
|
2200
|
+
message,
|
|
2201
|
+
localOpMetadata,
|
|
2202
|
+
local,
|
|
2203
|
+
modernRuntimeMessage,
|
|
2204
|
+
);
|
|
2035
2205
|
|
|
2036
|
-
|
|
2037
|
-
this.emit("op", message, runtimeMessage);
|
|
2038
|
-
}
|
|
2206
|
+
this.emit("op", message, modernRuntimeMessage);
|
|
2039
2207
|
|
|
2040
2208
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
2041
2209
|
|
|
@@ -2050,13 +2218,74 @@ export class ContainerRuntime
|
|
|
2050
2218
|
throw e;
|
|
2051
2219
|
}
|
|
2052
2220
|
}
|
|
2053
|
-
|
|
2054
|
-
|
|
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(
|
|
2055
2227
|
message: ISequencedDocumentMessage,
|
|
2056
2228
|
localOpMetadata: unknown,
|
|
2057
2229
|
local: boolean,
|
|
2058
|
-
|
|
2059
|
-
|
|
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
|
+
}
|
|
2060
2289
|
}
|
|
2061
2290
|
|
|
2062
2291
|
/**
|
|
@@ -2065,7 +2294,7 @@ export class ContainerRuntime
|
|
|
2065
2294
|
*/
|
|
2066
2295
|
private sendSignalTelemetryEvent(clientSignalSequenceNumber: number) {
|
|
2067
2296
|
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
2068
|
-
this.logger.sendPerformanceEvent({
|
|
2297
|
+
this.mc.logger.sendPerformanceEvent({
|
|
2069
2298
|
eventName: "SignalLatency",
|
|
2070
2299
|
duration,
|
|
2071
2300
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -2093,7 +2322,7 @@ export class ContainerRuntime
|
|
|
2093
2322
|
) {
|
|
2094
2323
|
this._perfSignalData.signalsLost++;
|
|
2095
2324
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
2096
|
-
this.logger.sendErrorEvent({
|
|
2325
|
+
this.mc.logger.sendErrorEvent({
|
|
2097
2326
|
eventName: "SignalLost",
|
|
2098
2327
|
type: envelope.contents.type,
|
|
2099
2328
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -2121,6 +2350,12 @@ export class ContainerRuntime
|
|
|
2121
2350
|
this.dataStores.processSignal(envelope.address, transformed, local);
|
|
2122
2351
|
}
|
|
2123
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
|
+
*/
|
|
2124
2359
|
public async getRootDataStore(id: string, wait = true): Promise<IFluidRouter> {
|
|
2125
2360
|
return this.getRootDataStoreChannel(id, wait);
|
|
2126
2361
|
}
|
|
@@ -2196,15 +2431,30 @@ export class ContainerRuntime
|
|
|
2196
2431
|
return result;
|
|
2197
2432
|
}
|
|
2198
2433
|
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
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;
|
|
2208
2458
|
}
|
|
2209
2459
|
|
|
2210
2460
|
public createDetachedRootDataStore(
|
|
@@ -2221,25 +2471,37 @@ export class ContainerRuntime
|
|
|
2221
2471
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
2222
2472
|
}
|
|
2223
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
|
+
*/
|
|
2224
2491
|
public async _createDataStoreWithProps(
|
|
2225
2492
|
pkg: string | string[],
|
|
2226
2493
|
props?: any,
|
|
2227
2494
|
id = uuid(),
|
|
2228
2495
|
): Promise<IDataStore> {
|
|
2229
|
-
|
|
2230
|
-
.
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
props?: any,
|
|
2239
|
-
): Promise<IFluidDataStoreChannel> {
|
|
2240
|
-
return this.dataStores
|
|
2241
|
-
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
2242
|
-
.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
|
+
);
|
|
2243
2505
|
}
|
|
2244
2506
|
|
|
2245
2507
|
private canSendOps() {
|
|
@@ -2255,13 +2517,14 @@ export class ContainerRuntime
|
|
|
2255
2517
|
return this.flushMode !== FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
2256
2518
|
}
|
|
2257
2519
|
|
|
2520
|
+
private readonly _quorum: IQuorumClients;
|
|
2258
2521
|
public getQuorum(): IQuorumClients {
|
|
2259
|
-
return this.
|
|
2522
|
+
return this._quorum;
|
|
2260
2523
|
}
|
|
2261
2524
|
|
|
2525
|
+
private readonly _audience: IAudience;
|
|
2262
2526
|
public getAudience(): IAudience {
|
|
2263
|
-
|
|
2264
|
-
return this.context.audience!;
|
|
2527
|
+
return this._audience;
|
|
2265
2528
|
}
|
|
2266
2529
|
|
|
2267
2530
|
/**
|
|
@@ -2272,7 +2535,7 @@ export class ContainerRuntime
|
|
|
2272
2535
|
return this.dirtyContainer;
|
|
2273
2536
|
}
|
|
2274
2537
|
|
|
2275
|
-
private isContainerMessageDirtyable(type
|
|
2538
|
+
private isContainerMessageDirtyable({ type, contents }: ContainerRuntimeMessage) {
|
|
2276
2539
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
2277
2540
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
2278
2541
|
if (type === ContainerMessageType.Attach) {
|
|
@@ -2321,12 +2584,12 @@ export class ContainerRuntime
|
|
|
2321
2584
|
public submitSignal(type: string, content: any) {
|
|
2322
2585
|
this.verifyNotClosed();
|
|
2323
2586
|
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
2324
|
-
return this.
|
|
2587
|
+
return this.submitSignalFn(envelope);
|
|
2325
2588
|
}
|
|
2326
2589
|
|
|
2327
2590
|
public submitDataStoreSignal(address: string, type: string, content: any) {
|
|
2328
2591
|
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
2329
|
-
return this.
|
|
2592
|
+
return this.submitSignalFn(envelope);
|
|
2330
2593
|
}
|
|
2331
2594
|
|
|
2332
2595
|
public setAttachState(attachState: AttachState.Attaching | AttachState.Attached): void {
|
|
@@ -2378,15 +2641,7 @@ export class ContainerRuntime
|
|
|
2378
2641
|
return summarizeResult.summary;
|
|
2379
2642
|
}
|
|
2380
2643
|
|
|
2381
|
-
public
|
|
2382
|
-
if (this.context.getAbsoluteUrl === undefined) {
|
|
2383
|
-
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
2384
|
-
}
|
|
2385
|
-
if (this.attachState !== AttachState.Attached) {
|
|
2386
|
-
return undefined;
|
|
2387
|
-
}
|
|
2388
|
-
return this.context.getAbsoluteUrl(relativeUrl);
|
|
2389
|
-
}
|
|
2644
|
+
public readonly getAbsoluteUrl: (relativeUrl: string) => Promise<string | undefined>;
|
|
2390
2645
|
|
|
2391
2646
|
private async summarizeInternal(
|
|
2392
2647
|
fullTree: boolean,
|
|
@@ -2427,7 +2682,7 @@ export class ContainerRuntime
|
|
|
2427
2682
|
fullGC?: boolean;
|
|
2428
2683
|
/** True to run GC sweep phase after the mark phase */
|
|
2429
2684
|
runSweep?: boolean;
|
|
2430
|
-
}): Promise<
|
|
2685
|
+
}): Promise<ISummaryTreeWithStats> {
|
|
2431
2686
|
this.verifyNotClosed();
|
|
2432
2687
|
|
|
2433
2688
|
const {
|
|
@@ -2450,9 +2705,8 @@ export class ContainerRuntime
|
|
|
2450
2705
|
});
|
|
2451
2706
|
|
|
2452
2707
|
try {
|
|
2453
|
-
let gcStats: IGCStats | undefined;
|
|
2454
2708
|
if (runGC) {
|
|
2455
|
-
|
|
2709
|
+
await this.collectGarbage(
|
|
2456
2710
|
{ logger: summaryLogger, runSweep, fullGC },
|
|
2457
2711
|
telemetryContext,
|
|
2458
2712
|
);
|
|
@@ -2469,9 +2723,9 @@ export class ContainerRuntime
|
|
|
2469
2723
|
0x12f /* "Container Runtime's summarize should always return a tree" */,
|
|
2470
2724
|
);
|
|
2471
2725
|
|
|
2472
|
-
return { stats, summary
|
|
2726
|
+
return { stats, summary };
|
|
2473
2727
|
} finally {
|
|
2474
|
-
this.logger.sendTelemetryEvent({
|
|
2728
|
+
this.mc.logger.sendTelemetryEvent({
|
|
2475
2729
|
eventName: "SummarizeTelemetry",
|
|
2476
2730
|
details: telemetryContext.serialize(),
|
|
2477
2731
|
});
|
|
@@ -2543,7 +2797,7 @@ export class ContainerRuntime
|
|
|
2543
2797
|
/**
|
|
2544
2798
|
* After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
|
|
2545
2799
|
* @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
|
|
2546
|
-
* @returns
|
|
2800
|
+
* @returns The routes of nodes that were deleted.
|
|
2547
2801
|
*/
|
|
2548
2802
|
public deleteSweepReadyNodes(sweepReadyRoutes: string[]): string[] {
|
|
2549
2803
|
const { dataStoreRoutes, blobManagerRoutes } =
|
|
@@ -2614,7 +2868,7 @@ export class ContainerRuntime
|
|
|
2614
2868
|
/**
|
|
2615
2869
|
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
2616
2870
|
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
2617
|
-
* @returns
|
|
2871
|
+
* @returns Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
2618
2872
|
* for data stores.
|
|
2619
2873
|
*/
|
|
2620
2874
|
private getDataStoreAndBlobManagerRoutes(routes: string[]) {
|
|
@@ -2670,12 +2924,15 @@ export class ContainerRuntime
|
|
|
2670
2924
|
* @param options - options controlling how the summary is generated or submitted
|
|
2671
2925
|
*/
|
|
2672
2926
|
public async submitSummary(options: ISubmitSummaryOptions): Promise<SubmitSummaryResult> {
|
|
2673
|
-
const { fullTree = false, refreshLatestAck, summaryLogger } = options;
|
|
2927
|
+
const { fullTree = false, finalAttempt = false, refreshLatestAck, summaryLogger } = options;
|
|
2674
2928
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
2675
2929
|
// use it for all events logged during this summary.
|
|
2676
2930
|
const summaryNumber = this.nextSummaryNumber;
|
|
2677
|
-
const summaryNumberLogger =
|
|
2678
|
-
|
|
2931
|
+
const summaryNumberLogger = createChildLogger({
|
|
2932
|
+
logger: summaryLogger,
|
|
2933
|
+
properties: {
|
|
2934
|
+
all: { summaryNumber },
|
|
2935
|
+
},
|
|
2679
2936
|
});
|
|
2680
2937
|
|
|
2681
2938
|
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
@@ -2683,7 +2940,10 @@ export class ContainerRuntime
|
|
|
2683
2940
|
let latestSnapshotVersionId: string | undefined;
|
|
2684
2941
|
if (refreshLatestAck) {
|
|
2685
2942
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
|
|
2686
|
-
|
|
2943
|
+
createChildLogger({
|
|
2944
|
+
logger: summaryNumberLogger,
|
|
2945
|
+
properties: { all: { safeSummary: true } },
|
|
2946
|
+
}),
|
|
2687
2947
|
);
|
|
2688
2948
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2689
2949
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
@@ -2692,6 +2952,50 @@ export class ContainerRuntime
|
|
|
2692
2952
|
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
2693
2953
|
}
|
|
2694
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
|
+
|
|
2695
2999
|
const shouldPauseInboundSignal =
|
|
2696
3000
|
this.mc.config.getBoolean(
|
|
2697
3001
|
"Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause",
|
|
@@ -2762,7 +3066,7 @@ export class ContainerRuntime
|
|
|
2762
3066
|
}
|
|
2763
3067
|
|
|
2764
3068
|
const trace = Trace.start();
|
|
2765
|
-
let summarizeResult:
|
|
3069
|
+
let summarizeResult: ISummaryTreeWithStats;
|
|
2766
3070
|
// If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
|
|
2767
3071
|
// state of all the nodes.
|
|
2768
3072
|
const forcedFullTree = this.garbageCollector.summaryStateNeedsReset;
|
|
@@ -2781,6 +3085,38 @@ export class ContainerRuntime
|
|
|
2781
3085
|
error,
|
|
2782
3086
|
};
|
|
2783
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
|
+
|
|
2784
3120
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
2785
3121
|
|
|
2786
3122
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -2808,7 +3144,7 @@ export class ContainerRuntime
|
|
|
2808
3144
|
summaryNumber,
|
|
2809
3145
|
...partialStats,
|
|
2810
3146
|
};
|
|
2811
|
-
const generateSummaryData = {
|
|
3147
|
+
const generateSummaryData: Omit<IGenerateSummaryTreeResult, "stage" | "error"> = {
|
|
2812
3148
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2813
3149
|
minimumSequenceNumber,
|
|
2814
3150
|
summaryTree,
|
|
@@ -2840,7 +3176,7 @@ export class ContainerRuntime
|
|
|
2840
3176
|
} else if (lastAck === undefined) {
|
|
2841
3177
|
summaryContext = {
|
|
2842
3178
|
proposalHandle: undefined,
|
|
2843
|
-
ackHandle: this.
|
|
3179
|
+
ackHandle: this.loadedFromVersionId,
|
|
2844
3180
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2845
3181
|
};
|
|
2846
3182
|
} else {
|
|
@@ -2894,7 +3230,15 @@ export class ContainerRuntime
|
|
|
2894
3230
|
submitOpDuration: trace.trace().duration,
|
|
2895
3231
|
} as const;
|
|
2896
3232
|
|
|
2897
|
-
|
|
3233
|
+
try {
|
|
3234
|
+
// If validateSummaryBeforeUpload is false, the summary should be validated in this step.
|
|
3235
|
+
this.summarizerNode.completeSummary(
|
|
3236
|
+
handle,
|
|
3237
|
+
!this.validateSummaryBeforeUpload /* validate */,
|
|
3238
|
+
);
|
|
3239
|
+
} catch (error) {
|
|
3240
|
+
return { stage: "upload", ...uploadData, error };
|
|
3241
|
+
}
|
|
2898
3242
|
return submitData;
|
|
2899
3243
|
} finally {
|
|
2900
3244
|
// Cleanup wip summary in case of failure
|
|
@@ -2911,8 +3255,78 @@ export class ContainerRuntime
|
|
|
2911
3255
|
}
|
|
2912
3256
|
}
|
|
2913
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
|
+
|
|
2914
3328
|
private hasPendingMessages() {
|
|
2915
|
-
return this.
|
|
3329
|
+
return this.pendingMessagesCount !== 0;
|
|
2916
3330
|
}
|
|
2917
3331
|
|
|
2918
3332
|
private updateDocumentDirtyState(dirty: boolean) {
|
|
@@ -2933,7 +3347,6 @@ export class ContainerRuntime
|
|
|
2933
3347
|
this.dirtyContainer = dirty;
|
|
2934
3348
|
if (this.emitDirtyDocumentEvent) {
|
|
2935
3349
|
this.emit(dirty ? "dirty" : "saved");
|
|
2936
|
-
this.context.updateDirtyContainerState(dirty);
|
|
2937
3350
|
}
|
|
2938
3351
|
}
|
|
2939
3352
|
|
|
@@ -2946,7 +3359,10 @@ export class ContainerRuntime
|
|
|
2946
3359
|
address: id,
|
|
2947
3360
|
contents,
|
|
2948
3361
|
};
|
|
2949
|
-
this.submit(
|
|
3362
|
+
this.submit(
|
|
3363
|
+
{ type: ContainerMessageType.FluidDataStoreOp, contents: envelope },
|
|
3364
|
+
localOpMetadata,
|
|
3365
|
+
);
|
|
2950
3366
|
}
|
|
2951
3367
|
|
|
2952
3368
|
public submitDataStoreAliasOp(contents: any, localOpMetadata: unknown): void {
|
|
@@ -2955,12 +3371,15 @@ export class ContainerRuntime
|
|
|
2955
3371
|
throw new UsageError("malformedDataStoreAliasMessage");
|
|
2956
3372
|
}
|
|
2957
3373
|
|
|
2958
|
-
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
3374
|
+
this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
2959
3375
|
}
|
|
2960
3376
|
|
|
2961
|
-
public async uploadBlob(
|
|
3377
|
+
public async uploadBlob(
|
|
3378
|
+
blob: ArrayBufferLike,
|
|
3379
|
+
signal?: AbortSignal,
|
|
3380
|
+
): Promise<IFluidHandle<ArrayBufferLike>> {
|
|
2962
3381
|
this.verifyNotClosed();
|
|
2963
|
-
return this.blobManager.createBlob(blob);
|
|
3382
|
+
return this.blobManager.createBlob(blob, signal);
|
|
2964
3383
|
}
|
|
2965
3384
|
|
|
2966
3385
|
private maybeSubmitIdAllocationOp(type: ContainerMessageType) {
|
|
@@ -2974,7 +3393,7 @@ export class ContainerRuntime
|
|
|
2974
3393
|
);
|
|
2975
3394
|
idRange = this.idCompressor.takeNextCreationRange();
|
|
2976
3395
|
// Don't include the idRange if there weren't any Ids allocated
|
|
2977
|
-
idRange = idRange?.ids
|
|
3396
|
+
idRange = idRange?.ids !== undefined ? idRange : undefined;
|
|
2978
3397
|
}
|
|
2979
3398
|
|
|
2980
3399
|
if (idRange !== undefined) {
|
|
@@ -2998,8 +3417,7 @@ export class ContainerRuntime
|
|
|
2998
3417
|
}
|
|
2999
3418
|
|
|
3000
3419
|
private submit(
|
|
3001
|
-
|
|
3002
|
-
contents: any,
|
|
3420
|
+
containerRuntimeMessage: ContainerRuntimeMessage,
|
|
3003
3421
|
localOpMetadata: unknown = undefined,
|
|
3004
3422
|
metadata: Record<string, unknown> | undefined = undefined,
|
|
3005
3423
|
): void {
|
|
@@ -3012,17 +3430,18 @@ export class ContainerRuntime
|
|
|
3012
3430
|
0x132 /* "sending ops in detached container" */,
|
|
3013
3431
|
);
|
|
3014
3432
|
|
|
3015
|
-
const serializedContent = JSON.stringify(
|
|
3433
|
+
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
3016
3434
|
|
|
3017
3435
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
3018
3436
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
3019
3437
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
3020
|
-
this.logger.sendTelemetryEvent({
|
|
3438
|
+
this.mc.logger.sendTelemetryEvent({
|
|
3021
3439
|
eventName: "SubmitOpInReadonly",
|
|
3022
3440
|
connected: this.connected,
|
|
3023
3441
|
});
|
|
3024
3442
|
}
|
|
3025
3443
|
|
|
3444
|
+
const type = containerRuntimeMessage.type;
|
|
3026
3445
|
const message: BatchMessage = {
|
|
3027
3446
|
contents: serializedContent,
|
|
3028
3447
|
type,
|
|
@@ -3064,6 +3483,9 @@ export class ContainerRuntime
|
|
|
3064
3483
|
this.disableAttachReorder !== true
|
|
3065
3484
|
) {
|
|
3066
3485
|
this.outbox.submitAttach(message);
|
|
3486
|
+
} else if (type === ContainerMessageType.BlobAttach) {
|
|
3487
|
+
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
3488
|
+
this.outbox.submitBlobAttach(message);
|
|
3067
3489
|
} else {
|
|
3068
3490
|
this.outbox.submit(message);
|
|
3069
3491
|
}
|
|
@@ -3078,7 +3500,7 @@ export class ContainerRuntime
|
|
|
3078
3500
|
throw error;
|
|
3079
3501
|
}
|
|
3080
3502
|
|
|
3081
|
-
if (this.isContainerMessageDirtyable(
|
|
3503
|
+
if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
|
|
3082
3504
|
this.updateDocumentDirtyState(true);
|
|
3083
3505
|
}
|
|
3084
3506
|
}
|
|
@@ -3134,9 +3556,9 @@ export class ContainerRuntime
|
|
|
3134
3556
|
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
3135
3557
|
|
|
3136
3558
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
3137
|
-
return this.
|
|
3138
|
-
? this.
|
|
3139
|
-
: this.
|
|
3559
|
+
return this.submitSummaryFn !== undefined
|
|
3560
|
+
? this.submitSummaryFn(contents, referenceSequenceNumber)
|
|
3561
|
+
: this.submitFn(MessageType.Summarize, contents, false);
|
|
3140
3562
|
}
|
|
3141
3563
|
|
|
3142
3564
|
/**
|
|
@@ -3157,7 +3579,7 @@ export class ContainerRuntime
|
|
|
3157
3579
|
this.mc.logger.sendTelemetryEvent(
|
|
3158
3580
|
{ eventName: "OpReentry" },
|
|
3159
3581
|
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
3160
|
-
new UsageError(errorMessage),
|
|
3582
|
+
getLongStack(() => new UsageError(errorMessage)),
|
|
3161
3583
|
);
|
|
3162
3584
|
this.opReentryCallsToReport--;
|
|
3163
3585
|
}
|
|
@@ -3180,44 +3602,49 @@ export class ContainerRuntime
|
|
|
3180
3602
|
}
|
|
3181
3603
|
}
|
|
3182
3604
|
|
|
3183
|
-
private
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3605
|
+
private reSubmitBatch(batch: IPendingBatchMessage[]) {
|
|
3606
|
+
this.orderSequentially(() => {
|
|
3607
|
+
for (const message of batch) {
|
|
3608
|
+
this.reSubmit(message);
|
|
3609
|
+
}
|
|
3610
|
+
});
|
|
3611
|
+
this.flush();
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
private reSubmit(message: IPendingBatchMessage) {
|
|
3188
3615
|
// Need to parse from string for back-compat
|
|
3189
|
-
const
|
|
3190
|
-
this.reSubmitCore(
|
|
3616
|
+
const containerRuntimeMessage = this.parseOpContent(message.content);
|
|
3617
|
+
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
3191
3618
|
}
|
|
3192
3619
|
|
|
3193
3620
|
/**
|
|
3194
3621
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
3195
3622
|
* reconnect and there are pending messages.
|
|
3196
|
-
* @param
|
|
3623
|
+
* @param message - The original ContainerRuntimeMessage.
|
|
3197
3624
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
3198
3625
|
*/
|
|
3199
3626
|
private reSubmitCore(
|
|
3200
|
-
|
|
3201
|
-
content: any,
|
|
3627
|
+
message: ContainerRuntimeMessage,
|
|
3202
3628
|
localOpMetadata: unknown,
|
|
3203
3629
|
opMetadata: Record<string, unknown> | undefined,
|
|
3204
3630
|
) {
|
|
3205
|
-
|
|
3631
|
+
const contents = message.contents;
|
|
3632
|
+
switch (message.type) {
|
|
3206
3633
|
case ContainerMessageType.FluidDataStoreOp:
|
|
3207
3634
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
3208
3635
|
// and trigger resubmission on it.
|
|
3209
|
-
this.dataStores.resubmitDataStoreOp(
|
|
3636
|
+
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
3210
3637
|
break;
|
|
3211
3638
|
case ContainerMessageType.Attach:
|
|
3212
3639
|
case ContainerMessageType.Alias:
|
|
3213
|
-
this.submit(
|
|
3640
|
+
this.submit(message, localOpMetadata);
|
|
3214
3641
|
break;
|
|
3215
3642
|
case ContainerMessageType.IdAllocation:
|
|
3216
3643
|
// Remove the stashedState from the op if it's a stashed op
|
|
3217
|
-
if (
|
|
3218
|
-
delete
|
|
3644
|
+
if (contents.stashedState !== undefined) {
|
|
3645
|
+
delete contents.stashedState;
|
|
3219
3646
|
}
|
|
3220
|
-
this.submit(
|
|
3647
|
+
this.submit(message, localOpMetadata);
|
|
3221
3648
|
break;
|
|
3222
3649
|
case ContainerMessageType.ChunkedOp:
|
|
3223
3650
|
throw new Error(`chunkedOp not expected here`);
|
|
@@ -3225,10 +3652,33 @@ export class ContainerRuntime
|
|
|
3225
3652
|
this.blobManager.reSubmit(opMetadata);
|
|
3226
3653
|
break;
|
|
3227
3654
|
case ContainerMessageType.Rejoin:
|
|
3228
|
-
this.submit(
|
|
3655
|
+
this.submit(message);
|
|
3229
3656
|
break;
|
|
3230
|
-
default:
|
|
3231
|
-
|
|
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
|
+
}
|
|
3232
3682
|
}
|
|
3233
3683
|
}
|
|
3234
3684
|
|
|
@@ -3242,6 +3692,7 @@ export class ContainerRuntime
|
|
|
3242
3692
|
this.dataStores.rollbackDataStoreOp(contents as IEnvelope, localOpMetadata);
|
|
3243
3693
|
break;
|
|
3244
3694
|
default:
|
|
3695
|
+
// Don't check message.compatDetails because this is for rolling back a local op so the type will be known
|
|
3245
3696
|
throw new Error(`Can't rollback ${type}`);
|
|
3246
3697
|
}
|
|
3247
3698
|
}
|
|
@@ -3269,12 +3720,22 @@ export class ContainerRuntime
|
|
|
3269
3720
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
3270
3721
|
public async refreshLatestSummaryAck(options: IRefreshSummaryAckOptions) {
|
|
3271
3722
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
3723
|
+
// proposalHandle is always passed from RunningSummarizer.
|
|
3724
|
+
assert(proposalHandle !== undefined, 0x766 /* proposalHandle should be available */);
|
|
3272
3725
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
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(
|
|
3278
3739
|
summaryLogger,
|
|
3279
3740
|
{
|
|
3280
3741
|
eventName: "RefreshLatestSummaryAckFetch",
|
|
@@ -3282,27 +3743,9 @@ export class ContainerRuntime
|
|
|
3282
3743
|
targetSequenceNumber: summaryRefSeq,
|
|
3283
3744
|
},
|
|
3284
3745
|
readAndParseBlob,
|
|
3746
|
+
null,
|
|
3285
3747
|
);
|
|
3286
3748
|
|
|
3287
|
-
/**
|
|
3288
|
-
* back-compat - Older loaders and drivers (pre 2.0.0-internal.1.4) don't have fetchSource as a param in the
|
|
3289
|
-
* getVersions API. So, they will not fetch the latest snapshot from network in the previous fetch call. For
|
|
3290
|
-
* these scenarios, fetch the snapshot corresponding to the ack handle to have the same behavior before the
|
|
3291
|
-
* change that started fetching latest snapshot always.
|
|
3292
|
-
*/
|
|
3293
|
-
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
3294
|
-
fetchResult = await this.fetchSnapshotFromStorage(
|
|
3295
|
-
summaryLogger,
|
|
3296
|
-
{
|
|
3297
|
-
eventName: "RefreshLatestSummaryAckFetchBackCompat",
|
|
3298
|
-
ackHandle,
|
|
3299
|
-
targetSequenceNumber: summaryRefSeq,
|
|
3300
|
-
},
|
|
3301
|
-
readAndParseBlob,
|
|
3302
|
-
ackHandle,
|
|
3303
|
-
);
|
|
3304
|
-
}
|
|
3305
|
-
|
|
3306
3749
|
/**
|
|
3307
3750
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
3308
3751
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -3324,33 +3767,16 @@ export class ContainerRuntime
|
|
|
3324
3767
|
fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
3325
3768
|
},
|
|
3326
3769
|
);
|
|
3327
|
-
this.
|
|
3770
|
+
this.disposeFn(error);
|
|
3328
3771
|
throw error;
|
|
3329
3772
|
}
|
|
3330
3773
|
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
fetchResult.latestSnapshotRefSeq,
|
|
3335
|
-
summaryLogger,
|
|
3336
|
-
);
|
|
3337
|
-
|
|
3338
|
-
return {
|
|
3339
|
-
snapshotTree: fetchResult.snapshotTree,
|
|
3340
|
-
snapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
3341
|
-
};
|
|
3342
|
-
};
|
|
3343
|
-
|
|
3344
|
-
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3345
|
-
proposalHandle,
|
|
3346
|
-
summaryRefSeq,
|
|
3347
|
-
fetchLatestSnapshot,
|
|
3348
|
-
readAndParseBlob,
|
|
3349
|
-
summaryLogger,
|
|
3350
|
-
);
|
|
3774
|
+
await this.closeStaleSummarizer("RefreshLatestSummaryAckFetch");
|
|
3775
|
+
return;
|
|
3776
|
+
}
|
|
3351
3777
|
|
|
3352
3778
|
// Notify the garbage collector so it can update its latest summary state.
|
|
3353
|
-
await this.garbageCollector.refreshLatestSummary(
|
|
3779
|
+
await this.garbageCollector.refreshLatestSummary(result);
|
|
3354
3780
|
}
|
|
3355
3781
|
|
|
3356
3782
|
/**
|
|
@@ -3363,51 +3789,49 @@ export class ContainerRuntime
|
|
|
3363
3789
|
summaryLogger: ITelemetryLoggerExt,
|
|
3364
3790
|
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined }> {
|
|
3365
3791
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
3366
|
-
const {
|
|
3367
|
-
await this.fetchLatestSnapshotFromStorage(
|
|
3368
|
-
summaryLogger,
|
|
3369
|
-
{
|
|
3370
|
-
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
3371
|
-
},
|
|
3372
|
-
readAndParseBlob,
|
|
3373
|
-
);
|
|
3374
|
-
const fetchLatestSnapshot: IFetchSnapshotResult = {
|
|
3375
|
-
snapshotTree,
|
|
3376
|
-
snapshotRefSeq: latestSnapshotRefSeq,
|
|
3377
|
-
};
|
|
3378
|
-
const result = await this.summarizerNode.refreshLatestSummary(
|
|
3379
|
-
undefined /* proposalHandle */,
|
|
3380
|
-
latestSnapshotRefSeq,
|
|
3381
|
-
async () => fetchLatestSnapshot,
|
|
3382
|
-
readAndParseBlob,
|
|
3792
|
+
const { versionId, latestSnapshotRefSeq } = await this.fetchSnapshotFromStorage(
|
|
3383
3793
|
summaryLogger,
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
await this.garbageCollector.refreshLatestSummary(
|
|
3388
|
-
undefined /* proposalHandle */,
|
|
3389
|
-
result,
|
|
3794
|
+
{
|
|
3795
|
+
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
3796
|
+
},
|
|
3390
3797
|
readAndParseBlob,
|
|
3798
|
+
null,
|
|
3391
3799
|
);
|
|
3392
3800
|
|
|
3801
|
+
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
3802
|
+
|
|
3393
3803
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
3394
3804
|
}
|
|
3395
3805
|
|
|
3396
|
-
private async
|
|
3397
|
-
logger
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
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();
|
|
3402
3821
|
}
|
|
3403
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
|
+
*/
|
|
3404
3828
|
private async fetchSnapshotFromStorage(
|
|
3405
3829
|
logger: ITelemetryLoggerExt,
|
|
3406
3830
|
event: ITelemetryGenericEvent,
|
|
3407
3831
|
readAndParseBlob: ReadAndParseBlob,
|
|
3408
3832
|
versionId: string | null,
|
|
3409
3833
|
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; latestSnapshotRefSeq: number }> {
|
|
3410
|
-
|
|
3834
|
+
return PerformanceEvent.timedExecAsync(
|
|
3411
3835
|
logger,
|
|
3412
3836
|
event,
|
|
3413
3837
|
async (perfEvent: {
|
|
@@ -3453,77 +3877,75 @@ export class ContainerRuntime
|
|
|
3453
3877
|
};
|
|
3454
3878
|
},
|
|
3455
3879
|
);
|
|
3456
|
-
|
|
3457
|
-
// We choose to close the summarizer after the snapshot cache is updated to avoid
|
|
3458
|
-
// situations which the main client (which is likely to be re-elected as the leader again)
|
|
3459
|
-
// loads the summarizer from cache.
|
|
3460
|
-
if (this.summaryStateUpdateMethod === "restart") {
|
|
3461
|
-
const error = new GenericError("Restarting summarizer instead of refreshing");
|
|
3462
|
-
|
|
3463
|
-
this.mc.logger.sendTelemetryEvent(
|
|
3464
|
-
{
|
|
3465
|
-
...event,
|
|
3466
|
-
eventName: "ClosingSummarizerOnSummaryStale",
|
|
3467
|
-
codePath: event.eventName,
|
|
3468
|
-
message: "Stopping fetch from storage",
|
|
3469
|
-
versionId: versionId != null ? versionId : undefined,
|
|
3470
|
-
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
3471
|
-
},
|
|
3472
|
-
error,
|
|
3473
|
-
);
|
|
3474
|
-
|
|
3475
|
-
// Delay 10 seconds before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
3476
|
-
await delay(this.closeSummarizerDelayMs);
|
|
3477
|
-
this._summarizer?.stop("latestSummaryStateStale");
|
|
3478
|
-
this.closeFn();
|
|
3479
|
-
throw error;
|
|
3480
|
-
}
|
|
3481
|
-
|
|
3482
|
-
return snapshotResults;
|
|
3483
3880
|
}
|
|
3484
3881
|
|
|
3485
3882
|
public notifyAttaching() {} // do nothing (deprecated method)
|
|
3486
3883
|
|
|
3487
|
-
public getPendingLocalState(
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
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();
|
|
3495
3910
|
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
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
|
+
);
|
|
3500
3922
|
}
|
|
3501
3923
|
|
|
3502
|
-
public
|
|
3503
|
-
if (this.
|
|
3504
|
-
return this.summarizer.summarizeOnDemand(
|
|
3924
|
+
public summarizeOnDemand(options: IOnDemandSummarizeOptions): ISummarizeResults {
|
|
3925
|
+
if (this.isSummarizerClient) {
|
|
3926
|
+
return this.summarizer.summarizeOnDemand(options);
|
|
3505
3927
|
} else if (this.summaryManager !== undefined) {
|
|
3506
|
-
return this.summaryManager.summarizeOnDemand(
|
|
3928
|
+
return this.summaryManager.summarizeOnDemand(options);
|
|
3507
3929
|
} else {
|
|
3508
3930
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
3509
3931
|
// disableSummaries is turned on. We are throwing instead of returning a failure here,
|
|
3510
3932
|
// because it is a misuse of the API rather than an expected failure.
|
|
3511
3933
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
3512
3934
|
}
|
|
3513
|
-
}
|
|
3935
|
+
}
|
|
3514
3936
|
|
|
3515
|
-
public
|
|
3516
|
-
if (this.
|
|
3517
|
-
return this.summarizer.enqueueSummarize(
|
|
3937
|
+
public enqueueSummarize(options: IEnqueueSummarizeOptions): EnqueueSummarizeResult {
|
|
3938
|
+
if (this.isSummarizerClient) {
|
|
3939
|
+
return this.summarizer.enqueueSummarize(options);
|
|
3518
3940
|
} else if (this.summaryManager !== undefined) {
|
|
3519
|
-
return this.summaryManager.enqueueSummarize(
|
|
3941
|
+
return this.summaryManager.enqueueSummarize(options);
|
|
3520
3942
|
} else {
|
|
3521
3943
|
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
3522
3944
|
// generateSummaries is turned off. We are throwing instead of returning a failure here,
|
|
3523
3945
|
// because it is a misuse of the API rather than an expected failure.
|
|
3524
3946
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
3525
3947
|
}
|
|
3526
|
-
}
|
|
3948
|
+
}
|
|
3527
3949
|
|
|
3528
3950
|
/**
|
|
3529
3951
|
* * Forms a function that will request a Summarizer.
|