@fluidframework/container-runtime 2.91.0 → 2.93.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/README.md +1 -1
- 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 +38 -11
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +118 -86
- 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 +3 -9
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +5 -3
- 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 +8 -11
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +42 -66
- 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 +8 -9
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +24 -22
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/public.d.ts +1 -1
- package/dist/runtimeLayerCompatState.d.ts +2 -2
- 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/eslint.config.mts +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 +38 -11
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +118 -87
- 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 +3 -9
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +5 -3
- 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 +8 -11
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +43 -67
- 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 +8 -9
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +24 -22
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/public.d.ts +1 -1
- package/lib/runtimeLayerCompatState.d.ts +2 -2
- 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/lib/tsdoc-metadata.json +1 -1
- package/package.json +27 -28
- package/src/containerCompatibility.ts +2 -0
- package/src/containerRuntime.ts +163 -106
- 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 +6 -13
- 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 +53 -86
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +31 -33
- package/src/summary/documentSchema.ts +25 -2
- package/src/summary/orderedClientElection.ts +2 -2
- package/src/summary/summaryManager.ts +11 -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,25 @@ 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
|
+
const batchInfos = discardOrCommit();
|
|
699
|
+
event.reportProgress({
|
|
700
|
+
details: {
|
|
701
|
+
autoFlushThreshold: this.stagingModeAutoFlushThreshold,
|
|
702
|
+
batches: batchInfos.length,
|
|
703
|
+
batchesAtOrOverThreshold: batchInfos.filter((b) => b.length >= this.stagingModeAutoFlushThreshold).length,
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
this.channelCollection.notifyStagingMode(false);
|
|
707
|
+
});
|
|
666
708
|
}
|
|
667
709
|
catch (error) {
|
|
668
710
|
const normalizedError = (0, internal_8.normalizeError)(error);
|
|
@@ -673,21 +715,22 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
673
715
|
const stageControls = {
|
|
674
716
|
discardChanges: () => exitStagingMode(() => {
|
|
675
717
|
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
676
|
-
this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
718
|
+
const batchInfos = this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
677
719
|
this.rollbackStagedChange(runtimeOp, localOpMetadata);
|
|
678
720
|
});
|
|
679
721
|
this.updateDocumentDirtyState();
|
|
680
|
-
|
|
722
|
+
return batchInfos;
|
|
723
|
+
}, "discard"),
|
|
681
724
|
commitChanges: (options) => {
|
|
682
725
|
const { squash } = { ...defaultStagingCommitOptions, ...options };
|
|
683
726
|
exitStagingMode(() => {
|
|
684
727
|
// Replay all staged batches in typical FIFO order.
|
|
685
728
|
// We'll be out of staging mode so they'll be sent to the service finally.
|
|
686
|
-
this.pendingStateManager.replayPendingStates({
|
|
729
|
+
return this.pendingStateManager.replayPendingStates({
|
|
687
730
|
committingStagedBatches: true,
|
|
688
731
|
squash,
|
|
689
732
|
});
|
|
690
|
-
});
|
|
733
|
+
}, "commit");
|
|
691
734
|
},
|
|
692
735
|
};
|
|
693
736
|
this.stageControls = stageControls;
|
|
@@ -870,14 +913,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
870
913
|
? this.getConnectionState() === internal_1.ConnectionState.Connected ||
|
|
871
914
|
this.getConnectionState() === internal_1.ConnectionState.CatchingUp
|
|
872
915
|
: 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
916
|
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? (0, uuid_1.v4)();
|
|
882
917
|
const opGroupingManager = new index_js_3.OpGroupingManager({
|
|
883
918
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -925,6 +960,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
925
960
|
this.closeFn(error);
|
|
926
961
|
throw error;
|
|
927
962
|
}
|
|
963
|
+
this.stagingModeAutoFlushThreshold =
|
|
964
|
+
this.mc.config.getNumber("Fluid.ContainerRuntime.StagingModeAutoFlushThreshold") ??
|
|
965
|
+
runtimeOptions.stagingModeAutoFlushThreshold ??
|
|
966
|
+
defaultStagingModeAutoFlushThreshold;
|
|
928
967
|
this.batchIdTrackingEnabled =
|
|
929
968
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineFull") ??
|
|
930
969
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableBatchIdTracking") ??
|
|
@@ -1034,9 +1073,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1034
1073
|
this.deltaScheduler = new deltaScheduler_js_1.DeltaScheduler(this.innerDeltaManager, this, (0, internal_8.createChildLogger)({ logger: this.baseLogger, namespace: "DeltaScheduler" }));
|
|
1035
1074
|
this.inboundBatchAggregator = new inboundBatchAggregator_js_1.InboundBatchAggregator(this.innerDeltaManager, () => this.clientId, (0, internal_8.createChildLogger)({ logger: this.baseLogger, namespace: "InboundBatchAggregator" }));
|
|
1036
1075
|
const legacySendBatchFn = (0, exports.makeLegacySendBatchFn)(submitFn, this.innerDeltaManager);
|
|
1037
|
-
this.skipSafetyFlushDuringProcessStack =
|
|
1038
|
-
// Keep the old flag name even though we renamed the class member (it shipped in 2.31.0)
|
|
1039
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableFlushBeforeProcess") === true;
|
|
1040
1076
|
this.outbox = new index_js_3.Outbox({
|
|
1041
1077
|
shouldSend: () => this.shouldSendOps(),
|
|
1042
1078
|
pendingStateManager: this.pendingStateManager,
|
|
@@ -1047,8 +1083,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1047
1083
|
config: {
|
|
1048
1084
|
compressionOptions,
|
|
1049
1085
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
1050
|
-
// If we disable flush before process, we must be ready to flush partial batches
|
|
1051
|
-
flushPartialBatches: this.skipSafetyFlushDuringProcessStack,
|
|
1052
1086
|
},
|
|
1053
1087
|
logger: this.mc.logger,
|
|
1054
1088
|
groupingManager: opGroupingManager,
|
|
@@ -1059,6 +1093,24 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1059
1093
|
}),
|
|
1060
1094
|
reSubmit: this.reSubmit.bind(this),
|
|
1061
1095
|
opReentrancy: () => this.dataModelChangeRunner.running,
|
|
1096
|
+
generateIdAllocationOp: () => {
|
|
1097
|
+
if (this._idCompressor === undefined) {
|
|
1098
|
+
return undefined;
|
|
1099
|
+
}
|
|
1100
|
+
const idRange = this._idCompressor.takeNextCreationRange();
|
|
1101
|
+
if (idRange.ids === undefined) {
|
|
1102
|
+
return undefined;
|
|
1103
|
+
}
|
|
1104
|
+
const idAllocationMessage = {
|
|
1105
|
+
type: messageTypes_js_1.ContainerMessageType.IdAllocation,
|
|
1106
|
+
contents: idRange,
|
|
1107
|
+
};
|
|
1108
|
+
return {
|
|
1109
|
+
runtimeOp: idAllocationMessage,
|
|
1110
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1111
|
+
staged: false,
|
|
1112
|
+
};
|
|
1113
|
+
},
|
|
1062
1114
|
});
|
|
1063
1115
|
this._quorum = quorum;
|
|
1064
1116
|
this._quorum.on("removeMember", (clientId) => {
|
|
@@ -1092,14 +1144,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1092
1144
|
// We haven't emitted dirty/saved yet, but this is the baseline so we know to emit when it changes
|
|
1093
1145
|
this.lastEmittedDirty = this.computeCurrentDirtyState();
|
|
1094
1146
|
context.updateDirtyContainerState(this.lastEmittedDirty);
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
this.deltaManager.on("op", () => this.flush());
|
|
1102
|
-
}
|
|
1147
|
+
// Reference Sequence Number may have just changed, and it must be consistent across a batch,
|
|
1148
|
+
// so we should flush now to clear the way for the next ops.
|
|
1149
|
+
// NOTE: This will be redundant whenever CR.process was called for the op (since we flush there too) -
|
|
1150
|
+
// But we need this coverage for old loaders that don't call ContainerRuntime.process for non-runtime messages.
|
|
1151
|
+
// (We have to call flush _before_ processing a runtime op, but after is ok for non-runtime op)
|
|
1152
|
+
this.deltaManager.on("op", () => this.flush());
|
|
1103
1153
|
// logging hardware telemetry
|
|
1104
1154
|
this.baseLogger.send({
|
|
1105
1155
|
category: "generic",
|
|
@@ -1113,7 +1163,9 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1113
1163
|
summaryNumber: loadSummaryNumber,
|
|
1114
1164
|
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
1115
1165
|
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
1166
|
+
// This is useful even for interactive clients since they track unreferenced nodes and log errors.
|
|
1116
1167
|
gcVersion: metadata?.gcFeature,
|
|
1168
|
+
gcConfigs: this.garbageCollector.serializedConfigs,
|
|
1117
1169
|
options: JSON.stringify(runtimeOptions),
|
|
1118
1170
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
1119
1171
|
idCompressorMode: this.sessionSchema.idCompressorMode,
|
|
@@ -1121,7 +1173,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1121
1173
|
featureGates: JSON.stringify({
|
|
1122
1174
|
...featureGatesForTelemetry,
|
|
1123
1175
|
closeSummarizerDelayOverride,
|
|
1124
|
-
disableFlushBeforeProcess: this.skipSafetyFlushDuringProcessStack,
|
|
1125
1176
|
}),
|
|
1126
1177
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
1127
1178
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -1578,12 +1629,10 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1578
1629
|
(0, internal_2.assert)(this.emitDirtyDocumentEvent, 0x127 /* "dirty document event not set on replay" */);
|
|
1579
1630
|
this.emitDirtyDocumentEvent = false;
|
|
1580
1631
|
try {
|
|
1581
|
-
// Any ID Allocation ops that failed to submit
|
|
1582
|
-
// the
|
|
1583
|
-
//
|
|
1584
|
-
|
|
1585
|
-
this.submitIdAllocationOpIfNeeded({ resubmitOutstandingRanges: true, staged: false });
|
|
1586
|
-
this.scheduleFlush();
|
|
1632
|
+
// Any ID Allocation ops that failed to submit need to have their ranges included
|
|
1633
|
+
// in the next allocation op. Reset the compressor's unfinalized range cursor so that the next
|
|
1634
|
+
// call to takeNextCreationRange (during replay) will include those unfinalized ranges.
|
|
1635
|
+
this._idCompressor?.resetUnfinalizedCreationRange();
|
|
1587
1636
|
// replay the ops
|
|
1588
1637
|
this.pendingStateManager.replayPendingStates();
|
|
1589
1638
|
}
|
|
@@ -1804,10 +1853,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
1804
1853
|
// spread operator above ensure we make a shallow copy of message, as the processing flow will modify it.
|
|
1805
1854
|
// There might be multiple container instances receiving the same message.
|
|
1806
1855
|
this.verifyNotClosed();
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
this.flush();
|
|
1810
|
-
}
|
|
1856
|
+
// Reference Sequence Number may be about to change, and it must be consistent across a batch, so flush now
|
|
1857
|
+
this.flush();
|
|
1811
1858
|
this.ensureNoDataModelChanges(() => {
|
|
1812
1859
|
this.processInboundMessageOrBatch(messageCopy, local);
|
|
1813
1860
|
});
|
|
@@ -2583,7 +2630,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2583
2630
|
outboxLength: this.outbox.messageCount,
|
|
2584
2631
|
mainBatchLength: this.outbox.mainBatchMessageCount,
|
|
2585
2632
|
blobAttachBatchLength: this.outbox.blobAttachBatchMessageCount,
|
|
2586
|
-
idAllocationBatchLength: this.outbox.idAllocationBatchMessageCount,
|
|
2587
2633
|
});
|
|
2588
2634
|
}
|
|
2589
2635
|
// If the container is dirty, i.e., there are pending unacked ops, the summary will not be eventual consistent
|
|
@@ -2853,7 +2899,8 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2853
2899
|
}
|
|
2854
2900
|
/**
|
|
2855
2901
|
* This helper is called during summarization. If the container is dirty, it will return a failed summarize result
|
|
2856
|
-
* (IBaseSummarizeResult) unless this is the final summarize attempt
|
|
2902
|
+
* (IBaseSummarizeResult) unless this is the final summarize attempt, in which case the summary is allowed to
|
|
2903
|
+
* proceed to make progress in documents where there are consistently pending ops in the summarizer.
|
|
2857
2904
|
* @param logger - The logger to be used for sending telemetry.
|
|
2858
2905
|
* @param referenceSequenceNumber - The reference sequence number of the summary attempt.
|
|
2859
2906
|
* @param minimumSequenceNumber - The minimum sequence number of the summary attempt.
|
|
@@ -2865,14 +2912,12 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2865
2912
|
if (!this.isDirty) {
|
|
2866
2913
|
return;
|
|
2867
2914
|
}
|
|
2868
|
-
//
|
|
2869
|
-
//
|
|
2870
|
-
|
|
2871
|
-
if (finalAttempt &&
|
|
2872
|
-
this.mc.config.getBoolean("Fluid.Summarizer.SkipFailingIncorrectSummary") === true) {
|
|
2915
|
+
// Don't fail the summary in the last attempt. This is a fallback to make progress in
|
|
2916
|
+
// documents where there are consistently pending ops in the summarizer.
|
|
2917
|
+
if (finalAttempt) {
|
|
2873
2918
|
const error = internal_8.DataProcessingError.create("Pending ops during summarization", "submitSummary", undefined, { pendingMessages: this.pendingMessagesCount });
|
|
2874
2919
|
logger.sendErrorEvent({
|
|
2875
|
-
eventName: "
|
|
2920
|
+
eventName: "PendingOpsDuringSummaryFinalAttempt",
|
|
2876
2921
|
referenceSequenceNumber,
|
|
2877
2922
|
minimumSequenceNumber,
|
|
2878
2923
|
beforeGenerate: beforeSummaryGeneration,
|
|
@@ -2942,26 +2987,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2942
2987
|
lookupTemporaryBlobStorageId(localId) {
|
|
2943
2988
|
return this.blobManager.lookupTemporaryBlobStorageId(localId);
|
|
2944
2989
|
}
|
|
2945
|
-
submitIdAllocationOpIfNeeded({ resubmitOutstandingRanges = false, staged, }) {
|
|
2946
|
-
if (this._idCompressor) {
|
|
2947
|
-
const idRange = resubmitOutstandingRanges
|
|
2948
|
-
? this._idCompressor.takeUnfinalizedCreationRange()
|
|
2949
|
-
: this._idCompressor.takeNextCreationRange();
|
|
2950
|
-
// Don't include the idRange if there weren't any Ids allocated
|
|
2951
|
-
if (idRange.ids !== undefined) {
|
|
2952
|
-
const idAllocationMessage = {
|
|
2953
|
-
type: messageTypes_js_1.ContainerMessageType.IdAllocation,
|
|
2954
|
-
contents: idRange,
|
|
2955
|
-
};
|
|
2956
|
-
const idAllocationBatchMessage = {
|
|
2957
|
-
runtimeOp: idAllocationMessage,
|
|
2958
|
-
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2959
|
-
staged,
|
|
2960
|
-
};
|
|
2961
|
-
this.outbox.submitIdAllocation(idAllocationBatchMessage);
|
|
2962
|
-
}
|
|
2963
|
-
}
|
|
2964
|
-
}
|
|
2965
2990
|
submit(containerRuntimeMessage, localOpMetadata = undefined, metadata) {
|
|
2966
2991
|
this.verifyNotClosed();
|
|
2967
2992
|
// There should be no ops in detached container state!
|
|
@@ -2982,10 +3007,6 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
2982
3007
|
// If we're resubmitting a batch, keep the same "staged" value as before. Otherwise, use the current "global" state.
|
|
2983
3008
|
const staged = this.batchRunner.resubmitInfo?.staged ?? this.inStagingMode;
|
|
2984
3009
|
(0, internal_2.assert)(!staged || canStageMessageOfType(type), 0xbba /* Unexpected message type submitted in Staging Mode */);
|
|
2985
|
-
// Before submitting any non-staged change, submit the ID Allocation op to cover any compressed IDs included in the op.
|
|
2986
|
-
if (!staged) {
|
|
2987
|
-
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
2988
|
-
}
|
|
2989
3010
|
// Allow document schema controller to send a message if it needs to propose change in document schema.
|
|
2990
3011
|
// If it needs to send a message, it will call provided callback with payload of such message and rely
|
|
2991
3012
|
// on this callback to do actual sending.
|
|
@@ -3038,6 +3059,17 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3038
3059
|
this.updateDocumentDirtyState();
|
|
3039
3060
|
}
|
|
3040
3061
|
scheduleFlush() {
|
|
3062
|
+
// During staging mode, suppress automatic flush scheduling until the main batch
|
|
3063
|
+
// reaches or exceeds the threshold.
|
|
3064
|
+
// Incoming ops still break the batch via direct this.flush() calls elsewhere
|
|
3065
|
+
// (deltaManager "op" handler, process(), connection changes, getPendingLocalState,
|
|
3066
|
+
// exitStagingMode). Those all bypass scheduleFlush(), so they're unaffected by this check.
|
|
3067
|
+
// Additionally, outbox.outboxSequenceNumberCoherencyCheck() (called on every submit) detects
|
|
3068
|
+
// sequence number changes and throws if unexpected changes are detected.
|
|
3069
|
+
if (this.inStagingMode &&
|
|
3070
|
+
this.outbox.mainBatchMessageCount < this.stagingModeAutoFlushThreshold) {
|
|
3071
|
+
return;
|
|
3072
|
+
}
|
|
3041
3073
|
if (this.flushScheduled) {
|
|
3042
3074
|
return;
|
|
3043
3075
|
}
|
|
@@ -3332,7 +3364,7 @@ class ContainerRuntime extends client_utils_1.TypedEventEmitter {
|
|
|
3332
3364
|
return internal_8.PerformanceEvent.timedExec(this.mc.logger, {
|
|
3333
3365
|
eventName: "getPendingLocalState",
|
|
3334
3366
|
}, (event) => {
|
|
3335
|
-
const pending = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
3367
|
+
const { pending } = this.pendingStateManager.getLocalState(props?.snapshotSequenceNumber);
|
|
3336
3368
|
const sessionExpiryTimerStarted = props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
3337
3369
|
const pendingIdCompressorState = this._idCompressor?.serialize(true);
|
|
3338
3370
|
const pendingAttachmentBlobs = this.blobManager.getPendingBlobs();
|