@fluidframework/container-runtime 2.90.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 +8 -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 +37 -10
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +105 -77
- 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 +9 -0
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +29 -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 +37 -10
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +106 -79
- 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 +9 -0
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +29 -0
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +28 -24
- package/src/containerCompatibility.ts +2 -0
- package/src/containerRuntime.ts +153 -93
- 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 +32 -0
package/dist/containerRuntime.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Licensed under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.isContainerMessageDirtyable = exports.ContainerRuntime = exports.loadContainerRuntime = exports.getSingleUseLegacyLogCallback = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isUnpackedRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = void 0;
|
|
7
|
+
exports.isContainerMessageDirtyable = exports.ContainerRuntime = exports.loadContainerRuntimeAlpha = exports.loadContainerRuntime = exports.getSingleUseLegacyLogCallback = exports.makeLegacySendBatchFn = exports.getDeviceSpec = exports.agentSchedulerId = exports.isUnpackedRuntimeMessage = exports.defaultPendingOpsRetryDelayMs = exports.defaultPendingOpsWaitTimeoutMs = exports.defaultRuntimeHeaderData = exports.InactiveResponseHeaderKey = exports.TombstoneResponseHeaderKey = exports.DeletedResponseHeaderKey = void 0;
|
|
8
8
|
const client_utils_1 = require("@fluid-internal/client-utils");
|
|
9
9
|
const container_definitions_1 = require("@fluidframework/container-definitions");
|
|
10
10
|
const internal_1 = require("@fluidframework/container-definitions/internal");
|
|
@@ -105,6 +105,15 @@ const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconn
|
|
|
105
105
|
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
106
106
|
const defaultMaxBatchSizeInBytes = 700 * 1024;
|
|
107
107
|
const defaultChunkSizeInBytes = 204800;
|
|
108
|
+
/**
|
|
109
|
+
* Default maximum ops per staging-mode batch before automatic flush scheduling resumes.
|
|
110
|
+
*
|
|
111
|
+
* Chosen based on production telemetry: copy-paste operations routinely produce batches
|
|
112
|
+
* of 1000+ ops (435K instances over 30 days), and receivers on modern Fluid versions
|
|
113
|
+
* handle them without issues. Uses {@link largeBatchThreshold} to stay aligned with
|
|
114
|
+
* the existing "large batch" telemetry threshold ({@link OpGroupingManager}).
|
|
115
|
+
*/
|
|
116
|
+
const defaultStagingModeAutoFlushThreshold = index_js_3.largeBatchThreshold;
|
|
108
117
|
/**
|
|
109
118
|
* The default time to wait for pending ops to be processed during summarization
|
|
110
119
|
*/
|
|
@@ -206,6 +215,22 @@ async function loadContainerRuntime(params) {
|
|
|
206
215
|
return ContainerRuntime.loadRuntime(params);
|
|
207
216
|
}
|
|
208
217
|
exports.loadContainerRuntime = loadContainerRuntime;
|
|
218
|
+
/**
|
|
219
|
+
* Alpha variant of {@link loadContainerRuntime} that returns the runtime in an
|
|
220
|
+
* extendable object, allowing additional properties to be added in the future.
|
|
221
|
+
*
|
|
222
|
+
* @param params - An object which specifies all required and optional params necessary to instantiate a runtime.
|
|
223
|
+
* @returns An object containing the runtime.
|
|
224
|
+
*
|
|
225
|
+
* @legacy @alpha
|
|
226
|
+
*/
|
|
227
|
+
async function loadContainerRuntimeAlpha(params) {
|
|
228
|
+
return ContainerRuntime.loadRuntime2({
|
|
229
|
+
...params,
|
|
230
|
+
registry: new dataStoreRegistry_js_1.FluidDataStoreRegistry(params.registryEntries),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
exports.loadContainerRuntimeAlpha = loadContainerRuntimeAlpha;
|
|
209
234
|
const defaultMaxConsecutiveReconnects = 7;
|
|
210
235
|
/**
|
|
211
236
|
* These are the ONLY message types that are allowed to be submitted while in staging mode
|
|
@@ -245,13 +270,14 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
245
270
|
return ContainerRuntime.loadRuntime2({
|
|
246
271
|
...params,
|
|
247
272
|
registry: new dataStoreRegistry_js_1.FluidDataStoreRegistry(params.registryEntries),
|
|
248
|
-
});
|
|
273
|
+
}).then((r) => r.runtime);
|
|
249
274
|
}
|
|
250
275
|
/**
|
|
251
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
276
|
+
* Load the stores from a snapshot and returns an object containing the runtime.
|
|
252
277
|
* @remarks
|
|
253
278
|
* Same as {@link ContainerRuntime.loadRuntime},
|
|
254
279
|
* but with `registry` instead of `registryEntries` and more `runtimeOptions`.
|
|
280
|
+
* Returns `{ runtime }` to allow future extensions (e.g. staging mode controls).
|
|
255
281
|
*/
|
|
256
282
|
static async loadRuntime2(params) {
|
|
257
283
|
const { context, registry, existing, requestHandler, provideEntryPoint, runtimeOptions = {}, containerScope = {}, containerRuntimeCtor = ContainerRuntime, minVersionForCollab = internal_7.defaultMinVersionForCollab, } = params;
|
|
@@ -288,6 +314,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
288
314
|
loadSequenceNumberVerification: "close",
|
|
289
315
|
maxBatchSizeInBytes: defaultMaxBatchSizeInBytes,
|
|
290
316
|
chunkSizeInBytes: defaultChunkSizeInBytes,
|
|
317
|
+
stagingModeAutoFlushThreshold: defaultStagingModeAutoFlushThreshold,
|
|
318
|
+
disableSchemaUpgrade: false,
|
|
291
319
|
};
|
|
292
320
|
const defaultConfigs = {
|
|
293
321
|
...defaultsAffectingDocSchema,
|
|
@@ -301,7 +329,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
301
329
|
// is enabled via runtimeOptions, we will throw an error later.
|
|
302
330
|
compressionOptions = enableGroupedBatching === false
|
|
303
331
|
? compressionDefinitions_js_1.disabledCompressionConfig
|
|
304
|
-
: defaultConfigs.compressionOptions, createBlobPayloadPending = defaultConfigs.createBlobPayloadPending, } = runtimeOptions;
|
|
332
|
+
: defaultConfigs.compressionOptions, createBlobPayloadPending = defaultConfigs.createBlobPayloadPending, stagingModeAutoFlushThreshold = defaultConfigs.stagingModeAutoFlushThreshold, disableSchemaUpgrade = defaultConfigs.disableSchemaUpgrade, } = runtimeOptions;
|
|
305
333
|
// If explicitSchemaControl is off, ensure that options which require explicitSchemaControl are not enabled.
|
|
306
334
|
if (!explicitSchemaControl) {
|
|
307
335
|
const disallowedKeys = Object.keys(runtimeOptions).filter((key) => containerCompatibility_js_1.runtimeOptionKeysThatRequireExplicitSchemaControl.includes(key) && runtimeOptions[key] !== undefined);
|
|
@@ -408,6 +436,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
408
436
|
else {
|
|
409
437
|
idCompressorMode = desiredIdCompressorMode;
|
|
410
438
|
}
|
|
439
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
411
440
|
const createIdCompressorFn = () => {
|
|
412
441
|
/**
|
|
413
442
|
* Because the IdCompressor emits so much telemetry, this function is used to sample
|
|
@@ -444,7 +473,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
444
473
|
disallowedVersions: [],
|
|
445
474
|
}, (schema) => {
|
|
446
475
|
runtime.onSchemaChange(schema);
|
|
447
|
-
}, { minVersionForCollab }, logger);
|
|
476
|
+
}, { minVersionForCollab }, logger, disableSchemaUpgrade);
|
|
448
477
|
// If the minVersionForCollab for this client is greater than the existing one, we should use that one going forward.
|
|
449
478
|
const existingMinVersionForCollab = documentSchemaController.sessionSchema.info.minVersionForCollab;
|
|
450
479
|
const updatedMinVersionForCollab = existingMinVersionForCollab === undefined ||
|
|
@@ -468,6 +497,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
468
497
|
enableGroupedBatching,
|
|
469
498
|
explicitSchemaControl,
|
|
470
499
|
createBlobPayloadPending,
|
|
500
|
+
stagingModeAutoFlushThreshold,
|
|
501
|
+
disableSchemaUpgrade,
|
|
471
502
|
};
|
|
472
503
|
(0, internal_7.validateMinimumVersionForCollab)(updatedMinVersionForCollab);
|
|
473
504
|
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
|
|
@@ -478,7 +509,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
478
509
|
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
|
|
479
510
|
// or zero. This must be done before Container replays saved ops.
|
|
480
511
|
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
481
|
-
return runtime;
|
|
512
|
+
return { runtime };
|
|
482
513
|
}
|
|
483
514
|
get clientId() {
|
|
484
515
|
return this._getClientId();
|
|
@@ -517,6 +548,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
517
548
|
/**
|
|
518
549
|
* {@inheritDoc @fluidframework/runtime-definitions#IContainerRuntimeBase.idCompressor}
|
|
519
550
|
*/
|
|
551
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
520
552
|
get idCompressor() {
|
|
521
553
|
// Expose ID Compressor only if it's On from the start.
|
|
522
554
|
// If container uses delayed mode, then we can only expose generateDocumentUniqueId() and nothing else.
|
|
@@ -595,7 +627,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
595
627
|
/***/
|
|
596
628
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope,
|
|
597
629
|
// Create a custom ITelemetryBaseLogger to output telemetry events.
|
|
598
|
-
baseLogger, existing, blobManagerLoadInfo, _storage,
|
|
630
|
+
baseLogger, existing, blobManagerLoadInfo, _storage,
|
|
631
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
632
|
+
createIdCompressorFn, documentsSchemaController, featureGatesForTelemetry, provideEntryPoint, minVersionForCollab, requestHandler,
|
|
599
633
|
// // eslint-disable-next-line unicorn/no-object-as-default-parameter
|
|
600
634
|
summaryConfiguration = {
|
|
601
635
|
// the defaults
|
|
@@ -652,17 +686,28 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
652
686
|
// Make sure Outbox is empty before entering staging mode,
|
|
653
687
|
// since we mark whole batches as "staged" or not to indicate whether to submit them.
|
|
654
688
|
this.flush();
|
|
655
|
-
const exitStagingMode = (discardOrCommit) => {
|
|
689
|
+
const exitStagingMode = (discardOrCommit, exitMethod) => {
|
|
656
690
|
try {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
691
|
+
internal_8.PerformanceEvent.timedExec(this.mc.logger, {
|
|
692
|
+
eventName: `ExitStagingMode_${exitMethod}`,
|
|
693
|
+
}, (event) => {
|
|
694
|
+
// Final flush of any last staged changes
|
|
695
|
+
// NOTE: We can't use this.flush() here, because orderSequentially uses StagingMode and in the rollback case we'll hit assert 0x24c
|
|
696
|
+
this.outbox.flush();
|
|
697
|
+
this.stageControls = undefined;
|
|
698
|
+
// During Staging Mode, we avoid submitting any ID Allocation ops (apart from resubmitting pre-staging ops).
|
|
699
|
+
// Now that we've exited, we need to submit an ID Allocation op for any IDs that were generated while in Staging Mode.
|
|
700
|
+
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
701
|
+
const batchInfos = discardOrCommit();
|
|
702
|
+
event.reportProgress({
|
|
703
|
+
details: {
|
|
704
|
+
autoFlushThreshold: this.stagingModeAutoFlushThreshold,
|
|
705
|
+
batches: batchInfos.length,
|
|
706
|
+
batchesAtOrOverThreshold: batchInfos.filter((b) => b.length >= this.stagingModeAutoFlushThreshold).length,
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
this.channelCollection.notifyStagingMode(false);
|
|
710
|
+
});
|
|
666
711
|
}
|
|
667
712
|
catch (error) {
|
|
668
713
|
const normalizedError = (0, internal_8.normalizeError)(error);
|
|
@@ -673,21 +718,22 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
673
718
|
const stageControls = {
|
|
674
719
|
discardChanges: () => exitStagingMode(() => {
|
|
675
720
|
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
676
|
-
this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
721
|
+
const batchInfos = this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
677
722
|
this.rollbackStagedChange(runtimeOp, localOpMetadata);
|
|
678
723
|
});
|
|
679
724
|
this.updateDocumentDirtyState();
|
|
680
|
-
|
|
725
|
+
return batchInfos;
|
|
726
|
+
}, "discard"),
|
|
681
727
|
commitChanges: (options) => {
|
|
682
728
|
const { squash } = { ...defaultStagingCommitOptions, ...options };
|
|
683
729
|
exitStagingMode(() => {
|
|
684
730
|
// Replay all staged batches in typical FIFO order.
|
|
685
731
|
// We'll be out of staging mode so they'll be sent to the service finally.
|
|
686
|
-
this.pendingStateManager.replayPendingStates({
|
|
732
|
+
return this.pendingStateManager.replayPendingStates({
|
|
687
733
|
committingStagedBatches: true,
|
|
688
734
|
squash,
|
|
689
735
|
});
|
|
690
|
-
});
|
|
736
|
+
}, "commit");
|
|
691
737
|
},
|
|
692
738
|
};
|
|
693
739
|
this.stageControls = stageControls;
|
|
@@ -748,7 +794,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
748
794
|
}
|
|
749
795
|
return eventEmitter;
|
|
750
796
|
});
|
|
751
|
-
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, signalAudience, pendingLocalState,
|
|
797
|
+
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, signalAudience, pendingLocalState, snapshotWithContents, getConnectionState, } = context;
|
|
752
798
|
this.getConnectionState = getConnectionState;
|
|
753
799
|
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
754
800
|
this.disposeFn = disposeFn ?? closeFn;
|
|
@@ -870,14 +916,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
870
916
|
? this.getConnectionState() === internal_1.ConnectionState.Connected ||
|
|
871
917
|
this.getConnectionState() === internal_1.ConnectionState.CatchingUp
|
|
872
918
|
: undefined;
|
|
873
|
-
this.mc.logger.sendTelemetryEvent({
|
|
874
|
-
eventName: "GCFeatureMatrix",
|
|
875
|
-
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
876
|
-
inputs: JSON.stringify({
|
|
877
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
878
|
-
gcOptions_gcGeneration: runtimeOptions.gcOptions[index_js_2.gcGenerationOptionName],
|
|
879
|
-
}),
|
|
880
|
-
});
|
|
881
919
|
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? (0, uuid_1.v4)();
|
|
882
920
|
const opGroupingManager = new index_js_3.OpGroupingManager({
|
|
883
921
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -918,20 +956,17 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
918
956
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.Test.DisableSummaries") === true;
|
|
919
957
|
this.maxConsecutiveReconnects =
|
|
920
958
|
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? defaultMaxConsecutiveReconnects;
|
|
921
|
-
|
|
922
|
-
//
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
!referenceSequenceNumbersSupported) {
|
|
928
|
-
// The loader does not support reference sequence numbers, falling back on FlushMode.TurnBased
|
|
929
|
-
this.mc.logger.sendErrorEvent({ eventName: "FlushModeFallback" });
|
|
930
|
-
this._flushMode = internal_6.FlushMode.TurnBased;
|
|
931
|
-
}
|
|
932
|
-
else {
|
|
933
|
-
this._flushMode = runtimeOptions.flushMode;
|
|
959
|
+
this._flushMode = runtimeOptions.flushMode;
|
|
960
|
+
// TODO: Added in 2.90.0 - Remove this validation once we've released and confirmed no consumer passes an invalid flushMode value.
|
|
961
|
+
if (this._flushMode !== internal_6.FlushMode.Immediate && this._flushMode !== internal_6.FlushMode.TurnBased) {
|
|
962
|
+
const error = new internal_8.UsageError("Invalid flushMode runtime option. Expected FlushMode.Immediate or FlushMode.TurnBased.");
|
|
963
|
+
this.closeFn(error);
|
|
964
|
+
throw error;
|
|
934
965
|
}
|
|
966
|
+
this.stagingModeAutoFlushThreshold =
|
|
967
|
+
this.mc.config.getNumber("Fluid.ContainerRuntime.StagingModeAutoFlushThreshold") ??
|
|
968
|
+
runtimeOptions.stagingModeAutoFlushThreshold ??
|
|
969
|
+
defaultStagingModeAutoFlushThreshold;
|
|
935
970
|
this.batchIdTrackingEnabled =
|
|
936
971
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineFull") ??
|
|
937
972
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableBatchIdTracking") ??
|
|
@@ -1041,9 +1076,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1041
1076
|
this.deltaScheduler = new deltaScheduler_js_1.DeltaScheduler(this.innerDeltaManager, this, (0, internal_8.createChildLogger)({ logger: this.baseLogger, namespace: "DeltaScheduler" }));
|
|
1042
1077
|
this.inboundBatchAggregator = new inboundBatchAggregator_js_1.InboundBatchAggregator(this.innerDeltaManager, () => this.clientId, (0, internal_8.createChildLogger)({ logger: this.baseLogger, namespace: "InboundBatchAggregator" }));
|
|
1043
1078
|
const legacySendBatchFn = (0, exports.makeLegacySendBatchFn)(submitFn, this.innerDeltaManager);
|
|
1044
|
-
this.skipSafetyFlushDuringProcessStack =
|
|
1045
|
-
// Keep the old flag name even though we renamed the class member (it shipped in 2.31.0)
|
|
1046
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableFlushBeforeProcess") === true;
|
|
1047
1079
|
this.outbox = new index_js_3.Outbox({
|
|
1048
1080
|
shouldSend: () => this.shouldSendOps(),
|
|
1049
1081
|
pendingStateManager: this.pendingStateManager,
|
|
@@ -1054,8 +1086,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1054
1086
|
config: {
|
|
1055
1087
|
compressionOptions,
|
|
1056
1088
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1057
|
-
// If we disable flush before process, we must be ready to flush partial batches
|
|
1058
|
-
flushPartialBatches: this.skipSafetyFlushDuringProcessStack,
|
|
1059
1089
|
},
|
|
1060
1090
|
logger: this.mc.logger,
|
|
1061
1091
|
groupingManager: opGroupingManager,
|
|
@@ -1099,14 +1129,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1099
1129
|
// We haven't emitted dirty/saved yet, but this is the baseline so we know to emit when it changes
|
|
1100
1130
|
this.lastEmittedDirty = this.computeCurrentDirtyState();
|
|
1101
1131
|
context.updateDirtyContainerState(this.lastEmittedDirty);
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
this.deltaManager.on("op", () => this.flush());
|
|
1109
|
-
}
|
|
1132
|
+
// Reference Sequence Number may have just changed, and it must be consistent across a batch,
|
|
1133
|
+
// so we should flush now to clear the way for the next ops.
|
|
1134
|
+
// NOTE: This will be redundant whenever CR.process was called for the op (since we flush there too) -
|
|
1135
|
+
// But we need this coverage for old loaders that don't call ContainerRuntime.process for non-runtime messages.
|
|
1136
|
+
// (We have to call flush _before_ processing a runtime op, but after is ok for non-runtime op)
|
|
1137
|
+
this.deltaManager.on("op", () => this.flush());
|
|
1110
1138
|
// logging hardware telemetry
|
|
1111
1139
|
this.baseLogger.send({
|
|
1112
1140
|
category: "generic",
|
|
@@ -1120,7 +1148,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1120
1148
|
summaryNumber: loadSummaryNumber,
|
|
1121
1149
|
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
1122
1150
|
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
1151
|
+
// This is useful even for interactive clients since they track unreferenced nodes and log errors.
|
|
1123
1152
|
gcVersion: metadata?.gcFeature,
|
|
1153
|
+
gcConfigs: this.garbageCollector.serializedConfigs,
|
|
1124
1154
|
options: JSON.stringify(runtimeOptions),
|
|
1125
1155
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
1126
1156
|
idCompressorMode: this.sessionSchema.idCompressorMode,
|
|
@@ -1128,7 +1158,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1128
1158
|
featureGates: JSON.stringify({
|
|
1129
1159
|
...featureGatesForTelemetry,
|
|
1130
1160
|
closeSummarizerDelayOverride,
|
|
1131
|
-
disableFlushBeforeProcess: this.skipSafetyFlushDuringProcessStack,
|
|
1132
1161
|
}),
|
|
1133
1162
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1134
1163
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -1585,12 +1614,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1585
1614
|
(0, internal_2.assert)(this.emitDirtyDocumentEvent, 0x127 /* "dirty document event not set on replay" */);
|
|
1586
1615
|
this.emitDirtyDocumentEvent = false;
|
|
1587
1616
|
try {
|
|
1588
|
-
// Any ID Allocation ops that failed to submit
|
|
1589
|
-
// the
|
|
1590
|
-
//
|
|
1591
|
-
|
|
1592
|
-
this.submitIdAllocationOpIfNeeded({ resubmitOutstandingRanges: true, staged: false });
|
|
1593
|
-
this.scheduleFlush();
|
|
1617
|
+
// Any ID Allocation ops that failed to submit need to have their ranges included
|
|
1618
|
+
// in the next allocation op. Reset the compressor's unfinalized range cursor so that the next
|
|
1619
|
+
// call to takeNextCreationRange (during replay) will include those unfinalized ranges.
|
|
1620
|
+
this._idCompressor?.resetUnfinalizedCreationRange();
|
|
1594
1621
|
// replay the ops
|
|
1595
1622
|
this.pendingStateManager.replayPendingStates();
|
|
1596
1623
|
}
|
|
@@ -1811,10 +1838,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1811
1838
|
// spread operator above ensure we make a shallow copy of message, as the processing flow will modify it.
|
|
1812
1839
|
// There might be multiple container instances receiving the same message.
|
|
1813
1840
|
this.verifyNotClosed();
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
this.flush();
|
|
1817
|
-
}
|
|
1841
|
+
// Reference Sequence Number may be about to change, and it must be consistent across a batch, so flush now
|
|
1842
|
+
this.flush();
|
|
1818
1843
|
this.ensureNoDataModelChanges(() => {
|
|
1819
1844
|
this.processInboundMessageOrBatch(messageCopy, local);
|
|
1820
1845
|
});
|
|
@@ -2305,7 +2330,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2305
2330
|
return this.lastEmittedDirty;
|
|
2306
2331
|
}
|
|
2307
2332
|
/**
|
|
2308
|
-
* Returns true if the container is dirty: not attached, or
|
|
2333
|
+
* Returns true if the container is dirty: not attached, or has pending user messages (ignores "non-dirtyable" ones though)
|
|
2309
2334
|
*/
|
|
2310
2335
|
computeCurrentDirtyState() {
|
|
2311
2336
|
return (this.attachState !== container_definitions_1.AttachState.Attached ||
|
|
@@ -3045,6 +3070,17 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3045
3070
|
this.updateDocumentDirtyState();
|
|
3046
3071
|
}
|
|
3047
3072
|
scheduleFlush() {
|
|
3073
|
+
// During staging mode, suppress automatic flush scheduling until the main batch
|
|
3074
|
+
// reaches or exceeds the threshold.
|
|
3075
|
+
// Incoming ops still break the batch via direct this.flush() calls elsewhere
|
|
3076
|
+
// (deltaManager "op" handler, process(), connection changes, getPendingLocalState,
|
|
3077
|
+
// exitStagingMode). Those all bypass scheduleFlush(), so they're unaffected by this check.
|
|
3078
|
+
// Additionally, outbox.maybeFlushPartialBatch() (called on every submit) detects
|
|
3079
|
+
// sequence number changes and throws if unexpected changes are detected.
|
|
3080
|
+
if (this.inStagingMode &&
|
|
3081
|
+
this.outbox.mainBatchMessageCount < this.stagingModeAutoFlushThreshold) {
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3048
3084
|
if (this.flushScheduled) {
|
|
3049
3085
|
return;
|
|
3050
3086
|
}
|
|
@@ -3064,14 +3100,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3064
3100
|
Promise.resolve().then(() => this.flush());
|
|
3065
3101
|
break;
|
|
3066
3102
|
}
|
|
3067
|
-
// FlushModeExperimental is experimental and not exposed directly in the runtime APIs
|
|
3068
|
-
case internal_6.FlushModeExperimental.Async: {
|
|
3069
|
-
// When in Async flush mode, the runtime will accumulate all operations across JS turns and send them as a single
|
|
3070
|
-
// batch when all micro-tasks are complete.
|
|
3071
|
-
// Compared to TurnBased, this flush mode will capture more ops into the same batch.
|
|
3072
|
-
setTimeout(() => this.flush(), 0);
|
|
3073
|
-
break;
|
|
3074
|
-
}
|
|
3075
3103
|
default: {
|
|
3076
3104
|
(0, internal_2.fail)(0x587 /* Unreachable unless manually accumulating a batch */);
|
|
3077
3105
|
}
|
|
@@ -3347,7 +3375,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3347
3375
|
return internal_8.PerformanceEvent.timedExec(this.mc.logger, {
|
|
3348
3376
|
eventName: "getPendingLocalState",
|
|
3349
3377
|
}, (event) => {
|
|
3350
|
-
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
3378
|
+
const { pending } = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
3351
3379
|
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
3352
3380
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
3353
3381
|
const pendingAttachmentBlobs = this.blobManager.getPendingBlobs();
|