@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/src/containerRuntime.ts
CHANGED
|
@@ -95,6 +95,7 @@ import { FetchSource, MessageType } from "@fluidframework/driver-definitions/int
|
|
|
95
95
|
import { readAndParse } from "@fluidframework/driver-utils/internal";
|
|
96
96
|
import type { IIdCompressor } from "@fluidframework/id-compressor";
|
|
97
97
|
import type {
|
|
98
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
98
99
|
IIdCompressorCore,
|
|
99
100
|
IdCreationRange,
|
|
100
101
|
SerializedIdCompressorWithNoSession,
|
|
@@ -130,6 +131,7 @@ import type {
|
|
|
130
131
|
IContainerRuntimeBaseInternal,
|
|
131
132
|
MinimumVersionForCollab,
|
|
132
133
|
ContainerExtensionExpectations,
|
|
134
|
+
ContainerRuntimeBaseAlpha,
|
|
133
135
|
} from "@fluidframework/runtime-definitions/internal";
|
|
134
136
|
import {
|
|
135
137
|
addBlobToSummary,
|
|
@@ -219,7 +221,6 @@ import {
|
|
|
219
221
|
type IGCRuntimeOptions,
|
|
220
222
|
type IGCStats,
|
|
221
223
|
type IGarbageCollector,
|
|
222
|
-
gcGenerationOptionName,
|
|
223
224
|
type GarbageCollectionMessage,
|
|
224
225
|
type IGarbageCollectionRuntime,
|
|
225
226
|
} from "./gc/index.js";
|
|
@@ -243,6 +244,7 @@ import {
|
|
|
243
244
|
DuplicateBatchDetector,
|
|
244
245
|
ensureContentsDeserialized,
|
|
245
246
|
type IBatchCheckpoint,
|
|
247
|
+
largeBatchThreshold,
|
|
246
248
|
OpCompressor,
|
|
247
249
|
OpDecompressor,
|
|
248
250
|
OpGroupingManager,
|
|
@@ -258,6 +260,7 @@ import {
|
|
|
258
260
|
type IPendingLocalState,
|
|
259
261
|
PendingStateManager,
|
|
260
262
|
type PendingBatchResubmitMetadata,
|
|
263
|
+
type IPendingMessage,
|
|
261
264
|
} from "./pendingStateManager.js";
|
|
262
265
|
import { BatchRunCounter, RunCounter } from "./runCounter.js";
|
|
263
266
|
import {
|
|
@@ -475,6 +478,25 @@ export interface ContainerRuntimeOptions {
|
|
|
475
478
|
* When enabled (`true`), createBlob will return a handle before the blob upload completes.
|
|
476
479
|
*/
|
|
477
480
|
readonly createBlobPayloadPending: true | undefined;
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Controls automatic batch flushing during staging mode.
|
|
484
|
+
* Normal turn-based/async flush scheduling is suppressed while in staging mode
|
|
485
|
+
* until the accumulated batch reaches this many ops, at which point the batch
|
|
486
|
+
* is flushed. Incoming ops always break the current batch regardless of this setting.
|
|
487
|
+
*
|
|
488
|
+
* Set to Infinity to only break batches on system events (incoming ops).
|
|
489
|
+
*
|
|
490
|
+
* @defaultValue `largeBatchThreshold` (currently 1000)
|
|
491
|
+
*/
|
|
492
|
+
readonly stagingModeAutoFlushThreshold: number;
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* When this property is set to true, the runtime will never send DocumentSchemaChange ops
|
|
496
|
+
* and will throw an error if any incoming DocumentSchemaChange ops are received.
|
|
497
|
+
* This effectively freezes the document schema at whatever state it was in when the document was created.
|
|
498
|
+
*/
|
|
499
|
+
readonly disableSchemaUpgrade: boolean;
|
|
478
500
|
}
|
|
479
501
|
|
|
480
502
|
/**
|
|
@@ -607,6 +629,16 @@ const defaultMaxBatchSizeInBytes = 700 * 1024;
|
|
|
607
629
|
|
|
608
630
|
const defaultChunkSizeInBytes = 204800;
|
|
609
631
|
|
|
632
|
+
/**
|
|
633
|
+
* Default maximum ops per staging-mode batch before automatic flush scheduling resumes.
|
|
634
|
+
*
|
|
635
|
+
* Chosen based on production telemetry: copy-paste operations routinely produce batches
|
|
636
|
+
* of 1000+ ops (435K instances over 30 days), and receivers on modern Fluid versions
|
|
637
|
+
* handle them without issues. Uses {@link largeBatchThreshold} to stay aligned with
|
|
638
|
+
* the existing "large batch" telemetry threshold ({@link OpGroupingManager}).
|
|
639
|
+
*/
|
|
640
|
+
const defaultStagingModeAutoFlushThreshold = largeBatchThreshold;
|
|
641
|
+
|
|
610
642
|
/**
|
|
611
643
|
* The default time to wait for pending ops to be processed during summarization
|
|
612
644
|
*/
|
|
@@ -804,6 +836,24 @@ export async function loadContainerRuntime(
|
|
|
804
836
|
return ContainerRuntime.loadRuntime(params);
|
|
805
837
|
}
|
|
806
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Alpha variant of {@link loadContainerRuntime} that returns the runtime in an
|
|
841
|
+
* extendable object, allowing additional properties to be added in the future.
|
|
842
|
+
*
|
|
843
|
+
* @param params - An object which specifies all required and optional params necessary to instantiate a runtime.
|
|
844
|
+
* @returns An object containing the runtime.
|
|
845
|
+
*
|
|
846
|
+
* @legacy @alpha
|
|
847
|
+
*/
|
|
848
|
+
export async function loadContainerRuntimeAlpha(params: LoadContainerRuntimeParams): Promise<{
|
|
849
|
+
runtime: IContainerRuntime & ContainerRuntimeBaseAlpha & IRuntime;
|
|
850
|
+
}> {
|
|
851
|
+
return ContainerRuntime.loadRuntime2({
|
|
852
|
+
...params,
|
|
853
|
+
registry: new FluidDataStoreRegistry(params.registryEntries),
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
|
|
807
857
|
const defaultMaxConsecutiveReconnects = 7;
|
|
808
858
|
|
|
809
859
|
/**
|
|
@@ -879,14 +929,15 @@ export class ContainerRuntime
|
|
|
879
929
|
return ContainerRuntime.loadRuntime2({
|
|
880
930
|
...params,
|
|
881
931
|
registry: new FluidDataStoreRegistry(params.registryEntries),
|
|
882
|
-
});
|
|
932
|
+
}).then((r) => r.runtime);
|
|
883
933
|
}
|
|
884
934
|
|
|
885
935
|
/**
|
|
886
|
-
* Load the stores from a snapshot and returns the runtime.
|
|
936
|
+
* Load the stores from a snapshot and returns an object containing the runtime.
|
|
887
937
|
* @remarks
|
|
888
938
|
* Same as {@link ContainerRuntime.loadRuntime},
|
|
889
939
|
* but with `registry` instead of `registryEntries` and more `runtimeOptions`.
|
|
940
|
+
* Returns `{ runtime }` to allow future extensions (e.g. staging mode controls).
|
|
890
941
|
*/
|
|
891
942
|
public static async loadRuntime2(
|
|
892
943
|
params: Omit<LoadContainerRuntimeParams, "registryEntries" | "runtimeOptions"> & {
|
|
@@ -905,7 +956,7 @@ export class ContainerRuntime
|
|
|
905
956
|
*/
|
|
906
957
|
runtimeOptions?: IContainerRuntimeOptionsInternal;
|
|
907
958
|
},
|
|
908
|
-
): Promise<ContainerRuntime> {
|
|
959
|
+
): Promise<{ runtime: ContainerRuntime }> {
|
|
909
960
|
const {
|
|
910
961
|
context,
|
|
911
962
|
registry,
|
|
@@ -961,6 +1012,8 @@ export class ContainerRuntime
|
|
|
961
1012
|
loadSequenceNumberVerification: "close",
|
|
962
1013
|
maxBatchSizeInBytes: defaultMaxBatchSizeInBytes,
|
|
963
1014
|
chunkSizeInBytes: defaultChunkSizeInBytes,
|
|
1015
|
+
stagingModeAutoFlushThreshold: defaultStagingModeAutoFlushThreshold,
|
|
1016
|
+
disableSchemaUpgrade: false,
|
|
964
1017
|
};
|
|
965
1018
|
|
|
966
1019
|
const defaultConfigs = {
|
|
@@ -986,6 +1039,8 @@ export class ContainerRuntime
|
|
|
986
1039
|
? disabledCompressionConfig
|
|
987
1040
|
: defaultConfigs.compressionOptions,
|
|
988
1041
|
createBlobPayloadPending = defaultConfigs.createBlobPayloadPending,
|
|
1042
|
+
stagingModeAutoFlushThreshold = defaultConfigs.stagingModeAutoFlushThreshold,
|
|
1043
|
+
disableSchemaUpgrade = defaultConfigs.disableSchemaUpgrade,
|
|
989
1044
|
}: IContainerRuntimeOptionsInternal = runtimeOptions;
|
|
990
1045
|
|
|
991
1046
|
// If explicitSchemaControl is off, ensure that options which require explicitSchemaControl are not enabled.
|
|
@@ -1130,6 +1185,7 @@ export class ContainerRuntime
|
|
|
1130
1185
|
idCompressorMode = desiredIdCompressorMode;
|
|
1131
1186
|
}
|
|
1132
1187
|
|
|
1188
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1133
1189
|
const createIdCompressorFn = (): IIdCompressor & IIdCompressorCore => {
|
|
1134
1190
|
/**
|
|
1135
1191
|
* Because the IdCompressor emits so much telemetry, this function is used to sample
|
|
@@ -1184,6 +1240,7 @@ export class ContainerRuntime
|
|
|
1184
1240
|
},
|
|
1185
1241
|
{ minVersionForCollab },
|
|
1186
1242
|
logger,
|
|
1243
|
+
disableSchemaUpgrade,
|
|
1187
1244
|
);
|
|
1188
1245
|
|
|
1189
1246
|
// If the minVersionForCollab for this client is greater than the existing one, we should use that one going forward.
|
|
@@ -1214,6 +1271,8 @@ export class ContainerRuntime
|
|
|
1214
1271
|
enableGroupedBatching,
|
|
1215
1272
|
explicitSchemaControl,
|
|
1216
1273
|
createBlobPayloadPending,
|
|
1274
|
+
stagingModeAutoFlushThreshold,
|
|
1275
|
+
disableSchemaUpgrade,
|
|
1217
1276
|
};
|
|
1218
1277
|
|
|
1219
1278
|
validateMinimumVersionForCollab(updatedMinVersionForCollab);
|
|
@@ -1249,7 +1308,7 @@ export class ContainerRuntime
|
|
|
1249
1308
|
// or zero. This must be done before Container replays saved ops.
|
|
1250
1309
|
await runtime.pendingStateManager.applyStashedOpsAt(runtimeSequenceNumber ?? 0);
|
|
1251
1310
|
|
|
1252
|
-
return runtime;
|
|
1311
|
+
return { runtime };
|
|
1253
1312
|
}
|
|
1254
1313
|
|
|
1255
1314
|
public readonly options: Record<string | number, unknown>;
|
|
@@ -1319,6 +1378,7 @@ export class ContainerRuntime
|
|
|
1319
1378
|
return this.documentsSchemaController.sessionSchema.runtime;
|
|
1320
1379
|
}
|
|
1321
1380
|
|
|
1381
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1322
1382
|
private _idCompressor: (IIdCompressor & IIdCompressorCore) | undefined;
|
|
1323
1383
|
|
|
1324
1384
|
// We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
|
|
@@ -1334,6 +1394,7 @@ export class ContainerRuntime
|
|
|
1334
1394
|
/**
|
|
1335
1395
|
* {@inheritDoc @fluidframework/runtime-definitions#IContainerRuntimeBase.idCompressor}
|
|
1336
1396
|
*/
|
|
1397
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1337
1398
|
public get idCompressor(): (IIdCompressor & IIdCompressorCore) | undefined {
|
|
1338
1399
|
// Expose ID Compressor only if it's On from the start.
|
|
1339
1400
|
// If container uses delayed mode, then we can only expose generateDocumentUniqueId() and nothing else.
|
|
@@ -1392,6 +1453,7 @@ export class ContainerRuntime
|
|
|
1392
1453
|
|
|
1393
1454
|
private readonly batchRunner = new BatchRunCounter();
|
|
1394
1455
|
private readonly _flushMode: FlushMode;
|
|
1456
|
+
private readonly stagingModeAutoFlushThreshold: number;
|
|
1395
1457
|
/**
|
|
1396
1458
|
* BatchId tracking is needed whenever there's a possibility of a "forked Container",
|
|
1397
1459
|
* where the same local state is pending in two different running Containers, each of
|
|
@@ -1533,13 +1595,6 @@ export class ContainerRuntime
|
|
|
1533
1595
|
return runtimeCompatDetailsForLoader;
|
|
1534
1596
|
}
|
|
1535
1597
|
|
|
1536
|
-
/**
|
|
1537
|
-
* If true, will skip Outbox flushing before processing an incoming message (and on DeltaManager "op" event for loader back-compat),
|
|
1538
|
-
* and instead the Outbox will check for a split batch on every submit.
|
|
1539
|
-
* This is a kill-bit switch for this simplification of logic, in case it causes unexpected issues.
|
|
1540
|
-
*/
|
|
1541
|
-
private readonly skipSafetyFlushDuringProcessStack: boolean;
|
|
1542
|
-
|
|
1543
1598
|
private readonly extensions = new Map<ContainerExtensionId, ExtensionEntry>();
|
|
1544
1599
|
|
|
1545
1600
|
/***/
|
|
@@ -1560,6 +1615,7 @@ export class ContainerRuntime
|
|
|
1560
1615
|
|
|
1561
1616
|
blobManagerLoadInfo: IBlobManagerLoadInfo,
|
|
1562
1617
|
private readonly _storage: IContainerStorageService,
|
|
1618
|
+
// eslint-disable-next-line import-x/no-deprecated -- Will be undeprecated in 2.100.0 when it becomes an internal API
|
|
1563
1619
|
private readonly createIdCompressorFn: () => IIdCompressor & IIdCompressorCore,
|
|
1564
1620
|
|
|
1565
1621
|
private readonly documentsSchemaController: DocumentsSchemaController,
|
|
@@ -1750,15 +1806,6 @@ export class ContainerRuntime
|
|
|
1750
1806
|
this.getConnectionState() === ConnectionState.CatchingUp
|
|
1751
1807
|
: undefined;
|
|
1752
1808
|
|
|
1753
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1754
|
-
eventName: "GCFeatureMatrix",
|
|
1755
|
-
metadataValue: JSON.stringify(metadata?.gcFeatureMatrix),
|
|
1756
|
-
inputs: JSON.stringify({
|
|
1757
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1758
|
-
gcOptions_gcGeneration: runtimeOptions.gcOptions[gcGenerationOptionName],
|
|
1759
|
-
}),
|
|
1760
|
-
});
|
|
1761
|
-
|
|
1762
1809
|
this.telemetryDocumentId = metadata?.telemetryDocumentId ?? uuid();
|
|
1763
1810
|
|
|
1764
1811
|
const opGroupingManager = new OpGroupingManager(
|
|
@@ -1840,6 +1887,10 @@ export class ContainerRuntime
|
|
|
1840
1887
|
this.closeFn(error);
|
|
1841
1888
|
throw error;
|
|
1842
1889
|
}
|
|
1890
|
+
this.stagingModeAutoFlushThreshold =
|
|
1891
|
+
this.mc.config.getNumber("Fluid.ContainerRuntime.StagingModeAutoFlushThreshold") ??
|
|
1892
|
+
runtimeOptions.stagingModeAutoFlushThreshold ??
|
|
1893
|
+
defaultStagingModeAutoFlushThreshold;
|
|
1843
1894
|
this.batchIdTrackingEnabled =
|
|
1844
1895
|
this.mc.config.getBoolean("Fluid.Container.enableOfflineFull") ??
|
|
1845
1896
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableBatchIdTracking") ??
|
|
@@ -1998,10 +2049,6 @@ export class ContainerRuntime
|
|
|
1998
2049
|
|
|
1999
2050
|
const legacySendBatchFn = makeLegacySendBatchFn(submitFn, this.innerDeltaManager);
|
|
2000
2051
|
|
|
2001
|
-
this.skipSafetyFlushDuringProcessStack =
|
|
2002
|
-
// Keep the old flag name even though we renamed the class member (it shipped in 2.31.0)
|
|
2003
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableFlushBeforeProcess") === true;
|
|
2004
|
-
|
|
2005
2052
|
this.outbox = new Outbox({
|
|
2006
2053
|
shouldSend: () => this.shouldSendOps(),
|
|
2007
2054
|
pendingStateManager: this.pendingStateManager,
|
|
@@ -2012,8 +2059,6 @@ export class ContainerRuntime
|
|
|
2012
2059
|
config: {
|
|
2013
2060
|
compressionOptions,
|
|
2014
2061
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
2015
|
-
// If we disable flush before process, we must be ready to flush partial batches
|
|
2016
|
-
flushPartialBatches: this.skipSafetyFlushDuringProcessStack,
|
|
2017
2062
|
},
|
|
2018
2063
|
logger: this.mc.logger,
|
|
2019
2064
|
groupingManager: opGroupingManager,
|
|
@@ -2024,6 +2069,24 @@ export class ContainerRuntime
|
|
|
2024
2069
|
}),
|
|
2025
2070
|
reSubmit: this.reSubmit.bind(this),
|
|
2026
2071
|
opReentrancy: () => this.dataModelChangeRunner.running,
|
|
2072
|
+
generateIdAllocationOp: (): LocalBatchMessage | undefined => {
|
|
2073
|
+
if (this._idCompressor === undefined) {
|
|
2074
|
+
return undefined;
|
|
2075
|
+
}
|
|
2076
|
+
const idRange = this._idCompressor.takeNextCreationRange();
|
|
2077
|
+
if (idRange.ids === undefined) {
|
|
2078
|
+
return undefined;
|
|
2079
|
+
}
|
|
2080
|
+
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
2081
|
+
type: ContainerMessageType.IdAllocation,
|
|
2082
|
+
contents: idRange,
|
|
2083
|
+
};
|
|
2084
|
+
return {
|
|
2085
|
+
runtimeOp: idAllocationMessage,
|
|
2086
|
+
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2087
|
+
staged: false,
|
|
2088
|
+
};
|
|
2089
|
+
},
|
|
2027
2090
|
});
|
|
2028
2091
|
|
|
2029
2092
|
this._quorum = quorum;
|
|
@@ -2070,14 +2133,12 @@ export class ContainerRuntime
|
|
|
2070
2133
|
this.lastEmittedDirty = this.computeCurrentDirtyState();
|
|
2071
2134
|
context.updateDirtyContainerState(this.lastEmittedDirty);
|
|
2072
2135
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
this.deltaManager.on("op", () => this.flush());
|
|
2080
|
-
}
|
|
2136
|
+
// Reference Sequence Number may have just changed, and it must be consistent across a batch,
|
|
2137
|
+
// so we should flush now to clear the way for the next ops.
|
|
2138
|
+
// NOTE: This will be redundant whenever CR.process was called for the op (since we flush there too) -
|
|
2139
|
+
// But we need this coverage for old loaders that don't call ContainerRuntime.process for non-runtime messages.
|
|
2140
|
+
// (We have to call flush _before_ processing a runtime op, but after is ok for non-runtime op)
|
|
2141
|
+
this.deltaManager.on("op", () => this.flush());
|
|
2081
2142
|
|
|
2082
2143
|
// logging hardware telemetry
|
|
2083
2144
|
this.baseLogger.send({
|
|
@@ -2093,7 +2154,9 @@ export class ContainerRuntime
|
|
|
2093
2154
|
summaryNumber: loadSummaryNumber,
|
|
2094
2155
|
summaryFormatVersion: metadata?.summaryFormatVersion,
|
|
2095
2156
|
disableIsolatedChannels: metadata?.disableIsolatedChannels,
|
|
2157
|
+
// This is useful even for interactive clients since they track unreferenced nodes and log errors.
|
|
2096
2158
|
gcVersion: metadata?.gcFeature,
|
|
2159
|
+
gcConfigs: this.garbageCollector.serializedConfigs,
|
|
2097
2160
|
options: JSON.stringify(runtimeOptions),
|
|
2098
2161
|
idCompressorModeMetadata: metadata?.documentSchema?.runtime?.idCompressorMode,
|
|
2099
2162
|
idCompressorMode: this.sessionSchema.idCompressorMode,
|
|
@@ -2101,7 +2164,6 @@ export class ContainerRuntime
|
|
|
2101
2164
|
featureGates: JSON.stringify({
|
|
2102
2165
|
...featureGatesForTelemetry,
|
|
2103
2166
|
closeSummarizerDelayOverride,
|
|
2104
|
-
disableFlushBeforeProcess: this.skipSafetyFlushDuringProcessStack,
|
|
2105
2167
|
}),
|
|
2106
2168
|
telemetryDocumentId: this.telemetryDocumentId,
|
|
2107
2169
|
groupedBatchingEnabled: this.groupedBatchingEnabled,
|
|
@@ -2723,12 +2785,10 @@ export class ContainerRuntime
|
|
|
2723
2785
|
this.emitDirtyDocumentEvent = false;
|
|
2724
2786
|
|
|
2725
2787
|
try {
|
|
2726
|
-
// Any ID Allocation ops that failed to submit
|
|
2727
|
-
// the
|
|
2728
|
-
//
|
|
2729
|
-
|
|
2730
|
-
this.submitIdAllocationOpIfNeeded({ resubmitOutstandingRanges: true, staged: false });
|
|
2731
|
-
this.scheduleFlush();
|
|
2788
|
+
// Any ID Allocation ops that failed to submit need to have their ranges included
|
|
2789
|
+
// in the next allocation op. Reset the compressor's unfinalized range cursor so that the next
|
|
2790
|
+
// call to takeNextCreationRange (during replay) will include those unfinalized ranges.
|
|
2791
|
+
this._idCompressor?.resetUnfinalizedCreationRange();
|
|
2732
2792
|
|
|
2733
2793
|
// replay the ops
|
|
2734
2794
|
this.pendingStateManager.replayPendingStates();
|
|
@@ -3023,10 +3083,8 @@ export class ContainerRuntime
|
|
|
3023
3083
|
|
|
3024
3084
|
this.verifyNotClosed();
|
|
3025
3085
|
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
this.flush();
|
|
3029
|
-
}
|
|
3086
|
+
// Reference Sequence Number may be about to change, and it must be consistent across a batch, so flush now
|
|
3087
|
+
this.flush();
|
|
3030
3088
|
|
|
3031
3089
|
this.ensureNoDataModelChanges(() => {
|
|
3032
3090
|
this.processInboundMessageOrBatch(messageCopy, local);
|
|
@@ -3630,20 +3688,36 @@ export class ContainerRuntime
|
|
|
3630
3688
|
// since we mark whole batches as "staged" or not to indicate whether to submit them.
|
|
3631
3689
|
this.flush();
|
|
3632
3690
|
|
|
3633
|
-
const exitStagingMode = (
|
|
3691
|
+
const exitStagingMode = (
|
|
3692
|
+
discardOrCommit: () => IPendingMessage["batchInfo"][],
|
|
3693
|
+
exitMethod: "commit" | "discard",
|
|
3694
|
+
): void => {
|
|
3634
3695
|
try {
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3696
|
+
PerformanceEvent.timedExec(
|
|
3697
|
+
this.mc.logger,
|
|
3698
|
+
{
|
|
3699
|
+
eventName: `ExitStagingMode_${exitMethod}`,
|
|
3700
|
+
},
|
|
3701
|
+
(event) => {
|
|
3702
|
+
// Final flush of any last staged changes
|
|
3703
|
+
// NOTE: We can't use this.flush() here, because orderSequentially uses StagingMode and in the rollback case we'll hit assert 0x24c
|
|
3704
|
+
this.outbox.flush();
|
|
3640
3705
|
|
|
3641
|
-
|
|
3642
|
-
// Now that we've exited, we need to submit an ID Allocation op for any IDs that were generated while in Staging Mode.
|
|
3643
|
-
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
3644
|
-
discardOrCommit();
|
|
3706
|
+
this.stageControls = undefined;
|
|
3645
3707
|
|
|
3646
|
-
|
|
3708
|
+
const batchInfos = discardOrCommit();
|
|
3709
|
+
event.reportProgress({
|
|
3710
|
+
details: {
|
|
3711
|
+
autoFlushThreshold: this.stagingModeAutoFlushThreshold,
|
|
3712
|
+
batches: batchInfos.length,
|
|
3713
|
+
batchesAtOrOverThreshold: batchInfos.filter(
|
|
3714
|
+
(b) => b.length >= this.stagingModeAutoFlushThreshold,
|
|
3715
|
+
).length,
|
|
3716
|
+
},
|
|
3717
|
+
});
|
|
3718
|
+
this.channelCollection.notifyStagingMode(false);
|
|
3719
|
+
},
|
|
3720
|
+
);
|
|
3647
3721
|
} catch (error) {
|
|
3648
3722
|
const normalizedError = normalizeError(error);
|
|
3649
3723
|
this.closeFn(normalizedError);
|
|
@@ -3655,21 +3729,24 @@ export class ContainerRuntime
|
|
|
3655
3729
|
discardChanges: () =>
|
|
3656
3730
|
exitStagingMode(() => {
|
|
3657
3731
|
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
3658
|
-
this.pendingStateManager.popStagedBatches(
|
|
3659
|
-
|
|
3660
|
-
|
|
3732
|
+
const batchInfos = this.pendingStateManager.popStagedBatches(
|
|
3733
|
+
({ runtimeOp, localOpMetadata }) => {
|
|
3734
|
+
this.rollbackStagedChange(runtimeOp, localOpMetadata);
|
|
3735
|
+
},
|
|
3736
|
+
);
|
|
3661
3737
|
this.updateDocumentDirtyState();
|
|
3662
|
-
|
|
3738
|
+
return batchInfos;
|
|
3739
|
+
}, "discard"),
|
|
3663
3740
|
commitChanges: (options) => {
|
|
3664
3741
|
const { squash } = { ...defaultStagingCommitOptions, ...options };
|
|
3665
3742
|
exitStagingMode(() => {
|
|
3666
3743
|
// Replay all staged batches in typical FIFO order.
|
|
3667
3744
|
// We'll be out of staging mode so they'll be sent to the service finally.
|
|
3668
|
-
this.pendingStateManager.replayPendingStates({
|
|
3745
|
+
return this.pendingStateManager.replayPendingStates({
|
|
3669
3746
|
committingStagedBatches: true,
|
|
3670
3747
|
squash,
|
|
3671
3748
|
});
|
|
3672
|
-
});
|
|
3749
|
+
}, "commit");
|
|
3673
3750
|
},
|
|
3674
3751
|
};
|
|
3675
3752
|
|
|
@@ -4210,7 +4287,6 @@ export class ContainerRuntime
|
|
|
4210
4287
|
outboxLength: this.outbox.messageCount,
|
|
4211
4288
|
mainBatchLength: this.outbox.mainBatchMessageCount,
|
|
4212
4289
|
blobAttachBatchLength: this.outbox.blobAttachBatchMessageCount,
|
|
4213
|
-
idAllocationBatchLength: this.outbox.idAllocationBatchMessageCount,
|
|
4214
4290
|
},
|
|
4215
4291
|
);
|
|
4216
4292
|
}
|
|
@@ -4543,7 +4619,8 @@ export class ContainerRuntime
|
|
|
4543
4619
|
|
|
4544
4620
|
/**
|
|
4545
4621
|
* This helper is called during summarization. If the container is dirty, it will return a failed summarize result
|
|
4546
|
-
* (IBaseSummarizeResult) unless this is the final summarize attempt
|
|
4622
|
+
* (IBaseSummarizeResult) unless this is the final summarize attempt, in which case the summary is allowed to
|
|
4623
|
+
* proceed to make progress in documents where there are consistently pending ops in the summarizer.
|
|
4547
4624
|
* @param logger - The logger to be used for sending telemetry.
|
|
4548
4625
|
* @param referenceSequenceNumber - The reference sequence number of the summary attempt.
|
|
4549
4626
|
* @param minimumSequenceNumber - The minimum sequence number of the summary attempt.
|
|
@@ -4562,13 +4639,9 @@ export class ContainerRuntime
|
|
|
4562
4639
|
return;
|
|
4563
4640
|
}
|
|
4564
4641
|
|
|
4565
|
-
//
|
|
4566
|
-
//
|
|
4567
|
-
|
|
4568
|
-
if (
|
|
4569
|
-
finalAttempt &&
|
|
4570
|
-
this.mc.config.getBoolean("Fluid.Summarizer.SkipFailingIncorrectSummary") === true
|
|
4571
|
-
) {
|
|
4642
|
+
// Don't fail the summary in the last attempt. This is a fallback to make progress in
|
|
4643
|
+
// documents where there are consistently pending ops in the summarizer.
|
|
4644
|
+
if (finalAttempt) {
|
|
4572
4645
|
const error = DataProcessingError.create(
|
|
4573
4646
|
"Pending ops during summarization",
|
|
4574
4647
|
"submitSummary",
|
|
@@ -4577,7 +4650,7 @@ export class ContainerRuntime
|
|
|
4577
4650
|
);
|
|
4578
4651
|
logger.sendErrorEvent(
|
|
4579
4652
|
{
|
|
4580
|
-
eventName: "
|
|
4653
|
+
eventName: "PendingOpsDuringSummaryFinalAttempt",
|
|
4581
4654
|
referenceSequenceNumber,
|
|
4582
4655
|
minimumSequenceNumber,
|
|
4583
4656
|
beforeGenerate: beforeSummaryGeneration,
|
|
@@ -4671,33 +4744,6 @@ export class ContainerRuntime
|
|
|
4671
4744
|
return this.blobManager.lookupTemporaryBlobStorageId(localId);
|
|
4672
4745
|
}
|
|
4673
4746
|
|
|
4674
|
-
private submitIdAllocationOpIfNeeded({
|
|
4675
|
-
resubmitOutstandingRanges = false,
|
|
4676
|
-
staged,
|
|
4677
|
-
}: {
|
|
4678
|
-
resubmitOutstandingRanges?: boolean;
|
|
4679
|
-
staged: boolean;
|
|
4680
|
-
}): void {
|
|
4681
|
-
if (this._idCompressor) {
|
|
4682
|
-
const idRange = resubmitOutstandingRanges
|
|
4683
|
-
? this._idCompressor.takeUnfinalizedCreationRange()
|
|
4684
|
-
: this._idCompressor.takeNextCreationRange();
|
|
4685
|
-
// Don't include the idRange if there weren't any Ids allocated
|
|
4686
|
-
if (idRange.ids !== undefined) {
|
|
4687
|
-
const idAllocationMessage: ContainerRuntimeIdAllocationMessage = {
|
|
4688
|
-
type: ContainerMessageType.IdAllocation,
|
|
4689
|
-
contents: idRange,
|
|
4690
|
-
};
|
|
4691
|
-
const idAllocationBatchMessage: LocalBatchMessage = {
|
|
4692
|
-
runtimeOp: idAllocationMessage,
|
|
4693
|
-
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
4694
|
-
staged,
|
|
4695
|
-
};
|
|
4696
|
-
this.outbox.submitIdAllocation(idAllocationBatchMessage);
|
|
4697
|
-
}
|
|
4698
|
-
}
|
|
4699
|
-
}
|
|
4700
|
-
|
|
4701
4747
|
private submit(
|
|
4702
4748
|
containerRuntimeMessage: LocalContainerRuntimeMessage,
|
|
4703
4749
|
localOpMetadata: unknown = undefined,
|
|
@@ -4741,11 +4787,6 @@ export class ContainerRuntime
|
|
|
4741
4787
|
0xbba /* Unexpected message type submitted in Staging Mode */,
|
|
4742
4788
|
);
|
|
4743
4789
|
|
|
4744
|
-
// Before submitting any non-staged change, submit the ID Allocation op to cover any compressed IDs included in the op.
|
|
4745
|
-
if (!staged) {
|
|
4746
|
-
this.submitIdAllocationOpIfNeeded({ staged: false });
|
|
4747
|
-
}
|
|
4748
|
-
|
|
4749
4790
|
// Allow document schema controller to send a message if it needs to propose change in document schema.
|
|
4750
4791
|
// If it needs to send a message, it will call provided callback with payload of such message and rely
|
|
4751
4792
|
// on this callback to do actual sending.
|
|
@@ -4800,6 +4841,20 @@ export class ContainerRuntime
|
|
|
4800
4841
|
}
|
|
4801
4842
|
|
|
4802
4843
|
private scheduleFlush(): void {
|
|
4844
|
+
// During staging mode, suppress automatic flush scheduling until the main batch
|
|
4845
|
+
// reaches or exceeds the threshold.
|
|
4846
|
+
// Incoming ops still break the batch via direct this.flush() calls elsewhere
|
|
4847
|
+
// (deltaManager "op" handler, process(), connection changes, getPendingLocalState,
|
|
4848
|
+
// exitStagingMode). Those all bypass scheduleFlush(), so they're unaffected by this check.
|
|
4849
|
+
// Additionally, outbox.outboxSequenceNumberCoherencyCheck() (called on every submit) detects
|
|
4850
|
+
// sequence number changes and throws if unexpected changes are detected.
|
|
4851
|
+
if (
|
|
4852
|
+
this.inStagingMode &&
|
|
4853
|
+
this.outbox.mainBatchMessageCount < this.stagingModeAutoFlushThreshold
|
|
4854
|
+
) {
|
|
4855
|
+
return;
|
|
4856
|
+
}
|
|
4857
|
+
|
|
4803
4858
|
if (this.flushScheduled) {
|
|
4804
4859
|
return;
|
|
4805
4860
|
}
|
|
@@ -5195,7 +5250,9 @@ export class ContainerRuntime
|
|
|
5195
5250
|
eventName: "getPendingLocalState",
|
|
5196
5251
|
},
|
|
5197
5252
|
(event) => {
|
|
5198
|
-
const pending = this.pendingStateManager.getLocalState(
|
|
5253
|
+
const { pending } = this.pendingStateManager.getLocalState(
|
|
5254
|
+
props?.snapshotSequenceNumber,
|
|
5255
|
+
);
|
|
5199
5256
|
const sessionExpiryTimerStarted =
|
|
5200
5257
|
props?.sessionExpiryTimerStarted ?? this.garbageCollector.sessionExpiryTimerStarted;
|
|
5201
5258
|
|
|
@@ -100,6 +100,10 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
100
100
|
|
|
101
101
|
private readonly configs: IGarbageCollectorConfigs;
|
|
102
102
|
|
|
103
|
+
public get serializedConfigs(): string {
|
|
104
|
+
return JSON.stringify(this.configs);
|
|
105
|
+
}
|
|
106
|
+
|
|
103
107
|
public get shouldRunGC(): boolean {
|
|
104
108
|
return this.configs.gcAllowed;
|
|
105
109
|
}
|
|
@@ -334,15 +338,6 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
334
338
|
|
|
335
339
|
return { gcData: { gcNodes }, usedRoutes };
|
|
336
340
|
});
|
|
337
|
-
|
|
338
|
-
// Log all the GC options and the state determined by the garbage collector.
|
|
339
|
-
// This is useful even for interactive clients since they track unreferenced nodes and log errors.
|
|
340
|
-
this.mc.logger.sendTelemetryEvent({
|
|
341
|
-
eventName: "GarbageCollectorLoaded",
|
|
342
|
-
gcConfigs: JSON.stringify(this.configs),
|
|
343
|
-
gcOptions: JSON.stringify(createParams.gcOptions),
|
|
344
|
-
...createParams.createContainerMetadata,
|
|
345
|
-
});
|
|
346
341
|
}
|
|
347
342
|
|
|
348
343
|
/**
|
package/src/gc/gcDefinitions.ts
CHANGED
|
@@ -373,6 +373,10 @@ export interface IGarbageCollectionRuntime {
|
|
|
373
373
|
* Defines the contract for the garbage collector.
|
|
374
374
|
*/
|
|
375
375
|
export interface IGarbageCollector {
|
|
376
|
+
/**
|
|
377
|
+
* The GC configurations serialized as a JSON string for telemetry.
|
|
378
|
+
*/
|
|
379
|
+
readonly serializedConfigs: string;
|
|
376
380
|
/**
|
|
377
381
|
* Tells the time at which session expiry timer started in a previous container.
|
|
378
382
|
* This is only set when loading from a stashed container and will be equal to the
|
package/src/index.ts
CHANGED
|
@@ -20,17 +20,8 @@ import { serializeOp } from "./opSerialization.js";
|
|
|
20
20
|
import type { BatchStartInfo } from "./remoteMessageProcessor.js";
|
|
21
21
|
|
|
22
22
|
export interface IBatchManagerOptions {
|
|
23
|
+
readonly disableGroupedBatching: boolean;
|
|
23
24
|
readonly compressionOptions?: ICompressionRuntimeOptions;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* If true, the outbox is allowed to rebase the batch during flushing.
|
|
27
|
-
*/
|
|
28
|
-
readonly canRebase: boolean;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* If true, don't compare batchID of incoming batches to this. e.g. ID Allocation Batch IDs should be ignored
|
|
32
|
-
*/
|
|
33
|
-
readonly ignoreBatchId?: boolean;
|
|
34
25
|
}
|
|
35
26
|
|
|
36
27
|
export interface BatchSequenceNumbers {
|
|
@@ -127,8 +118,9 @@ export class BatchManager {
|
|
|
127
118
|
|
|
128
119
|
/**
|
|
129
120
|
* Gets the pending batch and clears state for the next batch.
|
|
121
|
+
* The caller is responsible for calling {@link addBatchMetadata} after any modifications (e.g. prepending messages).
|
|
130
122
|
*/
|
|
131
|
-
public popBatch(
|
|
123
|
+
public popBatch(): LocalBatch {
|
|
132
124
|
assert(this.pendingBatch[0] !== undefined, 0xb8a /* expected non-empty batch */);
|
|
133
125
|
const batch: LocalBatch = {
|
|
134
126
|
messages: this.pendingBatch,
|
|
@@ -141,7 +133,7 @@ export class BatchManager {
|
|
|
141
133
|
this.clientSequenceNumber = undefined;
|
|
142
134
|
this.hasReentrantOps = false;
|
|
143
135
|
|
|
144
|
-
return
|
|
136
|
+
return batch;
|
|
145
137
|
}
|
|
146
138
|
|
|
147
139
|
/**
|
|
@@ -162,7 +154,8 @@ export class BatchManager {
|
|
|
162
154
|
throw new LoggingError("Ops generated during rollback", {
|
|
163
155
|
count,
|
|
164
156
|
...tagData(TelemetryDataTag.UserData, {
|
|
165
|
-
ops: serializeOp(this.pendingBatch.slice(startPoint).map((b) => b.runtimeOp))
|
|
157
|
+
ops: serializeOp(this.pendingBatch.slice(startPoint).map((b) => b.runtimeOp))
|
|
158
|
+
.content,
|
|
166
159
|
}),
|
|
167
160
|
});
|
|
168
161
|
}
|