@fluidframework/container-runtime 2.91.0 → 2.92.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 +4 -0
- package/api-report/container-runtime.legacy.beta.api.md +2 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/containerCompatibility.d.ts +1 -1
- package/dist/containerCompatibility.d.ts.map +1 -1
- package/dist/containerCompatibility.js.map +1 -1
- package/dist/containerRuntime.d.ts +36 -9
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +97 -54
- package/dist/containerRuntime.js.map +1 -1
- package/dist/gc/garbageCollection.d.ts +1 -0
- package/dist/gc/garbageCollection.d.ts.map +1 -1
- package/dist/gc/garbageCollection.js +3 -8
- package/dist/gc/garbageCollection.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +4 -0
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +2 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -1
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +2 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +6 -0
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +11 -2
- package/dist/opLifecycle/opGroupingManager.js.map +1 -1
- package/dist/opLifecycle/opSerialization.d.ts +3 -1
- package/dist/opLifecycle/opSerialization.d.ts.map +1 -1
- package/dist/opLifecycle/opSerialization.js +11 -9
- package/dist/opLifecycle/opSerialization.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +0 -6
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +2 -9
- 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 +7 -3
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +19 -7
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/runtimeLayerCompatState.d.ts +1 -1
- package/dist/summary/documentSchema.d.ts +9 -3
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +19 -3
- package/dist/summary/documentSchema.js.map +1 -1
- package/dist/summary/orderedClientElection.js +2 -2
- package/dist/summary/orderedClientElection.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +1 -0
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +9 -0
- package/dist/summary/summaryManager.js.map +1 -1
- package/internal.d.ts +1 -1
- package/legacy.d.ts +1 -1
- package/lib/containerCompatibility.d.ts +1 -1
- package/lib/containerCompatibility.d.ts.map +1 -1
- package/lib/containerCompatibility.js.map +1 -1
- package/lib/containerRuntime.d.ts +36 -9
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +97 -55
- package/lib/containerRuntime.js.map +1 -1
- package/lib/gc/garbageCollection.d.ts +1 -0
- package/lib/gc/garbageCollection.d.ts.map +1 -1
- package/lib/gc/garbageCollection.js +3 -8
- package/lib/gc/garbageCollection.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +4 -0
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +2 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -1
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -1
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +6 -0
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +10 -1
- package/lib/opLifecycle/opGroupingManager.js.map +1 -1
- package/lib/opLifecycle/opSerialization.d.ts +3 -1
- package/lib/opLifecycle/opSerialization.d.ts.map +1 -1
- package/lib/opLifecycle/opSerialization.js +11 -9
- package/lib/opLifecycle/opSerialization.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +0 -6
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +2 -9
- 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 +7 -3
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +19 -7
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/runtimeLayerCompatState.d.ts +1 -1
- package/lib/summary/documentSchema.d.ts +9 -3
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +19 -3
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/summary/orderedClientElection.js +2 -2
- package/lib/summary/orderedClientElection.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +1 -0
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +9 -0
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +27 -23
- package/src/containerCompatibility.ts +2 -0
- package/src/containerRuntime.ts +144 -66
- package/src/gc/garbageCollection.ts +4 -9
- package/src/gc/gcDefinitions.ts +4 -0
- package/src/index.ts +1 -0
- package/src/opLifecycle/batchManager.ts +2 -1
- package/src/opLifecycle/index.ts +1 -0
- package/src/opLifecycle/opGroupingManager.ts +11 -1
- package/src/opLifecycle/opSerialization.ts +14 -12
- package/src/opLifecycle/outbox.ts +2 -17
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +27 -11
- package/src/summary/documentSchema.ts +25 -2
- package/src/summary/orderedClientElection.ts +2 -2
- package/src/summary/summaryManager.ts +11 -0
package/lib/containerRuntime.js
CHANGED
|
@@ -28,10 +28,10 @@ import { channelToDataStore } from "./dataStore.js";
|
|
|
28
28
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
|
|
29
29
|
import { BaseDeltaManagerProxy, DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy, } from "./deltaManagerProxies.js";
|
|
30
30
|
import { DeltaScheduler } from "./deltaScheduler.js";
|
|
31
|
-
import { GCNodeType, GarbageCollector,
|
|
31
|
+
import { GCNodeType, GarbageCollector, } from "./gc/index.js";
|
|
32
32
|
import { InboundBatchAggregator } from "./inboundBatchAggregator.js";
|
|
33
33
|
import { ContainerMessageType, } from "./messageTypes.js";
|
|
34
|
-
import { DuplicateBatchDetector, ensureContentsDeserialized, OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, } from "./opLifecycle/index.js";
|
|
34
|
+
import { DuplicateBatchDetector, ensureContentsDeserialized, largeBatchThreshold, OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, } from "./opLifecycle/index.js";
|
|
35
35
|
import { pkgVersion } from "./packageVersion.js";
|
|
36
36
|
import { PendingStateManager, } from "./pendingStateManager.js";
|
|
37
37
|
import { BatchRunCounter, RunCounter } from "./runCounter.js";
|
|
@@ -104,6 +104,15 @@ const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconn
|
|
|
104
104
|
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
105
105
|
const defaultMaxBatchSizeInBytes = 700 * 1024;
|
|
106
106
|
const defaultChunkSizeInBytes = 204800;
|
|
107
|
+
/**
|
|
108
|
+
* Default maximum ops per staging-mode batch before automatic flush scheduling resumes.
|
|
109
|
+
*
|
|
110
|
+
* Chosen based on production telemetry: copy-paste operations routinely produce batches
|
|
111
|
+
* of 1000+ ops (435K instances over 30 days), and receivers on modern Fluid versions
|
|
112
|
+
* handle them without issues. Uses {@link largeBatchThreshold} to stay aligned with
|
|
113
|
+
* the existing "large batch" telemetry threshold ({@link OpGroupingManager}).
|
|
114
|
+
*/
|
|
115
|
+
const defaultStagingModeAutoFlushThreshold = largeBatchThreshold;
|
|
107
116
|
/**
|
|
108
117
|
* The default time to wait for pending ops to be processed during summarization
|
|
109
118
|
*/
|
|
@@ -200,6 +209,21 @@ export let getSingleUseLegacyLogCallback = (logger, type) => {
|
|
|
200
209
|
export async function loadContainerRuntime(params) {
|
|
201
210
|
return ContainerRuntime.loadRuntime(params);
|
|
202
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Alpha variant of {@link loadContainerRuntime} that returns the runtime in an
|
|
214
|
+
* extendable object, allowing additional properties to be added in the future.
|
|
215
|
+
*
|
|
216
|
+
* @param params - An object which specifies all required and optional params necessary to instantiate a runtime.
|
|
217
|
+
* @returns An object containing the runtime.
|
|
218
|
+
*
|
|
219
|
+
* @legacy @alpha
|
|
220
|
+
*/
|
|
221
|
+
export async function loadContainerRuntimeAlpha(params) {
|
|
222
|
+
return ContainerRuntime.loadRuntime2({
|
|
223
|
+
...params,
|
|
224
|
+
registry: new FluidDataStoreRegistry(params.registryEntries),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
203
227
|
const defaultMaxConsecutiveReconnects = 7;
|
|
204
228
|
/**
|
|
205
229
|
* These are the ONLY message types that are allowed to be submitted while in staging mode
|
|
@@ -239,13 +263,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
239
263
|
return ContainerRuntime.loadRuntime2({
|
|
240
264
|
...params,
|
|
241
265
|
registry: new FluidDataStoreRegistry(params.registryEntries),
|
|
242
|
-
});
|
|
266
|
+
}).then((r) => r.runtime);
|
|
243
267
|
}
|
|
244
268
|
/**
|
|
245
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
269
|
+
* Load the stores from a snapshot and returns an object containing the runtime.
|
|
246
270
|
* @remarks
|
|
247
271
|
* Same as {@link ContainerRuntime.loadRuntime},
|
|
248
272
|
* but with `registry` instead of `registryEntries` and more `runtimeOptions`.
|
|
273
|
+
* Returns `{ runtime }` to allow future extensions (e.g. staging mode controls).
|
|
249
274
|
*/
|
|
250
275
|
static async loadRuntime2(params) {
|
|
251
276
|
const { context, registry, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, minVersionForCollab = defaultMinVersionForCollab, } = params;
|
|
@@ -282,6 +307,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
282
307
|
loadSequenceNumberVerification: "close",
|
|
283
308
|
maxBatchSizeInBytes: defaultMaxBatchSizeInBytes,
|
|
284
309
|
chunkSizeInBytes: defaultChunkSizeInBytes,
|
|
310
|
+
stagingModeAutoFlushThreshold: defaultStagingModeAutoFlushThreshold,
|
|
311
|
+
disableSchemaUpgrade: false,
|
|
285
312
|
};
|
|
286
313
|
const defaultConfigs = {
|
|
287
314
|
...defaultsAffectingDocSchema,
|
|
@@ -295,7 +322,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
295
322
|
// is enabled via runtimeOptions, we will throw an error later.
|
|
296
323
|
compressionOptions = enableGroupedBatching === false
|
|
297
324
|
? disabledCompressionConfig
|
|
298
|
-
: defaultConfigs.compressionOptions, createBlobPayloadPending = defaultConfigs.createBlobPayloadPending, } = runtimeOptions;
|
|
325
|
+
: defaultConfigs.compressionOptions, createBlobPayloadPending = defaultConfigs.createBlobPayloadPending, stagingModeAutoFlushThreshold = defaultConfigs.stagingModeAutoFlushThreshold, disableSchemaUpgrade = defaultConfigs.disableSchemaUpgrade, } = runtimeOptions;
|
|
299
326
|
// If explicitSchemaControl is off, ensure that options which require explicitSchemaControl are not enabled.
|
|
300
327
|
if (!explicitSchemaControl) {
|
|
301
328
|
const disallowedKeys = Object.keys(runtimeOptions).filter((key) => runtimeOptionKeysThatRequireExplicitSchemaControl.includes(key) && runtimeOptions[key] !== undefined);
|
|
@@ -402,6 +429,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
402
429
|
else {
|
|
403
430
|
idCompressorMode = desiredIdCompressorMode;
|
|
404
431
|
}
|
|
432
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
405
433
|
const createIdCompressorFn = () => {
|
|
406
434
|
/**
|
|
407
435
|
* Because the IdCompressor emits so much telemetry, this function is used to sample
|
|
@@ -438,7 +466,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
438
466
|
disallowedVersions: [],
|
|
439
467
|
}, (schema) => {
|
|
440
468
|
runtime.onSchemaChange(schema);
|
|
441
|
-
}, { minVersionForCollab }, logger);
|
|
469
|
+
}, { minVersionForCollab }, logger, disableSchemaUpgrade);
|
|
442
470
|
// If the minVersionForCollab for this client is greater than the existing one, we should use that one going forward.
|
|
443
471
|
const existingMinVersionForCollab = documentSchemaController.sessionSchema.info.minVersionForCollab;
|
|
444
472
|
const updatedMinVersionForCollab = existingMinVersionForCollab === undefined ||
|
|
@@ -462,6 +490,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
462
490
|
enableGroupedBatching,
|
|
463
491
|
explicitSchemaControl,
|
|
464
492
|
createBlobPayloadPending,
|
|
493
|
+
stagingModeAutoFlushThreshold,
|
|
494
|
+
disableSchemaUpgrade,
|
|
465
495
|
};
|
|
466
496
|
validateMinimumVersionForCollab(updatedMinVersionForCollab);
|
|
467
497
|
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, updatedMinVersionForCollab, requestHandler, undefined, // summaryConfiguration
|
|
@@ -472,7 +502,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
472
502
|
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
473
503
|
// or zero. This must be done before Container replays saved ops.
|
|
474
504
|
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
475
|
-
return runtime;
|
|
505
|
+
return { runtime };
|
|
476
506
|
}
|
|
477
507
|
get clientId() {
|
|
478
508
|
return this._getClientId();
|
|
@@ -511,6 +541,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
511
541
|
/**
|
|
512
542
|
* {@inheritDoc @fluidframework/runtime-definitions#IContainerRuntimeBase.idCompressor}
|
|
513
543
|
*/
|
|
544
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
514
545
|
get idCompressor() {
|
|
515
546
|
// Expose ID Compressor only if it's On from the start.
|
|
516
547
|
// If container uses delayed mode, then we can only expose generateDocumentUniqueId() and nothing else.
|
|
@@ -589,7 +620,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
589
620
|
/***/
|
|
590
621
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope,
|
|
591
622
|
// Create a custom ITelemetryBaseLogger to output telemetry events.
|
|
592
|
-
baseLogger, existing, blobManagerLoadInfo, _storage,
|
|
623
|
+
baseLogger, existing, blobManagerLoadInfo, _storage,
|
|
624
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
625
|
+
createIdCompressorFn, documentsSchemaController, featureGatesForTelemetry, provideEntryPoint, minVersionForCollab, requestHandler,
|
|
593
626
|
// // eslint-disable-next-line unicorn/no-object-as-default-parameter
|
|
594
627
|
summaryConfiguration = {
|
|
595
628
|
// the defaults
|
|
@@ -646,17 +679,28 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
646
679
|
// Make sure Outbox is empty before entering staging mode,
|
|
647
680
|
// since we mark whole batches as "staged" or not to indicate whether to submit them.
|
|
648
681
|
this.flush();
|
|
649
|
-
const exitStagingMode = (discardOrCommit) => {
|
|
682
|
+
const exitStagingMode = (discardOrCommit, exitMethod) => {
|
|
650
683
|
try {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
684
|
+
PerformanceEvent.timedExec(this.mc.logger, {
|
|
685
|
+
eventName: `ExitStagingMode_${exitMethod}`,
|
|
686
|
+
}, (event) => {
|
|
687
|
+
// Final flush of any last staged changes
|
|
688
|
+
// NOTE: We can't use this.flush() here, because orderSequentially uses StagingMode and in the rollback case we'll hit assert 0x24c
|
|
689
|
+
this.outbox.flush();
|
|
690
|
+
this.stageControls = undefined;
|
|
691
|
+
// During Staging Mode, we avoid submitting any ID Allocation ops (apart from resubmitting pre-staging ops).
|
|
692
|
+
// Now that we've exited, we need to submit an ID Allocation op for any IDs that were generated while in Staging Mode.
|
|
693
|
+
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
694
|
+
const batchInfos = discardOrCommit();
|
|
695
|
+
event.reportProgress({
|
|
696
|
+
details: {
|
|
697
|
+
autoFlushThreshold: this.stagingModeAutoFlushThreshold,
|
|
698
|
+
batches: batchInfos.length,
|
|
699
|
+
batchesAtOrOverThreshold: batchInfos.filter((b) => b.length >= this.stagingModeAutoFlushThreshold).length,
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
this.channelCollection.notifyStagingMode(false);
|
|
703
|
+
});
|
|
660
704
|
}
|
|
661
705
|
catch (error) {
|
|
662
706
|
const normalizedError = normalizeError(error);
|
|
@@ -667,21 +711,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
667
711
|
const stageControls = {
|
|
668
712
|
discardChanges: () => exitStagingMode(() => {
|
|
669
713
|
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
670
|
-
this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
714
|
+
const batchInfos = this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
671
715
|
this.rollbackStagedChange(runtimeOp, localOpMetadata);
|
|
672
716
|
});
|
|
673
717
|
this.updateDocumentDirtyState();
|
|
674
|
-
|
|
718
|
+
return batchInfos;
|
|
719
|
+
}, "discard"),
|
|
675
720
|
commitChanges: (options) => {
|
|
676
721
|
const { squash } = { ...defaultStagingCommitOptions, ...options };
|
|
677
722
|
exitStagingMode(() => {
|
|
678
723
|
// Replay all staged batches in typical FIFO order.
|
|
679
724
|
// We'll be out of staging mode so they'll be sent to the service finally.
|
|
680
|
-
this.pendingStateManager.replayPendingStates({
|
|
725
|
+
return this.pendingStateManager.replayPendingStates({
|
|
681
726
|
committingStagedBatches: true,
|
|
682
727
|
squash,
|
|
683
728
|
});
|
|
684
|
-
});
|
|
729
|
+
}, "commit");
|
|
685
730
|
},
|
|
686
731
|
};
|
|
687
732
|
this.stageControls = stageControls;
|
|
@@ -864,14 +909,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
864
909
|
? this.getConnectionState() === ConnectionState.Connected ||
|
|
865
910
|
this.getConnectionState() === ConnectionState.CatchingUp
|
|
866
911
|
: undefined;
|
|
867
|
-
this.mc.logger.sendTelemetryEvent({
|
|
868
|
-
eventName: "GCFeatureMatrix",
|
|
869
|
-
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
870
|
-
inputs: JSON.stringify({
|
|
871
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
872
|
-
gcOptions_gcGeneration: runtimeOptions.gcOptions[gcGenerationOptionName],
|
|
873
|
-
}),
|
|
874
|
-
});
|
|
875
912
|
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? uuid();
|
|
876
913
|
const opGroupingManager = new OpGroupingManager({
|
|
877
914
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -919,6 +956,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
919
956
|
this.closeFn(error);
|
|
920
957
|
throw error;
|
|
921
958
|
}
|
|
959
|
+
this.stagingModeAutoFlushThreshold =
|
|
960
|
+
this.mc.config.getNumber("Fluid.ContainerRuntime.StagingModeAutoFlushThreshold") ??
|
|
961
|
+
runtimeOptions.stagingModeAutoFlushThreshold ??
|
|
962
|
+
defaultStagingModeAutoFlushThreshold;
|
|
922
963
|
this.batchIdTrackingEnabled =
|
|
923
964
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineFull") ??
|
|
924
965
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableBatchIdTracking") ??
|
|
@@ -1028,9 +1069,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1028
1069
|
this.deltaScheduler = new DeltaScheduler(this.innerDeltaManager, this, createChildLogger({ logger: this.baseLogger, namespace: "DeltaScheduler" }));
|
|
1029
1070
|
this.inboundBatchAggregator = new InboundBatchAggregator(this.innerDeltaManager, () => this.clientId, createChildLogger({ logger: this.baseLogger, namespace: "InboundBatchAggregator" }));
|
|
1030
1071
|
const legacySendBatchFn = makeLegacySendBatchFn(submitFn, this.innerDeltaManager);
|
|
1031
|
-
this.skipSafetyFlushDuringProcessStack =
|
|
1032
|
-
// Keep the old flag name even though we renamed the class member (it shipped in 2.31.0)
|
|
1033
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableFlushBeforeProcess") === true;
|
|
1034
1072
|
this.outbox = new Outbox({
|
|
1035
1073
|
shouldSend: () => this.shouldSendOps(),
|
|
1036
1074
|
pendingStateManager: this.pendingStateManager,
|
|
@@ -1041,8 +1079,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1041
1079
|
config: {
|
|
1042
1080
|
compressionOptions,
|
|
1043
1081
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1044
|
-
// If we disable flush before process, we must be ready to flush partial batches
|
|
1045
|
-
flushPartialBatches: this.skipSafetyFlushDuringProcessStack,
|
|
1046
1082
|
},
|
|
1047
1083
|
logger: this.mc.logger,
|
|
1048
1084
|
groupingManager: opGroupingManager,
|
|
@@ -1086,14 +1122,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1086
1122
|
// We haven't emitted dirty/saved yet, but this is the baseline so we know to emit when it changes
|
|
1087
1123
|
this.lastEmittedDirty = this.computeCurrentDirtyState();
|
|
1088
1124
|
context.updateDirtyContainerState(this.lastEmittedDirty);
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
this.deltaManager.on("op", () => this.flush());
|
|
1096
|
-
}
|
|
1125
|
+
// Reference Sequence Number may have just changed, and it must be consistent across a batch,
|
|
1126
|
+
// so we should flush now to clear the way for the next ops.
|
|
1127
|
+
// NOTE: This will be redundant whenever CR.process was called for the op (since we flush there too) -
|
|
1128
|
+
// But we need this coverage for old loaders that don't call ContainerRuntime.process for non-runtime messages.
|
|
1129
|
+
// (We have to call flush _before_ processing a runtime op, but after is ok for non-runtime op)
|
|
1130
|
+
this.deltaManager.on("op", () => this.flush());
|
|
1097
1131
|
// logging hardware telemetry
|
|
1098
1132
|
this.baseLogger.send({
|
|
1099
1133
|
category: "generic",
|
|
@@ -1107,7 +1141,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1107
1141
|
summaryNumber: loadSummaryNumber,
|
|
1108
1142
|
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
1109
1143
|
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
1144
|
+
// This is useful even for interactive clients since they track unreferenced nodes and log errors.
|
|
1110
1145
|
gcVersion: metadata?.gcFeature,
|
|
1146
|
+
gcConfigs: this.garbageCollector.serializedConfigs,
|
|
1111
1147
|
options: JSON.stringify(runtimeOptions),
|
|
1112
1148
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
1113
1149
|
idCompressorMode: this.sessionSchema.idCompressorMode,
|
|
@@ -1115,7 +1151,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1115
1151
|
featureGates: JSON.stringify({
|
|
1116
1152
|
...featureGatesForTelemetry,
|
|
1117
1153
|
closeSummarizerDelayOverride,
|
|
1118
|
-
disableFlushBeforeProcess: this.skipSafetyFlushDuringProcessStack,
|
|
1119
1154
|
}),
|
|
1120
1155
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1121
1156
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -1572,12 +1607,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1572
1607
|
assert(this.emitDirtyDocumentEvent, 0x127 /* "dirty document event not set on replay" */);
|
|
1573
1608
|
this.emitDirtyDocumentEvent = false;
|
|
1574
1609
|
try {
|
|
1575
|
-
// Any ID Allocation ops that failed to submit
|
|
1576
|
-
// the
|
|
1577
|
-
//
|
|
1578
|
-
|
|
1579
|
-
this.submitIdAllocationOpIfNeeded({ resubmitOutstandingRanges: true, staged: false });
|
|
1580
|
-
this.scheduleFlush();
|
|
1610
|
+
// Any ID Allocation ops that failed to submit need to have their ranges included
|
|
1611
|
+
// in the next allocation op. Reset the compressor's unfinalized range cursor so that the next
|
|
1612
|
+
// call to takeNextCreationRange (during replay) will include those unfinalized ranges.
|
|
1613
|
+
this._idCompressor?.resetUnfinalizedCreationRange();
|
|
1581
1614
|
// replay the ops
|
|
1582
1615
|
this.pendingStateManager.replayPendingStates();
|
|
1583
1616
|
}
|
|
@@ -1798,10 +1831,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1798
1831
|
// spread operator above ensure we make a shallow copy of message, as the processing flow will modify it.
|
|
1799
1832
|
// There might be multiple container instances receiving the same message.
|
|
1800
1833
|
this.verifyNotClosed();
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
this.flush();
|
|
1804
|
-
}
|
|
1834
|
+
// Reference Sequence Number may be about to change, and it must be consistent across a batch, so flush now
|
|
1835
|
+
this.flush();
|
|
1805
1836
|
this.ensureNoDataModelChanges(() => {
|
|
1806
1837
|
this.processInboundMessageOrBatch(messageCopy, local);
|
|
1807
1838
|
});
|
|
@@ -3032,6 +3063,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
3032
3063
|
this.updateDocumentDirtyState();
|
|
3033
3064
|
}
|
|
3034
3065
|
scheduleFlush() {
|
|
3066
|
+
// During staging mode, suppress automatic flush scheduling until the main batch
|
|
3067
|
+
// reaches or exceeds the threshold.
|
|
3068
|
+
// Incoming ops still break the batch via direct this.flush() calls elsewhere
|
|
3069
|
+
// (deltaManager "op" handler, process(), connection changes, getPendingLocalState,
|
|
3070
|
+
// exitStagingMode). Those all bypass scheduleFlush(), so they're unaffected by this check.
|
|
3071
|
+
// Additionally, outbox.maybeFlushPartialBatch() (called on every submit) detects
|
|
3072
|
+
// sequence number changes and throws if unexpected changes are detected.
|
|
3073
|
+
if (this.inStagingMode &&
|
|
3074
|
+
this.outbox.mainBatchMessageCount < this.stagingModeAutoFlushThreshold) {
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3035
3077
|
if (this.flushScheduled) {
|
|
3036
3078
|
return;
|
|
3037
3079
|
}
|
|
@@ -3326,7 +3368,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
3326
3368
|
return PerformanceEvent.timedExec(this.mc.logger, {
|
|
3327
3369
|
eventName: "getPendingLocalState",
|
|
3328
3370
|
}, (event) => {
|
|
3329
|
-
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
3371
|
+
const { pending } = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
3330
3372
|
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
3331
3373
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
3332
3374
|
const pendingAttachmentBlobs = this.blobManager.getPendingBlobs();
|