@fluidframework/container-runtime 2.102.0 → 2.110.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 +112 -9
- 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/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.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/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 +1 -1
- 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.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 +112 -9
- 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/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 +1 -1
- 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/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +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 +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +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 +55 -2
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +1 -1
- 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.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 +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 +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 +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 +1 -1
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +19 -19
- package/src/batchTracker.ts +3 -3
- package/src/blobManager/blobManagerSnapSum.ts +2 -2
- package/src/connectionTelemetry.ts +5 -5
- package/src/containerRuntime.ts +134 -15
- package/src/dataStore.ts +3 -3
- package/src/dataStoreContexts.ts +2 -2
- package/src/deltaScheduler.ts +2 -5
- package/src/gc/garbageCollection.ts +6 -6
- package/src/gc/gcDefinitions.ts +3 -3
- package/src/gc/gcTelemetry.ts +5 -5
- package/src/inboundBatchAggregator.ts +2 -2
- package/src/opLifecycle/opCompressor.ts +3 -3
- package/src/opLifecycle/opDecompressor.ts +2 -2
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +3 -3
- package/src/opLifecycle/outbox.ts +4 -4
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +82 -4
- package/src/signalTelemetryProcessing.ts +2 -2
- package/src/summary/documentSchema.ts +2 -2
- package/src/summary/orderedClientElection.ts +4 -4
- 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 +4 -6
- package/src/summary/summaryDelayLoadedModule/summarizer.ts +4 -4
- package/src/summary/summaryDelayLoadedModule/summarizerHeuristics.ts +2 -2
- package/src/summary/summaryDelayLoadedModule/summaryGenerator.ts +2 -2
- package/src/summary/summaryManager.ts +3 -3
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;
|
|
@@ -1214,6 +1253,13 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1214
1253
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1215
1254
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1216
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),
|
|
1217
1263
|
minVersionForCollab: this.minVersionForCollab,
|
|
1218
1264
|
// logging hardware telemetry
|
|
1219
1265
|
deviceSpec: { ...getDeviceSpec() },
|
|
@@ -1666,6 +1712,24 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1666
1712
|
if (!this.shouldSendOps()) {
|
|
1667
1713
|
return;
|
|
1668
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 */);
|
|
1669
1733
|
// Replaying is an internal operation and we don't want to generate noise while doing it.
|
|
1670
1734
|
// So temporarily disable dirty state change events, and save the old state.
|
|
1671
1735
|
// When we're done, we'll emit the event if the state changed.
|
|
@@ -3062,6 +3126,45 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3062
3126
|
}
|
|
3063
3127
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
3064
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
|
+
}
|
|
3065
3168
|
// There should be no ops in detached container state!
|
|
3066
3169
|
(0, internal_2.assert)(this.attachState !== container_definitions_1.AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
3067
3170
|
(0, internal_2.assert)(metadata === undefined ||
|