@fluidframework/container-runtime 2.101.1 → 2.103.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/batchTracker.d.ts +1 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.d.ts +2 -2
- package/dist/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/dist/blobManager/blobManagerSnapSum.js.map +1 -1
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +16 -5
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +160 -18
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +2 -2
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +1 -4
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaScheduler.d.ts +2 -2
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +2 -2
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +12 -5
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +3 -3
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +3 -3
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js.map +1 -1
- package/dist/inboundBatchAggregator.d.ts +2 -2
- package/dist/inboundBatchAggregator.d.ts.map +1 -1
- package/dist/inboundBatchAggregator.js.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.d.ts +39 -3
- package/dist/opLifecycle/duplicateBatchDetector.d.ts.map +1 -1
- package/dist/opLifecycle/duplicateBatchDetector.js +57 -15
- package/dist/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.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 +48 -1
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +54 -1
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +2 -2
- package/dist/signalTelemetryProcessing.d.ts +2 -2
- package/dist/signalTelemetryProcessing.d.ts.map +1 -1
- package/dist/signalTelemetryProcessing.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +2 -2
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +35 -3
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +2 -2
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.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.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +3 -3
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -2
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +3 -3
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +2 -2
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.d.ts +2 -2
- package/lib/blobManager/blobManagerSnapSum.d.ts.map +1 -1
- package/lib/blobManager/blobManagerSnapSum.js.map +1 -1
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +16 -5
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +160 -18
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +2 -2
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +2 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaScheduler.d.ts +2 -2
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +2 -2
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +12 -5
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +3 -3
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +3 -3
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js.map +1 -1
- package/lib/inboundBatchAggregator.d.ts +2 -2
- package/lib/inboundBatchAggregator.d.ts.map +1 -1
- package/lib/inboundBatchAggregator.js.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.d.ts +39 -3
- package/lib/opLifecycle/duplicateBatchDetector.d.ts.map +1 -1
- package/lib/opLifecycle/duplicateBatchDetector.js +57 -15
- package/lib/opLifecycle/duplicateBatchDetector.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.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 +48 -1
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +54 -1
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +2 -2
- package/lib/signalTelemetryProcessing.d.ts +2 -2
- package/lib/signalTelemetryProcessing.d.ts.map +1 -1
- package/lib/signalTelemetryProcessing.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +2 -2
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +35 -3
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +2 -2
- package/lib/summary/orderedClientElection.d.ts.map +1 -1
- package/lib/summary/orderedClientElection.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.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +3 -3
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -2
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +3 -3
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +2 -2
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +18 -18
- package/src/batchTracker.ts +3 -3
- package/src/blobManager/blobManagerSnapSum.ts +2 -2
- package/src/connectionTelemetry.ts +3 -3
- package/src/containerRuntime.ts +188 -25
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContext.ts +2 -4
- package/src/dataStoreContexts.ts +2 -2
- package/src/deltaScheduler.ts +2 -5
- package/src/gc/garbageCollection.ts +16 -9
- package/src/gc/gcDefinitions.ts +3 -3
- package/src/gc/gcTelemetry.ts +3 -3
- package/src/inboundBatchAggregator.ts +2 -2
- package/src/opLifecycle/duplicateBatchDetector.ts +103 -23
- package/src/opLifecycle/opCompressor.ts +2 -2
- package/src/opLifecycle/opDecompressor.ts +2 -2
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +2 -2
- package/src/opLifecycle/outbox.ts +2 -2
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +80 -2
- package/src/signalTelemetryProcessing.ts +2 -2
- package/src/summary/documentSchema.ts +58 -5
- package/src/summary/orderedClientElection.ts +3 -3
- package/src/summary/summarizerClientElection.ts +2 -2
- package/src/summary/summarizerNode/summarizerNode.ts +3 -3
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -2
- package/src/summary/summarizerTypes.ts +3 -3
- package/src/summary/summaryCollection.ts +2 -2
- package/src/summary/summaryDelayLoadedModule/runningSummarizer.ts +2 -4
- package/src/summary/summaryDelayLoadedModule/summarizer.ts +3 -3
- package/src/summary/summaryDelayLoadedModule/summarizerHeuristics.ts +2 -2
- package/src/summary/summaryDelayLoadedModule/summaryGenerator.ts +2 -2
- package/src/summary/summaryManager.ts +2 -2
package/dist/containerRuntime.js
CHANGED
|
@@ -632,14 +632,31 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
632
632
|
this.electedSummarizerData = electedSummarizerData;
|
|
633
633
|
this.runtimeOptions = runtimeOptions;
|
|
634
634
|
this.containerScope = containerScope;
|
|
635
|
-
this.baseLogger = baseLogger;
|
|
636
635
|
this._storage = _storage;
|
|
637
636
|
this.createIdCompressorFn = createIdCompressorFn;
|
|
638
637
|
this.documentsSchemaController = documentsSchemaController;
|
|
639
638
|
this.minVersionForCollab = minVersionForCollab;
|
|
640
639
|
this.requestHandler = requestHandler;
|
|
641
640
|
this.summaryConfiguration = summaryConfiguration;
|
|
642
|
-
|
|
641
|
+
/**
|
|
642
|
+
* Whether local op submission is currently disallowed.
|
|
643
|
+
*
|
|
644
|
+
* This is `true` in two distinct situations.
|
|
645
|
+
*
|
|
646
|
+
* First: the delta manager reports a read-only connection (host/service-imposed permission or connection state — the historical meaning of `readOnly`).
|
|
647
|
+
*
|
|
648
|
+
* Second: the `PendingStateManager` is replaying stashed ops (`isApplyingStashedOps`). During this window DDSes must not submit new local ops, as doing so would interleave fresh content ahead of the stashed pending stream and corrupt pending local state. Surfacing it through `isReadOnly()` lets DDSes that consult `readOnly` at realize time self-suppress; see the apply-lifecycle docs on `PendingStateManager` for the full rationale.
|
|
649
|
+
*
|
|
650
|
+
* Note this layers a third meaning ("transiently quiescing for stashed-op replay") onto the `readOnly` predicate, which is broader than its host/connection-permission origin.
|
|
651
|
+
*/
|
|
652
|
+
this.isReadOnly = () =>
|
|
653
|
+
// `_deltaManager` and `pendingStateManager` are both assigned partway
|
|
654
|
+
// through the constructor; `baseLogger` is built earlier and stamps
|
|
655
|
+
// `isReadOnly` on every error event (e.g. layer-compat failures
|
|
656
|
+
// during construction), so this can be called before either is
|
|
657
|
+
// assigned. Optional chains keep that window safe.
|
|
658
|
+
this._deltaManager?.readOnlyInfo.readonly === true ||
|
|
659
|
+
this.pendingStateManager?.isApplyingStashedOps === true;
|
|
643
660
|
// We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
|
|
644
661
|
// Once it loads, it will process all such ops and we will stop accumulating further ops - ops will be processes as they come in.
|
|
645
662
|
this.pendingIdCompressorOps = [];
|
|
@@ -658,7 +675,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
658
675
|
expiry: { policy: "absolute", durationMs: 60000 },
|
|
659
676
|
});
|
|
660
677
|
this.extensions = new Map();
|
|
661
|
-
|
|
678
|
+
// Boolean payload from the `"readonly"` delta-manager event is intentionally
|
|
679
|
+
// ignored — `isReadOnly()` aggregates delta-manager readonly with the PSM
|
|
680
|
+
// apply window, and that aggregation is the source of truth for fanout.
|
|
681
|
+
// `channelCollection?.` guards against future wiring changes; both callers
|
|
682
|
+
// today (the `"readonly"` listener and `onAfterStashedOpsApplied`) fire
|
|
683
|
+
// after `channelCollection` is assigned.
|
|
684
|
+
this.notifyReadOnlyState = (_readonly) => this.channelCollection?.notifyReadOnlyState(this.isReadOnly());
|
|
662
685
|
/**
|
|
663
686
|
* Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
|
|
664
687
|
* To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
|
|
@@ -817,15 +840,20 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
817
840
|
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
818
841
|
this.disposeFn = disposeFn ?? closeFn;
|
|
819
842
|
this.isSnapshotInstanceOfISnapshot = snapshotWithContents !== undefined;
|
|
820
|
-
this.
|
|
821
|
-
logger:
|
|
822
|
-
namespace: "ContainerRuntime",
|
|
843
|
+
this.baseLogger = (0, internal_8.createChildLogger)({
|
|
844
|
+
logger: baseLogger,
|
|
823
845
|
properties: {
|
|
824
|
-
|
|
825
|
-
inStagingMode: this.inStagingMode,
|
|
846
|
+
error: {
|
|
847
|
+
inStagingMode: () => this.inStagingMode,
|
|
848
|
+
isApplyingStashedOps: () => this.pendingStateManager?.isApplyingStashedOps,
|
|
849
|
+
isReadOnly: () => this.isReadOnly(),
|
|
826
850
|
},
|
|
827
851
|
},
|
|
828
852
|
});
|
|
853
|
+
this.mc = (0, internal_8.createChildMonitoringContext)({
|
|
854
|
+
logger: this.baseLogger,
|
|
855
|
+
namespace: "ContainerRuntime",
|
|
856
|
+
});
|
|
829
857
|
// Validate that the Loader is compatible with this Runtime.
|
|
830
858
|
const maybeLoaderCompatDetailsForRuntime = context;
|
|
831
859
|
(0, runtimeLayerCompatState_js_1.validateLoaderCompatibility)(maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails, this.disposeFn, this.mc);
|
|
@@ -948,7 +976,18 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
948
976
|
reSubmitBatch: this.reSubmitBatch.bind(this),
|
|
949
977
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
950
978
|
isAttached: () => this.attachState !== container_definitions_1.AttachState.Detached,
|
|
951
|
-
}, pendingRuntimeState?.pending, this.baseLogger
|
|
979
|
+
}, pendingRuntimeState?.pending, this.baseLogger, {
|
|
980
|
+
// PSM has cleared `isApplyingStashedOps`; `isReadOnly()` now
|
|
981
|
+
// reflects the network-readonly state again. Fan out so DDSes
|
|
982
|
+
// know they can submit once more. No open hook is needed —
|
|
983
|
+
// the apply window opens before `channelCollection` exists,
|
|
984
|
+
// so a fanout there would be a no-op; data stores instead
|
|
985
|
+
// pick up the initial readonly state from `isReadOnly()`
|
|
986
|
+
// when they're first asked.
|
|
987
|
+
onAfterStashedOpsApplied: () => {
|
|
988
|
+
this.notifyReadOnlyState();
|
|
989
|
+
},
|
|
990
|
+
});
|
|
952
991
|
let outerDeltaManager = this.innerDeltaManager;
|
|
953
992
|
this.useDeltaManagerOpsProxy =
|
|
954
993
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") === true;
|
|
@@ -985,18 +1024,39 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
985
1024
|
this.mc.config.getNumber("Fluid.ContainerRuntime.StagingModeAutoFlushThreshold") ??
|
|
986
1025
|
runtimeOptions.stagingModeAutoFlushThreshold ??
|
|
987
1026
|
defaultStagingModeAutoFlushThreshold;
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1027
|
+
// BatchId tracking powers DuplicateBatchDetector (catching forked-container duplicates)
|
|
1028
|
+
// and is also a prerequisite for the Offline Load feature. It is enabled by default
|
|
1029
|
+
// when both TurnBased flush mode and grouped batching are active; the kill-switch
|
|
1030
|
+
// below allows disabling it without a code change if a regression is observed.
|
|
1031
|
+
// Grouped batching is required because resubmits can produce empty batches that must
|
|
1032
|
+
// still be sent on the wire as a placeholder grouped batch to preserve their batchId
|
|
1033
|
+
// (see OpGroupingManager.createEmptyGroupedBatch / outbox.flushEmptyBatch).
|
|
1034
|
+
// Offline Load requires all three prerequisites (TurnBased, grouped batching, and
|
|
1035
|
+
// batchId tracking not killed by config), so a consumer that opts into it without
|
|
1036
|
+
// them gets an explicit UsageError rather than silent degradation.
|
|
1037
|
+
const offlineLoadRequested = this.mc.config.getBoolean("Fluid.Container.enableOfflineFull") === true;
|
|
1038
|
+
const disableBatchIdTracking = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableBatchIdTracking") === true;
|
|
1039
|
+
if (offlineLoadRequested && this._flushMode !== internal_6.FlushMode.TurnBased) {
|
|
993
1040
|
const error = new internal_8.UsageError("Offline mode is only supported in turn-based mode");
|
|
994
1041
|
this.closeFn(error);
|
|
995
1042
|
throw error;
|
|
996
1043
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1044
|
+
if (offlineLoadRequested && !this.groupedBatchingEnabled) {
|
|
1045
|
+
const error = new internal_8.UsageError("Offline mode requires grouped batching to be enabled");
|
|
1046
|
+
this.closeFn(error);
|
|
1047
|
+
throw error;
|
|
1048
|
+
}
|
|
1049
|
+
if (offlineLoadRequested && disableBatchIdTracking) {
|
|
1050
|
+
const error = new internal_8.UsageError("Offline mode requires batchId tracking; remove Fluid.ContainerRuntime.DisableBatchIdTracking");
|
|
1051
|
+
this.closeFn(error);
|
|
1052
|
+
throw error;
|
|
1053
|
+
}
|
|
1054
|
+
this.batchIdTrackingEnabled =
|
|
1055
|
+
!disableBatchIdTracking &&
|
|
1056
|
+
this._flushMode === internal_6.FlushMode.TurnBased &&
|
|
1057
|
+
this.groupedBatchingEnabled;
|
|
1058
|
+
// DuplicateBatchDetector maintains a cache of all batchIds/sequenceNumbers within the
|
|
1059
|
+
// collab window. Skip allocating it when batchId tracking is off.
|
|
1000
1060
|
if (this.batchIdTrackingEnabled) {
|
|
1001
1061
|
this.duplicateBatchDetector = new index_js_3.DuplicateBatchDetector(recentBatchInfo);
|
|
1002
1062
|
}
|
|
@@ -1193,6 +1253,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1193
1253
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1194
1254
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1195
1255
|
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
1256
|
+
// Number of ops since the last summary that this client is aware of (including ops still
|
|
1257
|
+
// queued for processing). Computed as the gap between the latest known op sequence number
|
|
1258
|
+
// and the sequence number of the message at which the last summary was taken (per snapshot
|
|
1259
|
+
// metadata). Falls back to lastKnownSeqNumber when no prior summary message is recorded
|
|
1260
|
+
// (e.g. new container or older snapshot without metadata).
|
|
1261
|
+
numUnsummarizedOps: this.deltaManager.lastKnownSeqNumber -
|
|
1262
|
+
(this.messageAtLastSummary?.sequenceNumber ?? 0),
|
|
1196
1263
|
minVersionForCollab: this.minVersionForCollab,
|
|
1197
1264
|
// logging hardware telemetry
|
|
1198
1265
|
deviceSpec: { ...getDeviceSpec() },
|
|
@@ -1645,6 +1712,24 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1645
1712
|
if (!this.shouldSendOps()) {
|
|
1646
1713
|
return;
|
|
1647
1714
|
}
|
|
1715
|
+
// Invariant: the canSendOps edge in `setConnectionStateCore` — the
|
|
1716
|
+
// only caller of this method — cannot fire while
|
|
1717
|
+
// `applyStashedOpsAt` is in flight, because the loader awaits the
|
|
1718
|
+
// apply before transitioning the runtime to a write-capable
|
|
1719
|
+
// connection. If this assert ever fires, that contract has changed
|
|
1720
|
+
// and the submit guard at `submit()` would catch a runtime-internal
|
|
1721
|
+
// resubmit (`Rejoin`, `GC`, `FluidDataStoreOp`) for an op type
|
|
1722
|
+
// outside the apply-window allowlist.
|
|
1723
|
+
//
|
|
1724
|
+
// The precondition is held by the load sequence: `loadRuntime2`
|
|
1725
|
+
// awaits `pendingStateManager.applyStashedOpsAt(...)` before
|
|
1726
|
+
// returning the runtime, and the loader gates `setLoaded` on that
|
|
1727
|
+
// completion before any write-capable connection edge fires. A
|
|
1728
|
+
// maintainer reordering either sequence (or adding a new
|
|
1729
|
+
// `canSendOps` edge that fires before the apply resolves) is what
|
|
1730
|
+
// would trip this assert.
|
|
1731
|
+
// @see {@link ContainerRuntime.loadRuntime2} (awaits `applyStashedOpsAt`)
|
|
1732
|
+
(0, internal_2.assert)(!this.pendingStateManager.isApplyingStashedOps, 0xd01 /* replayPendingStates must not be called during stashed-op apply window */);
|
|
1648
1733
|
// Replaying is an internal operation and we don't want to generate noise while doing it.
|
|
1649
1734
|
// So temporarily disable dirty state change events, and save the old state.
|
|
1650
1735
|
// When we're done, we'll emit the event if the state changed.
|
|
@@ -1918,14 +2003,32 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1918
2003
|
eventName: "DuplicateBatch",
|
|
1919
2004
|
details: {
|
|
1920
2005
|
batchId: batchStart.batchId,
|
|
2006
|
+
batchIdExplicit: batchStart.batchId !== undefined,
|
|
1921
2007
|
clientId: batchStart.clientId,
|
|
1922
2008
|
batchStartCsn: batchStart.batchStartCsn,
|
|
1923
2009
|
size: inboundResult.length,
|
|
1924
2010
|
duplicateBatchSequenceNumber: result.otherSequenceNumber,
|
|
2011
|
+
// Identifying info for the ORIGINAL occurrence of this batch, so we can
|
|
2012
|
+
// disambiguate the duplicate's source (e.g. resubmit vs fresh submit, same
|
|
2013
|
+
// vs different wire clientId). Undefined fields indicate the original was
|
|
2014
|
+
// loaded from a summary snapshot rather than seen at runtime.
|
|
2015
|
+
otherClientId: result.otherBatchInfo?.clientId,
|
|
2016
|
+
otherBatchStartCsn: result.otherBatchInfo?.batchStartCsn,
|
|
2017
|
+
otherBatchIdExplicit: result.otherBatchInfo?.batchIdExplicit,
|
|
2018
|
+
otherFromSnapshot: result.otherBatchInfo === undefined,
|
|
1925
2019
|
...(0, internal_8.extractSafePropertiesFromMessage)(batchStart.keyMessage),
|
|
2020
|
+
// For grouped batches, `keyMessage` is one of the sub-messages produced by
|
|
2021
|
+
// `OpGroupingManager.ungroupOp`, which overwrites `clientSequenceNumber`
|
|
2022
|
+
// with a synthetic counter (1, 2, 3, ...). Override with the real outer
|
|
2023
|
+
// envelope's clientSequenceNumber so downstream telemetry doesn't get a
|
|
2024
|
+
// misleading "fake csn" value.
|
|
2025
|
+
messageClientSequenceNumber: batchStart.batchStartCsn,
|
|
1926
2026
|
},
|
|
1927
2027
|
}, error);
|
|
1928
|
-
throw error
|
|
2028
|
+
// Due to a live incident where we had a bug in the service that caused duplicate batches to be sent to clients, we want to log when we detect a duplicate batch, but we don't want to throw an error
|
|
2029
|
+
// as it could hit the same service bug. We need to monitor below event to catch legitimate container forking scenarios and reenable throwing the data corruption error once the service bug is fixed and we stop seeing duplicate batches in the wild
|
|
2030
|
+
// or once we are able to identify batch duplication reason (forking vs service bug).
|
|
2031
|
+
// throw error;
|
|
1929
2032
|
}
|
|
1930
2033
|
}
|
|
1931
2034
|
// Reach out to PendingStateManager, either to zip localOpMetadata into the *local* message list,
|
|
@@ -3023,6 +3126,45 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3023
3126
|
}
|
|
3024
3127
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
3025
3128
|
this.verifyNotClosed();
|
|
3129
|
+
// Nothing should be submitting while we're replaying stashed ops.
|
|
3130
|
+
// The runtime is readonly during the apply window (see
|
|
3131
|
+
// `PendingStateManager._applyLifecycle`), so a compliant DDS skips
|
|
3132
|
+
// submits. If we land here anyway, a DDS bypassed the readonly gate
|
|
3133
|
+
// (e.g. a realize-time write that doesn't consult `readOnly`) and
|
|
3134
|
+
// produced a local op that has no counterpart in the saved-op
|
|
3135
|
+
// replay — we cannot reconcile the mismatch, so fail fatally. We
|
|
3136
|
+
// check here (rather than at flush) because outbox flushes are
|
|
3137
|
+
// deferred and the apply window could close before the offending op
|
|
3138
|
+
// reaches the pending queue.
|
|
3139
|
+
//
|
|
3140
|
+
// Allowlist: `BlobAttach` is a runtime-internal op type that may
|
|
3141
|
+
// legitimately fire during apply — produced by `sharePendingBlobs`,
|
|
3142
|
+
// which is invoked from `loadRuntime2` before `applyStashedOpsAt`
|
|
3143
|
+
// resolves. `IdAllocation` is not in this allowlist because the
|
|
3144
|
+
// assert at 0x9a5 below enforces that it never reaches `submit()`
|
|
3145
|
+
// at all; treating that assert as the single source of truth.
|
|
3146
|
+
//
|
|
3147
|
+
// Always surface the error event to telemetry on a bypass so we can
|
|
3148
|
+
// attribute incidents regardless of the on-switch state. The
|
|
3149
|
+
// `EnableSubmitDuringStashedApplyThrow` config opts in to the
|
|
3150
|
+
// throw + container close; by default we log only, so a first- or
|
|
3151
|
+
// third-party DDS that quietly bypasses the readonly gate in
|
|
3152
|
+
// production is observable without escalating to a fatal close.
|
|
3153
|
+
if (this.pendingStateManager.isApplyingStashedOps &&
|
|
3154
|
+
containerRuntimeMessage.type !== messageTypes_js_1.ContainerMessageType.BlobAttach) {
|
|
3155
|
+
const error = new internal_8.UsageError("Local op submitted during stashed-op apply window", {
|
|
3156
|
+
messageType: containerRuntimeMessage.type,
|
|
3157
|
+
});
|
|
3158
|
+
this.mc.logger.sendErrorEvent({ eventName: "SubmitDuringStashedOpApply" }, error);
|
|
3159
|
+
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableSubmitDuringStashedApplyThrow") === true) {
|
|
3160
|
+
// Close the container before throwing so the "throw + close"
|
|
3161
|
+
// contract is enforced by this code path rather than by
|
|
3162
|
+
// whichever caller happens to wrap the throw in `.catch(closeFn)`.
|
|
3163
|
+
// `closeFn` is idempotent; a caller that also closes won't double-close.
|
|
3164
|
+
this.closeFn(error);
|
|
3165
|
+
throw error;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3026
3168
|
// There should be no ops in detached container state!
|
|
3027
3169
|
(0, internal_2.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
3028
3170
|
(0, internal_2.assert)(metadata === undefined ||
|