@fluidframework/container-runtime 2.0.0-dev-rc.3.0.0.254674 → 2.0.0-dev-rc.4.0.0.261659
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 +23 -0
- package/api-report/container-runtime.api.md +33 -19
- package/dist/batchTracker.d.ts +1 -1
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/channelCollection.d.ts +5 -3
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +67 -15
- package/dist/channelCollection.js.map +1 -1
- package/dist/connectionTelemetry.d.ts +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +54 -5
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +17 -27
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +174 -143
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts +1 -1
- 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 -0
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.d.ts +2 -0
- package/dist/dataStoreContexts.d.ts.map +1 -1
- package/dist/dataStoreContexts.js +7 -0
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/deltaManagerSummarizerProxy.d.ts +18 -6
- package/dist/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/dist/deltaManagerSummarizerProxy.js +38 -19
- package/dist/deltaManagerSummarizerProxy.js.map +1 -1
- package/dist/deltaScheduler.d.ts +1 -1
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +5 -12
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +45 -29
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +27 -6
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts +5 -4
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +14 -2
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/gc/gcTelemetry.d.ts +14 -4
- package/dist/gc/gcTelemetry.d.ts.map +1 -1
- package/dist/gc/gcTelemetry.js +24 -21
- package/dist/gc/gcTelemetry.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 +2 -2
- package/dist/gc/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/{alpha.d.ts → legacy.d.ts} +3 -1
- package/dist/metadata.d.ts +2 -2
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +0 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +0 -10
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +6 -6
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +2 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSplitter.js +1 -1
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +0 -4
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +2 -34
- 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 +3 -2
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +17 -10
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +3 -0
- package/dist/scheduleManager.d.ts +1 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +3 -1
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +34 -16
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.d.ts +1 -1
- package/dist/summary/orderedClientElection.d.ts.map +1 -1
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/summarizer.d.ts +1 -2
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerHeuristics.d.ts +1 -1
- package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.d.ts +4 -3
- package/dist/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +4 -10
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
- package/dist/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +2 -3
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryCollection.d.ts +1 -1
- package/dist/summary/summaryCollection.d.ts.map +1 -1
- package/dist/summary/summaryCollection.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +1 -2
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +3 -2
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js.map +1 -1
- package/{dist/beta.d.ts → internal.d.ts} +2 -0
- package/{lib/beta.d.ts → legacy.d.ts} +2 -0
- package/lib/batchTracker.d.ts +1 -1
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/channelCollection.d.ts +5 -3
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +69 -17
- package/lib/channelCollection.js.map +1 -1
- package/lib/connectionTelemetry.d.ts +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +49 -0
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +17 -27
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +174 -143
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +1 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +1 -0
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.d.ts +2 -0
- package/lib/dataStoreContexts.d.ts.map +1 -1
- package/lib/dataStoreContexts.js +7 -0
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/deltaManagerSummarizerProxy.d.ts +18 -6
- package/lib/deltaManagerSummarizerProxy.d.ts.map +1 -1
- package/lib/deltaManagerSummarizerProxy.js +36 -18
- package/lib/deltaManagerSummarizerProxy.js.map +1 -1
- package/lib/deltaScheduler.d.ts +1 -1
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +5 -12
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +47 -31
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +27 -6
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts +5 -4
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +12 -1
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/gc/gcTelemetry.d.ts +14 -4
- package/lib/gc/gcTelemetry.d.ts.map +1 -1
- package/lib/gc/gcTelemetry.js +24 -21
- package/lib/gc/gcTelemetry.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 +1 -1
- package/lib/gc/index.js.map +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/{alpha.d.ts → legacy.d.ts} +3 -1
- package/lib/metadata.d.ts +2 -2
- package/lib/metadata.d.ts.map +1 -1
- package/lib/metadata.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +0 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +0 -10
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +6 -6
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +2 -2
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +0 -4
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +2 -34
- 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 +3 -2
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +18 -11
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +3 -0
- package/lib/scheduleManager.d.ts +1 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +3 -1
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +34 -16
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.d.ts +1 -1
- 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/summarizer.d.ts +1 -2
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerHeuristics.d.ts +1 -1
- package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.d.ts +4 -3
- package/lib/summary/summarizerNode/summarizerNode.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +4 -10
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts +2 -3
- package/lib/summary/summarizerNode/summarizerNodeUtils.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeUtils.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +1 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +2 -9
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +2 -3
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryCollection.d.ts +1 -1
- package/lib/summary/summaryCollection.d.ts.map +1 -1
- package/lib/summary/summaryCollection.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +1 -2
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +4 -3
- package/lib/summary/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 +33 -57
- package/src/batchTracker.ts +1 -2
- package/src/channelCollection.ts +87 -35
- package/src/connectionTelemetry.ts +58 -3
- package/src/containerRuntime.ts +214 -222
- package/src/dataStore.ts +5 -2
- package/src/dataStoreContext.ts +1 -0
- package/src/dataStoreContexts.ts +13 -2
- package/src/deltaManagerSummarizerProxy.ts +43 -21
- package/src/deltaScheduler.ts +1 -2
- package/src/gc/garbageCollection.ts +64 -42
- package/src/gc/gcDefinitions.ts +22 -10
- package/src/gc/gcHelpers.ts +14 -1
- package/src/gc/gcTelemetry.ts +57 -50
- package/src/gc/index.ts +2 -1
- package/src/index.ts +2 -0
- package/src/metadata.ts +2 -2
- package/src/opLifecycle/README.md +4 -4
- package/src/opLifecycle/batchManager.ts +0 -14
- package/src/opLifecycle/opDecompressor.ts +12 -6
- package/src/opLifecycle/opGroupingManager.ts +2 -2
- package/src/opLifecycle/opSplitter.ts +1 -1
- package/src/opLifecycle/outbox.ts +2 -49
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +28 -15
- package/src/scheduleManager.ts +1 -1
- package/src/summary/documentSchema.ts +52 -18
- package/src/summary/orderedClientElection.ts +5 -2
- package/src/summary/summarizer.ts +1 -1
- package/src/summary/summarizerClientElection.ts +1 -1
- package/src/summary/summarizerHeuristics.ts +1 -1
- package/src/summary/summarizerNode/summarizerNode.ts +3 -12
- package/src/summary/summarizerNode/summarizerNodeUtils.ts +2 -3
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +1 -10
- package/src/summary/summarizerTypes.ts +5 -3
- package/src/summary/summaryCollection.ts +1 -1
- package/src/summary/summaryGenerator.ts +19 -8
- package/src/summary/summaryManager.ts +5 -2
package/src/containerRuntime.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Trace, TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
|
7
7
|
import {
|
|
8
8
|
AttachState,
|
|
9
9
|
IAudience,
|
|
10
|
+
ISelf,
|
|
10
11
|
ICriticalContainerError,
|
|
11
12
|
IDeltaManager,
|
|
12
13
|
} from "@fluidframework/container-definitions";
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
ILoader,
|
|
18
19
|
IRuntime,
|
|
19
20
|
LoaderHeader,
|
|
21
|
+
type IAudienceEvents,
|
|
20
22
|
} from "@fluidframework/container-definitions/internal";
|
|
21
23
|
import {
|
|
22
24
|
IContainerRuntime,
|
|
@@ -99,11 +101,9 @@ import {
|
|
|
99
101
|
responseToException,
|
|
100
102
|
seqFromTree,
|
|
101
103
|
} from "@fluidframework/runtime-utils/internal";
|
|
104
|
+
import type { ITelemetryGenericEventExt } from "@fluidframework/telemetry-utils/internal";
|
|
102
105
|
import {
|
|
103
|
-
type ITelemetryGenericEventExt,
|
|
104
106
|
ITelemetryLoggerExt,
|
|
105
|
-
} from "@fluidframework/telemetry-utils";
|
|
106
|
-
import {
|
|
107
107
|
DataCorruptionError,
|
|
108
108
|
DataProcessingError,
|
|
109
109
|
GenericError,
|
|
@@ -150,7 +150,7 @@ import {
|
|
|
150
150
|
type OutboundContainerRuntimeMessage,
|
|
151
151
|
type UnknownContainerRuntimeMessage,
|
|
152
152
|
} from "./messageTypes.js";
|
|
153
|
-
import { IBatchMetadata,
|
|
153
|
+
import { IBatchMetadata, ISavedOpMetadata } from "./metadata.js";
|
|
154
154
|
import {
|
|
155
155
|
BatchMessage,
|
|
156
156
|
IBatch,
|
|
@@ -161,7 +161,6 @@ import {
|
|
|
161
161
|
OpSplitter,
|
|
162
162
|
Outbox,
|
|
163
163
|
RemoteMessageProcessor,
|
|
164
|
-
getLongStack,
|
|
165
164
|
} from "./opLifecycle/index.js";
|
|
166
165
|
import { pkgVersion } from "./packageVersion.js";
|
|
167
166
|
import {
|
|
@@ -461,16 +460,6 @@ export interface IContainerRuntimeOptions {
|
|
|
461
460
|
*/
|
|
462
461
|
readonly enableRuntimeIdCompressor?: IdCompressorMode;
|
|
463
462
|
|
|
464
|
-
/**
|
|
465
|
-
* If enabled, the runtime will block all attempts to send an op inside the
|
|
466
|
-
* {@link ContainerRuntime#ensureNoDataModelChanges} callback. The callback is used by
|
|
467
|
-
* {@link @fluidframework/shared-object-base#SharedObjectCore} for event handlers so enabling this
|
|
468
|
-
* will disallow modifying DDSes while handling DDS events.
|
|
469
|
-
*
|
|
470
|
-
* By default, the feature is disabled. If enabled from options, the `Fluid.ContainerRuntime.DisableOpReentryCheck`
|
|
471
|
-
* can be used to disable it at runtime.
|
|
472
|
-
*/
|
|
473
|
-
readonly enableOpReentryCheck?: boolean;
|
|
474
463
|
/**
|
|
475
464
|
* If enabled, the runtime will group messages within a batch into a single
|
|
476
465
|
* message to be sent to the service.
|
|
@@ -493,6 +482,11 @@ export interface IContainerRuntimeOptions {
|
|
|
493
482
|
readonly explicitSchemaControl?: boolean;
|
|
494
483
|
}
|
|
495
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Error responses when requesting a deleted object will have this header set to true
|
|
487
|
+
* @alpha
|
|
488
|
+
*/
|
|
489
|
+
export const DeletedResponseHeaderKey = "wasDeleted";
|
|
496
490
|
/**
|
|
497
491
|
* Tombstone error responses will have this header set to true
|
|
498
492
|
* @alpha
|
|
@@ -666,11 +660,13 @@ type MessageWithContext =
|
|
|
666
660
|
message: InboundSequencedContainerRuntimeMessage;
|
|
667
661
|
modernRuntimeMessage: true;
|
|
668
662
|
local: boolean;
|
|
663
|
+
savedOp?: boolean;
|
|
669
664
|
}
|
|
670
665
|
| {
|
|
671
666
|
message: InboundSequencedContainerRuntimeMessageOrSystemMessage;
|
|
672
667
|
modernRuntimeMessage: false;
|
|
673
668
|
local: boolean;
|
|
669
|
+
savedOp?: boolean;
|
|
674
670
|
};
|
|
675
671
|
|
|
676
672
|
const summarizerRequestUrl = "_summarizer";
|
|
@@ -805,7 +801,6 @@ export class ContainerRuntime
|
|
|
805
801
|
maxBatchSizeInBytes = defaultMaxBatchSizeInBytes,
|
|
806
802
|
enableRuntimeIdCompressor,
|
|
807
803
|
chunkSizeInBytes = defaultChunkSizeInBytes,
|
|
808
|
-
enableOpReentryCheck = false,
|
|
809
804
|
enableGroupedBatching = false,
|
|
810
805
|
explicitSchemaControl = false,
|
|
811
806
|
} = runtimeOptions;
|
|
@@ -852,9 +847,9 @@ export class ContainerRuntime
|
|
|
852
847
|
|
|
853
848
|
// Verify summary runtime sequence number matches protocol sequence number.
|
|
854
849
|
const runtimeSequenceNumber = messageAtLastSummary?.sequenceNumber;
|
|
850
|
+
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
855
851
|
// When we load with pending state, we reuse an old snapshot so we don't expect these numbers to match
|
|
856
852
|
if (!context.pendingLocalState && runtimeSequenceNumber !== undefined) {
|
|
857
|
-
const protocolSequenceNumber = context.deltaManager.initialSequenceNumber;
|
|
858
853
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
859
854
|
if (
|
|
860
855
|
loadSequenceNumberVerification !== "bypass" &&
|
|
@@ -914,7 +909,16 @@ export class ContainerRuntime
|
|
|
914
909
|
|
|
915
910
|
// This is the only exception to the rule above - we have proper plumbing to load ID compressor on schema change
|
|
916
911
|
// event. It is loaded async (relative to op processing), so this conversion is only safe for off -> delayed conversion!
|
|
917
|
-
|
|
912
|
+
// Clients do not expect ID compressor ops unless ID compressor is On for them, and that could be achieved only through
|
|
913
|
+
// explicit schema change, i.e. only if explicitSchemaControl is on.
|
|
914
|
+
// Note: it would be better if we throw on combination of options (explicitSchemaControl = off, desiredIdCompressorMode === "delayed")
|
|
915
|
+
// that is not supported. But our service tests are oblivious to these problems and throwing here will cause a ton of failures
|
|
916
|
+
// We ignored incompatible ID compressor changes from the start (they were sticky), so that's not a new problem being introduced...
|
|
917
|
+
if (
|
|
918
|
+
idCompressorMode === undefined &&
|
|
919
|
+
desiredIdCompressorMode === "delayed" &&
|
|
920
|
+
explicitSchemaControl
|
|
921
|
+
) {
|
|
918
922
|
idCompressorMode = desiredIdCompressorMode;
|
|
919
923
|
}
|
|
920
924
|
} else {
|
|
@@ -973,6 +977,7 @@ export class ContainerRuntime
|
|
|
973
977
|
|
|
974
978
|
const documentSchemaController = new DocumentsSchemaController(
|
|
975
979
|
existing,
|
|
980
|
+
protocolSequenceNumber,
|
|
976
981
|
metadata?.documentSchema,
|
|
977
982
|
{
|
|
978
983
|
explicitSchemaControl,
|
|
@@ -1008,7 +1013,6 @@ export class ContainerRuntime
|
|
|
1008
1013
|
chunkSizeInBytes,
|
|
1009
1014
|
// Requires<> drops undefined from IdCompressorType
|
|
1010
1015
|
enableRuntimeIdCompressor: enableRuntimeIdCompressor as "on" | "delayed",
|
|
1011
|
-
enableOpReentryCheck,
|
|
1012
1016
|
enableGroupedBatching,
|
|
1013
1017
|
explicitSchemaControl,
|
|
1014
1018
|
},
|
|
@@ -1101,7 +1105,17 @@ export class ContainerRuntime
|
|
|
1101
1105
|
return this._getAttachState();
|
|
1102
1106
|
}
|
|
1103
1107
|
|
|
1104
|
-
|
|
1108
|
+
/**
|
|
1109
|
+
* Current session schema - defines what options are on & off.
|
|
1110
|
+
* It's overlap of document schema (controlled by summary & ops) and options controlling this session.
|
|
1111
|
+
* For example, document schema might have compression ON, but feature gates / runtime options turn it Off.
|
|
1112
|
+
* In such case it will be off in session schema (i.e. this session should not use compression), but this client
|
|
1113
|
+
* has to deal with compressed ops as other clients might send them.
|
|
1114
|
+
* And in reverse, session schema can have compression Off, but feature gates / runtime options want it On.
|
|
1115
|
+
* In such case it will be off in session schema, however this client will propose change to schema, and once / if
|
|
1116
|
+
* this op rountrips, compression will be On. Client can't send compressed ops until it's change in schema.
|
|
1117
|
+
*/
|
|
1118
|
+
public get sessionSchema() {
|
|
1105
1119
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
1106
1120
|
}
|
|
1107
1121
|
|
|
@@ -1114,11 +1128,11 @@ export class ContainerRuntime
|
|
|
1114
1128
|
// Id Compressor serializes final state (see getPendingLocalState()). As result, it needs to skip all ops that preceeded that state
|
|
1115
1129
|
// (such ops will be marked by Loader layer as savedOp === true)
|
|
1116
1130
|
// That said, in "delayed" mode it's possible that Id Compressor was never initialized before getPendingLocalState() is called.
|
|
1117
|
-
// In such case we have to process all ops, including those marked with
|
|
1131
|
+
// In such case we have to process all ops, including those marked with savedOp === true.
|
|
1118
1132
|
private readonly skipSavedCompressorOps: boolean;
|
|
1119
1133
|
|
|
1120
1134
|
public get idCompressorMode() {
|
|
1121
|
-
return this.
|
|
1135
|
+
return this.sessionSchema.idCompressorMode;
|
|
1122
1136
|
}
|
|
1123
1137
|
/**
|
|
1124
1138
|
* See IContainerRuntimeBase.idCompressor() for details.
|
|
@@ -1201,14 +1215,6 @@ export class ContainerRuntime
|
|
|
1201
1215
|
|
|
1202
1216
|
private ensureNoDataModelChangesCalls = 0;
|
|
1203
1217
|
|
|
1204
|
-
/**
|
|
1205
|
-
* Tracks the number of detected reentrant ops to report,
|
|
1206
|
-
* in order to self-throttle the telemetry events.
|
|
1207
|
-
*
|
|
1208
|
-
* This should be removed as part of ADO:2322
|
|
1209
|
-
*/
|
|
1210
|
-
private opReentryCallsToReport = 5;
|
|
1211
|
-
|
|
1212
1218
|
/**
|
|
1213
1219
|
* Invokes the given callback and expects that no ops are submitted
|
|
1214
1220
|
* until execution finishes. If an op is submitted, an error will be raised.
|
|
@@ -1242,16 +1248,8 @@ export class ContainerRuntime
|
|
|
1242
1248
|
|
|
1243
1249
|
private dirtyContainer: boolean;
|
|
1244
1250
|
private emitDirtyDocumentEvent = true;
|
|
1245
|
-
private readonly enableOpReentryCheck: boolean;
|
|
1246
1251
|
private readonly disableAttachReorder: boolean | undefined;
|
|
1247
1252
|
private readonly closeSummarizerDelayMs: number;
|
|
1248
|
-
/**
|
|
1249
|
-
* If true, summary generated is validate before uploading it to the server. With single commit summaries,
|
|
1250
|
-
* summaries will be accepted once uploaded, so they should be validated before upload. However, this can
|
|
1251
|
-
* currently be controlled via a feature flag as its a new functionality.
|
|
1252
|
-
*/
|
|
1253
|
-
private readonly validateSummaryBeforeUpload: boolean;
|
|
1254
|
-
|
|
1255
1253
|
private readonly defaultTelemetrySignalSampleCount = 100;
|
|
1256
1254
|
private readonly _perfSignalData: IPerfSignalReport = {
|
|
1257
1255
|
signalsLost: 0,
|
|
@@ -1408,7 +1406,7 @@ export class ContainerRuntime
|
|
|
1408
1406
|
// If it's not in the list, then we will need to either use no compression, or fallback to some other (supported by format)
|
|
1409
1407
|
// compression.
|
|
1410
1408
|
const compressionOptions: ICompressionRuntimeOptions = {
|
|
1411
|
-
minimumBatchSizeInBytes: this.
|
|
1409
|
+
minimumBatchSizeInBytes: this.sessionSchema.compressionLz4
|
|
1412
1410
|
? runtimeOptions.compressionOptions.minimumBatchSizeInBytes
|
|
1413
1411
|
: Number.POSITIVE_INFINITY,
|
|
1414
1412
|
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
@@ -1526,14 +1524,6 @@ export class ContainerRuntime
|
|
|
1526
1524
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1527
1525
|
}
|
|
1528
1526
|
|
|
1529
|
-
const disableOpReentryCheck = this.mc.config.getBoolean(
|
|
1530
|
-
"Fluid.ContainerRuntime.DisableOpReentryCheck",
|
|
1531
|
-
);
|
|
1532
|
-
this.enableOpReentryCheck =
|
|
1533
|
-
runtimeOptions.enableOpReentryCheck === true &&
|
|
1534
|
-
// Allow for a break-glass config to override the options
|
|
1535
|
-
disableOpReentryCheck !== true;
|
|
1536
|
-
|
|
1537
1527
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
1538
1528
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
1539
1529
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
@@ -1630,22 +1620,7 @@ export class ContainerRuntime
|
|
|
1630
1620
|
getSummaryForDatastores(baseSnapshot, metadata),
|
|
1631
1621
|
parentContext,
|
|
1632
1622
|
this.mc.logger,
|
|
1633
|
-
(
|
|
1634
|
-
path: string,
|
|
1635
|
-
reason: "Loaded" | "Changed",
|
|
1636
|
-
timestampMs?: number,
|
|
1637
|
-
packagePath?: readonly string[],
|
|
1638
|
-
request?: IRequest,
|
|
1639
|
-
headerData?: RuntimeHeaderData,
|
|
1640
|
-
) =>
|
|
1641
|
-
this.garbageCollector.nodeUpdated(
|
|
1642
|
-
path,
|
|
1643
|
-
reason,
|
|
1644
|
-
timestampMs,
|
|
1645
|
-
packagePath,
|
|
1646
|
-
request,
|
|
1647
|
-
headerData,
|
|
1648
|
-
),
|
|
1623
|
+
(props) => this.garbageCollector.nodeUpdated(props),
|
|
1649
1624
|
(path: string) => this.garbageCollector.isNodeDeleted(path),
|
|
1650
1625
|
new Map<string, string>(dataStoreAliasMap),
|
|
1651
1626
|
async (runtime: ChannelCollection) => provideEntryPoint,
|
|
@@ -1668,7 +1643,10 @@ export class ContainerRuntime
|
|
|
1668
1643
|
}
|
|
1669
1644
|
},
|
|
1670
1645
|
blobRequested: (blobPath: string) =>
|
|
1671
|
-
this.garbageCollector.nodeUpdated(
|
|
1646
|
+
this.garbageCollector.nodeUpdated({
|
|
1647
|
+
node: { type: "Blob", path: blobPath },
|
|
1648
|
+
reason: "Loaded",
|
|
1649
|
+
}),
|
|
1672
1650
|
isBlobDeleted: (blobPath: string) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
1673
1651
|
runtime: this,
|
|
1674
1652
|
stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
|
|
@@ -1734,16 +1712,37 @@ export class ContainerRuntime
|
|
|
1734
1712
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
1735
1713
|
});
|
|
1736
1714
|
|
|
1737
|
-
|
|
1738
|
-
|
|
1715
|
+
this._audience = audience;
|
|
1716
|
+
if (audience.getSelf === undefined) {
|
|
1717
|
+
// back-compat, added in 2.0 RC3.
|
|
1718
|
+
// Purpose: deal with cases when we run against old loader that does not have newly added capabilities
|
|
1719
|
+
audience.getSelf = () => {
|
|
1720
|
+
const clientId = this._getClientId();
|
|
1721
|
+
return clientId === undefined
|
|
1722
|
+
? undefined
|
|
1723
|
+
: ({
|
|
1724
|
+
clientId,
|
|
1725
|
+
client: audience.getMember(clientId),
|
|
1726
|
+
} satisfies ISelf);
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
let oldClientId = this.clientId;
|
|
1730
|
+
this.on("connected", () => {
|
|
1731
|
+
const clientId = this.clientId;
|
|
1732
|
+
assert(clientId !== undefined, "can't be undefined");
|
|
1733
|
+
(audience as unknown as TypedEventEmitter<IAudienceEvents>).emit(
|
|
1734
|
+
"selfChanged",
|
|
1735
|
+
{ clientId: oldClientId },
|
|
1736
|
+
{ clientId, client: audience.getMember(clientId) },
|
|
1737
|
+
);
|
|
1738
|
+
oldClientId = clientId;
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1739
1741
|
|
|
1740
1742
|
const closeSummarizerDelayOverride = this.mc.config.getNumber(
|
|
1741
1743
|
"Fluid.ContainerRuntime.Test.CloseSummarizerDelayOverrideMs",
|
|
1742
1744
|
);
|
|
1743
1745
|
this.closeSummarizerDelayMs = closeSummarizerDelayOverride ?? defaultCloseSummarizerDelayMs;
|
|
1744
|
-
this.validateSummaryBeforeUpload =
|
|
1745
|
-
this.mc.config.getBoolean("Fluid.Summarizer.ValidateSummaryBeforeUpload") ?? false;
|
|
1746
|
-
|
|
1747
1746
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
1748
1747
|
|
|
1749
1748
|
this.dirtyContainer =
|
|
@@ -1855,9 +1854,9 @@ export class ContainerRuntime
|
|
|
1855
1854
|
options: JSON.stringify(runtimeOptions),
|
|
1856
1855
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
1857
1856
|
idCompressorMode: this.idCompressorMode,
|
|
1857
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
1858
1858
|
featureGates: JSON.stringify({
|
|
1859
1859
|
...featureGatesForTelemetry,
|
|
1860
|
-
disableOpReentryCheck,
|
|
1861
1860
|
disableChunking,
|
|
1862
1861
|
disableAttachReorder: this.disableAttachReorder,
|
|
1863
1862
|
disablePartialFlush,
|
|
@@ -1865,6 +1864,7 @@ export class ContainerRuntime
|
|
|
1865
1864
|
}),
|
|
1866
1865
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1867
1866
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
1867
|
+
initialSequenceNumber: this.deltaManager.initialSequenceNumber,
|
|
1868
1868
|
});
|
|
1869
1869
|
|
|
1870
1870
|
ReportOpPerfTelemetry(this.clientId, this.deltaManager, this, this.logger);
|
|
@@ -1887,6 +1887,11 @@ export class ContainerRuntime
|
|
|
1887
1887
|
}
|
|
1888
1888
|
|
|
1889
1889
|
public onSchemaChange(schema: IDocumentSchemaCurrent) {
|
|
1890
|
+
this.logger.sendTelemetryEvent({
|
|
1891
|
+
eventName: "SchemaChangeAccept",
|
|
1892
|
+
sessionRuntimeSchema: JSON.stringify(schema),
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1890
1895
|
// Most of the settings will be picked up only by new sessions (i.e. after reload).
|
|
1891
1896
|
// We can make it better in the future (i.e. start to use op compression right away), but for simplicity
|
|
1892
1897
|
// this is not done.
|
|
@@ -1936,9 +1941,9 @@ export class ContainerRuntime
|
|
|
1936
1941
|
this.idCompressorMode === "on" ||
|
|
1937
1942
|
(this.idCompressorMode === "delayed" && this.connected)
|
|
1938
1943
|
) {
|
|
1944
|
+
this._idCompressor = await this.createIdCompressor();
|
|
1939
1945
|
// This is called from loadRuntime(), long before we process any ops, so there should be no ops accumulated yet.
|
|
1940
1946
|
assert(this.pendingIdCompressorOps.length === 0, 0x8ec /* no pending ops */);
|
|
1941
|
-
this._idCompressor = await this.createIdCompressor();
|
|
1942
1947
|
}
|
|
1943
1948
|
|
|
1944
1949
|
await this.garbageCollector.initializeBaseState();
|
|
@@ -2409,12 +2414,14 @@ export class ContainerRuntime
|
|
|
2409
2414
|
) {
|
|
2410
2415
|
this._loadIdCompressor = this.createIdCompressor()
|
|
2411
2416
|
.then((compressor) => {
|
|
2412
|
-
this._idCompressor = compressor;
|
|
2413
2417
|
// Finalize any ranges we received while the compressor was turned off.
|
|
2414
|
-
|
|
2415
|
-
this._idCompressor.finalizeCreationRange(range);
|
|
2416
|
-
}
|
|
2418
|
+
const ops = this.pendingIdCompressorOps;
|
|
2417
2419
|
this.pendingIdCompressorOps = [];
|
|
2420
|
+
for (const range of ops) {
|
|
2421
|
+
compressor.finalizeCreationRange(range);
|
|
2422
|
+
}
|
|
2423
|
+
assert(this.pendingIdCompressorOps.length === 0, "No new ops added");
|
|
2424
|
+
this._idCompressor = compressor;
|
|
2418
2425
|
})
|
|
2419
2426
|
.catch((error) => {
|
|
2420
2427
|
this.logger.sendErrorEvent({ eventName: "IdCompressorDelayedLoad" }, error);
|
|
@@ -2425,6 +2432,11 @@ export class ContainerRuntime
|
|
|
2425
2432
|
}
|
|
2426
2433
|
|
|
2427
2434
|
public setConnectionState(connected: boolean, clientId?: string) {
|
|
2435
|
+
// Validate we have consistent state
|
|
2436
|
+
const currentClientId = this._audience.getSelf()?.clientId;
|
|
2437
|
+
assert(clientId === currentClientId, "input clientId does not match Audience");
|
|
2438
|
+
assert(this.clientId === currentClientId, "this.clientId does not match Audience");
|
|
2439
|
+
|
|
2428
2440
|
if (connected && this.idCompressorMode === "delayed") {
|
|
2429
2441
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
2430
2442
|
this.loadIdCompressor();
|
|
@@ -2538,21 +2550,28 @@ export class ContainerRuntime
|
|
|
2538
2550
|
// We do not need to make a deep copy. Each layer will just replace message.contents itself,
|
|
2539
2551
|
// but will not modify the contents object (likely it will replace it on the message).
|
|
2540
2552
|
const messageCopy = { ...messageArg };
|
|
2553
|
+
const savedOp = (messageCopy.metadata as ISavedOpMetadata)?.savedOp;
|
|
2541
2554
|
for (const message of this.remoteMessageProcessor.process(messageCopy)) {
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2555
|
+
const msg: MessageWithContext = modernRuntimeMessage
|
|
2556
|
+
? {
|
|
2557
|
+
// Cast it since we expect it to be this based on modernRuntimeMessage computation above.
|
|
2558
|
+
// There is nothing really ensuring that anytime original message.type is Operation that
|
|
2559
|
+
// the result messages will be so. In the end modern bool being true only directs to
|
|
2560
|
+
// throw error if ultimately unrecognized without compat details saying otherwise.
|
|
2561
|
+
message: message as InboundSequencedContainerRuntimeMessage,
|
|
2562
|
+
local,
|
|
2563
|
+
modernRuntimeMessage,
|
|
2564
|
+
}
|
|
2565
|
+
: // Unrecognized message will be ignored.
|
|
2566
|
+
{
|
|
2567
|
+
message,
|
|
2568
|
+
local,
|
|
2569
|
+
modernRuntimeMessage,
|
|
2570
|
+
};
|
|
2571
|
+
msg.savedOp = savedOp;
|
|
2572
|
+
|
|
2573
|
+
// ensure that we observe any re-entrancy, and if needed, rebase ops
|
|
2574
|
+
this.ensureNoDataModelChanges(() => this.processCore(msg));
|
|
2556
2575
|
}
|
|
2557
2576
|
}
|
|
2558
2577
|
|
|
@@ -2577,7 +2596,7 @@ export class ContainerRuntime
|
|
|
2577
2596
|
// 2) this.resetReconnectCount() below
|
|
2578
2597
|
assert(
|
|
2579
2598
|
message.type !== ContainerMessageType.ChunkedOp,
|
|
2580
|
-
|
|
2599
|
+
0x93b /* we should never get here with chunked ops */,
|
|
2581
2600
|
);
|
|
2582
2601
|
|
|
2583
2602
|
let localOpMetadata: unknown;
|
|
@@ -2640,23 +2659,21 @@ export class ContainerRuntime
|
|
|
2640
2659
|
// stashed ops flow. The compressor is stashed with these ops already processed.
|
|
2641
2660
|
// That said, in idCompressorMode === "delayed", we might not serialize ID compressor, and
|
|
2642
2661
|
// thus we need to process all the ops.
|
|
2643
|
-
if (
|
|
2644
|
-
!(
|
|
2645
|
-
this.skipSavedCompressorOps &&
|
|
2646
|
-
(messageWithContext.message.metadata as IIdAllocationMetadata)?.savedOp ===
|
|
2647
|
-
true
|
|
2648
|
-
)
|
|
2649
|
-
) {
|
|
2662
|
+
if (!(this.skipSavedCompressorOps && messageWithContext.savedOp === true)) {
|
|
2650
2663
|
const range = messageWithContext.message.contents;
|
|
2651
2664
|
// Some other client turned on the id compressor. If we have not turned it on,
|
|
2652
2665
|
// put it in a pending queue and delay finalization.
|
|
2653
2666
|
if (this._idCompressor === undefined) {
|
|
2654
2667
|
assert(
|
|
2655
2668
|
this.idCompressorMode !== undefined,
|
|
2656
|
-
|
|
2669
|
+
0x93c /* id compressor should be enabled */,
|
|
2657
2670
|
);
|
|
2658
2671
|
this.pendingIdCompressorOps.push(range);
|
|
2659
2672
|
} else {
|
|
2673
|
+
assert(
|
|
2674
|
+
this.pendingIdCompressorOps.length === 0,
|
|
2675
|
+
"there should be no pending ops!",
|
|
2676
|
+
);
|
|
2660
2677
|
this._idCompressor.finalizeCreationRange(range);
|
|
2661
2678
|
}
|
|
2662
2679
|
}
|
|
@@ -2667,7 +2684,7 @@ export class ContainerRuntime
|
|
|
2667
2684
|
case ContainerMessageType.ChunkedOp:
|
|
2668
2685
|
// From observability POV, we should not exppse the rest of the system (including "op" events on object) to these messages.
|
|
2669
2686
|
// Also resetReconnectCount() would be wrong - see comment that was there before this change was made.
|
|
2670
|
-
assert(false,
|
|
2687
|
+
assert(false, 0x93d /* should not even get here */);
|
|
2671
2688
|
case ContainerMessageType.Rejoin:
|
|
2672
2689
|
break;
|
|
2673
2690
|
case ContainerMessageType.DocumentSchemaChange:
|
|
@@ -2807,9 +2824,9 @@ export class ContainerRuntime
|
|
|
2807
2824
|
let checkpoint: IBatchCheckpoint | undefined;
|
|
2808
2825
|
let result: T;
|
|
2809
2826
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
2810
|
-
// Note: we are not touching
|
|
2811
|
-
// 1. It would not help, as
|
|
2812
|
-
// 2. There is no way to undo process of data store creation.
|
|
2827
|
+
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
2828
|
+
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
2829
|
+
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
2813
2830
|
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
2814
2831
|
}
|
|
2815
2832
|
try {
|
|
@@ -2892,12 +2909,11 @@ export class ContainerRuntime
|
|
|
2892
2909
|
"entryPoint must be defined on data store runtime for using getAliasedDataStoreEntryPoint",
|
|
2893
2910
|
);
|
|
2894
2911
|
}
|
|
2895
|
-
this.garbageCollector.nodeUpdated(
|
|
2896
|
-
`/${internalId}
|
|
2897
|
-
"Loaded",
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
);
|
|
2912
|
+
this.garbageCollector.nodeUpdated({
|
|
2913
|
+
node: { type: "DataStore", path: `/${internalId}` },
|
|
2914
|
+
reason: "Loaded",
|
|
2915
|
+
packagePath: context.packagePath,
|
|
2916
|
+
});
|
|
2901
2917
|
return channel.entryPoint;
|
|
2902
2918
|
}
|
|
2903
2919
|
|
|
@@ -3083,7 +3099,7 @@ export class ContainerRuntime
|
|
|
3083
3099
|
if (idRange !== undefined) {
|
|
3084
3100
|
assert(
|
|
3085
3101
|
idRange.ids === undefined || idRange.ids.firstGenCount === 1,
|
|
3086
|
-
|
|
3102
|
+
0x93e /* No other ranges should be taken while container is detached. */,
|
|
3087
3103
|
);
|
|
3088
3104
|
this._idCompressor?.finalizeCreationRange(idRange);
|
|
3089
3105
|
}
|
|
@@ -3392,10 +3408,14 @@ export class ContainerRuntime
|
|
|
3392
3408
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
3393
3409
|
// use it for all events logged during this summary.
|
|
3394
3410
|
const summaryNumber = this.nextSummaryNumber;
|
|
3411
|
+
let summaryRefSeqNum: number | undefined;
|
|
3395
3412
|
const summaryNumberLogger = createChildLogger({
|
|
3396
3413
|
logger: summaryLogger,
|
|
3397
3414
|
properties: {
|
|
3398
|
-
all: {
|
|
3415
|
+
all: {
|
|
3416
|
+
summaryNumber,
|
|
3417
|
+
referenceSequenceNumber: () => summaryRefSeqNum,
|
|
3418
|
+
},
|
|
3399
3419
|
},
|
|
3400
3420
|
});
|
|
3401
3421
|
|
|
@@ -3414,7 +3434,7 @@ export class ContainerRuntime
|
|
|
3414
3434
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
3415
3435
|
// and it may even be incorrect. So, wait for the container to be saved with a timeout. If the container is not
|
|
3416
3436
|
// saved within the timeout, check if it should be failed or can continue.
|
|
3417
|
-
if (this.
|
|
3437
|
+
if (this.isDirty) {
|
|
3418
3438
|
const countBefore = this.pendingMessagesCount;
|
|
3419
3439
|
// The timeout for waiting for pending ops can be overridden via configurations.
|
|
3420
3440
|
const pendingOpsTimeout =
|
|
@@ -3464,8 +3484,6 @@ export class ContainerRuntime
|
|
|
3464
3484
|
"Fluid.ContainerRuntime.SubmitSummary.shouldValidatePreSummaryState",
|
|
3465
3485
|
) === true;
|
|
3466
3486
|
|
|
3467
|
-
let summaryRefSeqNum: number | undefined;
|
|
3468
|
-
|
|
3469
3487
|
try {
|
|
3470
3488
|
await this.deltaManager.inbound.pause();
|
|
3471
3489
|
if (shouldPauseInboundSignal) {
|
|
@@ -3483,11 +3501,19 @@ export class ContainerRuntime
|
|
|
3483
3501
|
latestSummaryRefSeqNum,
|
|
3484
3502
|
);
|
|
3485
3503
|
|
|
3504
|
+
/**
|
|
3505
|
+
* This was added to validate that the summarizer node tree has the same reference sequence number from the
|
|
3506
|
+
* top running summarizer down to the lowest summarizer node.
|
|
3507
|
+
*
|
|
3508
|
+
* The order of mismatch numbers goes (validate sequence number)-(node sequence number).
|
|
3509
|
+
* Generally the validate sequence number comes from the running summarizer and the node sequence number comes from the
|
|
3510
|
+
* summarizer nodes.
|
|
3511
|
+
*/
|
|
3486
3512
|
if (
|
|
3487
3513
|
startSummaryResult.invalidNodes > 0 ||
|
|
3488
3514
|
startSummaryResult.mismatchNumbers.size > 0
|
|
3489
3515
|
) {
|
|
3490
|
-
summaryLogger.
|
|
3516
|
+
summaryLogger.sendTelemetryEvent({
|
|
3491
3517
|
eventName: "LatestSummaryRefSeqNumMismatch",
|
|
3492
3518
|
details: {
|
|
3493
3519
|
...startSummaryResult,
|
|
@@ -3500,7 +3526,9 @@ export class ContainerRuntime
|
|
|
3500
3526
|
stage: "base",
|
|
3501
3527
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3502
3528
|
minimumSequenceNumber,
|
|
3503
|
-
error:
|
|
3529
|
+
error: new LoggingError(
|
|
3530
|
+
`Summarizer node state inconsistent with summarizer state.`,
|
|
3531
|
+
),
|
|
3504
3532
|
};
|
|
3505
3533
|
}
|
|
3506
3534
|
}
|
|
@@ -3550,7 +3578,7 @@ export class ContainerRuntime
|
|
|
3550
3578
|
stage: "base",
|
|
3551
3579
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3552
3580
|
minimumSequenceNumber,
|
|
3553
|
-
error: continueResult.error,
|
|
3581
|
+
error: new LoggingError(continueResult.error),
|
|
3554
3582
|
};
|
|
3555
3583
|
}
|
|
3556
3584
|
|
|
@@ -3571,39 +3599,38 @@ export class ContainerRuntime
|
|
|
3571
3599
|
stage: "base",
|
|
3572
3600
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
3573
3601
|
minimumSequenceNumber,
|
|
3574
|
-
error,
|
|
3602
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3575
3603
|
};
|
|
3576
3604
|
}
|
|
3577
3605
|
|
|
3578
|
-
//
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
const
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
validateResult.retryAfterSeconds,
|
|
3587
|
-
{ ...loggingProps },
|
|
3588
|
-
);
|
|
3589
|
-
return {
|
|
3590
|
-
stage: "base",
|
|
3591
|
-
referenceSequenceNumber: summaryRefSeqNum,
|
|
3592
|
-
minimumSequenceNumber,
|
|
3593
|
-
error,
|
|
3594
|
-
};
|
|
3595
|
-
}
|
|
3596
|
-
|
|
3597
|
-
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
3598
|
-
summaryNumberLogger,
|
|
3599
|
-
summaryRefSeqNum,
|
|
3600
|
-
minimumSequenceNumber,
|
|
3601
|
-
finalAttempt,
|
|
3602
|
-
false /* beforeSummaryGeneration */,
|
|
3606
|
+
// Validate that the summary generated by summarizer nodes is correct before uploading.
|
|
3607
|
+
const validateResult = this.summarizerNode.validateSummary();
|
|
3608
|
+
if (!validateResult.success) {
|
|
3609
|
+
const { success, ...loggingProps } = validateResult;
|
|
3610
|
+
const error = new RetriableSummaryError(
|
|
3611
|
+
validateResult.reason,
|
|
3612
|
+
validateResult.retryAfterSeconds,
|
|
3613
|
+
{ ...loggingProps },
|
|
3603
3614
|
);
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3615
|
+
return {
|
|
3616
|
+
stage: "base",
|
|
3617
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
3618
|
+
minimumSequenceNumber,
|
|
3619
|
+
error,
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3623
|
+
// If there are pending unacked ops, this summary attempt may fail as the uploaded
|
|
3624
|
+
// summary would be eventually inconsistent.
|
|
3625
|
+
const pendingMessagesFailResult = await this.shouldFailSummaryOnPendingOps(
|
|
3626
|
+
summaryNumberLogger,
|
|
3627
|
+
summaryRefSeqNum,
|
|
3628
|
+
minimumSequenceNumber,
|
|
3629
|
+
finalAttempt,
|
|
3630
|
+
false /* beforeSummaryGeneration */,
|
|
3631
|
+
);
|
|
3632
|
+
if (pendingMessagesFailResult !== undefined) {
|
|
3633
|
+
return pendingMessagesFailResult;
|
|
3607
3634
|
}
|
|
3608
3635
|
|
|
3609
3636
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
@@ -3644,7 +3671,11 @@ export class ContainerRuntime
|
|
|
3644
3671
|
|
|
3645
3672
|
continueResult = checkContinue();
|
|
3646
3673
|
if (!continueResult.continue) {
|
|
3647
|
-
return {
|
|
3674
|
+
return {
|
|
3675
|
+
stage: "generate",
|
|
3676
|
+
...generateSummaryData,
|
|
3677
|
+
error: new LoggingError(continueResult.error),
|
|
3678
|
+
};
|
|
3648
3679
|
}
|
|
3649
3680
|
|
|
3650
3681
|
const summaryContext =
|
|
@@ -3667,7 +3698,11 @@ export class ContainerRuntime
|
|
|
3667
3698
|
summaryContext,
|
|
3668
3699
|
);
|
|
3669
3700
|
} catch (error) {
|
|
3670
|
-
return {
|
|
3701
|
+
return {
|
|
3702
|
+
stage: "generate",
|
|
3703
|
+
...generateSummaryData,
|
|
3704
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3705
|
+
};
|
|
3671
3706
|
}
|
|
3672
3707
|
|
|
3673
3708
|
const parent = summaryContext.ackHandle;
|
|
@@ -3686,14 +3721,22 @@ export class ContainerRuntime
|
|
|
3686
3721
|
|
|
3687
3722
|
continueResult = checkContinue();
|
|
3688
3723
|
if (!continueResult.continue) {
|
|
3689
|
-
return {
|
|
3724
|
+
return {
|
|
3725
|
+
stage: "upload",
|
|
3726
|
+
...uploadData,
|
|
3727
|
+
error: new LoggingError(continueResult.error),
|
|
3728
|
+
};
|
|
3690
3729
|
}
|
|
3691
3730
|
|
|
3692
3731
|
let clientSequenceNumber: number;
|
|
3693
3732
|
try {
|
|
3694
3733
|
clientSequenceNumber = this.submitSummaryMessage(summaryMessage, summaryRefSeqNum);
|
|
3695
3734
|
} catch (error) {
|
|
3696
|
-
return {
|
|
3735
|
+
return {
|
|
3736
|
+
stage: "upload",
|
|
3737
|
+
...uploadData,
|
|
3738
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3739
|
+
};
|
|
3697
3740
|
}
|
|
3698
3741
|
|
|
3699
3742
|
const submitData = {
|
|
@@ -3704,13 +3747,13 @@ export class ContainerRuntime
|
|
|
3704
3747
|
} as const;
|
|
3705
3748
|
|
|
3706
3749
|
try {
|
|
3707
|
-
|
|
3708
|
-
this.summarizerNode.completeSummary(
|
|
3709
|
-
handle,
|
|
3710
|
-
!this.validateSummaryBeforeUpload /* validate */,
|
|
3711
|
-
);
|
|
3750
|
+
this.summarizerNode.completeSummary(handle);
|
|
3712
3751
|
} catch (error) {
|
|
3713
|
-
return {
|
|
3752
|
+
return {
|
|
3753
|
+
stage: "upload",
|
|
3754
|
+
...uploadData,
|
|
3755
|
+
error: wrapError(error, (msg) => new LoggingError(msg)),
|
|
3756
|
+
};
|
|
3714
3757
|
}
|
|
3715
3758
|
return submitData;
|
|
3716
3759
|
} finally {
|
|
@@ -3866,7 +3909,6 @@ export class ContainerRuntime
|
|
|
3866
3909
|
metadata?: { localId: string; blobId?: string },
|
|
3867
3910
|
): void {
|
|
3868
3911
|
this.verifyNotClosed();
|
|
3869
|
-
this.verifyCanSubmitOps();
|
|
3870
3912
|
|
|
3871
3913
|
// There should be no ops in detached container state!
|
|
3872
3914
|
assert(
|
|
@@ -3877,7 +3919,7 @@ export class ContainerRuntime
|
|
|
3877
3919
|
assert(
|
|
3878
3920
|
metadata === undefined ||
|
|
3879
3921
|
containerRuntimeMessage.type === ContainerMessageType.BlobAttach,
|
|
3880
|
-
|
|
3922
|
+
0x93f /* metadata */,
|
|
3881
3923
|
);
|
|
3882
3924
|
|
|
3883
3925
|
const serializedContent = JSON.stringify(containerRuntimeMessage);
|
|
@@ -3914,6 +3956,14 @@ export class ContainerRuntime
|
|
|
3914
3956
|
// on this callback to do actual sending.
|
|
3915
3957
|
const contents = this.documentsSchemaController.maybeSendSchemaMessage();
|
|
3916
3958
|
if (contents) {
|
|
3959
|
+
this.logger.sendTelemetryEvent({
|
|
3960
|
+
eventName: "SchemaChangeProposal",
|
|
3961
|
+
refSeq: contents.refSeq,
|
|
3962
|
+
version: contents.version,
|
|
3963
|
+
newRuntimeSchema: JSON.stringify(contents.runtime),
|
|
3964
|
+
sessionRuntimeSchema: JSON.stringify(this.sessionSchema),
|
|
3965
|
+
oldRuntimeSchema: JSON.stringify(this.metadata?.documentSchema?.runtime),
|
|
3966
|
+
});
|
|
3917
3967
|
const msg: ContainerRuntimeDocumentSchemaMessage = {
|
|
3918
3968
|
type: ContainerMessageType.DocumentSchemaChange,
|
|
3919
3969
|
contents,
|
|
@@ -3924,33 +3974,7 @@ export class ContainerRuntime
|
|
|
3924
3974
|
});
|
|
3925
3975
|
}
|
|
3926
3976
|
|
|
3927
|
-
|
|
3928
|
-
// Is it safe:
|
|
3929
|
-
// Yes, this should be safe reordering. Newly created data stores are not visible through API surface.
|
|
3930
|
-
// They become visible only when aliased, or handle to some sub-element of newly created datastore
|
|
3931
|
-
// is stored in some DDS, i.e. only after some other op.
|
|
3932
|
-
// Why:
|
|
3933
|
-
// Attach ops are large, and expensive to process. Plus there are scenarios where a lot of new data
|
|
3934
|
-
// stores are created, causing issues like relay service throttling (too many ops) and catastrophic
|
|
3935
|
-
// failure (batch is too large). Pushing them earlier and outside of main batch should alleviate
|
|
3936
|
-
// these issues.
|
|
3937
|
-
// Cons:
|
|
3938
|
-
// 1. With large batches, relay service may throttle clients. Clients may disconnect while throttled.
|
|
3939
|
-
// This change creates new possibility of a lot of newly created data stores never being referenced
|
|
3940
|
-
// because client died before it had a change to submit the rest of the ops. This will create more
|
|
3941
|
-
// garbage that needs to be collected leveraging GC (Garbage Collection) feature.
|
|
3942
|
-
// 2. Sending ops out of order means they are excluded from rollback functionality. This is not an issue
|
|
3943
|
-
// today as rollback can't undo creation of data store. To some extent not sending them is a bigger
|
|
3944
|
-
// issue than sending.
|
|
3945
|
-
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
3946
|
-
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
3947
|
-
if (
|
|
3948
|
-
this.currentlyBatching() &&
|
|
3949
|
-
type === ContainerMessageType.Attach &&
|
|
3950
|
-
this.disableAttachReorder !== true
|
|
3951
|
-
) {
|
|
3952
|
-
this.outbox.submitAttach(message);
|
|
3953
|
-
} else if (type === ContainerMessageType.BlobAttach) {
|
|
3977
|
+
if (type === ContainerMessageType.BlobAttach) {
|
|
3954
3978
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
3955
3979
|
this.outbox.submitBlobAttach(message);
|
|
3956
3980
|
} else {
|
|
@@ -4039,37 +4063,6 @@ export class ContainerRuntime
|
|
|
4039
4063
|
}
|
|
4040
4064
|
}
|
|
4041
4065
|
|
|
4042
|
-
private verifyCanSubmitOps() {
|
|
4043
|
-
if (this.ensureNoDataModelChangesCalls > 0) {
|
|
4044
|
-
const errorMessage =
|
|
4045
|
-
"Op was submitted from within a `ensureNoDataModelChanges` callback";
|
|
4046
|
-
if (this.opReentryCallsToReport > 0) {
|
|
4047
|
-
this.mc.logger.sendTelemetryEvent(
|
|
4048
|
-
{ eventName: "OpReentry" },
|
|
4049
|
-
// We need to capture the call stack in order to inspect the source of this usage pattern
|
|
4050
|
-
getLongStack(() => new UsageError(errorMessage)),
|
|
4051
|
-
);
|
|
4052
|
-
this.opReentryCallsToReport--;
|
|
4053
|
-
}
|
|
4054
|
-
|
|
4055
|
-
// Creating ops while processing ops can lead
|
|
4056
|
-
// to undefined behavior and events observed in the wrong order.
|
|
4057
|
-
// For example, we have two callbacks registered for a DDS, A and B.
|
|
4058
|
-
// Then if on change #1 callback A creates change #2, the invocation flow will be:
|
|
4059
|
-
//
|
|
4060
|
-
// A because of #1
|
|
4061
|
-
// A because of #2
|
|
4062
|
-
// B because of #2
|
|
4063
|
-
// B because of #1
|
|
4064
|
-
//
|
|
4065
|
-
// The runtime must enforce op coherence by not allowing ops to be submitted
|
|
4066
|
-
// while ops are being processed.
|
|
4067
|
-
if (this.enableOpReentryCheck) {
|
|
4068
|
-
throw new UsageError(errorMessage);
|
|
4069
|
-
}
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
|
|
4073
4066
|
private reSubmitBatch(batch: IPendingBatchMessage[]) {
|
|
4074
4067
|
this.orderSequentially(() => {
|
|
4075
4068
|
for (const message of batch) {
|
|
@@ -4234,7 +4227,7 @@ export class ContainerRuntime
|
|
|
4234
4227
|
|
|
4235
4228
|
return {
|
|
4236
4229
|
stage: "base",
|
|
4237
|
-
error: "summary state stale - Unsupported option 'refreshLatestAck'",
|
|
4230
|
+
error: new LoggingError("summary state stale - Unsupported option 'refreshLatestAck'"),
|
|
4238
4231
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4239
4232
|
minimumSequenceNumber: this.deltaManager.minimumSequenceNumber,
|
|
4240
4233
|
};
|
|
@@ -4316,10 +4309,9 @@ export class ContainerRuntime
|
|
|
4316
4309
|
const getSyncState = (
|
|
4317
4310
|
pendingAttachmentBlobs?: IPendingBlobs,
|
|
4318
4311
|
): IPendingRuntimeState | undefined => {
|
|
4319
|
-
const pending = this.pendingStateManager.getLocalState();
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
}
|
|
4312
|
+
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
4313
|
+
const sessionExpiryTimerStarted =
|
|
4314
|
+
props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
4323
4315
|
|
|
4324
4316
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
4325
4317
|
|
|
@@ -4327,7 +4319,7 @@ export class ContainerRuntime
|
|
|
4327
4319
|
pending,
|
|
4328
4320
|
pendingIdCompressorState,
|
|
4329
4321
|
pendingAttachmentBlobs,
|
|
4330
|
-
sessionExpiryTimerStarted
|
|
4322
|
+
sessionExpiryTimerStarted,
|
|
4331
4323
|
};
|
|
4332
4324
|
};
|
|
4333
4325
|
const perfEvent = {
|
|
@@ -4418,6 +4410,6 @@ export class ContainerRuntime
|
|
|
4418
4410
|
}
|
|
4419
4411
|
|
|
4420
4412
|
private get groupedBatchingEnabled(): boolean {
|
|
4421
|
-
return this.
|
|
4413
|
+
return this.sessionSchema.opGroupingEnabled === true;
|
|
4422
4414
|
}
|
|
4423
4415
|
}
|