@fluidframework/container-runtime 2.0.0-dev.5.3.2.178189 → 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 +123 -0
- package/README.md +4 -3
- package/dist/batchTracker.d.ts +3 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +6 -5
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +10 -16
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +184 -172
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +25 -16
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +161 -35
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +697 -449
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +15 -7
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -2
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +83 -87
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +1 -2
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +8 -9
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStoreRegistry.js +2 -2
- package/dist/dataStoreRegistry.js.map +1 -1
- package/dist/dataStores.d.ts +22 -6
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +123 -81
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaManagerProxyBase.d.ts +35 -0
- package/dist/deltaManagerProxyBase.d.ts.map +1 -0
- package/dist/deltaManagerProxyBase.js +77 -0
- package/dist/deltaManagerProxyBase.js.map +1 -0
- package/dist/deltaManagerSummarizerProxy.d.ts +1 -1
- package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +4 -2
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +10 -10
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/error.d.ts +14 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +21 -0
- package/dist/error.js.map +1 -0
- package/dist/gc/garbageCollection.d.ts +10 -9
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +61 -53
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcConfigs.d.ts.map +1 -1
- package/dist/gc/gcConfigs.js +18 -14
- package/dist/gc/gcConfigs.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +17 -4
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js +14 -15
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +0 -8
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +11 -24
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcSummaryStateTracker.d.ts +4 -7
- package/dist/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/dist/gc/gcSummaryStateTracker.js +19 -58
- package/dist/gc/gcSummaryStateTracker.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +45 -35
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/gc/gcUnreferencedStateTracker.js +4 -4
- package/dist/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/dist/gc/index.d.ts +2 -2
- package/dist/gc/index.d.ts.map +1 -1
- package/dist/gc/index.js +3 -5
- package/dist/gc/index.js.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.d.ts +8 -30
- package/dist/id-compressor/appendOnlySortedMap.d.ts.map +1 -1
- package/dist/id-compressor/appendOnlySortedMap.js +26 -68
- package/dist/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/dist/id-compressor/finalSpace.d.ts +29 -0
- package/dist/id-compressor/finalSpace.d.ts.map +1 -0
- package/dist/id-compressor/finalSpace.js +62 -0
- package/dist/id-compressor/finalSpace.js.map +1 -0
- package/dist/id-compressor/idCompressor.d.ts +25 -250
- package/dist/id-compressor/idCompressor.d.ts.map +1 -1
- package/dist/id-compressor/idCompressor.js +390 -1153
- package/dist/id-compressor/idCompressor.js.map +1 -1
- package/dist/id-compressor/identifiers.d.ts +32 -0
- package/dist/id-compressor/identifiers.d.ts.map +1 -0
- package/dist/id-compressor/identifiers.js +15 -0
- package/dist/id-compressor/identifiers.js.map +1 -0
- package/dist/id-compressor/index.d.ts +5 -6
- package/dist/id-compressor/index.d.ts.map +1 -1
- package/dist/id-compressor/index.js +20 -26
- package/dist/id-compressor/index.js.map +1 -1
- package/dist/id-compressor/persistanceUtilities.d.ts +22 -0
- package/dist/id-compressor/persistanceUtilities.d.ts.map +1 -0
- package/dist/id-compressor/persistanceUtilities.js +43 -0
- package/dist/id-compressor/persistanceUtilities.js.map +1 -0
- package/dist/id-compressor/sessionSpaceNormalizer.d.ts +46 -0
- package/dist/id-compressor/sessionSpaceNormalizer.d.ts.map +1 -0
- package/dist/id-compressor/sessionSpaceNormalizer.js +80 -0
- package/dist/id-compressor/sessionSpaceNormalizer.js.map +1 -0
- package/dist/id-compressor/sessions.d.ts +115 -0
- package/dist/id-compressor/sessions.d.ts.map +1 -0
- package/dist/id-compressor/sessions.js +305 -0
- package/dist/id-compressor/sessions.js.map +1 -0
- package/dist/id-compressor/utilities.d.ts +49 -0
- package/dist/id-compressor/utilities.d.ts.map +1 -0
- package/dist/id-compressor/utilities.js +166 -0
- package/dist/id-compressor/utilities.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.js +10 -6
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts +2 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +12 -7
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +2 -2
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +23 -20
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +19 -9
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +2 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +22 -19
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +7 -5
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +22 -31
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +6 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +19 -7
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/dist/opProperties.js +1 -2
- package/dist/opProperties.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/pendingStateManager.d.ts +18 -8
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +73 -51
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +36 -32
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/index.d.ts +3 -3
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +2 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +3 -3
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js +26 -27
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/runWhileConnectedCoordinator.js +3 -3
- package/dist/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +31 -10
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +271 -139
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts +8 -7
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +79 -78
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts +2 -2
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +7 -11
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.js +10 -14
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/index.d.ts +1 -1
- package/dist/summary/summarizerNode/index.d.ts.map +1 -1
- package/dist/summary/summarizerNode/index.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +10 -19
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +57 -130
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +6 -30
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js +2 -4
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -14
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +15 -101
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +38 -25
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +2 -3
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js +12 -13
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryFormat.d.ts +3 -0
- package/dist/summary/summaryFormat.d.ts.map +1 -1
- package/dist/summary/summaryFormat.js +6 -4
- package/dist/summary/summaryFormat.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +10 -4
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +106 -57
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +8 -8
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +38 -28
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +3 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +5 -4
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +10 -16
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +159 -147
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +15 -6
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +161 -35
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +651 -402
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +13 -5
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -2
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +47 -51
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +1 -2
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +3 -4
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStoreRegistry.js +1 -1
- package/lib/dataStoreRegistry.js.map +1 -1
- package/lib/dataStores.d.ts +22 -6
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +107 -65
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaManagerProxyBase.d.ts +35 -0
- package/lib/deltaManagerProxyBase.d.ts.map +1 -0
- package/lib/deltaManagerProxyBase.js +73 -0
- package/lib/deltaManagerProxyBase.js.map +1 -0
- package/lib/deltaManagerSummarizerProxy.d.ts +1 -1
- package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +3 -1
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +7 -7
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/error.d.ts +14 -0
- package/lib/error.d.ts.map +1 -0
- package/lib/error.js +17 -0
- package/lib/error.js.map +1 -0
- package/lib/gc/garbageCollection.d.ts +10 -9
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +61 -53
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcConfigs.d.ts.map +1 -1
- package/lib/gc/gcConfigs.js +16 -12
- package/lib/gc/gcConfigs.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +17 -4
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js +13 -14
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +0 -8
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +5 -17
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcSummaryStateTracker.d.ts +4 -7
- package/lib/gc/gcSummaryStateTracker.d.ts.map +1 -1
- package/lib/gc/gcSummaryStateTracker.js +20 -59
- package/lib/gc/gcSummaryStateTracker.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +46 -36
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js +1 -1
- package/lib/gc/gcUnreferencedStateTracker.js.map +1 -1
- package/lib/gc/index.d.ts +2 -2
- package/lib/gc/index.d.ts.map +1 -1
- package/lib/gc/index.js +2 -2
- package/lib/gc/index.js.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.d.ts +8 -30
- package/lib/id-compressor/appendOnlySortedMap.d.ts.map +1 -1
- package/lib/id-compressor/appendOnlySortedMap.js +25 -66
- package/lib/id-compressor/appendOnlySortedMap.js.map +1 -1
- package/lib/id-compressor/finalSpace.d.ts +29 -0
- package/lib/id-compressor/finalSpace.d.ts.map +1 -0
- package/lib/id-compressor/finalSpace.js +58 -0
- package/lib/id-compressor/finalSpace.js.map +1 -0
- package/lib/id-compressor/idCompressor.d.ts +25 -250
- package/lib/id-compressor/idCompressor.d.ts.map +1 -1
- package/lib/id-compressor/idCompressor.js +385 -1142
- package/lib/id-compressor/idCompressor.js.map +1 -1
- package/lib/id-compressor/identifiers.d.ts +32 -0
- package/lib/id-compressor/identifiers.d.ts.map +1 -0
- package/lib/id-compressor/identifiers.js +11 -0
- package/lib/id-compressor/identifiers.js.map +1 -0
- package/lib/id-compressor/index.d.ts +5 -6
- package/lib/id-compressor/index.d.ts.map +1 -1
- package/lib/id-compressor/index.js +5 -6
- package/lib/id-compressor/index.js.map +1 -1
- package/lib/id-compressor/persistanceUtilities.d.ts +22 -0
- package/lib/id-compressor/persistanceUtilities.d.ts.map +1 -0
- package/lib/id-compressor/persistanceUtilities.js +34 -0
- package/lib/id-compressor/persistanceUtilities.js.map +1 -0
- package/lib/id-compressor/sessionSpaceNormalizer.d.ts +46 -0
- package/lib/id-compressor/sessionSpaceNormalizer.d.ts.map +1 -0
- package/lib/id-compressor/sessionSpaceNormalizer.js +76 -0
- package/lib/id-compressor/sessionSpaceNormalizer.js.map +1 -0
- package/lib/id-compressor/sessions.d.ts +115 -0
- package/lib/id-compressor/sessions.d.ts.map +1 -0
- package/lib/id-compressor/sessions.js +290 -0
- package/lib/id-compressor/sessions.js.map +1 -0
- package/lib/id-compressor/utilities.d.ts +49 -0
- package/lib/id-compressor/utilities.d.ts.map +1 -0
- package/lib/id-compressor/utilities.js +148 -0
- package/lib/id-compressor/utilities.js.map +1 -0
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.js +10 -6
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts +2 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +10 -5
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +2 -2
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +15 -12
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +17 -7
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +2 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +13 -10
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +7 -5
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +13 -22
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +6 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +20 -8
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -1
- package/lib/opProperties.js +1 -2
- package/lib/opProperties.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/pendingStateManager.d.ts +18 -8
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +62 -40
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +18 -14
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/index.d.ts +3 -3
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -1
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +3 -3
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.js +21 -22
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js +1 -1
- package/lib/summary/runWhileConnectedCoordinator.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +31 -10
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +265 -133
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts +8 -7
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +75 -74
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts +2 -2
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +6 -10
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.js +9 -13
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/index.d.ts +1 -1
- package/lib/summary/summarizerNode/index.d.ts.map +1 -1
- package/lib/summary/summarizerNode/index.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +10 -19
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +42 -115
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +6 -30
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js +2 -4
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +4 -14
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +14 -100
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +38 -25
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +2 -3
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js +5 -6
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryFormat.d.ts +3 -0
- package/lib/summary/summaryFormat.d.ts.map +1 -1
- package/lib/summary/summaryFormat.js +5 -3
- package/lib/summary/summaryFormat.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +10 -4
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +100 -51
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +8 -8
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +35 -25
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +28 -31
- package/src/batchTracker.ts +7 -5
- package/src/blobManager.ts +188 -166
- package/src/connectionTelemetry.ts +9 -4
- package/src/containerRuntime.ts +806 -441
- package/src/dataStore.ts +12 -4
- package/src/dataStoreContext.ts +39 -43
- package/src/dataStoreContexts.ts +4 -6
- package/src/dataStoreRegistry.ts +1 -1
- package/src/dataStores.ts +114 -80
- package/src/deltaManagerProxyBase.ts +111 -0
- package/src/deltaManagerSummarizerProxy.ts +4 -1
- package/src/deltaScheduler.ts +7 -11
- package/src/error.ts +18 -0
- package/src/gc/garbageCollection.md +53 -5
- package/src/gc/garbageCollection.ts +58 -52
- package/src/gc/gcConfigs.ts +4 -2
- package/src/gc/gcDefinitions.ts +17 -20
- package/src/gc/gcEarlyAdoption.md +145 -0
- package/src/gc/gcHelpers.ts +1 -12
- package/src/gc/gcSummaryStateTracker.ts +19 -65
- package/src/gc/gcTelemetry.ts +14 -12
- package/src/gc/gcUnreferencedStateTracker.ts +1 -1
- package/src/gc/index.ts +2 -4
- package/src/id-compressor/appendOnlySortedMap.ts +26 -87
- package/src/id-compressor/finalSpace.ts +67 -0
- package/src/id-compressor/idCompressor.ts +458 -1682
- package/src/id-compressor/identifiers.ts +42 -0
- package/src/id-compressor/index.ts +11 -20
- package/src/id-compressor/persistanceUtilities.ts +58 -0
- package/src/id-compressor/sessionSpaceNormalizer.ts +83 -0
- package/src/id-compressor/sessions.ts +405 -0
- package/src/id-compressor/utilities.ts +187 -0
- package/src/index.ts +7 -2
- package/src/opLifecycle/opCompressor.ts +6 -5
- package/src/opLifecycle/opDecompressor.ts +6 -4
- package/src/opLifecycle/opGroupingManager.ts +9 -6
- package/src/opLifecycle/opSplitter.ts +7 -6
- package/src/opLifecycle/outbox.ts +23 -32
- package/src/opLifecycle/remoteMessageProcessor.ts +27 -8
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +72 -42
- package/src/scheduleManager.ts +7 -5
- package/src/summary/index.ts +4 -3
- package/src/summary/orderedClientElection.ts +10 -6
- package/src/summary/runWhileConnectedCoordinator.ts +1 -1
- package/src/summary/runningSummarizer.ts +291 -163
- package/src/summary/summarizer.ts +27 -16
- package/src/summary/summarizerClientElection.ts +2 -2
- package/src/summary/summarizerHeuristics.ts +1 -1
- package/src/summary/summarizerNode/index.ts +1 -2
- package/src/summary/summarizerNode/summarizerNode.ts +36 -160
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +7 -38
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +11 -130
- package/src/summary/summarizerTypes.ts +40 -25
- package/src/summary/summaryCollection.ts +3 -3
- package/src/summary/summaryFormat.ts +4 -1
- package/src/summary/summaryGenerator.ts +52 -52
- package/src/summary/summaryManager.ts +44 -17
- package/dist/id-compressor/idRange.d.ts +0 -11
- package/dist/id-compressor/idRange.d.ts.map +0 -1
- package/dist/id-compressor/idRange.js +0 -29
- package/dist/id-compressor/idRange.js.map +0 -1
- package/dist/id-compressor/numericUuid.d.ts +0 -59
- package/dist/id-compressor/numericUuid.d.ts.map +0 -1
- package/dist/id-compressor/numericUuid.js +0 -325
- package/dist/id-compressor/numericUuid.js.map +0 -1
- package/dist/id-compressor/sessionIdNormalizer.d.ts +0 -138
- package/dist/id-compressor/sessionIdNormalizer.d.ts.map +0 -1
- package/dist/id-compressor/sessionIdNormalizer.js +0 -488
- package/dist/id-compressor/sessionIdNormalizer.js.map +0 -1
- package/dist/id-compressor/utils.d.ts +0 -57
- package/dist/id-compressor/utils.d.ts.map +0 -1
- package/dist/id-compressor/utils.js +0 -90
- package/dist/id-compressor/utils.js.map +0 -1
- package/dist/id-compressor/uuidUtilities.d.ts +0 -28
- package/dist/id-compressor/uuidUtilities.d.ts.map +0 -1
- package/dist/id-compressor/uuidUtilities.js +0 -104
- package/dist/id-compressor/uuidUtilities.js.map +0 -1
- package/lib/id-compressor/idRange.d.ts +0 -11
- package/lib/id-compressor/idRange.d.ts.map +0 -1
- package/lib/id-compressor/idRange.js +0 -25
- package/lib/id-compressor/idRange.js.map +0 -1
- package/lib/id-compressor/numericUuid.d.ts +0 -59
- package/lib/id-compressor/numericUuid.d.ts.map +0 -1
- package/lib/id-compressor/numericUuid.js +0 -315
- package/lib/id-compressor/numericUuid.js.map +0 -1
- package/lib/id-compressor/sessionIdNormalizer.d.ts +0 -138
- package/lib/id-compressor/sessionIdNormalizer.d.ts.map +0 -1
- package/lib/id-compressor/sessionIdNormalizer.js +0 -484
- package/lib/id-compressor/sessionIdNormalizer.js.map +0 -1
- package/lib/id-compressor/utils.d.ts +0 -57
- package/lib/id-compressor/utils.d.ts.map +0 -1
- package/lib/id-compressor/utils.js +0 -79
- package/lib/id-compressor/utils.js.map +0 -1
- package/lib/id-compressor/uuidUtilities.d.ts +0 -28
- package/lib/id-compressor/uuidUtilities.d.ts.map +0 -1
- package/lib/id-compressor/uuidUtilities.js +0 -96
- package/lib/id-compressor/uuidUtilities.js.map +0 -1
- package/src/id-compressor/idRange.ts +0 -35
- package/src/id-compressor/numericUuid.ts +0 -383
- package/src/id-compressor/sessionIdNormalizer.ts +0 -609
- package/src/id-compressor/utils.ts +0 -114
- package/src/id-compressor/uuidUtilities.ts +0 -120
package/dist/containerRuntime.js
CHANGED
|
@@ -18,26 +18,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
18
|
__setModuleDefault(result, mod);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
22
|
-
var t = {};
|
|
23
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
24
|
-
t[p] = s[p];
|
|
25
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
26
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
27
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
28
|
-
t[p[i]] = s[p[i]];
|
|
29
|
-
}
|
|
30
|
-
return t;
|
|
31
|
-
};
|
|
32
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
33
|
-
exports.ContainerRuntime = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.TombstoneResponseHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
22
|
+
exports.ContainerRuntime = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isRuntimeMessage = exports.RuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.CompressionAlgorithms = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.AllowInactiveRequestHeaderKey = exports.AllowTombstoneRequestHeaderKey = exports.RuntimeHeaders = exports.DefaultSummaryConfiguration = exports.ContainerMessageType = void 0;
|
|
34
23
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
35
|
-
const common_utils_1 = require("@fluidframework/common-utils");
|
|
36
24
|
const core_utils_1 = require("@fluidframework/core-utils");
|
|
25
|
+
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
37
26
|
const telemetry_utils_1 = require("@fluidframework/telemetry-utils");
|
|
38
27
|
const driver_definitions_1 = require("@fluidframework/driver-definitions");
|
|
39
28
|
const driver_utils_1 = require("@fluidframework/driver-utils");
|
|
40
|
-
const container_utils_1 = require("@fluidframework/container-utils");
|
|
41
29
|
const protocol_definitions_1 = require("@fluidframework/protocol-definitions");
|
|
42
30
|
const runtime_definitions_1 = require("@fluidframework/runtime-definitions");
|
|
43
31
|
const runtime_utils_1 = require("@fluidframework/runtime-utils");
|
|
@@ -78,6 +66,18 @@ var ContainerMessageType;
|
|
|
78
66
|
*/
|
|
79
67
|
ContainerMessageType["IdAllocation"] = "idAllocation";
|
|
80
68
|
})(ContainerMessageType = exports.ContainerMessageType || (exports.ContainerMessageType = {}));
|
|
69
|
+
/**
|
|
70
|
+
* Utility to implement compat behaviors given an unknown message type
|
|
71
|
+
* The parameters are typed to support compile-time enforcement of handling all known types/behaviors
|
|
72
|
+
*
|
|
73
|
+
* @param _unknownContainerRuntimeMessageType - Typed as never, to ensure all known types have been
|
|
74
|
+
* handled before calling this function (e.g. in a switch statement).
|
|
75
|
+
* @param compatBehavior - Typed redundantly with CompatModeBehavior to ensure handling is added when updating that type
|
|
76
|
+
*/
|
|
77
|
+
function compatBehaviorAllowsMessageType(_unknownContainerRuntimeMessageType, compatBehavior) {
|
|
78
|
+
// undefined defaults to same behavior as "FailToProcess"
|
|
79
|
+
return compatBehavior === "Ignore";
|
|
80
|
+
}
|
|
81
81
|
exports.DefaultSummaryConfiguration = {
|
|
82
82
|
state: "enabled",
|
|
83
83
|
minIdleTime: 0,
|
|
@@ -104,8 +104,12 @@ var RuntimeHeaders;
|
|
|
104
104
|
})(RuntimeHeaders = exports.RuntimeHeaders || (exports.RuntimeHeaders = {}));
|
|
105
105
|
/** True if a tombstoned object should be returned without erroring */
|
|
106
106
|
exports.AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
107
|
+
/** [IRRELEVANT IF throwOnInactiveLoad OPTION NOT SET] True if an inactive object should be returned without erroring */
|
|
108
|
+
exports.AllowInactiveRequestHeaderKey = "allowInactive"; // Belongs in the enum above, but avoiding the breaking change
|
|
107
109
|
/** Tombstone error responses will have this header set to true */
|
|
108
110
|
exports.TombstoneResponseHeaderKey = "isTombstoned";
|
|
111
|
+
/** Inactive error responses will have this header set to true */
|
|
112
|
+
exports.InactiveResponseHeaderKey = "isInactive";
|
|
109
113
|
/** Default values for Runtime Headers */
|
|
110
114
|
exports.defaultRuntimeHeaderData = {
|
|
111
115
|
wait: true,
|
|
@@ -132,12 +136,16 @@ const defaultCompressionConfig = {
|
|
|
132
136
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
133
137
|
};
|
|
134
138
|
const defaultChunkSizeInBytes = 204800;
|
|
139
|
+
/** The default time to wait for pending ops to be processed during summarization */
|
|
140
|
+
exports.defaultPendingOpsWaitTimeoutMs = 1000;
|
|
141
|
+
/** The default time to delay a summarization retry attempt when there are pending ops */
|
|
142
|
+
exports.defaultPendingOpsRetryDelayMs = 1000;
|
|
135
143
|
/**
|
|
136
144
|
* Instead of refreshing from latest because we do not have 100% confidence in the state
|
|
137
145
|
* of the current system, we should close the summarizer and let it recover.
|
|
138
146
|
* This delay's goal is to prevent tight restart loops
|
|
139
147
|
*/
|
|
140
|
-
const defaultCloseSummarizerDelayMs =
|
|
148
|
+
const defaultCloseSummarizerDelayMs = 5000; // 5 seconds
|
|
141
149
|
/**
|
|
142
150
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
143
151
|
*/
|
|
@@ -174,23 +182,40 @@ function getDeviceSpec() {
|
|
|
174
182
|
};
|
|
175
183
|
}
|
|
176
184
|
}
|
|
177
|
-
catch
|
|
185
|
+
catch { }
|
|
178
186
|
return {};
|
|
179
187
|
}
|
|
180
188
|
exports.getDeviceSpec = getDeviceSpec;
|
|
189
|
+
/**
|
|
190
|
+
* Older loader doesn't have a submitBatchFn member, this is the older way of submitting a batch.
|
|
191
|
+
* Rather than exposing the submitFn (now deprecated) and IDeltaManager (dangerous to hand out) to the Outbox,
|
|
192
|
+
* we can provide a partially-applied function to keep those items private to the ContainerRuntime.
|
|
193
|
+
*/
|
|
194
|
+
const makeLegacySendBatchFn = (submitFn, deltaManager) => (batch) => {
|
|
195
|
+
for (const message of batch.content) {
|
|
196
|
+
submitFn(protocol_definitions_1.MessageType.Operation,
|
|
197
|
+
// For back-compat (submitFn only works on deserialized content)
|
|
198
|
+
message.contents === undefined ? undefined : JSON.parse(message.contents), true, // batch
|
|
199
|
+
message.metadata);
|
|
200
|
+
}
|
|
201
|
+
deltaManager.flush();
|
|
202
|
+
};
|
|
203
|
+
exports.makeLegacySendBatchFn = makeLegacySendBatchFn;
|
|
181
204
|
/**
|
|
182
205
|
* Represents the runtime of the container. Contains helper functions/state of the container.
|
|
183
206
|
* It will define the store level mappings.
|
|
184
207
|
*/
|
|
185
|
-
class ContainerRuntime extends
|
|
208
|
+
class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
186
209
|
/**
|
|
187
210
|
* @internal
|
|
188
211
|
*/
|
|
189
|
-
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration
|
|
190
|
-
|
|
191
|
-
|
|
212
|
+
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, idCompressor, requestHandler, summaryConfiguration = {
|
|
213
|
+
// the defaults
|
|
214
|
+
...exports.DefaultSummaryConfiguration,
|
|
215
|
+
// the runtime configuration overrides
|
|
216
|
+
...runtimeOptions.summaryOptions?.summaryConfigOverrides,
|
|
217
|
+
}, initializeEntryPoint) {
|
|
192
218
|
super();
|
|
193
|
-
this.context = context;
|
|
194
219
|
this.registry = registry;
|
|
195
220
|
this.runtimeOptions = runtimeOptions;
|
|
196
221
|
this.containerScope = containerScope;
|
|
@@ -219,51 +244,56 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
219
244
|
signalTimestamp: 0,
|
|
220
245
|
trackingSignalSequenceNumber: undefined,
|
|
221
246
|
};
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// generateSummaries is turned off. We are throwing instead of returning a failure here,
|
|
246
|
-
// because it is a misuse of the API rather than an expected failure.
|
|
247
|
-
throw new container_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
248
|
-
}
|
|
247
|
+
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, loader, pendingLocalState, supportedFeatures, } = context;
|
|
248
|
+
this.innerDeltaManager = deltaManager;
|
|
249
|
+
this.deltaManager = new deltaManagerSummarizerProxy_1.DeltaManagerSummarizerProxy(this.innerDeltaManager);
|
|
250
|
+
// Here we could wrap/intercept on these functions to block/modify outgoing messages if needed.
|
|
251
|
+
// This makes ContainerRuntime the final gatekeeper for outgoing messages.
|
|
252
|
+
this.submitFn = submitFn;
|
|
253
|
+
this.submitBatchFn = submitBatchFn;
|
|
254
|
+
this.submitSummaryFn = submitSummaryFn;
|
|
255
|
+
this.submitSignalFn = submitSignalFn;
|
|
256
|
+
this.options = options;
|
|
257
|
+
this.clientDetails = clientDetails;
|
|
258
|
+
this.isSummarizerClient = this.clientDetails.type === summary_1.summarizerClientType;
|
|
259
|
+
this.loadedFromVersionId = context.getLoadedFromVersion()?.id;
|
|
260
|
+
this._getClientId = () => context.clientId;
|
|
261
|
+
this._getAttachState = () => context.attachState;
|
|
262
|
+
this.getAbsoluteUrl = async (relativeUrl) => {
|
|
263
|
+
if (context.getAbsoluteUrl === undefined) {
|
|
264
|
+
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
265
|
+
}
|
|
266
|
+
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
return context.getAbsoluteUrl(relativeUrl);
|
|
249
270
|
};
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.
|
|
271
|
+
// TODO: Consider that the Container could just listen to these events itself, or even more appropriately maybe the
|
|
272
|
+
// customer should observe dirty state on the runtime (the owner of dirty state) directly, rather than on the IContainer.
|
|
273
|
+
this.on("dirty", () => context.updateDirtyContainerState(true));
|
|
274
|
+
this.on("saved", () => context.updateDirtyContainerState(false));
|
|
275
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
276
|
+
this.disposeFn = disposeFn ?? closeFn;
|
|
277
|
+
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
278
|
+
this.closeFn = this.isSummarizerClient ? this.disposeFn : closeFn;
|
|
279
|
+
this.mc = (0, telemetry_utils_1.createChildMonitoringContext)({
|
|
280
|
+
logger: this.logger,
|
|
281
|
+
namespace: "ContainerRuntime",
|
|
282
|
+
});
|
|
253
283
|
let loadSummaryNumber;
|
|
254
284
|
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
255
285
|
// get the values from the metadata blob.
|
|
256
286
|
if (existing) {
|
|
257
287
|
this.createContainerMetadata = {
|
|
258
|
-
createContainerRuntimeVersion: metadata
|
|
259
|
-
createContainerTimestamp: metadata
|
|
288
|
+
createContainerRuntimeVersion: metadata?.createContainerRuntimeVersion,
|
|
289
|
+
createContainerTimestamp: metadata?.createContainerTimestamp,
|
|
260
290
|
};
|
|
261
291
|
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
262
292
|
// the count is reset to 0.
|
|
263
|
-
loadSummaryNumber =
|
|
293
|
+
loadSummaryNumber = metadata?.summaryNumber ?? 0;
|
|
264
294
|
// Enabling the IdCompressor is a one-way operation and we only want to
|
|
265
295
|
// allow new containers to turn it on
|
|
266
|
-
this.idCompressorEnabled =
|
|
296
|
+
this.idCompressorEnabled = metadata?.idCompressorEnabled ?? false;
|
|
267
297
|
}
|
|
268
298
|
else {
|
|
269
299
|
this.createContainerMetadata = {
|
|
@@ -272,24 +302,27 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
272
302
|
};
|
|
273
303
|
loadSummaryNumber = 0;
|
|
274
304
|
this.idCompressorEnabled =
|
|
275
|
-
|
|
305
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.IdCompressorEnabled") ??
|
|
306
|
+
idCompressor !== undefined;
|
|
276
307
|
}
|
|
277
308
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
278
|
-
this.messageAtLastSummary = metadata
|
|
279
|
-
|
|
280
|
-
|
|
309
|
+
this.messageAtLastSummary = metadata?.message;
|
|
310
|
+
// Note that we only need to pull the *initial* connected state from the context.
|
|
311
|
+
// Later updates come through calls to setConnectionState.
|
|
312
|
+
this._connected = connected;
|
|
313
|
+
this.gcTombstoneEnforcementAllowed = (0, gc_1.shouldAllowGcTombstoneEnforcement)(metadata?.gcFeatureMatrix?.tombstoneGeneration /* persisted */, this.runtimeOptions.gcOptions[gc_1.gcTombstoneGenerationOptionName] /* current */);
|
|
281
314
|
this.mc.logger.sendTelemetryEvent({
|
|
282
315
|
eventName: "GCFeatureMatrix",
|
|
283
|
-
metadataValue: JSON.stringify(metadata
|
|
316
|
+
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
284
317
|
inputs: JSON.stringify({
|
|
285
318
|
gcOptions_gcTombstoneGeneration: this.runtimeOptions.gcOptions[gc_1.gcTombstoneGenerationOptionName],
|
|
286
319
|
}),
|
|
287
320
|
});
|
|
288
|
-
this.telemetryDocumentId =
|
|
321
|
+
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? (0, uuid_1.v4)();
|
|
289
322
|
this.disableAttachReorder = this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder");
|
|
290
323
|
const disableChunking = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionChunkingDisabled");
|
|
291
324
|
const opGroupingManager = new opLifecycle_1.OpGroupingManager(this.groupedBatchingEnabled);
|
|
292
|
-
const opSplitter = new opLifecycle_1.OpSplitter(chunks, this.
|
|
325
|
+
const opSplitter = new opLifecycle_1.OpSplitter(chunks, this.submitBatchFn, disableChunking === true ? Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
293
326
|
this.remoteMessageProcessor = new opLifecycle_1.RemoteMessageProcessor(opSplitter, new opLifecycle_1.OpDecompressor(this.mc.logger), opGroupingManager);
|
|
294
327
|
this.handleContext = new containerHandleContext_1.ContainerFluidHandleContext("", this);
|
|
295
328
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -308,9 +341,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
308
341
|
this.idCompressor = idCompressor;
|
|
309
342
|
}
|
|
310
343
|
this.maxConsecutiveReconnects =
|
|
311
|
-
|
|
344
|
+
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ??
|
|
345
|
+
this.defaultMaxConsecutiveReconnects;
|
|
312
346
|
if (runtimeOptions.flushMode === runtime_definitions_1.FlushModeExperimental.Async &&
|
|
313
|
-
|
|
347
|
+
supportedFeatures?.get("referenceSequenceNumbers") !== true) {
|
|
314
348
|
// The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
|
|
315
349
|
this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
|
|
316
350
|
this._flushMode = runtime_definitions_1.FlushMode.TurnBased;
|
|
@@ -318,39 +352,39 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
318
352
|
else {
|
|
319
353
|
this._flushMode = runtimeOptions.flushMode;
|
|
320
354
|
}
|
|
321
|
-
const pendingRuntimeState =
|
|
322
|
-
const maxSnapshotCacheDurationMs =
|
|
355
|
+
const pendingRuntimeState = pendingLocalState;
|
|
356
|
+
const maxSnapshotCacheDurationMs = this._storage?.policies?.maximumCacheDurationMs;
|
|
323
357
|
if (maxSnapshotCacheDurationMs !== undefined &&
|
|
324
358
|
maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
325
359
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
326
360
|
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
327
361
|
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
328
|
-
throw new
|
|
362
|
+
throw new telemetry_utils_1.UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
329
363
|
}
|
|
330
364
|
this.garbageCollector = gc_1.GarbageCollector.create({
|
|
331
365
|
runtime: this,
|
|
332
366
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
333
|
-
baseSnapshot
|
|
367
|
+
baseSnapshot,
|
|
334
368
|
baseLogger: this.mc.logger,
|
|
335
369
|
existing,
|
|
336
370
|
metadata,
|
|
337
371
|
createContainerMetadata: this.createContainerMetadata,
|
|
338
|
-
isSummarizerClient: this.
|
|
372
|
+
isSummarizerClient: this.isSummarizerClient,
|
|
339
373
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
340
|
-
getLastSummaryTimestampMs: () =>
|
|
374
|
+
getLastSummaryTimestampMs: () => this.messageAtLastSummary?.timestamp,
|
|
341
375
|
readAndParseBlob: async (id) => (0, driver_utils_1.readAndParse)(this.storage, id),
|
|
342
376
|
// GC runs in summarizer client and needs access to the real (non-proxy) active information. The proxy
|
|
343
377
|
// delta manager would always return false for summarizer client.
|
|
344
378
|
activeConnection: () => this.innerDeltaManager.active,
|
|
345
379
|
});
|
|
346
380
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
347
|
-
this.summarizerNode = (0, summary_1.createRootSummarizerNodeWithGC)(telemetry_utils_1.
|
|
381
|
+
this.summarizerNode = (0, summary_1.createRootSummarizerNodeWithGC)((0, telemetry_utils_1.createChildLogger)({ logger: this.logger, namespace: "SummarizerNode" }),
|
|
348
382
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
349
383
|
async (fullTree, trackState, telemetryContext) => this.summarizeInternal(fullTree, trackState, telemetryContext),
|
|
350
384
|
// Latest change sequence number, no changes since summary applied yet
|
|
351
385
|
loadedFromSequenceNumber,
|
|
352
386
|
// Summary reference sequence number, undefined if no summary yet
|
|
353
|
-
|
|
387
|
+
baseSnapshot !== undefined ? loadedFromSequenceNumber : undefined, {
|
|
354
388
|
// Must set to false to prevent sending summary handle which would be pointing to
|
|
355
389
|
// a summary with an older protocol state.
|
|
356
390
|
canReuseHandle: false,
|
|
@@ -364,19 +398,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
364
398
|
async (fullGC) => this.getGCDataInternal(fullGC),
|
|
365
399
|
// Function to get the GC details from the base snapshot we loaded from.
|
|
366
400
|
async () => this.garbageCollector.getBaseGCDetails());
|
|
367
|
-
if (
|
|
368
|
-
this.summarizerNode.updateBaseSummaryState(
|
|
401
|
+
if (baseSnapshot) {
|
|
402
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
369
403
|
}
|
|
370
|
-
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(
|
|
404
|
+
this.dataStores = new dataStores_1.DataStores((0, dataStores_1.getSummaryForDatastores)(baseSnapshot, metadata), this, (attachMsg) => this.submit({ type: ContainerMessageType.Attach, contents: attachMsg }), (id, createParam) => (summarizeInternal, getGCDataFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap));
|
|
371
405
|
this.blobManager = new blobManager_1.BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
372
406
|
if (!this.disposed) {
|
|
373
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
407
|
+
this.submit({ type: ContainerMessageType.BlobAttach, contents: undefined }, undefined, {
|
|
374
408
|
localId,
|
|
375
409
|
blobId,
|
|
376
410
|
});
|
|
377
411
|
}
|
|
378
|
-
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState
|
|
379
|
-
this.scheduleManager = new scheduleManager_1.ScheduleManager(
|
|
412
|
+
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (blobPath) => this.garbageCollector.isNodeDeleted(blobPath), this, pendingRuntimeState?.pendingAttachmentBlobs, (error) => this.closeFn(error));
|
|
413
|
+
this.scheduleManager = new scheduleManager_1.ScheduleManager(this.innerDeltaManager, this, () => this.clientId, (0, telemetry_utils_1.createChildLogger)({ logger: this.logger, namespace: "ScheduleManager" }));
|
|
380
414
|
this.pendingStateManager = new pendingStateManager_1.PendingStateManager({
|
|
381
415
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
382
416
|
clientId: () => this.clientId,
|
|
@@ -384,7 +418,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
384
418
|
connected: () => this.connected,
|
|
385
419
|
reSubmit: this.reSubmit.bind(this),
|
|
386
420
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
387
|
-
|
|
421
|
+
isActiveConnection: () => this.innerDeltaManager.active,
|
|
422
|
+
}, pendingRuntimeState?.pending, this.logger);
|
|
388
423
|
const disableCompression = this.mc.config.getBoolean("Fluid.ContainerRuntime.CompressionDisabled");
|
|
389
424
|
const compressionOptions = disableCompression === true
|
|
390
425
|
? {
|
|
@@ -393,10 +428,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
393
428
|
}
|
|
394
429
|
: runtimeOptions.compressionOptions;
|
|
395
430
|
const disablePartialFlush = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisablePartialFlush");
|
|
431
|
+
const legacySendBatchFn = (0, exports.makeLegacySendBatchFn)(this.submitFn, this.innerDeltaManager);
|
|
396
432
|
this.outbox = new opLifecycle_1.Outbox({
|
|
397
433
|
shouldSend: () => this.canSendOps(),
|
|
398
434
|
pendingStateManager: this.pendingStateManager,
|
|
399
|
-
|
|
435
|
+
submitBatchFn: this.submitBatchFn,
|
|
436
|
+
legacySendBatchFn,
|
|
400
437
|
compressor: new opLifecycle_1.OpCompressor(this.mc.logger),
|
|
401
438
|
splitter: opSplitter,
|
|
402
439
|
config: {
|
|
@@ -415,44 +452,50 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
415
452
|
opReentrancy: () => this.ensureNoDataModelChangesCalls > 0,
|
|
416
453
|
closeContainer: this.closeFn,
|
|
417
454
|
});
|
|
418
|
-
this.
|
|
455
|
+
this._quorum = quorum;
|
|
456
|
+
this._quorum.on("removeMember", (clientId) => {
|
|
419
457
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
420
458
|
});
|
|
421
|
-
|
|
459
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
460
|
+
this._audience = audience;
|
|
422
461
|
const closeSummarizerDelayOverride = this.mc.config.getNumber("Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs");
|
|
423
|
-
this.closeSummarizerDelayMs = closeSummarizerDelayOverride
|
|
462
|
+
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
424
463
|
this.validateSummaryBeforeUpload =
|
|
425
|
-
|
|
464
|
+
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
426
465
|
this.summaryCollection = new summary_1.SummaryCollection(this.deltaManager, this.logger);
|
|
427
466
|
this.dirtyContainer =
|
|
428
|
-
this.
|
|
429
|
-
|
|
430
|
-
this.context.updateDirtyContainerState(this.dirtyContainer);
|
|
467
|
+
this.attachState !== container_definitions_1.AttachState.Attached || this.hasPendingMessages();
|
|
468
|
+
context.updateDirtyContainerState(this.dirtyContainer);
|
|
431
469
|
if (this.summariesDisabled) {
|
|
432
470
|
this.mc.logger.sendTelemetryEvent({ eventName: "SummariesDisabled" });
|
|
433
471
|
}
|
|
434
472
|
else {
|
|
435
|
-
const orderedClientLogger =
|
|
436
|
-
|
|
437
|
-
|
|
473
|
+
const orderedClientLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
474
|
+
logger: this.logger,
|
|
475
|
+
namespace: "OrderedClientElection",
|
|
476
|
+
});
|
|
477
|
+
const orderedClientCollection = new summary_1.OrderedClientCollection(orderedClientLogger, this.innerDeltaManager, this._quorum);
|
|
478
|
+
const orderedClientElectionForSummarizer = new summary_1.OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData ?? this.innerDeltaManager.lastSequenceNumber, summary_1.SummarizerClientElection.isClientEligible);
|
|
438
479
|
this.summarizerClientElection = new summary_1.SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, this.maxOpsSinceLastSummary);
|
|
439
|
-
if (this.
|
|
480
|
+
if (this.isSummarizerClient) {
|
|
440
481
|
this._summarizer = new summary_1.Summarizer(this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => summary_1.RunWhileConnectedCoordinator.create(runtime,
|
|
441
482
|
// Summarization runs in summarizer client and needs access to the real (non-proxy) active
|
|
442
483
|
// information. The proxy delta manager would always return false for summarizer client.
|
|
443
484
|
() => this.innerDeltaManager.active));
|
|
444
485
|
}
|
|
445
|
-
else if (summary_1.SummarizerClientElection.clientDetailsPermitElection(this.
|
|
486
|
+
else if (summary_1.SummarizerClientElection.clientDetailsPermitElection(this.clientDetails)) {
|
|
446
487
|
// Only create a SummaryManager and SummarizerClientElection
|
|
447
488
|
// if summaries are enabled and we are not the summarizer client.
|
|
448
489
|
const defaultAction = () => {
|
|
449
490
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
450
|
-
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
491
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
451
492
|
// unregister default to no log on every op after falling behind
|
|
452
493
|
// and register summary ack handler to re-register this handler
|
|
453
494
|
// after successful summary
|
|
454
495
|
this.summaryCollection.once(protocol_definitions_1.MessageType.SummaryAck, () => {
|
|
455
|
-
this.logger.sendTelemetryEvent({
|
|
496
|
+
this.mc.logger.sendTelemetryEvent({
|
|
497
|
+
eventName: "SummaryStatus:CaughtUp",
|
|
498
|
+
});
|
|
456
499
|
// we've caught up, so re-register the default action to monitor for
|
|
457
500
|
// falling behind, and unregister ourself
|
|
458
501
|
this.summaryCollection.on("default", defaultAction);
|
|
@@ -463,12 +506,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
463
506
|
this.summaryCollection.on("default", defaultAction);
|
|
464
507
|
// Create the SummaryManager and mark the initial state
|
|
465
508
|
this.summaryManager = new summary_1.SummaryManager(this.summarizerClientElection, this, // IConnectedState
|
|
466
|
-
this.summaryCollection, this.logger, this.formRequestSummarizerFn(
|
|
509
|
+
this.summaryCollection, this.logger, this.formRequestSummarizerFn(loader), new throttler_1.Throttler(60 * 1000, // 60 sec delay window
|
|
467
510
|
30 * 1000, // 30 sec max delay
|
|
468
511
|
// throttling function increases exponentially (0ms, 40ms, 80ms, 160ms, etc)
|
|
469
512
|
(0, throttler_1.formExponentialFn)({ coefficient: 20, initialDelay: 0 })), {
|
|
470
513
|
initialDelayMs: this.initialSummarizerDelayMs,
|
|
471
514
|
}, this.heuristicsDisabled);
|
|
515
|
+
this.summaryManager.on("summarize", (eventProps) => {
|
|
516
|
+
this.emit("summarize", eventProps);
|
|
517
|
+
});
|
|
472
518
|
this.summaryManager.start();
|
|
473
519
|
}
|
|
474
520
|
}
|
|
@@ -476,7 +522,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
476
522
|
// we accumulate ops while being in read-only state.
|
|
477
523
|
// once user gets write permissions and we have active connection, flush all pending ops.
|
|
478
524
|
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
479
|
-
(0,
|
|
525
|
+
(0, core_utils_1.assert)(readonly === this.innerDeltaManager.readOnlyInfo.readonly, 0x124 /* "inconsistent readonly property/event state" */);
|
|
480
526
|
// We need to be very careful with when we (re)send pending ops, to ensure that we only send ops
|
|
481
527
|
// when we either never send an op, or attempted to send it but we know for sure it was not
|
|
482
528
|
// sequenced by server and will never be sequenced (i.e. was lost)
|
|
@@ -489,31 +535,48 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
489
535
|
// can rely on same safety mechanism and resend ops only when we establish new connection.
|
|
490
536
|
// This is applicable for read-only permissions (event is raised before connection is properly registered),
|
|
491
537
|
// but it's an extra requirement for Container.forceReadonly() API
|
|
492
|
-
(0,
|
|
538
|
+
(0, core_utils_1.assert)(!readonly || !this.connected, 0x125 /* "Unsafe to transition to read-only state!" */);
|
|
493
539
|
this.replayPendingStates();
|
|
494
540
|
});
|
|
495
541
|
// logging hardware telemetry
|
|
496
|
-
logger.sendTelemetryEvent(
|
|
497
|
-
|
|
542
|
+
logger.sendTelemetryEvent({
|
|
543
|
+
eventName: "DeviceSpec",
|
|
544
|
+
...getDeviceSpec(),
|
|
545
|
+
});
|
|
546
|
+
this.mc.logger.sendTelemetryEvent({
|
|
547
|
+
eventName: "ContainerLoadStats",
|
|
548
|
+
...this.createContainerMetadata,
|
|
549
|
+
...this.dataStores.containerLoadStats,
|
|
550
|
+
summaryNumber: loadSummaryNumber,
|
|
551
|
+
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
552
|
+
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
553
|
+
gcVersion: metadata?.gcFeature,
|
|
554
|
+
options: JSON.stringify(runtimeOptions),
|
|
555
|
+
featureGates: JSON.stringify({
|
|
498
556
|
disableCompression,
|
|
499
557
|
disableOpReentryCheck,
|
|
500
558
|
disableChunking,
|
|
501
559
|
disableAttachReorder: this.disableAttachReorder,
|
|
502
560
|
disablePartialFlush,
|
|
503
561
|
idCompressorEnabled: this.idCompressorEnabled,
|
|
504
|
-
summaryStateUpdateMethod: this.summaryStateUpdateMethod,
|
|
505
562
|
closeSummarizerDelayOverride,
|
|
506
|
-
}),
|
|
507
|
-
|
|
563
|
+
}),
|
|
564
|
+
telemetryDocumentId: this.telemetryDocumentId,
|
|
565
|
+
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
566
|
+
});
|
|
567
|
+
(0, connectionTelemetry_1.ReportOpPerfTelemetry)(this.clientId, this.deltaManager, this.logger);
|
|
508
568
|
(0, batchTracker_1.BindBatchTracker)(this, this.logger);
|
|
509
569
|
this.entryPoint = new core_utils_1.LazyPromise(async () => {
|
|
510
|
-
if (this.
|
|
511
|
-
(0,
|
|
570
|
+
if (this.isSummarizerClient) {
|
|
571
|
+
(0, core_utils_1.assert)(this._summarizer !== undefined, 0x5bf /* Summarizer object is undefined in a summarizer client */);
|
|
512
572
|
return this._summarizer;
|
|
513
573
|
}
|
|
514
|
-
return initializeEntryPoint
|
|
574
|
+
return initializeEntryPoint?.(this);
|
|
515
575
|
});
|
|
516
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
579
|
+
*/
|
|
517
580
|
get IFluidRouter() {
|
|
518
581
|
return this;
|
|
519
582
|
}
|
|
@@ -559,26 +622,38 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
559
622
|
* This object should provide all the functionality that the Container is expected to provide to the loader layer.
|
|
560
623
|
*/
|
|
561
624
|
static async loadRuntime(params) {
|
|
562
|
-
|
|
563
|
-
const
|
|
625
|
+
const { context, registryEntries, existing, requestHandler, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, } = params;
|
|
626
|
+
const initializeEntryPoint = params.initializeEntryPoint ??
|
|
627
|
+
(async (containerRuntime) => ({
|
|
628
|
+
get IFluidRouter() {
|
|
629
|
+
return this;
|
|
630
|
+
},
|
|
631
|
+
async request(req) {
|
|
632
|
+
return containerRuntime.request(req);
|
|
633
|
+
},
|
|
634
|
+
}));
|
|
564
635
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
565
636
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
566
637
|
const backCompatContext = context;
|
|
567
|
-
const passLogger =
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
638
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
639
|
+
// eslint-disable-next-line import/no-deprecated
|
|
640
|
+
new telemetry_utils_1.TaggedLoggerAdapter(backCompatContext.logger);
|
|
641
|
+
const logger = (0, telemetry_utils_1.createChildLogger)({
|
|
642
|
+
logger: passLogger,
|
|
643
|
+
properties: {
|
|
644
|
+
all: {
|
|
645
|
+
runtimeVersion: packageVersion_1.pkgVersion,
|
|
646
|
+
},
|
|
571
647
|
},
|
|
572
648
|
});
|
|
573
649
|
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, compressionOptions = defaultCompressionConfig, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, enableRuntimeIdCompressor = false, chunkSizeInBytes = defaultChunkSizeInBytes, enableOpReentryCheck = false, enableGroupedBatching = false, } = runtimeOptions;
|
|
574
650
|
const registry = new dataStoreRegistry_1.FluidDataStoreRegistry(registryEntries);
|
|
575
651
|
const tryFetchBlob = async (blobName) => {
|
|
576
|
-
|
|
577
|
-
const blobId = (_a = context.baseSnapshot) === null || _a === void 0 ? void 0 : _a.blobs[blobName];
|
|
652
|
+
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
578
653
|
if (context.baseSnapshot && blobId) {
|
|
579
654
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
580
655
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
581
|
-
(0,
|
|
656
|
+
(0, core_utils_1.assert)(context.storage !== undefined, 0x1f5 /* "Attached state should have storage" */);
|
|
582
657
|
return (0, driver_utils_1.readAndParse)(context.storage, blobId);
|
|
583
658
|
}
|
|
584
659
|
};
|
|
@@ -589,16 +664,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
589
664
|
tryFetchBlob(summary_1.aliasBlobName),
|
|
590
665
|
tryFetchBlob(summary_1.idCompressorBlobName),
|
|
591
666
|
]);
|
|
592
|
-
const loadExisting = existing === true || context.existing === true;
|
|
593
667
|
// read snapshot blobs needed for BlobManager to load
|
|
594
|
-
const blobManagerSnapshot = await blobManager_1.BlobManager.load(
|
|
668
|
+
const blobManagerSnapshot = await blobManager_1.BlobManager.load(context.baseSnapshot?.trees[summary_1.blobsTreeName], async (id) => {
|
|
595
669
|
// IContainerContext storage api return type still has undefined in 0.39 package version.
|
|
596
670
|
// So once we release 0.40 container-defn package we can remove this check.
|
|
597
|
-
(0,
|
|
671
|
+
(0, core_utils_1.assert)(context.storage !== undefined, 0x256 /* "storage undefined in attached container" */);
|
|
598
672
|
return (0, driver_utils_1.readAndParse)(context.storage, id);
|
|
599
673
|
});
|
|
600
674
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
601
|
-
const runtimeSequenceNumber =
|
|
675
|
+
const runtimeSequenceNumber = metadata?.message?.sequenceNumber;
|
|
602
676
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
603
677
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
604
678
|
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
@@ -606,29 +680,27 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
606
680
|
if (loadSequenceNumberVerification !== "bypass" &&
|
|
607
681
|
runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
608
682
|
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
609
|
-
const error = new
|
|
683
|
+
const error = new telemetry_utils_1.DataCorruptionError(
|
|
610
684
|
// pre-0.58 error message: SummaryMetadataMismatch
|
|
611
685
|
"Summary metadata mismatch", { runtimeVersion: packageVersion_1.pkgVersion, runtimeSequenceNumber, protocolSequenceNumber });
|
|
612
686
|
if (loadSequenceNumberVerification === "log") {
|
|
613
687
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
614
688
|
}
|
|
615
689
|
else {
|
|
616
|
-
// Call both close and dispose as closeFn implementation will no longer dispose runtime in future
|
|
617
690
|
context.closeFn(error);
|
|
618
|
-
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
619
691
|
}
|
|
620
692
|
}
|
|
621
693
|
}
|
|
622
|
-
const idCompressorEnabled =
|
|
694
|
+
const idCompressorEnabled = metadata?.idCompressorEnabled ?? runtimeOptions.enableRuntimeIdCompressor ?? false;
|
|
623
695
|
let idCompressor;
|
|
624
696
|
if (idCompressorEnabled) {
|
|
625
697
|
const { IdCompressor, createSessionId } = await Promise.resolve().then(() => __importStar(require("./id-compressor")));
|
|
626
698
|
idCompressor =
|
|
627
699
|
serializedIdCompressor !== undefined
|
|
628
700
|
? IdCompressor.deserialize(serializedIdCompressor, createSessionId())
|
|
629
|
-
:
|
|
701
|
+
: IdCompressor.create(logger);
|
|
630
702
|
}
|
|
631
|
-
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks
|
|
703
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], {
|
|
632
704
|
summaryOptions,
|
|
633
705
|
gcOptions,
|
|
634
706
|
loadSequenceNumberVerification,
|
|
@@ -639,8 +711,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
639
711
|
enableRuntimeIdCompressor,
|
|
640
712
|
enableOpReentryCheck,
|
|
641
713
|
enableGroupedBatching,
|
|
642
|
-
}, containerScope, logger,
|
|
714
|
+
}, containerScope, logger, existing, blobManagerSnapshot, context.storage, idCompressor, requestHandler, undefined, // summaryConfiguration
|
|
643
715
|
initializeEntryPoint);
|
|
716
|
+
await runtime.blobManager.processStashedChanges();
|
|
644
717
|
// It's possible to have ops with a reference sequence number of 0. Op sequence numbers start
|
|
645
718
|
// at 1, so we won't see a replayed saved op with a sequence number of 0.
|
|
646
719
|
await runtime.pendingStateManager.applyStashedOpsAt(0);
|
|
@@ -648,38 +721,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
648
721
|
await runtime.initializeBaseState();
|
|
649
722
|
return runtime;
|
|
650
723
|
}
|
|
651
|
-
get options() {
|
|
652
|
-
return this.context.options;
|
|
653
|
-
}
|
|
654
724
|
get clientId() {
|
|
655
|
-
return this.
|
|
656
|
-
}
|
|
657
|
-
get clientDetails() {
|
|
658
|
-
return this.context.clientDetails;
|
|
725
|
+
return this._getClientId();
|
|
659
726
|
}
|
|
660
727
|
get storage() {
|
|
661
728
|
return this._storage;
|
|
662
729
|
}
|
|
730
|
+
/** @deprecated - The functionality is no longer exposed publicly */
|
|
663
731
|
get reSubmitFn() {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
667
|
-
get disposeFn() {
|
|
668
|
-
var _a;
|
|
669
|
-
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
670
|
-
return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
|
|
671
|
-
}
|
|
672
|
-
get closeFn() {
|
|
673
|
-
if (this._summarizer !== undefined) {
|
|
674
|
-
// In cases of summarizer, we want to dispose instead since consumer doesn't interact with this container
|
|
675
|
-
return this.disposeFn;
|
|
676
|
-
}
|
|
677
|
-
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
678
|
-
return (error) => {
|
|
679
|
-
var _a, _b;
|
|
680
|
-
this.context.closeFn(error);
|
|
681
|
-
(_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
682
|
-
};
|
|
732
|
+
return (type, contents, localOpMetadata, opMetadata) => this.reSubmitCore({ type, contents }, localOpMetadata, opMetadata);
|
|
733
|
+
// Note: compatDetails is not included in this deprecated API
|
|
683
734
|
}
|
|
684
735
|
get flushMode() {
|
|
685
736
|
return this._flushMode;
|
|
@@ -691,7 +742,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
691
742
|
return this.registry;
|
|
692
743
|
}
|
|
693
744
|
get attachState() {
|
|
694
|
-
return this.
|
|
745
|
+
return this._getAttachState();
|
|
695
746
|
}
|
|
696
747
|
get IFluidHandleContext() {
|
|
697
748
|
return this.handleContext;
|
|
@@ -718,14 +769,13 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
718
769
|
}
|
|
719
770
|
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
720
771
|
get summarizerClientId() {
|
|
721
|
-
|
|
722
|
-
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
772
|
+
return this.summarizerClientElection?.electedClientId;
|
|
723
773
|
}
|
|
724
774
|
get disposed() {
|
|
725
775
|
return this._disposed;
|
|
726
776
|
}
|
|
727
777
|
get summarizer() {
|
|
728
|
-
(0,
|
|
778
|
+
(0, core_utils_1.assert)(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
729
779
|
return this._summarizer;
|
|
730
780
|
}
|
|
731
781
|
isSummariesDisabled() {
|
|
@@ -756,12 +806,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
756
806
|
await this.garbageCollector.initializeBaseState();
|
|
757
807
|
}
|
|
758
808
|
dispose(error) {
|
|
759
|
-
var _a;
|
|
760
809
|
if (this._disposed) {
|
|
761
810
|
return;
|
|
762
811
|
}
|
|
763
812
|
this._disposed = true;
|
|
764
|
-
this.logger.sendTelemetryEvent({
|
|
813
|
+
this.mc.logger.sendTelemetryEvent({
|
|
765
814
|
eventName: "ContainerRuntimeDisposed",
|
|
766
815
|
isDirty: this.isDirty,
|
|
767
816
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
@@ -771,7 +820,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
771
820
|
this.summaryManager.dispose();
|
|
772
821
|
}
|
|
773
822
|
this.garbageCollector.dispose();
|
|
774
|
-
|
|
823
|
+
this._summarizer?.dispose();
|
|
775
824
|
this.dataStores.dispose();
|
|
776
825
|
this.pendingStateManager.dispose();
|
|
777
826
|
this.emit("dispose");
|
|
@@ -780,6 +829,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
780
829
|
/**
|
|
781
830
|
* Notifies this object about the request made to the container.
|
|
782
831
|
* @param request - Request made to the handler.
|
|
832
|
+
* @deprecated - Will be removed in future major release. Migrate all usage of IFluidRouter to the "entryPoint" pattern. Refer to Removing-IFluidRouter.md
|
|
783
833
|
*/
|
|
784
834
|
async request(request) {
|
|
785
835
|
try {
|
|
@@ -830,8 +880,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
830
880
|
const subRequest = requestParser.createSubRequest(1);
|
|
831
881
|
// We always expect createSubRequest to include a leading slash, but asserting here to protect against
|
|
832
882
|
// unintentionally modifying the url if that changes.
|
|
833
|
-
(0,
|
|
834
|
-
return dataStore.
|
|
883
|
+
(0, core_utils_1.assert)(subRequest.url.startsWith("/"), 0x126 /* "Expected createSubRequest url to include a leading slash" */);
|
|
884
|
+
return dataStore.request(subRequest);
|
|
835
885
|
}
|
|
836
886
|
return (0, runtime_utils_1.create404Response)(request);
|
|
837
887
|
}
|
|
@@ -846,19 +896,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
846
896
|
return this.entryPoint;
|
|
847
897
|
}
|
|
848
898
|
internalId(maybeAlias) {
|
|
849
|
-
|
|
850
|
-
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
899
|
+
return this.dataStores.aliases.get(maybeAlias) ?? maybeAlias;
|
|
851
900
|
}
|
|
852
901
|
async getDataStoreFromRequest(id, request) {
|
|
853
|
-
var _a, _b, _c;
|
|
854
902
|
const headerData = {};
|
|
855
|
-
if (typeof
|
|
903
|
+
if (typeof request.headers?.[RuntimeHeaders.wait] === "boolean") {
|
|
856
904
|
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
857
905
|
}
|
|
858
|
-
if (typeof
|
|
906
|
+
if (typeof request.headers?.[RuntimeHeaders.viaHandle] === "boolean") {
|
|
859
907
|
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
860
908
|
}
|
|
861
|
-
if (typeof
|
|
909
|
+
if (typeof request.headers?.[exports.AllowTombstoneRequestHeaderKey] === "boolean") {
|
|
862
910
|
headerData.allowTombstone = request.headers[exports.AllowTombstoneRequestHeaderKey];
|
|
863
911
|
}
|
|
864
912
|
await this.dataStores.waitIfPendingAlias(id);
|
|
@@ -868,25 +916,30 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
868
916
|
// Remove query params, leading and trailing slashes from the url. This is done to make sure the format is
|
|
869
917
|
// the same as GC nodes id.
|
|
870
918
|
const urlWithoutQuery = (0, gc_1.trimLeadingAndTrailingSlashes)(request.url.split("?")[0]);
|
|
871
|
-
this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request
|
|
919
|
+
this.garbageCollector.nodeUpdated(`/${urlWithoutQuery}`, "Loaded", undefined /* timestampMs */, dataStoreContext.packagePath, request?.headers);
|
|
872
920
|
return dataStoreChannel;
|
|
873
921
|
}
|
|
874
922
|
/** Adds the container's metadata to the given summary tree. */
|
|
875
923
|
addMetadataToSummary(summaryTree) {
|
|
876
|
-
|
|
877
|
-
|
|
924
|
+
const metadata = {
|
|
925
|
+
...this.createContainerMetadata,
|
|
878
926
|
// Increment the summary number for the next summary that will be generated.
|
|
879
|
-
summaryNumber: this.nextSummaryNumber++,
|
|
927
|
+
summaryNumber: this.nextSummaryNumber++,
|
|
928
|
+
summaryFormatVersion: 1,
|
|
929
|
+
...this.garbageCollector.getMetadata(),
|
|
880
930
|
// The last message processed at the time of summary. If there are no new messages, use the message from the
|
|
881
931
|
// last summary.
|
|
882
|
-
message: (
|
|
932
|
+
message: (0, summary_1.extractSummaryMetadataMessage)(this.deltaManager.lastMessage) ??
|
|
933
|
+
this.messageAtLastSummary,
|
|
934
|
+
telemetryDocumentId: this.telemetryDocumentId,
|
|
935
|
+
idCompressorEnabled: this.idCompressorEnabled ? true : undefined,
|
|
936
|
+
};
|
|
883
937
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.metadataBlobName, JSON.stringify(metadata));
|
|
884
938
|
}
|
|
885
939
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
886
|
-
var _a;
|
|
887
940
|
this.addMetadataToSummary(summaryTree);
|
|
888
941
|
if (this.idCompressorEnabled) {
|
|
889
|
-
(0,
|
|
942
|
+
(0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67a /* IdCompressor should be defined if enabled */);
|
|
890
943
|
const idCompressorState = JSON.stringify(this.idCompressor.serialize(false));
|
|
891
944
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.idCompressorBlobName, idCompressorState);
|
|
892
945
|
}
|
|
@@ -899,7 +952,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
899
952
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.aliasBlobName, JSON.stringify([...dataStoreAliases]));
|
|
900
953
|
}
|
|
901
954
|
if (this.summarizerClientElection) {
|
|
902
|
-
const electedSummarizerContent = JSON.stringify(
|
|
955
|
+
const electedSummarizerContent = JSON.stringify(this.summarizerClientElection?.serialize());
|
|
903
956
|
(0, runtime_utils_1.addBlobToSummary)(summaryTree, summary_1.electedSummarizerBlobName, electedSummarizerContent);
|
|
904
957
|
}
|
|
905
958
|
const blobManagerSummary = this.blobManager.summarize();
|
|
@@ -936,7 +989,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
936
989
|
this.mc.logger.sendTelemetryEvent({
|
|
937
990
|
eventName: "ReconnectsWithNoProgress",
|
|
938
991
|
attempts: this.consecutiveReconnects,
|
|
939
|
-
pendingMessages: this.
|
|
992
|
+
pendingMessages: this.pendingMessagesCount,
|
|
940
993
|
});
|
|
941
994
|
}
|
|
942
995
|
return this.consecutiveReconnects < this.maxConsecutiveReconnects;
|
|
@@ -946,7 +999,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
946
999
|
// in their own batches before the originating batch is sent.
|
|
947
1000
|
// Therefore, receiving them while attempting to send the originating batch
|
|
948
1001
|
// does not mean that the container is making any progress.
|
|
949
|
-
if (
|
|
1002
|
+
if (message?.type !== ContainerMessageType.ChunkedOp) {
|
|
950
1003
|
this.consecutiveReconnects = 0;
|
|
951
1004
|
}
|
|
952
1005
|
}
|
|
@@ -961,7 +1014,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
961
1014
|
// Save the old state, reset to false, disable event emit
|
|
962
1015
|
const oldState = this.dirtyContainer;
|
|
963
1016
|
this.dirtyContainer = false;
|
|
964
|
-
(0,
|
|
1017
|
+
(0, core_utils_1.assert)(this.emitDirtyDocumentEvent, 0x127 /* "dirty document event not set on replay" */);
|
|
965
1018
|
this.emitDirtyDocumentEvent = false;
|
|
966
1019
|
let newState;
|
|
967
1020
|
try {
|
|
@@ -995,21 +1048,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
995
1048
|
* ! Note: this format needs to be in-line with what is set in the "ContainerRuntime.submit(...)" method
|
|
996
1049
|
*/
|
|
997
1050
|
parseOpContent(serializedContent) {
|
|
998
|
-
(0,
|
|
999
|
-
const
|
|
1000
|
-
(0,
|
|
1001
|
-
return { type
|
|
1051
|
+
(0, core_utils_1.assert)(serializedContent !== undefined, 0x6d5 /* content must be defined */);
|
|
1052
|
+
const { type, contents, compatDetails } = JSON.parse(serializedContent);
|
|
1053
|
+
(0, core_utils_1.assert)(type !== undefined, 0x6d6 /* incorrect op content format */);
|
|
1054
|
+
return { type, contents, compatDetails };
|
|
1002
1055
|
}
|
|
1003
1056
|
async applyStashedOp(op) {
|
|
1004
1057
|
// Need to parse from string for back-compat
|
|
1005
|
-
const { type, contents } = this.parseOpContent(op);
|
|
1058
|
+
const { type, contents, compatDetails } = this.parseOpContent(op);
|
|
1006
1059
|
switch (type) {
|
|
1007
1060
|
case ContainerMessageType.FluidDataStoreOp:
|
|
1008
1061
|
return this.dataStores.applyStashedOp(contents);
|
|
1009
1062
|
case ContainerMessageType.Attach:
|
|
1010
1063
|
return this.dataStores.applyStashedAttachOp(contents);
|
|
1011
1064
|
case ContainerMessageType.IdAllocation:
|
|
1012
|
-
(0,
|
|
1065
|
+
(0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67b /* IdCompressor should be defined if enabled */);
|
|
1013
1066
|
return this.applyStashedIdAllocationOp(contents);
|
|
1014
1067
|
case ContainerMessageType.Alias:
|
|
1015
1068
|
case ContainerMessageType.BlobAttach:
|
|
@@ -1018,8 +1071,22 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1018
1071
|
throw new Error("chunkedOp not expected here");
|
|
1019
1072
|
case ContainerMessageType.Rejoin:
|
|
1020
1073
|
throw new Error("rejoin not expected here");
|
|
1021
|
-
default:
|
|
1022
|
-
|
|
1074
|
+
default: {
|
|
1075
|
+
// This should be extremely rare for stashed ops.
|
|
1076
|
+
// It would require a newer runtime stashing ops and then an older one applying them,
|
|
1077
|
+
// e.g. if an app rolled back its container version
|
|
1078
|
+
const compatBehavior = compatDetails?.behavior;
|
|
1079
|
+
if (!compatBehaviorAllowsMessageType(type, compatBehavior)) {
|
|
1080
|
+
const error = telemetry_utils_1.DataProcessingError.create("Stashed runtime message of unknown type", "applyStashedOp", undefined /* sequencedMessage */, {
|
|
1081
|
+
messageDetails: JSON.stringify({
|
|
1082
|
+
type,
|
|
1083
|
+
compatBehavior,
|
|
1084
|
+
}),
|
|
1085
|
+
});
|
|
1086
|
+
this.closeFn(error);
|
|
1087
|
+
throw error;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1023
1090
|
}
|
|
1024
1091
|
}
|
|
1025
1092
|
setConnectionState(connected, clientId) {
|
|
@@ -1031,32 +1098,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1031
1098
|
// Don't propagate "disconnected" event because we didn't propagate the previous "connected" event
|
|
1032
1099
|
return;
|
|
1033
1100
|
}
|
|
1034
|
-
// If attachment blobs were added while disconnected, we need to delay
|
|
1035
|
-
// propagation of the "connected" event until we have uploaded them to
|
|
1036
|
-
// ensure we don't submit ops referencing a blob that has not been uploaded
|
|
1037
|
-
// Note that the inner (non-proxy) delta manager is needed here to get the readonly information.
|
|
1038
|
-
const connecting = connected && !this._connected && !this.innerDeltaManager.readOnlyInfo.readonly;
|
|
1039
|
-
if (connecting && this.blobManager.hasPendingOfflineUploads) {
|
|
1040
|
-
(0, common_utils_1.assert)(!this.delayConnectClientId, 0x392 /* Connect event delay must be canceled before subsequent connect event */);
|
|
1041
|
-
(0, common_utils_1.assert)(!!clientId, 0x393 /* Must have clientId when connecting */);
|
|
1042
|
-
this.delayConnectClientId = clientId;
|
|
1043
|
-
this.blobManager.onConnected().then(() => {
|
|
1044
|
-
// make sure we didn't reconnect before the promise resolved
|
|
1045
|
-
if (this.delayConnectClientId === clientId && !this.disposed) {
|
|
1046
|
-
this.delayConnectClientId = undefined;
|
|
1047
|
-
this.setConnectionStateCore(connected, clientId);
|
|
1048
|
-
}
|
|
1049
|
-
}, (error) => this.closeFn(error));
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
1101
|
this.setConnectionStateCore(connected, clientId);
|
|
1053
1102
|
}
|
|
1054
1103
|
setConnectionStateCore(connected, clientId) {
|
|
1055
|
-
(0,
|
|
1104
|
+
(0, core_utils_1.assert)(!this.delayConnectClientId, 0x394 /* connect event delay must be cleared before propagating connect event */);
|
|
1056
1105
|
this.verifyNotClosed();
|
|
1057
1106
|
// There might be no change of state due to Container calling this API after loading runtime.
|
|
1058
1107
|
const changeOfState = this._connected !== connected;
|
|
1059
1108
|
const reconnection = changeOfState && !connected;
|
|
1109
|
+
// We need to flush the ops currently collected by Outbox to preserve original order.
|
|
1110
|
+
// This flush NEEDS to happen before we set the ContainerRuntime to "connected".
|
|
1111
|
+
// We want these ops to get to the PendingStateManager without sending to service and have them return to the Outbox upon calling "replayPendingStates".
|
|
1112
|
+
if (changeOfState && connected) {
|
|
1113
|
+
this.flush();
|
|
1114
|
+
}
|
|
1060
1115
|
this._connected = connected;
|
|
1061
1116
|
if (!connected) {
|
|
1062
1117
|
this._perfSignalData.signalsLost = 0;
|
|
@@ -1064,16 +1119,16 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1064
1119
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1065
1120
|
}
|
|
1066
1121
|
else {
|
|
1067
|
-
(0,
|
|
1122
|
+
(0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x3cd /* Connection is possible only if container exists in storage */);
|
|
1068
1123
|
}
|
|
1069
1124
|
// Fail while disconnected
|
|
1070
1125
|
if (reconnection) {
|
|
1071
1126
|
this.consecutiveReconnects++;
|
|
1072
1127
|
if (!this.shouldContinueReconnecting()) {
|
|
1073
|
-
this.closeFn(
|
|
1128
|
+
this.closeFn(telemetry_utils_1.DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops.", "setConnectionState", undefined, {
|
|
1074
1129
|
dataLoss: 1,
|
|
1075
1130
|
attempts: this.consecutiveReconnects,
|
|
1076
|
-
pendingMessages: this.
|
|
1131
|
+
pendingMessages: this.pendingMessagesCount,
|
|
1077
1132
|
}));
|
|
1078
1133
|
return;
|
|
1079
1134
|
}
|
|
@@ -1090,18 +1145,23 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1090
1145
|
}
|
|
1091
1146
|
process(messageArg, local) {
|
|
1092
1147
|
this.verifyNotClosed();
|
|
1093
|
-
// Whether or not the message
|
|
1148
|
+
// Whether or not the message appears to be a runtime message from an up-to-date client.
|
|
1094
1149
|
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
1095
1150
|
// or something different, like a system message.
|
|
1096
|
-
const
|
|
1151
|
+
const modernRuntimeMessage = messageArg.type === protocol_definitions_1.MessageType.Operation;
|
|
1097
1152
|
// Do shallow copy of message, as the processing flow will modify it.
|
|
1098
|
-
const messageCopy =
|
|
1153
|
+
const messageCopy = { ...messageArg };
|
|
1099
1154
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
1100
|
-
this.processCore(message, local,
|
|
1155
|
+
this.processCore(message, local, modernRuntimeMessage);
|
|
1101
1156
|
}
|
|
1102
1157
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1158
|
+
/**
|
|
1159
|
+
* Direct the message to the correct subsystem for processing, and implement other side effects
|
|
1160
|
+
* @param message - The unpacked message. Likely a ContainerRuntimeMessage, but could also be a system op
|
|
1161
|
+
* @param local - Did this client send the op?
|
|
1162
|
+
* @param modernRuntimeMessage - Does this appear like a current ContainerRuntimeMessage?
|
|
1163
|
+
*/
|
|
1164
|
+
processCore(message, local, modernRuntimeMessage) {
|
|
1105
1165
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
1106
1166
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
1107
1167
|
// messages once a batch has been fully processed.
|
|
@@ -1109,7 +1169,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1109
1169
|
this._processedClientSequenceNumber = message.clientSequenceNumber;
|
|
1110
1170
|
try {
|
|
1111
1171
|
let localOpMetadata;
|
|
1112
|
-
if (local &&
|
|
1172
|
+
if (local && modernRuntimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
1113
1173
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
1114
1174
|
}
|
|
1115
1175
|
// If there are no more pending messages after processing a local message,
|
|
@@ -1117,45 +1177,8 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1117
1177
|
if (!this.hasPendingMessages()) {
|
|
1118
1178
|
this.updateDocumentDirtyState(false);
|
|
1119
1179
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
case ContainerMessageType.Attach:
|
|
1123
|
-
this.dataStores.processAttachMessage(message, local);
|
|
1124
|
-
break;
|
|
1125
|
-
case ContainerMessageType.Alias:
|
|
1126
|
-
this.processAliasMessage(message, localOpMetadata, local);
|
|
1127
|
-
break;
|
|
1128
|
-
case ContainerMessageType.FluidDataStoreOp:
|
|
1129
|
-
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1130
|
-
break;
|
|
1131
|
-
case ContainerMessageType.BlobAttach:
|
|
1132
|
-
this.blobManager.processBlobAttachOp(message, local);
|
|
1133
|
-
break;
|
|
1134
|
-
case ContainerMessageType.IdAllocation:
|
|
1135
|
-
(0, common_utils_1.assert)(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
|
|
1136
|
-
this.idCompressor.finalizeCreationRange(message.contents);
|
|
1137
|
-
break;
|
|
1138
|
-
case ContainerMessageType.ChunkedOp:
|
|
1139
|
-
case ContainerMessageType.Rejoin:
|
|
1140
|
-
break;
|
|
1141
|
-
default:
|
|
1142
|
-
if (runtimeMessage) {
|
|
1143
|
-
const error = container_utils_1.DataProcessingError.create(
|
|
1144
|
-
// Former assert 0x3ce
|
|
1145
|
-
"Runtime message of unknown type", "OpProcessing", message, {
|
|
1146
|
-
local,
|
|
1147
|
-
type: message.type,
|
|
1148
|
-
contentType: typeof message.contents,
|
|
1149
|
-
batch: (_a = message.metadata) === null || _a === void 0 ? void 0 : _a.batch,
|
|
1150
|
-
compression: message.compression,
|
|
1151
|
-
});
|
|
1152
|
-
this.closeFn(error);
|
|
1153
|
-
throw error;
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
if (runtimeMessage || this.groupedBatchingEnabled) {
|
|
1157
|
-
this.emit("op", message, runtimeMessage);
|
|
1158
|
-
}
|
|
1180
|
+
this.validateAndProcessRuntimeMessage(message, localOpMetadata, local, modernRuntimeMessage);
|
|
1181
|
+
this.emit("op", message, modernRuntimeMessage);
|
|
1159
1182
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1160
1183
|
if (local) {
|
|
1161
1184
|
// If we have processed a local op, this means that the container is
|
|
@@ -1169,8 +1192,59 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1169
1192
|
throw e;
|
|
1170
1193
|
}
|
|
1171
1194
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1195
|
+
/**
|
|
1196
|
+
* Assuming the given message is also a ContainerRuntimeMessage,
|
|
1197
|
+
* checks its type and dispatches the message to the appropriate handler in the runtime.
|
|
1198
|
+
* Throws a DataProcessingError if the message doesn't conform to the ContainerRuntimeMessage type.
|
|
1199
|
+
*/
|
|
1200
|
+
validateAndProcessRuntimeMessage(message, localOpMetadata, local, expectRuntimeMessageType) {
|
|
1201
|
+
// Optimistically extract ContainerRuntimeMessage-specific props from the message
|
|
1202
|
+
const { type: maybeContainerMessageType, compatDetails } = message;
|
|
1203
|
+
switch (maybeContainerMessageType) {
|
|
1204
|
+
case ContainerMessageType.Attach:
|
|
1205
|
+
this.dataStores.processAttachMessage(message, local);
|
|
1206
|
+
break;
|
|
1207
|
+
case ContainerMessageType.Alias:
|
|
1208
|
+
this.dataStores.processAliasMessage(message, localOpMetadata, local);
|
|
1209
|
+
break;
|
|
1210
|
+
case ContainerMessageType.FluidDataStoreOp:
|
|
1211
|
+
this.dataStores.processFluidDataStoreOp(message, local, localOpMetadata);
|
|
1212
|
+
break;
|
|
1213
|
+
case ContainerMessageType.BlobAttach:
|
|
1214
|
+
this.blobManager.processBlobAttachOp(message, local);
|
|
1215
|
+
break;
|
|
1216
|
+
case ContainerMessageType.IdAllocation:
|
|
1217
|
+
(0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67c /* IdCompressor should be defined if enabled */);
|
|
1218
|
+
this.idCompressor.finalizeCreationRange(message.contents);
|
|
1219
|
+
break;
|
|
1220
|
+
case ContainerMessageType.ChunkedOp:
|
|
1221
|
+
case ContainerMessageType.Rejoin:
|
|
1222
|
+
break;
|
|
1223
|
+
default: {
|
|
1224
|
+
// If we didn't necessarily expect a runtime message type, then no worries - just return
|
|
1225
|
+
// e.g. this case applies to system ops, or legacy ops that would have fallen into the above cases anyway.
|
|
1226
|
+
if (!expectRuntimeMessageType) {
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
const compatBehavior = compatDetails?.behavior;
|
|
1230
|
+
if (!compatBehaviorAllowsMessageType(maybeContainerMessageType, compatBehavior)) {
|
|
1231
|
+
const error = telemetry_utils_1.DataProcessingError.create(
|
|
1232
|
+
// Former assert 0x3ce
|
|
1233
|
+
"Runtime message of unknown type", "OpProcessing", message, {
|
|
1234
|
+
local,
|
|
1235
|
+
messageDetails: JSON.stringify({
|
|
1236
|
+
type: message.type,
|
|
1237
|
+
contentType: typeof message.contents,
|
|
1238
|
+
compatBehavior,
|
|
1239
|
+
batch: message.metadata?.batch,
|
|
1240
|
+
compression: message.compression,
|
|
1241
|
+
}),
|
|
1242
|
+
});
|
|
1243
|
+
this.closeFn(error);
|
|
1244
|
+
throw error;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1174
1248
|
}
|
|
1175
1249
|
/**
|
|
1176
1250
|
* Emits the Signal event and update the perf signal data.
|
|
@@ -1178,7 +1252,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1178
1252
|
*/
|
|
1179
1253
|
sendSignalTelemetryEvent(clientSignalSequenceNumber) {
|
|
1180
1254
|
const duration = Date.now() - this._perfSignalData.signalTimestamp;
|
|
1181
|
-
this.logger.sendPerformanceEvent({
|
|
1255
|
+
this.mc.logger.sendPerformanceEvent({
|
|
1182
1256
|
eventName: "SignalLatency",
|
|
1183
1257
|
duration,
|
|
1184
1258
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -1201,7 +1275,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1201
1275
|
this._perfSignalData.trackingSignalSequenceNumber) {
|
|
1202
1276
|
this._perfSignalData.signalsLost++;
|
|
1203
1277
|
this._perfSignalData.trackingSignalSequenceNumber = undefined;
|
|
1204
|
-
this.logger.sendErrorEvent({
|
|
1278
|
+
this.mc.logger.sendErrorEvent({
|
|
1205
1279
|
eventName: "SignalLost",
|
|
1206
1280
|
type: envelope.contents.type,
|
|
1207
1281
|
signalsLost: this._perfSignalData.signalsLost,
|
|
@@ -1225,6 +1299,12 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1225
1299
|
}
|
|
1226
1300
|
this.dataStores.processSignal(envelope.address, transformed, local);
|
|
1227
1301
|
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Returns the runtime of the data store.
|
|
1304
|
+
* @param id - Id supplied during creating the data store.
|
|
1305
|
+
* @param wait - True if you want to wait for it.
|
|
1306
|
+
* @deprecated - Use getAliasedDataStoreEntryPoint instead to get an aliased data store's entry point.
|
|
1307
|
+
*/
|
|
1228
1308
|
async getRootDataStore(id, wait = true) {
|
|
1229
1309
|
return this.getRootDataStoreChannel(id, wait);
|
|
1230
1310
|
}
|
|
@@ -1232,7 +1312,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1232
1312
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1233
1313
|
const internalId = this.internalId(id);
|
|
1234
1314
|
const context = await this.dataStores.getDataStore(internalId, { wait });
|
|
1235
|
-
(0,
|
|
1315
|
+
(0, core_utils_1.assert)(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1236
1316
|
return context.realize();
|
|
1237
1317
|
}
|
|
1238
1318
|
/**
|
|
@@ -1240,9 +1320,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1240
1320
|
* This method is expected to be called at the end of a batch.
|
|
1241
1321
|
*/
|
|
1242
1322
|
flush() {
|
|
1243
|
-
(0,
|
|
1323
|
+
(0, core_utils_1.assert)(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1244
1324
|
this.outbox.flush();
|
|
1245
|
-
(0,
|
|
1325
|
+
(0, core_utils_1.assert)(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1246
1326
|
}
|
|
1247
1327
|
orderSequentially(callback) {
|
|
1248
1328
|
let checkpoint;
|
|
@@ -1265,7 +1345,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1265
1345
|
}
|
|
1266
1346
|
catch (err) {
|
|
1267
1347
|
const error2 = (0, telemetry_utils_1.wrapError)(err, (message) => {
|
|
1268
|
-
return
|
|
1348
|
+
return telemetry_utils_1.DataProcessingError.create(`RollbackError: ${message}`, "checkpointRollback", undefined);
|
|
1269
1349
|
});
|
|
1270
1350
|
this.closeFn(error2);
|
|
1271
1351
|
throw error2;
|
|
@@ -1273,7 +1353,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1273
1353
|
}
|
|
1274
1354
|
else {
|
|
1275
1355
|
// pre-0.58 error message: orderSequentiallyCallbackException
|
|
1276
|
-
this.closeFn(new
|
|
1356
|
+
this.closeFn(new telemetry_utils_1.GenericError("orderSequentially callback exception", error));
|
|
1277
1357
|
}
|
|
1278
1358
|
throw error; // throw the original error for the consumer of the runtime
|
|
1279
1359
|
}
|
|
@@ -1286,29 +1366,49 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1286
1366
|
}
|
|
1287
1367
|
return result;
|
|
1288
1368
|
}
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1369
|
+
/**
|
|
1370
|
+
* Returns the aliased data store's entryPoint, given the alias.
|
|
1371
|
+
* @param alias - The alias for the data store.
|
|
1372
|
+
* @returns The data store's entry point ({@link @fluidframework/core-interfaces#IFluidHandle}) if it exists and is aliased.
|
|
1373
|
+
* Returns undefined if no data store has been assigned the given alias.
|
|
1374
|
+
*/
|
|
1375
|
+
async getAliasedDataStoreEntryPoint(alias) {
|
|
1376
|
+
await this.dataStores.waitIfPendingAlias(alias);
|
|
1377
|
+
const internalId = this.internalId(alias);
|
|
1378
|
+
const context = await this.dataStores.getDataStoreIfAvailable(internalId, { wait: false });
|
|
1379
|
+
// If the data store is not available or not an alias, return undefined.
|
|
1380
|
+
if (context === undefined || !(await context.isRoot())) {
|
|
1381
|
+
return undefined;
|
|
1382
|
+
}
|
|
1383
|
+
const channel = await context.realize();
|
|
1384
|
+
if (channel.entryPoint === undefined) {
|
|
1385
|
+
throw new telemetry_utils_1.UsageError("entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint");
|
|
1386
|
+
}
|
|
1387
|
+
return channel.entryPoint;
|
|
1292
1388
|
}
|
|
1293
1389
|
createDetachedRootDataStore(pkg, rootDataStoreId) {
|
|
1294
1390
|
if (rootDataStoreId.includes("/")) {
|
|
1295
|
-
throw new
|
|
1391
|
+
throw new telemetry_utils_1.UsageError(`Id cannot contain slashes: '${rootDataStoreId}'`);
|
|
1296
1392
|
}
|
|
1297
1393
|
return this.dataStores.createDetachedDataStoreCore(pkg, true, rootDataStoreId);
|
|
1298
1394
|
}
|
|
1299
1395
|
createDetachedDataStore(pkg) {
|
|
1300
1396
|
return this.dataStores.createDetachedDataStoreCore(pkg, false);
|
|
1301
1397
|
}
|
|
1302
|
-
async
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
.
|
|
1306
|
-
|
|
1398
|
+
async createDataStore(pkg) {
|
|
1399
|
+
const id = (0, uuid_1.v4)();
|
|
1400
|
+
return (0, dataStore_1.channelToDataStore)(await this.dataStores
|
|
1401
|
+
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id)
|
|
1402
|
+
.realize(), id, this, this.dataStores, this.mc.logger);
|
|
1307
1403
|
}
|
|
1308
|
-
|
|
1309
|
-
|
|
1404
|
+
/**
|
|
1405
|
+
* @deprecated 0.16 Issue #1537, #3631
|
|
1406
|
+
* @internal
|
|
1407
|
+
*/
|
|
1408
|
+
async _createDataStoreWithProps(pkg, props, id = (0, uuid_1.v4)()) {
|
|
1409
|
+
return (0, dataStore_1.channelToDataStore)(await this.dataStores
|
|
1310
1410
|
._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, props)
|
|
1311
|
-
.realize();
|
|
1411
|
+
.realize(), id, this, this.dataStores, this.mc.logger);
|
|
1312
1412
|
}
|
|
1313
1413
|
canSendOps() {
|
|
1314
1414
|
// Note that the real (non-proxy) delta manager is needed here to get the readonly info. This is because
|
|
@@ -1322,11 +1422,10 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1322
1422
|
return this.flushMode !== runtime_definitions_1.FlushMode.Immediate || this._orderSequentiallyCalls !== 0;
|
|
1323
1423
|
}
|
|
1324
1424
|
getQuorum() {
|
|
1325
|
-
return this.
|
|
1425
|
+
return this._quorum;
|
|
1326
1426
|
}
|
|
1327
1427
|
getAudience() {
|
|
1328
|
-
|
|
1329
|
-
return this.context.audience;
|
|
1428
|
+
return this._audience;
|
|
1330
1429
|
}
|
|
1331
1430
|
/**
|
|
1332
1431
|
* Returns true of container is dirty, i.e. there are some pending local changes that
|
|
@@ -1335,7 +1434,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1335
1434
|
get isDirty() {
|
|
1336
1435
|
return this.dirtyContainer;
|
|
1337
1436
|
}
|
|
1338
|
-
isContainerMessageDirtyable(type, contents) {
|
|
1437
|
+
isContainerMessageDirtyable({ type, contents }) {
|
|
1339
1438
|
// For legacy purposes, exclude the old built-in AgentScheduler from dirty consideration as a special-case.
|
|
1340
1439
|
// Ultimately we should have no special-cases from the ContainerRuntime's perspective.
|
|
1341
1440
|
if (type === ContainerMessageType.Attach) {
|
|
@@ -1375,18 +1474,18 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1375
1474
|
submitSignal(type, content) {
|
|
1376
1475
|
this.verifyNotClosed();
|
|
1377
1476
|
const envelope = this.createNewSignalEnvelope(undefined /* address */, type, content);
|
|
1378
|
-
return this.
|
|
1477
|
+
return this.submitSignalFn(envelope);
|
|
1379
1478
|
}
|
|
1380
1479
|
submitDataStoreSignal(address, type, content) {
|
|
1381
1480
|
const envelope = this.createNewSignalEnvelope(address, type, content);
|
|
1382
|
-
return this.
|
|
1481
|
+
return this.submitSignalFn(envelope);
|
|
1383
1482
|
}
|
|
1384
1483
|
setAttachState(attachState) {
|
|
1385
1484
|
if (attachState === container_definitions_1.AttachState.Attaching) {
|
|
1386
|
-
(0,
|
|
1485
|
+
(0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attaching, 0x12d /* "Container Context should already be in attaching state" */);
|
|
1387
1486
|
}
|
|
1388
1487
|
else {
|
|
1389
|
-
(0,
|
|
1488
|
+
(0, core_utils_1.assert)(this.attachState === container_definitions_1.AttachState.Attached, 0x12e /* "Container Context should already be in attached state" */);
|
|
1390
1489
|
this.emit("attached");
|
|
1391
1490
|
}
|
|
1392
1491
|
if (attachState === container_definitions_1.AttachState.Attached && !this.hasPendingMessages()) {
|
|
@@ -1412,22 +1511,17 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1412
1511
|
this.addContainerStateToSummary(summarizeResult, true /* fullTree */, false /* trackState */, telemetryContext);
|
|
1413
1512
|
return summarizeResult.summary;
|
|
1414
1513
|
}
|
|
1415
|
-
async getAbsoluteUrl(relativeUrl) {
|
|
1416
|
-
if (this.context.getAbsoluteUrl === undefined) {
|
|
1417
|
-
throw new Error("Driver does not implement getAbsoluteUrl");
|
|
1418
|
-
}
|
|
1419
|
-
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
1420
|
-
return undefined;
|
|
1421
|
-
}
|
|
1422
|
-
return this.context.getAbsoluteUrl(relativeUrl);
|
|
1423
|
-
}
|
|
1424
1514
|
async summarizeInternal(fullTree, trackState, telemetryContext) {
|
|
1425
1515
|
const summarizeResult = await this.dataStores.summarize(fullTree, trackState, telemetryContext);
|
|
1426
1516
|
// Wrap data store summaries in .channels subtree.
|
|
1427
1517
|
(0, summary_1.wrapSummaryInChannelsTree)(summarizeResult);
|
|
1428
1518
|
const pathPartsForChildren = [runtime_definitions_1.channelsTreeName];
|
|
1429
1519
|
this.addContainerStateToSummary(summarizeResult, fullTree, trackState, telemetryContext);
|
|
1430
|
-
return
|
|
1520
|
+
return {
|
|
1521
|
+
...summarizeResult,
|
|
1522
|
+
id: "",
|
|
1523
|
+
pathPartsForChildren,
|
|
1524
|
+
};
|
|
1431
1525
|
}
|
|
1432
1526
|
/**
|
|
1433
1527
|
* Returns a summary of the runtime at the current sequence number.
|
|
@@ -1445,16 +1539,15 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1445
1539
|
runSweep,
|
|
1446
1540
|
});
|
|
1447
1541
|
try {
|
|
1448
|
-
let gcStats;
|
|
1449
1542
|
if (runGC) {
|
|
1450
|
-
|
|
1543
|
+
await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC }, telemetryContext);
|
|
1451
1544
|
}
|
|
1452
1545
|
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState, telemetryContext);
|
|
1453
|
-
(0,
|
|
1454
|
-
return { stats, summary
|
|
1546
|
+
(0, core_utils_1.assert)(summary.type === protocol_definitions_1.SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1547
|
+
return { stats, summary };
|
|
1455
1548
|
}
|
|
1456
1549
|
finally {
|
|
1457
|
-
this.logger.sendTelemetryEvent({
|
|
1550
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1458
1551
|
eventName: "SummarizeTelemetry",
|
|
1459
1552
|
details: telemetryContext.serialize(),
|
|
1460
1553
|
});
|
|
@@ -1516,7 +1609,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1516
1609
|
/**
|
|
1517
1610
|
* After GC has run and identified nodes that are sweep ready, this is called to delete the sweep ready nodes.
|
|
1518
1611
|
* @param sweepReadyRoutes - The routes of nodes that are sweep ready and should be deleted.
|
|
1519
|
-
* @returns
|
|
1612
|
+
* @returns The routes of nodes that were deleted.
|
|
1520
1613
|
*/
|
|
1521
1614
|
deleteSweepReadyNodes(sweepReadyRoutes) {
|
|
1522
1615
|
const { dataStoreRoutes, blobManagerRoutes } = this.getDataStoreAndBlobManagerRoutes(sweepReadyRoutes);
|
|
@@ -1536,21 +1629,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1536
1629
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
1537
1630
|
*/
|
|
1538
1631
|
getCurrentReferenceTimestampMs() {
|
|
1539
|
-
var _a, _b, _c;
|
|
1540
1632
|
// Use the timestamp of the last message seen by this client as that is server generated. If no messages have
|
|
1541
1633
|
// been processed, use the timestamp of the message from the last summary.
|
|
1542
|
-
return
|
|
1634
|
+
return this.deltaManager.lastMessage?.timestamp ?? this.messageAtLastSummary?.timestamp;
|
|
1543
1635
|
}
|
|
1544
1636
|
/**
|
|
1545
1637
|
* Returns the type of the GC node. Currently, there are nodes that belong to the root ("/"), data stores or
|
|
1546
1638
|
* blob manager.
|
|
1547
1639
|
*/
|
|
1548
1640
|
getNodeType(nodePath) {
|
|
1549
|
-
var _a;
|
|
1550
1641
|
if (this.isBlobPath(nodePath)) {
|
|
1551
1642
|
return gc_1.GCNodeType.Blob;
|
|
1552
1643
|
}
|
|
1553
|
-
return
|
|
1644
|
+
return this.dataStores.getGCNodeType(nodePath) ?? gc_1.GCNodeType.Other;
|
|
1554
1645
|
}
|
|
1555
1646
|
/**
|
|
1556
1647
|
* Called by GC to retrieve the package path of the node with the given path. The node should belong to a
|
|
@@ -1564,7 +1655,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1564
1655
|
case gc_1.GCNodeType.SubDataStore:
|
|
1565
1656
|
return this.dataStores.getDataStorePackagePath(nodePath);
|
|
1566
1657
|
default:
|
|
1567
|
-
(0,
|
|
1658
|
+
(0, core_utils_1.assert)(false, 0x2de /* "Package path requested for unsupported node type." */);
|
|
1568
1659
|
}
|
|
1569
1660
|
}
|
|
1570
1661
|
/**
|
|
@@ -1580,7 +1671,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1580
1671
|
/**
|
|
1581
1672
|
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
1582
1673
|
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
1583
|
-
* @returns
|
|
1674
|
+
* @returns Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
1584
1675
|
* for data stores.
|
|
1585
1676
|
*/
|
|
1586
1677
|
getDataStoreAndBlobManagerRoutes(routes) {
|
|
@@ -1621,23 +1712,62 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1621
1712
|
* @param options - options controlling how the summary is generated or submitted
|
|
1622
1713
|
*/
|
|
1623
1714
|
async submitSummary(options) {
|
|
1624
|
-
|
|
1625
|
-
const { fullTree = false, refreshLatestAck, summaryLogger } = options;
|
|
1715
|
+
const { fullTree = false, finalAttempt = false, refreshLatestAck, summaryLogger } = options;
|
|
1626
1716
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
1627
1717
|
// use it for all events logged during this summary.
|
|
1628
1718
|
const summaryNumber = this.nextSummaryNumber;
|
|
1629
|
-
const summaryNumberLogger = telemetry_utils_1.
|
|
1630
|
-
|
|
1719
|
+
const summaryNumberLogger = (0, telemetry_utils_1.createChildLogger)({
|
|
1720
|
+
logger: summaryLogger,
|
|
1721
|
+
properties: {
|
|
1722
|
+
all: { summaryNumber },
|
|
1723
|
+
},
|
|
1631
1724
|
});
|
|
1632
|
-
(0,
|
|
1725
|
+
(0, core_utils_1.assert)(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1633
1726
|
let latestSnapshotVersionId;
|
|
1634
1727
|
if (refreshLatestAck) {
|
|
1635
|
-
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(telemetry_utils_1.
|
|
1728
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer((0, telemetry_utils_1.createChildLogger)({
|
|
1729
|
+
logger: summaryNumberLogger,
|
|
1730
|
+
properties: { all: { safeSummary: true } },
|
|
1731
|
+
}));
|
|
1636
1732
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1637
1733
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1638
1734
|
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1639
1735
|
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1640
1736
|
}
|
|
1737
|
+
// If there are pending (unacked ops), the summary will not be eventual consistent and it may even be
|
|
1738
|
+
// incorrect. So, wait for the container to be saved with a timeout. If the container is not saved
|
|
1739
|
+
// within the timeout, check if it should be failed or can continue.
|
|
1740
|
+
if (this.validateSummaryBeforeUpload && this.hasPendingMessages()) {
|
|
1741
|
+
const countBefore = this.pendingMessagesCount;
|
|
1742
|
+
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
1743
|
+
const pendingOpsTimeout = this.mc.config.getNumber("Fluid.Summarizer.waitForPendingOpsTimeoutMs") ??
|
|
1744
|
+
exports.defaultPendingOpsWaitTimeoutMs;
|
|
1745
|
+
await new Promise((resolve, reject) => {
|
|
1746
|
+
const timeoutId = setTimeout(() => resolve(), pendingOpsTimeout);
|
|
1747
|
+
this.once("saved", () => {
|
|
1748
|
+
clearTimeout(timeoutId);
|
|
1749
|
+
resolve();
|
|
1750
|
+
});
|
|
1751
|
+
this.once("dispose", () => {
|
|
1752
|
+
clearTimeout(timeoutId);
|
|
1753
|
+
reject(new Error("Runtime is disposed while summarizing"));
|
|
1754
|
+
});
|
|
1755
|
+
});
|
|
1756
|
+
// Log that there are pending ops while summarizing. This will help us gather data on how often this
|
|
1757
|
+
// happens, whether we attempted to wait for these ops to be acked and what was the result.
|
|
1758
|
+
summaryNumberLogger.sendTelemetryEvent({
|
|
1759
|
+
eventName: "PendingOpsWhileSummarizing",
|
|
1760
|
+
saved: this.hasPendingMessages() ? false : true,
|
|
1761
|
+
timeout: pendingOpsTimeout,
|
|
1762
|
+
countBefore,
|
|
1763
|
+
countAfter: this.pendingMessagesCount,
|
|
1764
|
+
});
|
|
1765
|
+
// There could still be pending ops. Check if summary should fail or continue.
|
|
1766
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, this.deltaManager.lastSequenceNumber, this.deltaManager.minimumSequenceNumber, finalAttempt, true /* beforeSummaryGeneration */);
|
|
1767
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
1768
|
+
return pendingMessagesFailResult;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1641
1771
|
const shouldPauseInboundSignal = this.mc.config.getBoolean("Fluid.ContainerRuntime.SubmitSummary.disableInboundSignalPause") !== true;
|
|
1642
1772
|
let summaryRefSeqNum;
|
|
1643
1773
|
try {
|
|
@@ -1652,7 +1782,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1652
1782
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryNumberLogger);
|
|
1653
1783
|
// Helper function to check whether we should still continue between each async step.
|
|
1654
1784
|
const checkContinue = () => {
|
|
1655
|
-
var _a;
|
|
1656
1785
|
// Do not check for loss of connectivity directly! Instead leave it up to
|
|
1657
1786
|
// RunWhileConnectedCoordinator to control policy in a single place.
|
|
1658
1787
|
// This will allow easier change of design if we chose to. For example, we may chose to allow
|
|
@@ -1665,7 +1794,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1665
1794
|
// That said, we rely on submitSystemMessage() that today only works in connected state.
|
|
1666
1795
|
// So if we fail here, it either means that RunWhileConnectedCoordinator does not work correctly,
|
|
1667
1796
|
// OR that design changed and we need to remove this check and fix submitSystemMessage.
|
|
1668
|
-
(0,
|
|
1797
|
+
(0, core_utils_1.assert)(this.connected, 0x258 /* "connected" */);
|
|
1669
1798
|
// Ensure that lastSequenceNumber has not changed after pausing.
|
|
1670
1799
|
// We need the summary op's reference sequence number to match our summary sequence number,
|
|
1671
1800
|
// otherwise we'll get the wrong sequence number stamped on the summary's .protocol attributes.
|
|
@@ -1675,7 +1804,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1675
1804
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1676
1805
|
};
|
|
1677
1806
|
}
|
|
1678
|
-
(0,
|
|
1807
|
+
(0, core_utils_1.assert)(summaryRefSeqNum === this.deltaManager.lastMessage?.sequenceNumber, 0x395 /* it's one and the same thing */);
|
|
1679
1808
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1680
1809
|
return {
|
|
1681
1810
|
continue: false,
|
|
@@ -1693,7 +1822,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1693
1822
|
error: continueResult.error,
|
|
1694
1823
|
};
|
|
1695
1824
|
}
|
|
1696
|
-
const trace =
|
|
1825
|
+
const trace = client_utils_1.Trace.start();
|
|
1697
1826
|
let summarizeResult;
|
|
1698
1827
|
// If the GC state needs to be reset, we need to force a full tree summary and update the unreferenced
|
|
1699
1828
|
// state of all the nodes.
|
|
@@ -1714,6 +1843,25 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1714
1843
|
error,
|
|
1715
1844
|
};
|
|
1716
1845
|
}
|
|
1846
|
+
// If validateSummaryBeforeUpload is true, validate that the summary generated is correct before uploading.
|
|
1847
|
+
if (this.validateSummaryBeforeUpload) {
|
|
1848
|
+
// Validate that the summaries generated by summarize nodes is correct.
|
|
1849
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
1850
|
+
if (!validateResult.success) {
|
|
1851
|
+
const { success, ...loggingProps } = validateResult;
|
|
1852
|
+
const error = new summary_1.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, { ...loggingProps });
|
|
1853
|
+
return {
|
|
1854
|
+
stage: "base",
|
|
1855
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
1856
|
+
minimumSequenceNumber,
|
|
1857
|
+
error,
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(summaryNumberLogger, summaryRefSeqNum, minimumSequenceNumber, finalAttempt, false /* beforeSummaryGeneration */);
|
|
1861
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
1862
|
+
return pendingMessagesFailResult;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1717
1865
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
1718
1866
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
1719
1867
|
this.messageAtLastSummary = this.deltaManager.lastMessage;
|
|
@@ -1721,12 +1869,20 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1721
1869
|
// Because handles are unchanged dataStores in the current logic,
|
|
1722
1870
|
// summarized dataStore count is total dataStore count minus handle count
|
|
1723
1871
|
const dataStoreTree = summaryTree.tree[runtime_definitions_1.channelsTreeName];
|
|
1724
|
-
(0,
|
|
1872
|
+
(0, core_utils_1.assert)(dataStoreTree.type === protocol_definitions_1.SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1725
1873
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === protocol_definitions_1.SummaryType.Handle).length;
|
|
1726
1874
|
const gcSummaryTreeStats = summaryTree.tree[runtime_definitions_1.gcTreeKey]
|
|
1727
1875
|
? (0, runtime_utils_1.calculateStats)(summaryTree.tree[runtime_definitions_1.gcTreeKey])
|
|
1728
1876
|
: undefined;
|
|
1729
|
-
const summaryStats =
|
|
1877
|
+
const summaryStats = {
|
|
1878
|
+
dataStoreCount: this.dataStores.size,
|
|
1879
|
+
summarizedDataStoreCount: this.dataStores.size - handleCount,
|
|
1880
|
+
gcStateUpdatedDataStoreCount: this.garbageCollector.updatedDSCountSinceLastSummary,
|
|
1881
|
+
gcBlobNodeCount: gcSummaryTreeStats?.blobNodeCount,
|
|
1882
|
+
gcTotalBlobsSize: gcSummaryTreeStats?.totalBlobSize,
|
|
1883
|
+
summaryNumber,
|
|
1884
|
+
...partialStats,
|
|
1885
|
+
};
|
|
1730
1886
|
const generateSummaryData = {
|
|
1731
1887
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1732
1888
|
minimumSequenceNumber,
|
|
@@ -1735,19 +1891,9 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1735
1891
|
generateDuration: trace.trace().duration,
|
|
1736
1892
|
forcedFullTree,
|
|
1737
1893
|
};
|
|
1738
|
-
// If validateSummaryBeforeUpload is true, validate that the summary generated by the summarizer nodes is
|
|
1739
|
-
// correct before this summary is uploaded.
|
|
1740
|
-
if (this.validateSummaryBeforeUpload) {
|
|
1741
|
-
const validateResult = this.summarizerNode.validateSummary();
|
|
1742
|
-
if (!validateResult.success) {
|
|
1743
|
-
const { success } = validateResult, loggingProps = __rest(validateResult, ["success"]);
|
|
1744
|
-
const error = new summary_1.RetriableSummaryError(validateResult.reason, validateResult.retryAfterSeconds, Object.assign({}, loggingProps));
|
|
1745
|
-
return Object.assign(Object.assign({ stage: "base" }, generateSummaryData), { error });
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
1894
|
continueResult = checkContinue();
|
|
1749
1895
|
if (!continueResult.continue) {
|
|
1750
|
-
return
|
|
1896
|
+
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
1751
1897
|
}
|
|
1752
1898
|
// It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
|
|
1753
1899
|
// summary. So if the previous summarizer closes just after submitting the summary and before
|
|
@@ -1755,7 +1901,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1755
1901
|
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
1756
1902
|
// the one fetched from storage as parent as that is the latest.
|
|
1757
1903
|
let summaryContext;
|
|
1758
|
-
if (
|
|
1904
|
+
if (lastAck?.summaryAck.contents.handle !== latestSnapshotVersionId &&
|
|
1759
1905
|
latestSnapshotVersionId !== undefined) {
|
|
1760
1906
|
summaryContext = {
|
|
1761
1907
|
proposalHandle: undefined,
|
|
@@ -1766,7 +1912,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1766
1912
|
else if (lastAck === undefined) {
|
|
1767
1913
|
summaryContext = {
|
|
1768
1914
|
proposalHandle: undefined,
|
|
1769
|
-
ackHandle:
|
|
1915
|
+
ackHandle: this.loadedFromVersionId,
|
|
1770
1916
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1771
1917
|
};
|
|
1772
1918
|
}
|
|
@@ -1782,7 +1928,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1782
1928
|
handle = await this.storage.uploadSummaryWithContext(summarizeResult.summary, summaryContext);
|
|
1783
1929
|
}
|
|
1784
1930
|
catch (error) {
|
|
1785
|
-
return
|
|
1931
|
+
return { stage: "generate", ...generateSummaryData, error };
|
|
1786
1932
|
}
|
|
1787
1933
|
const parent = summaryContext.ackHandle;
|
|
1788
1934
|
const summaryMessage = {
|
|
@@ -1792,25 +1938,34 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1792
1938
|
message,
|
|
1793
1939
|
parents: parent ? [parent] : [],
|
|
1794
1940
|
};
|
|
1795
|
-
const uploadData =
|
|
1941
|
+
const uploadData = {
|
|
1942
|
+
...generateSummaryData,
|
|
1943
|
+
handle,
|
|
1944
|
+
uploadDuration: trace.trace().duration,
|
|
1945
|
+
};
|
|
1796
1946
|
continueResult = checkContinue();
|
|
1797
1947
|
if (!continueResult.continue) {
|
|
1798
|
-
return
|
|
1948
|
+
return { stage: "upload", ...uploadData, error: continueResult.error };
|
|
1799
1949
|
}
|
|
1800
1950
|
let clientSequenceNumber;
|
|
1801
1951
|
try {
|
|
1802
1952
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
1803
1953
|
}
|
|
1804
1954
|
catch (error) {
|
|
1805
|
-
return
|
|
1955
|
+
return { stage: "upload", ...uploadData, error };
|
|
1806
1956
|
}
|
|
1807
|
-
const submitData =
|
|
1957
|
+
const submitData = {
|
|
1958
|
+
stage: "submit",
|
|
1959
|
+
...uploadData,
|
|
1960
|
+
clientSequenceNumber,
|
|
1961
|
+
submitOpDuration: trace.trace().duration,
|
|
1962
|
+
};
|
|
1808
1963
|
try {
|
|
1809
1964
|
// If validateSummaryBeforeUpload is false, the summary should be validated in this step.
|
|
1810
1965
|
this.summarizerNode.completeSummary(handle, !this.validateSummaryBeforeUpload /* validate */);
|
|
1811
1966
|
}
|
|
1812
1967
|
catch (error) {
|
|
1813
|
-
return
|
|
1968
|
+
return { stage: "upload", ...uploadData, error };
|
|
1814
1969
|
}
|
|
1815
1970
|
return submitData;
|
|
1816
1971
|
}
|
|
@@ -1818,7 +1973,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1818
1973
|
// Cleanup wip summary in case of failure
|
|
1819
1974
|
this.summarizerNode.clearSummary();
|
|
1820
1975
|
// ! This needs to happen before we resume inbound queues to ensure heuristics are tracked correctly
|
|
1821
|
-
|
|
1976
|
+
this._summarizer?.recordSummaryAttempt?.(summaryRefSeqNum);
|
|
1822
1977
|
// Restart the delta manager
|
|
1823
1978
|
this.deltaManager.inbound.resume();
|
|
1824
1979
|
if (shouldPauseInboundSignal) {
|
|
@@ -1826,16 +1981,63 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1826
1981
|
}
|
|
1827
1982
|
}
|
|
1828
1983
|
}
|
|
1984
|
+
/**
|
|
1985
|
+
* This helper is called during summarization. If there are pending ops, it will return a failed summarize result
|
|
1986
|
+
* (IBaseSummarizeResult) unless this is the final summarize attempt and SkipFailingIncorrectSummary option is set.
|
|
1987
|
+
* @param logger - The logger to be used for sending telemetry.
|
|
1988
|
+
* @param referenceSequenceNumber - The reference sequence number of the summary attempt.
|
|
1989
|
+
* @param minimumSequenceNumber - The minimum sequence number of the summary attempt.
|
|
1990
|
+
* @param finalAttempt - Whether this is the final summary attempt.
|
|
1991
|
+
* @param beforeSummaryGeneration - Whether this is called before summary generation or after.
|
|
1992
|
+
* @returns failed summarize result (IBaseSummarizeResult) if summary should be failed, undefined otherwise.
|
|
1993
|
+
*/
|
|
1994
|
+
async shouldFailSummaryOnPendingOps(logger, referenceSequenceNumber, minimumSequenceNumber, finalAttempt, beforeSummaryGeneration) {
|
|
1995
|
+
if (!this.hasPendingMessages()) {
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
// If "SkipFailingIncorrectSummary" option is true, don't fail the summary in the last attempt.
|
|
1999
|
+
// This is a fallback to make progress in documents where there are consistently pending ops in
|
|
2000
|
+
// the summarizer.
|
|
2001
|
+
if (finalAttempt &&
|
|
2002
|
+
this.mc.config.getBoolean("Fluid.Summarizer.SkipFailingIncorrectSummary")) {
|
|
2003
|
+
const error = telemetry_utils_1.DataProcessingError.create("Pending ops during summarization", "submitSummary", undefined, { pendingMessages: this.pendingMessagesCount });
|
|
2004
|
+
logger.sendErrorEvent({
|
|
2005
|
+
eventName: "SkipFailingIncorrectSummary",
|
|
2006
|
+
referenceSequenceNumber,
|
|
2007
|
+
minimumSequenceNumber,
|
|
2008
|
+
beforeGenerate: beforeSummaryGeneration,
|
|
2009
|
+
}, error);
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
// The retry delay when there are pending ops can be overridden via config so that we can adjust it
|
|
2013
|
+
// based on telemetry while we decide on a stable number.
|
|
2014
|
+
const retryDelayMs = this.mc.config.getNumber("Fluid.Summarizer.PendingOpsRetryDelayMs") ??
|
|
2015
|
+
exports.defaultPendingOpsRetryDelayMs;
|
|
2016
|
+
const error = new summary_1.RetriableSummaryError("PendingOpsWhileSummarizing", retryDelayMs / 1000, {
|
|
2017
|
+
count: this.pendingMessagesCount,
|
|
2018
|
+
beforeGenerate: beforeSummaryGeneration,
|
|
2019
|
+
});
|
|
2020
|
+
return {
|
|
2021
|
+
stage: "base",
|
|
2022
|
+
referenceSequenceNumber,
|
|
2023
|
+
minimumSequenceNumber,
|
|
2024
|
+
error,
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
get pendingMessagesCount() {
|
|
2029
|
+
return this.pendingStateManager.pendingMessagesCount + this.outbox.messageCount;
|
|
2030
|
+
}
|
|
1829
2031
|
hasPendingMessages() {
|
|
1830
|
-
return this.
|
|
2032
|
+
return this.pendingMessagesCount !== 0;
|
|
1831
2033
|
}
|
|
1832
2034
|
updateDocumentDirtyState(dirty) {
|
|
1833
2035
|
if (this.attachState !== container_definitions_1.AttachState.Attached) {
|
|
1834
|
-
(0,
|
|
2036
|
+
(0, core_utils_1.assert)(dirty, 0x3d2 /* Non-attached container is dirty */);
|
|
1835
2037
|
}
|
|
1836
2038
|
else {
|
|
1837
2039
|
// Other way is not true = see this.isContainerMessageDirtyable()
|
|
1838
|
-
(0,
|
|
2040
|
+
(0, core_utils_1.assert)(!dirty || this.hasPendingMessages(), 0x3d3 /* if doc is dirty, there has to be pending ops */);
|
|
1839
2041
|
}
|
|
1840
2042
|
if (this.dirtyContainer === dirty) {
|
|
1841
2043
|
return;
|
|
@@ -1843,7 +2045,6 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1843
2045
|
this.dirtyContainer = dirty;
|
|
1844
2046
|
if (this.emitDirtyDocumentEvent) {
|
|
1845
2047
|
this.emit(dirty ? "dirty" : "saved");
|
|
1846
|
-
this.context.updateDirtyContainerState(dirty);
|
|
1847
2048
|
}
|
|
1848
2049
|
}
|
|
1849
2050
|
submitDataStoreOp(id, contents, localOpMetadata = undefined) {
|
|
@@ -1851,29 +2052,28 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1851
2052
|
address: id,
|
|
1852
2053
|
contents,
|
|
1853
2054
|
};
|
|
1854
|
-
this.submit(ContainerMessageType.FluidDataStoreOp, envelope, localOpMetadata);
|
|
2055
|
+
this.submit({ type: ContainerMessageType.FluidDataStoreOp, contents: envelope }, localOpMetadata);
|
|
1855
2056
|
}
|
|
1856
2057
|
submitDataStoreAliasOp(contents, localOpMetadata) {
|
|
1857
2058
|
const aliasMessage = contents;
|
|
1858
2059
|
if (!(0, dataStore_1.isDataStoreAliasMessage)(aliasMessage)) {
|
|
1859
|
-
throw new
|
|
2060
|
+
throw new telemetry_utils_1.UsageError("malformedDataStoreAliasMessage");
|
|
1860
2061
|
}
|
|
1861
|
-
this.submit(ContainerMessageType.Alias, contents, localOpMetadata);
|
|
2062
|
+
this.submit({ type: ContainerMessageType.Alias, contents }, localOpMetadata);
|
|
1862
2063
|
}
|
|
1863
|
-
async uploadBlob(blob) {
|
|
2064
|
+
async uploadBlob(blob, signal) {
|
|
1864
2065
|
this.verifyNotClosed();
|
|
1865
|
-
return this.blobManager.createBlob(blob);
|
|
2066
|
+
return this.blobManager.createBlob(blob, signal);
|
|
1866
2067
|
}
|
|
1867
2068
|
maybeSubmitIdAllocationOp(type) {
|
|
1868
|
-
var _a, _b;
|
|
1869
2069
|
if (type !== ContainerMessageType.IdAllocation) {
|
|
1870
2070
|
let idAllocationBatchMessage;
|
|
1871
2071
|
let idRange;
|
|
1872
2072
|
if (this.idCompressorEnabled) {
|
|
1873
|
-
(0,
|
|
2073
|
+
(0, core_utils_1.assert)(this.idCompressor !== undefined, 0x67d /* IdCompressor should be defined if enabled */);
|
|
1874
2074
|
idRange = this.idCompressor.takeNextCreationRange();
|
|
1875
2075
|
// Don't include the idRange if there weren't any Ids allocated
|
|
1876
|
-
idRange =
|
|
2076
|
+
idRange = idRange?.ids !== undefined ? idRange : undefined;
|
|
1877
2077
|
}
|
|
1878
2078
|
if (idRange !== undefined) {
|
|
1879
2079
|
const idAllocationMessage = {
|
|
@@ -1884,7 +2084,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1884
2084
|
contents: JSON.stringify(idAllocationMessage),
|
|
1885
2085
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1886
2086
|
metadata: undefined,
|
|
1887
|
-
localOpMetadata:
|
|
2087
|
+
localOpMetadata: this.idCompressor?.serialize(true),
|
|
1888
2088
|
type: ContainerMessageType.IdAllocation,
|
|
1889
2089
|
};
|
|
1890
2090
|
}
|
|
@@ -1893,20 +2093,21 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1893
2093
|
}
|
|
1894
2094
|
}
|
|
1895
2095
|
}
|
|
1896
|
-
submit(
|
|
2096
|
+
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata = undefined) {
|
|
1897
2097
|
this.verifyNotClosed();
|
|
1898
2098
|
this.verifyCanSubmitOps();
|
|
1899
2099
|
// There should be no ops in detached container state!
|
|
1900
|
-
(0,
|
|
1901
|
-
const serializedContent = JSON.stringify(
|
|
2100
|
+
(0, core_utils_1.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
2101
|
+
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
1902
2102
|
// Note that the real (non-proxy) delta manager is used here to get the readonly info. This is because
|
|
1903
2103
|
// container runtime's ability to submit ops depend on the actual readonly state of the delta manager.
|
|
1904
2104
|
if (this.innerDeltaManager.readOnlyInfo.readonly) {
|
|
1905
|
-
this.logger.sendTelemetryEvent({
|
|
2105
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1906
2106
|
eventName: "SubmitOpInReadonly",
|
|
1907
2107
|
connected: this.connected,
|
|
1908
2108
|
});
|
|
1909
2109
|
}
|
|
2110
|
+
const type = containerRuntimeMessage.type;
|
|
1910
2111
|
const message = {
|
|
1911
2112
|
contents: serializedContent,
|
|
1912
2113
|
type,
|
|
@@ -1963,7 +2164,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1963
2164
|
this.closeFn(error);
|
|
1964
2165
|
throw error;
|
|
1965
2166
|
}
|
|
1966
|
-
if (this.isContainerMessageDirtyable(
|
|
2167
|
+
if (this.isContainerMessageDirtyable(containerRuntimeMessage)) {
|
|
1967
2168
|
this.updateDocumentDirtyState(true);
|
|
1968
2169
|
}
|
|
1969
2170
|
}
|
|
@@ -1996,19 +2197,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
1996
2197
|
setTimeout(flush, 0);
|
|
1997
2198
|
break;
|
|
1998
2199
|
default:
|
|
1999
|
-
(0,
|
|
2200
|
+
(0, core_utils_1.assert)(this._orderSequentiallyCalls > 0, 0x587 /* Unreachable unless running under orderSequentially */);
|
|
2000
2201
|
break;
|
|
2001
2202
|
}
|
|
2002
2203
|
}
|
|
2003
2204
|
submitSummaryMessage(contents, referenceSequenceNumber) {
|
|
2004
2205
|
this.verifyNotClosed();
|
|
2005
|
-
(0,
|
|
2206
|
+
(0, core_utils_1.assert)(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
2006
2207
|
// System message should not be sent in the middle of the batch.
|
|
2007
|
-
(0,
|
|
2208
|
+
(0, core_utils_1.assert)(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
2008
2209
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
2009
|
-
return this.
|
|
2010
|
-
? this.
|
|
2011
|
-
: this.
|
|
2210
|
+
return this.submitSummaryFn !== undefined
|
|
2211
|
+
? this.submitSummaryFn(contents, referenceSequenceNumber)
|
|
2212
|
+
: this.submitFn(protocol_definitions_1.MessageType.Summarize, contents, false);
|
|
2012
2213
|
}
|
|
2013
2214
|
/**
|
|
2014
2215
|
* Throw an error if the runtime is closed. Methods that are expected to potentially
|
|
@@ -2025,7 +2226,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2025
2226
|
if (this.opReentryCallsToReport > 0) {
|
|
2026
2227
|
this.mc.logger.sendTelemetryEvent({ eventName: "OpReentry" },
|
|
2027
2228
|
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
2028
|
-
(0, opLifecycle_1.getLongStack)(() => new
|
|
2229
|
+
(0, opLifecycle_1.getLongStack)(() => new telemetry_utils_1.UsageError(errorMessage)));
|
|
2029
2230
|
this.opReentryCallsToReport--;
|
|
2030
2231
|
}
|
|
2031
2232
|
// Creating ops while processing ops can lead
|
|
@@ -2041,7 +2242,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2041
2242
|
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
2042
2243
|
// while ops are being processed.
|
|
2043
2244
|
if (this.enableOpReentryCheck) {
|
|
2044
|
-
throw new
|
|
2245
|
+
throw new telemetry_utils_1.UsageError(errorMessage);
|
|
2045
2246
|
}
|
|
2046
2247
|
}
|
|
2047
2248
|
}
|
|
@@ -2055,32 +2256,33 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2055
2256
|
}
|
|
2056
2257
|
reSubmit(message) {
|
|
2057
2258
|
// Need to parse from string for back-compat
|
|
2058
|
-
const
|
|
2059
|
-
this.reSubmitCore(
|
|
2259
|
+
const containerRuntimeMessage = this.parseOpContent(message.content);
|
|
2260
|
+
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2060
2261
|
}
|
|
2061
2262
|
/**
|
|
2062
2263
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
2063
2264
|
* reconnect and there are pending messages.
|
|
2064
|
-
* @param
|
|
2265
|
+
* @param message - The original ContainerRuntimeMessage.
|
|
2065
2266
|
* @param localOpMetadata - The local metadata associated with the original message.
|
|
2066
2267
|
*/
|
|
2067
|
-
reSubmitCore(
|
|
2068
|
-
|
|
2268
|
+
reSubmitCore(message, localOpMetadata, opMetadata) {
|
|
2269
|
+
const contents = message.contents;
|
|
2270
|
+
switch (message.type) {
|
|
2069
2271
|
case ContainerMessageType.FluidDataStoreOp:
|
|
2070
2272
|
// For Operations, call resubmitDataStoreOp which will find the right store
|
|
2071
2273
|
// and trigger resubmission on it.
|
|
2072
|
-
this.dataStores.resubmitDataStoreOp(
|
|
2274
|
+
this.dataStores.resubmitDataStoreOp(contents, localOpMetadata);
|
|
2073
2275
|
break;
|
|
2074
2276
|
case ContainerMessageType.Attach:
|
|
2075
2277
|
case ContainerMessageType.Alias:
|
|
2076
|
-
this.submit(
|
|
2278
|
+
this.submit(message, localOpMetadata);
|
|
2077
2279
|
break;
|
|
2078
2280
|
case ContainerMessageType.IdAllocation:
|
|
2079
2281
|
// Remove the stashedState from the op if it's a stashed op
|
|
2080
|
-
if (
|
|
2081
|
-
delete
|
|
2282
|
+
if (contents.stashedState !== undefined) {
|
|
2283
|
+
delete contents.stashedState;
|
|
2082
2284
|
}
|
|
2083
|
-
this.submit(
|
|
2285
|
+
this.submit(message, localOpMetadata);
|
|
2084
2286
|
break;
|
|
2085
2287
|
case ContainerMessageType.ChunkedOp:
|
|
2086
2288
|
throw new Error(`chunkedOp not expected here`);
|
|
@@ -2088,10 +2290,29 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2088
2290
|
this.blobManager.reSubmit(opMetadata);
|
|
2089
2291
|
break;
|
|
2090
2292
|
case ContainerMessageType.Rejoin:
|
|
2091
|
-
this.submit(
|
|
2293
|
+
this.submit(message);
|
|
2092
2294
|
break;
|
|
2093
|
-
default:
|
|
2094
|
-
|
|
2295
|
+
default: {
|
|
2296
|
+
// This case should be very rare - it would imply an op was stashed from a
|
|
2297
|
+
// future version of runtime code and now is being applied on an older version
|
|
2298
|
+
const compatBehavior = message.compatDetails?.behavior;
|
|
2299
|
+
if (compatBehaviorAllowsMessageType(message.type, compatBehavior)) {
|
|
2300
|
+
this.logger.sendTelemetryEvent({
|
|
2301
|
+
eventName: "resubmitUnrecognizedMessageTypeAllowed",
|
|
2302
|
+
messageDetails: { type: message.type, compatBehavior },
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
else {
|
|
2306
|
+
const error = telemetry_utils_1.DataProcessingError.create("Resubmitting runtime message of unknown type", "reSubmitCore", undefined /* sequencedMessage */, {
|
|
2307
|
+
messageDetails: JSON.stringify({
|
|
2308
|
+
type: message.type,
|
|
2309
|
+
compatBehavior,
|
|
2310
|
+
}),
|
|
2311
|
+
});
|
|
2312
|
+
this.closeFn(error);
|
|
2313
|
+
throw error;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2095
2316
|
}
|
|
2096
2317
|
}
|
|
2097
2318
|
rollback(content, localOpMetadata) {
|
|
@@ -2104,6 +2325,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2104
2325
|
this.dataStores.rollbackDataStoreOp(contents, localOpMetadata);
|
|
2105
2326
|
break;
|
|
2106
2327
|
default:
|
|
2328
|
+
// Don't check message.compatDetails because this is for rolling back a local op so the type will be known
|
|
2107
2329
|
throw new Error(`Can't rollback ${type}`);
|
|
2108
2330
|
}
|
|
2109
2331
|
}
|
|
@@ -2121,29 +2343,22 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2121
2343
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
2122
2344
|
async refreshLatestSummaryAck(options) {
|
|
2123
2345
|
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
2346
|
+
// proposalHandle is always passed from RunningSummarizer.
|
|
2347
|
+
(0, core_utils_1.assert)(proposalHandle !== undefined, 0x766 /* proposalHandle should be available */);
|
|
2124
2348
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2349
|
+
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq);
|
|
2350
|
+
/**
|
|
2351
|
+
* When refreshing a summary ack, this check indicates a new ack of a summary that is newer than the
|
|
2352
|
+
* current summary that is tracked, but this summarizer runtime did not produce/track that summary. Thus
|
|
2353
|
+
* it needs to refresh its state. Today refresh is done by fetching the latest snapshot to update the cache
|
|
2354
|
+
* and then close as the current main client is likely to be re-elected as the parent summarizer again.
|
|
2355
|
+
*/
|
|
2356
|
+
if (!result.isSummaryTracked && result.isSummaryNewer) {
|
|
2357
|
+
const fetchResult = await this.fetchSnapshotFromStorage(summaryLogger, {
|
|
2130
2358
|
eventName: "RefreshLatestSummaryAckFetch",
|
|
2131
2359
|
ackHandle,
|
|
2132
2360
|
targetSequenceNumber: summaryRefSeq,
|
|
2133
|
-
}, readAndParseBlob);
|
|
2134
|
-
/**
|
|
2135
|
-
* back-compat - Older loaders and drivers (pre 2.0.0-internal.1.4) don't have fetchSource as a param in the
|
|
2136
|
-
* getVersions API. So, they will not fetch the latest snapshot from network in the previous fetch call. For
|
|
2137
|
-
* these scenarios, fetch the snapshot corresponding to the ack handle to have the same behavior before the
|
|
2138
|
-
* change that started fetching latest snapshot always.
|
|
2139
|
-
*/
|
|
2140
|
-
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
2141
|
-
fetchResult = await this.fetchSnapshotFromStorage(summaryLogger, {
|
|
2142
|
-
eventName: "RefreshLatestSummaryAckFetchBackCompat",
|
|
2143
|
-
ackHandle,
|
|
2144
|
-
targetSequenceNumber: summaryRefSeq,
|
|
2145
|
-
}, readAndParseBlob, ackHandle);
|
|
2146
|
-
}
|
|
2361
|
+
}, readAndParseBlob, null);
|
|
2147
2362
|
/**
|
|
2148
2363
|
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
2149
2364
|
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
@@ -2155,25 +2370,19 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2155
2370
|
* state.
|
|
2156
2371
|
*/
|
|
2157
2372
|
if (fetchResult.latestSnapshotRefSeq < summaryRefSeq) {
|
|
2158
|
-
const error =
|
|
2373
|
+
const error = telemetry_utils_1.DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
|
|
2159
2374
|
ackHandle,
|
|
2160
2375
|
summaryRefSeq,
|
|
2161
2376
|
fetchedSnapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
2162
2377
|
});
|
|
2163
|
-
this.
|
|
2378
|
+
this.disposeFn(error);
|
|
2164
2379
|
throw error;
|
|
2165
2380
|
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
return {
|
|
2170
|
-
snapshotTree: fetchResult.snapshotTree,
|
|
2171
|
-
snapshotRefSeq: fetchResult.latestSnapshotRefSeq,
|
|
2172
|
-
};
|
|
2173
|
-
};
|
|
2174
|
-
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, fetchLatestSnapshot, readAndParseBlob, summaryLogger);
|
|
2381
|
+
await this.closeStaleSummarizer("RefreshLatestSummaryAckFetch");
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2175
2384
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2176
|
-
await this.garbageCollector.refreshLatestSummary(
|
|
2385
|
+
await this.garbageCollector.refreshLatestSummary(result);
|
|
2177
2386
|
}
|
|
2178
2387
|
/**
|
|
2179
2388
|
* Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
|
|
@@ -2183,31 +2392,38 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2183
2392
|
*/
|
|
2184
2393
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
2185
2394
|
const readAndParseBlob = async (id) => (0, driver_utils_1.readAndParse)(this.storage, id);
|
|
2186
|
-
const {
|
|
2395
|
+
const { versionId, latestSnapshotRefSeq } = await this.fetchSnapshotFromStorage(summaryLogger, {
|
|
2187
2396
|
eventName: "RefreshLatestSummaryFromServerFetch",
|
|
2188
|
-
}, readAndParseBlob);
|
|
2189
|
-
|
|
2190
|
-
snapshotTree,
|
|
2191
|
-
snapshotRefSeq: latestSnapshotRefSeq,
|
|
2192
|
-
};
|
|
2193
|
-
const result = await this.summarizerNode.refreshLatestSummary(undefined /* proposalHandle */, latestSnapshotRefSeq, async () => fetchLatestSnapshot, readAndParseBlob, summaryLogger);
|
|
2194
|
-
// Notify the garbage collector so it can update its latest summary state.
|
|
2195
|
-
await this.garbageCollector.refreshLatestSummary(undefined /* proposalHandle */, result, readAndParseBlob);
|
|
2397
|
+
}, readAndParseBlob, null);
|
|
2398
|
+
await this.closeStaleSummarizer("RefreshLatestSummaryFromServerFetch");
|
|
2196
2399
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
2197
2400
|
}
|
|
2198
|
-
async
|
|
2199
|
-
|
|
2401
|
+
async closeStaleSummarizer(codePath) {
|
|
2402
|
+
this.mc.logger.sendTelemetryEvent({
|
|
2403
|
+
eventName: "ClosingSummarizerOnSummaryStale",
|
|
2404
|
+
codePath,
|
|
2405
|
+
message: "Stopping fetch from storage",
|
|
2406
|
+
closeSummarizerDelayMs: this.closeSummarizerDelayMs,
|
|
2407
|
+
}, new telemetry_utils_1.GenericError("Restarting summarizer instead of refreshing"));
|
|
2408
|
+
// Delay before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2409
|
+
await (0, core_utils_1.delay)(this.closeSummarizerDelayMs);
|
|
2410
|
+
this._summarizer?.stop("latestSummaryStateStale");
|
|
2411
|
+
this.disposeFn();
|
|
2200
2412
|
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Downloads snapshot from storage with the given versionId or latest if versionId is null.
|
|
2415
|
+
* By default, it also closes the container after downloading the snapshot. However, this may be
|
|
2416
|
+
* overridden via options.
|
|
2417
|
+
*/
|
|
2201
2418
|
async fetchSnapshotFromStorage(logger, event, readAndParseBlob, versionId) {
|
|
2202
|
-
|
|
2203
|
-
const snapshotResults = await telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2419
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
2204
2420
|
const stats = {};
|
|
2205
|
-
const trace =
|
|
2421
|
+
const trace = client_utils_1.Trace.start();
|
|
2206
2422
|
const versions = await this.storage.getVersions(versionId, 1, "refreshLatestSummaryAckFromServer", versionId === null ? driver_definitions_1.FetchSource.noCache : undefined);
|
|
2207
|
-
(0,
|
|
2423
|
+
(0, core_utils_1.assert)(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
2208
2424
|
stats.getVersionDuration = trace.trace().duration;
|
|
2209
2425
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
2210
|
-
(0,
|
|
2426
|
+
(0, core_utils_1.assert)(!!maybeSnapshot, 0x138 /* "Failed to get snapshot from storage" */);
|
|
2211
2427
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
2212
2428
|
const latestSnapshotRefSeq = await (0, runtime_utils_1.seqFromTree)(maybeSnapshot, readAndParseBlob);
|
|
2213
2429
|
stats.snapshotRefSeq = latestSnapshotRefSeq;
|
|
@@ -2219,33 +2435,65 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2219
2435
|
latestSnapshotRefSeq,
|
|
2220
2436
|
};
|
|
2221
2437
|
});
|
|
2222
|
-
// We choose to close the summarizer after the snapshot cache is updated to avoid
|
|
2223
|
-
// situations which the main client (which is likely to be re-elected as the leader again)
|
|
2224
|
-
// loads the summarizer from cache.
|
|
2225
|
-
if (this.summaryStateUpdateMethod === "restart") {
|
|
2226
|
-
const error = new container_utils_1.GenericError("Restarting summarizer instead of refreshing");
|
|
2227
|
-
this.mc.logger.sendTelemetryEvent(Object.assign(Object.assign({}, event), { eventName: "ClosingSummarizerOnSummaryStale", codePath: event.eventName, message: "Stopping fetch from storage", versionId: versionId != null ? versionId : undefined, closeSummarizerDelayMs: this.closeSummarizerDelayMs }), error);
|
|
2228
|
-
// Delay 10 seconds before restarting summarizer to prevent the summarizer from restarting too frequently.
|
|
2229
|
-
await (0, common_utils_1.delay)(this.closeSummarizerDelayMs);
|
|
2230
|
-
(_a = this._summarizer) === null || _a === void 0 ? void 0 : _a.stop("latestSummaryStateStale");
|
|
2231
|
-
this.closeFn();
|
|
2232
|
-
throw error;
|
|
2233
|
-
}
|
|
2234
|
-
return snapshotResults;
|
|
2235
2438
|
}
|
|
2236
2439
|
notifyAttaching() { } // do nothing (deprecated method)
|
|
2237
|
-
getPendingLocalState() {
|
|
2238
|
-
|
|
2239
|
-
|
|
2440
|
+
async getPendingLocalState(props) {
|
|
2441
|
+
return telemetry_utils_1.PerformanceEvent.timedExecAsync(this.mc.logger, {
|
|
2442
|
+
eventName: "getPendingLocalState",
|
|
2443
|
+
notifyImminentClosure: props?.notifyImminentClosure,
|
|
2444
|
+
}, async (event) => {
|
|
2445
|
+
this.verifyNotClosed();
|
|
2446
|
+
const waitBlobsToAttach = props?.notifyImminentClosure;
|
|
2447
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
2448
|
+
throw new telemetry_utils_1.UsageError("can't get state during orderSequentially");
|
|
2449
|
+
}
|
|
2450
|
+
const pendingAttachmentBlobs = await this.blobManager.getPendingBlobs(waitBlobsToAttach);
|
|
2451
|
+
const pending = this.pendingStateManager.getLocalState();
|
|
2452
|
+
if (!pendingAttachmentBlobs && !this.hasPendingMessages()) {
|
|
2453
|
+
return; // no pending state to save
|
|
2454
|
+
}
|
|
2455
|
+
// Flush pending batch.
|
|
2456
|
+
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2457
|
+
// to close current batch.
|
|
2458
|
+
this.flush();
|
|
2459
|
+
const pendingState = {
|
|
2460
|
+
pending,
|
|
2461
|
+
pendingAttachmentBlobs,
|
|
2462
|
+
};
|
|
2463
|
+
event.end({
|
|
2464
|
+
attachmentBlobsSize: Object.keys(pendingAttachmentBlobs ?? {}).length,
|
|
2465
|
+
pendingOpsSize: pending?.pendingStates.length,
|
|
2466
|
+
});
|
|
2467
|
+
return pendingState;
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
summarizeOnDemand(options) {
|
|
2471
|
+
if (this.isSummarizerClient) {
|
|
2472
|
+
return this.summarizer.summarizeOnDemand(options);
|
|
2473
|
+
}
|
|
2474
|
+
else if (this.summaryManager !== undefined) {
|
|
2475
|
+
return this.summaryManager.summarizeOnDemand(options);
|
|
2476
|
+
}
|
|
2477
|
+
else {
|
|
2478
|
+
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
2479
|
+
// disableSummaries is turned on. We are throwing instead of returning a failure here,
|
|
2480
|
+
// because it is a misuse of the API rather than an expected failure.
|
|
2481
|
+
throw new telemetry_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
enqueueSummarize(options) {
|
|
2485
|
+
if (this.isSummarizerClient) {
|
|
2486
|
+
return this.summarizer.enqueueSummarize(options);
|
|
2487
|
+
}
|
|
2488
|
+
else if (this.summaryManager !== undefined) {
|
|
2489
|
+
return this.summaryManager.enqueueSummarize(options);
|
|
2490
|
+
}
|
|
2491
|
+
else {
|
|
2492
|
+
// If we're not the summarizer, and we don't have a summaryManager, we expect that
|
|
2493
|
+
// generateSummaries is turned off. We are throwing instead of returning a failure here,
|
|
2494
|
+
// because it is a misuse of the API rather than an expected failure.
|
|
2495
|
+
throw new telemetry_utils_1.UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
2240
2496
|
}
|
|
2241
|
-
// Flush pending batch.
|
|
2242
|
-
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
2243
|
-
// to close current batch.
|
|
2244
|
-
this.flush();
|
|
2245
|
-
return {
|
|
2246
|
-
pending: this.pendingStateManager.getLocalState(),
|
|
2247
|
-
pendingAttachmentBlobs: this.blobManager.getPendingBlobs(),
|
|
2248
|
-
};
|
|
2249
2497
|
}
|
|
2250
2498
|
/**
|
|
2251
2499
|
* * Forms a function that will request a Summarizer.
|
|
@@ -2268,7 +2516,7 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2268
2516
|
const fluidObject = await (0, runtime_utils_1.requestFluidObject)(loaderRouter, request);
|
|
2269
2517
|
const summarizer = fluidObject.ISummarizer;
|
|
2270
2518
|
if (!summarizer) {
|
|
2271
|
-
throw new
|
|
2519
|
+
throw new telemetry_utils_1.UsageError("Fluid object does not implement ISummarizer");
|
|
2272
2520
|
}
|
|
2273
2521
|
return summarizer;
|
|
2274
2522
|
};
|
|
@@ -2277,11 +2525,11 @@ class ContainerRuntime extends common_utils_1.TypedEventEmitter {
|
|
|
2277
2525
|
// eslint-disable-next-line no-restricted-syntax
|
|
2278
2526
|
for (const prop in configuration) {
|
|
2279
2527
|
if (typeof configuration[prop] === "number" && configuration[prop] < 0) {
|
|
2280
|
-
throw new
|
|
2528
|
+
throw new telemetry_utils_1.UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
2281
2529
|
}
|
|
2282
2530
|
}
|
|
2283
2531
|
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
2284
|
-
throw new
|
|
2532
|
+
throw new telemetry_utils_1.UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
2285
2533
|
}
|
|
2286
2534
|
}
|
|
2287
2535
|
get groupedBatchingEnabled() {
|