@fluidframework/container-runtime 2.32.0 → 2.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/api-report/container-runtime.legacy.alpha.api.md +71 -67
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +7 -4
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +38 -12
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +4 -0
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js +24 -0
- package/dist/channelCollection.js.map +1 -1
- package/dist/compatUtils.d.ts +74 -0
- package/dist/compatUtils.d.ts.map +1 -0
- package/dist/compatUtils.js +151 -0
- package/dist/compatUtils.js.map +1 -0
- package/dist/compressionDefinitions.d.ts +39 -0
- package/dist/compressionDefinitions.d.ts.map +1 -0
- package/dist/compressionDefinitions.js +30 -0
- package/dist/compressionDefinitions.js.map +1 -0
- package/dist/containerRuntime.d.ts +78 -52
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +141 -54
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -0
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +122 -66
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/deltaManagerProxies.d.ts +55 -12
- package/dist/deltaManagerProxies.d.ts.map +1 -1
- package/dist/deltaManagerProxies.js +63 -55
- package/dist/deltaManagerProxies.js.map +1 -1
- package/dist/gc/gcDefinitions.d.ts +2 -0
- package/dist/gc/gcDefinitions.d.ts.map +1 -1
- package/dist/gc/gcDefinitions.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/legacy.d.ts +1 -0
- package/dist/opLifecycle/batchManager.d.ts +3 -15
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +5 -39
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +44 -11
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +3 -3
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +3 -2
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +4 -4
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +3 -3
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +2 -2
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/dist/opLifecycle/opGroupingManager.js +1 -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 +4 -2
- package/dist/opLifecycle/opSerialization.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +2 -2
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +25 -3
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +112 -61
- 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 +36 -7
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +83 -16
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts.map +1 -1
- package/dist/runtimeLayerCompatState.js +1 -1
- package/dist/runtimeLayerCompatState.js.map +1 -1
- package/dist/summary/documentSchema.d.ts +1 -0
- package/dist/summary/documentSchema.d.ts.map +1 -1
- package/dist/summary/documentSchema.js +2 -0
- package/dist/summary/documentSchema.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +7 -4
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +38 -12
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +4 -0
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js +24 -0
- package/lib/channelCollection.js.map +1 -1
- package/lib/compatUtils.d.ts +74 -0
- package/lib/compatUtils.d.ts.map +1 -0
- package/lib/compatUtils.js +142 -0
- package/lib/compatUtils.js.map +1 -0
- package/lib/compressionDefinitions.d.ts +39 -0
- package/lib/compressionDefinitions.d.ts.map +1 -0
- package/lib/compressionDefinitions.js +27 -0
- package/lib/compressionDefinitions.js.map +1 -0
- package/lib/containerRuntime.d.ts +78 -52
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +143 -56
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -0
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +57 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/deltaManagerProxies.d.ts +55 -12
- package/lib/deltaManagerProxies.d.ts.map +1 -1
- package/lib/deltaManagerProxies.js +63 -55
- package/lib/deltaManagerProxies.js.map +1 -1
- package/lib/gc/gcDefinitions.d.ts +2 -0
- package/lib/gc/gcDefinitions.d.ts.map +1 -1
- package/lib/gc/gcDefinitions.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/legacy.d.ts +1 -0
- package/lib/opLifecycle/batchManager.d.ts +3 -15
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +4 -37
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +44 -11
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +3 -3
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +2 -2
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +2 -2
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +1 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +2 -2
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -1
- package/lib/opLifecycle/opGroupingManager.js +1 -2
- 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 +4 -2
- package/lib/opLifecycle/opSerialization.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +1 -1
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +25 -3
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +110 -61
- 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 +36 -7
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +84 -17
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts.map +1 -1
- package/lib/runtimeLayerCompatState.js +2 -2
- package/lib/runtimeLayerCompatState.js.map +1 -1
- package/lib/summary/documentSchema.d.ts +1 -0
- package/lib/summary/documentSchema.d.ts.map +1 -1
- package/lib/summary/documentSchema.js +2 -0
- package/lib/summary/documentSchema.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/package.json +21 -20
- package/src/blobManager/blobManager.ts +48 -15
- package/src/channelCollection.ts +27 -0
- package/src/compatUtils.ts +211 -0
- package/src/compressionDefinitions.ts +47 -0
- package/src/containerRuntime.ts +259 -108
- package/src/dataStoreContext.ts +82 -2
- package/src/deltaManagerProxies.ts +132 -70
- package/src/gc/gcDefinitions.ts +2 -0
- package/src/index.ts +5 -3
- package/src/opLifecycle/batchManager.ts +7 -52
- package/src/opLifecycle/definitions.ts +45 -11
- package/src/opLifecycle/index.ts +7 -2
- package/src/opLifecycle/opCompressor.ts +2 -2
- package/src/opLifecycle/opDecompressor.ts +1 -1
- package/src/opLifecycle/opGroupingManager.ts +7 -5
- package/src/opLifecycle/opSerialization.ts +6 -2
- package/src/opLifecycle/opSplitter.ts +1 -1
- package/src/opLifecycle/outbox.ts +154 -85
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +135 -21
- package/src/runtimeLayerCompatState.ts +5 -2
- package/src/summary/documentSchema.ts +3 -0
package/lib/containerRuntime.js
CHANGED
|
@@ -10,7 +10,7 @@ import { SummaryType } from "@fluidframework/driver-definitions";
|
|
|
10
10
|
import { FetchSource, MessageType } from "@fluidframework/driver-definitions/internal";
|
|
11
11
|
import { readAndParse } from "@fluidframework/driver-utils/internal";
|
|
12
12
|
import { FlushMode, FlushModeExperimental, channelsTreeName, gcTreeKey, } from "@fluidframework/runtime-definitions/internal";
|
|
13
|
-
import { GCDataBuilder, RequestParser, TelemetryContext, addBlobToSummary, addSummarizeResultToSummary, calculateStats, create404Response, exceptionToResponse, seqFromTree, } from "@fluidframework/runtime-utils/internal";
|
|
13
|
+
import { GCDataBuilder, RequestParser, RuntimeHeaders, TelemetryContext, addBlobToSummary, addSummarizeResultToSummary, calculateStats, create404Response, exceptionToResponse, seqFromTree, } from "@fluidframework/runtime-utils/internal";
|
|
14
14
|
import { DataCorruptionError, DataProcessingError, extractSafePropertiesFromMessage, GenericError, LoggingError, PerformanceEvent,
|
|
15
15
|
// eslint-disable-next-line import/no-deprecated
|
|
16
16
|
TaggedLoggerAdapter, UsageError, createChildLogger, createChildMonitoringContext, createSampledLogger, loggerToMonitoringContext, raiseConnectedEvent, wrapError, tagCodeArtifacts, normalizeError, } from "@fluidframework/telemetry-utils/internal";
|
|
@@ -18,22 +18,24 @@ import { v4 as uuid } from "uuid";
|
|
|
18
18
|
import { BindBatchTracker } from "./batchTracker.js";
|
|
19
19
|
import { BlobManager, blobManagerBasePath, blobsTreeName, isBlobPath, loadBlobManagerLoadInfo, } from "./blobManager/index.js";
|
|
20
20
|
import { ChannelCollection, getSummaryForDatastores, wrapContext, } from "./channelCollection.js";
|
|
21
|
+
import { defaultCompatibilityVersion, getCompatibilityVersionDefaults, isValidCompatVersion, } from "./compatUtils.js";
|
|
22
|
+
import { CompressionAlgorithms, disabledCompressionConfig } from "./compressionDefinitions.js";
|
|
21
23
|
import { ReportOpPerfTelemetry } from "./connectionTelemetry.js";
|
|
22
24
|
import { ContainerFluidHandleContext } from "./containerHandleContext.js";
|
|
23
25
|
import { channelToDataStore } from "./dataStore.js";
|
|
24
26
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry.js";
|
|
25
|
-
import { DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy, } from "./deltaManagerProxies.js";
|
|
27
|
+
import { BaseDeltaManagerProxy, DeltaManagerPendingOpsProxy, DeltaManagerSummarizerProxy, } from "./deltaManagerProxies.js";
|
|
26
28
|
import { DeltaScheduler } from "./deltaScheduler.js";
|
|
27
29
|
import { GCNodeType, GarbageCollector, gcGenerationOptionName, } from "./gc/index.js";
|
|
28
30
|
import { InboundBatchAggregator } from "./inboundBatchAggregator.js";
|
|
29
31
|
import { ContainerMessageType, } from "./messageTypes.js";
|
|
30
|
-
import { DuplicateBatchDetector, ensureContentsDeserialized, OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor,
|
|
32
|
+
import { DuplicateBatchDetector, ensureContentsDeserialized, OpCompressor, OpDecompressor, OpGroupingManager, OpSplitter, Outbox, RemoteMessageProcessor, } from "./opLifecycle/index.js";
|
|
31
33
|
import { pkgVersion } from "./packageVersion.js";
|
|
32
34
|
import { PendingStateManager, } from "./pendingStateManager.js";
|
|
33
35
|
import { RunCounter } from "./runCounter.js";
|
|
34
36
|
import { runtimeCompatDetailsForLoader, validateLoaderCompatibility, } from "./runtimeLayerCompatState.js";
|
|
35
37
|
import { SignalTelemetryManager } from "./signalTelemetryProcessing.js";
|
|
36
|
-
import { DocumentsSchemaController, OrderedClientElection, RetriableSummaryError, aliasBlobName, chunksBlobName, recentBatchInfoBlobName, createRootSummarizerNodeWithGC, electedSummarizerBlobName, extractSummaryMetadataMessage, idCompressorBlobName, metadataBlobName, rootHasIsolatedChannels,
|
|
38
|
+
import { DocumentsSchemaController, OrderedClientElection, RetriableSummaryError, aliasBlobName, chunksBlobName, recentBatchInfoBlobName, createRootSummarizerNodeWithGC, electedSummarizerBlobName, extractSummaryMetadataMessage, idCompressorBlobName, metadataBlobName, rootHasIsolatedChannels, wrapSummaryInChannelsTree, formCreateSummarizerFn, summarizerRequestUrl, SummaryManager, SummarizerClientElection, SummaryCollection, OrderedClientCollection, validateSummaryHeuristicConfiguration, DefaultSummaryConfiguration, isSummariesDisabled, summarizerClientType, } from "./summary/index.js";
|
|
37
39
|
import { Throttler, formExponentialFn } from "./throttler.js";
|
|
38
40
|
/**
|
|
39
41
|
* Creates an error object to be thrown / passed to Container's close fn in case of an unknown message type.
|
|
@@ -82,35 +84,12 @@ export const defaultRuntimeHeaderData = {
|
|
|
82
84
|
viaHandle: false,
|
|
83
85
|
allowTombstone: false,
|
|
84
86
|
};
|
|
85
|
-
/**
|
|
86
|
-
* Available compression algorithms for op compression.
|
|
87
|
-
* @legacy
|
|
88
|
-
* @alpha
|
|
89
|
-
*/
|
|
90
|
-
export var CompressionAlgorithms;
|
|
91
|
-
(function (CompressionAlgorithms) {
|
|
92
|
-
CompressionAlgorithms["lz4"] = "lz4";
|
|
93
|
-
})(CompressionAlgorithms || (CompressionAlgorithms = {}));
|
|
94
|
-
/**
|
|
95
|
-
* @legacy
|
|
96
|
-
* @alpha
|
|
97
|
-
*/
|
|
98
|
-
export const disabledCompressionConfig = {
|
|
99
|
-
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
100
|
-
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
101
|
-
};
|
|
102
87
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
103
|
-
const defaultFlushMode = FlushMode.TurnBased;
|
|
104
88
|
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
105
89
|
// We can't estimate it fully, as we
|
|
106
90
|
// - do not know what properties relay service will add
|
|
107
91
|
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
108
92
|
const defaultMaxBatchSizeInBytes = 700 * 1024;
|
|
109
|
-
const defaultCompressionConfig = {
|
|
110
|
-
// Batches with content size exceeding this value will be compressed
|
|
111
|
-
minimumBatchSizeInBytes: 614400,
|
|
112
|
-
compressionAlgorithm: CompressionAlgorithms.lz4,
|
|
113
|
-
};
|
|
114
93
|
const defaultChunkSizeInBytes = 204800;
|
|
115
94
|
/**
|
|
116
95
|
* The default time to wait for pending ops to be processed during summarization
|
|
@@ -249,9 +228,42 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
249
228
|
},
|
|
250
229
|
});
|
|
251
230
|
const mc = loggerToMonitoringContext(logger);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
231
|
+
// Some options require a minimum version of the FF runtime to operate, so the default configs will be generated
|
|
232
|
+
// based on the compatibility mode.
|
|
233
|
+
// For example, if compatibility mode is set to "1.0.0", the default configs will ensure compatibility with FF runtime
|
|
234
|
+
// 1.0.0 or later. If the compatibility mode is set to "2.10.0", the default values will be generated to ensure compatibility
|
|
235
|
+
// with FF runtime 2.10.0 or later.
|
|
236
|
+
// TODO: We will add in a way for users to pass in compatibilityVersion in a follow up PR.
|
|
237
|
+
const compatibilityVersion = defaultCompatibilityVersion;
|
|
238
|
+
if (!isValidCompatVersion(compatibilityVersion)) {
|
|
239
|
+
throw new UsageError(`Invalid compatibility version: ${compatibilityVersion}. It must be an existing FF version (i.e. 2.22.1).`);
|
|
240
|
+
}
|
|
241
|
+
const defaultVersionDependentConfigs = getCompatibilityVersionDefaults(compatibilityVersion);
|
|
242
|
+
// The following are the default values for the options that do not affect the DocumentSchema.
|
|
243
|
+
const defaultConfigsNonVersionDependent = {
|
|
244
|
+
summaryOptions: {},
|
|
245
|
+
loadSequenceNumberVerification: "close",
|
|
246
|
+
maxBatchSizeInBytes: defaultMaxBatchSizeInBytes,
|
|
247
|
+
chunkSizeInBytes: defaultChunkSizeInBytes,
|
|
248
|
+
};
|
|
249
|
+
const defaultConfigs = {
|
|
250
|
+
...defaultVersionDependentConfigs,
|
|
251
|
+
...defaultConfigsNonVersionDependent,
|
|
252
|
+
};
|
|
253
|
+
// Here we set each option to its corresponding default config value if it's not provided in runtimeOptions.
|
|
254
|
+
// Note: We cannot do a simple object merge of defaultConfigs/runtimeOptions because in most cases we don't want
|
|
255
|
+
// a option that is undefined in runtimeOptions to override the default value (except for idCompressor, see below).
|
|
256
|
+
const { summaryOptions = defaultConfigs.summaryOptions, gcOptions = defaultConfigs.gcOptions, loadSequenceNumberVerification = defaultConfigs.loadSequenceNumberVerification, maxBatchSizeInBytes = defaultConfigs.maxBatchSizeInBytes, chunkSizeInBytes = defaultConfigs.chunkSizeInBytes, explicitSchemaControl = defaultConfigs.explicitSchemaControl, enableGroupedBatching = defaultConfigs.enableGroupedBatching, flushMode = defaultConfigs.flushMode,
|
|
257
|
+
// If batching is disabled then we should disable compression as well. If batching is disabled and compression
|
|
258
|
+
// is enabled via runtimeOptions, we will throw an error later.
|
|
259
|
+
compressionOptions = enableGroupedBatching === false
|
|
260
|
+
? disabledCompressionConfig
|
|
261
|
+
: defaultConfigs.compressionOptions, createBlobPayloadPending = defaultConfigs.createBlobPayloadPending, } = runtimeOptions;
|
|
262
|
+
// The logic for enableRuntimeIdCompressor is a bit different. Since `undefined` represents a logical state (off)
|
|
263
|
+
// we need to check it's explicitly set in runtimeOptions. If so, we should use that value even if it's undefined.
|
|
264
|
+
const enableRuntimeIdCompressor = "enableRuntimeIdCompressor" in runtimeOptions
|
|
265
|
+
? runtimeOptions.enableRuntimeIdCompressor
|
|
266
|
+
: defaultConfigs.enableRuntimeIdCompressor;
|
|
255
267
|
const registry = new FluidDataStoreRegistry(registryEntries);
|
|
256
268
|
const tryFetchBlob = async (blobName) => {
|
|
257
269
|
const blobId = context.baseSnapshot?.blobs[blobName];
|
|
@@ -380,6 +392,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
380
392
|
compressionLz4,
|
|
381
393
|
idCompressorMode,
|
|
382
394
|
opGroupingEnabled: enableGroupedBatching,
|
|
395
|
+
createBlobPayloadPending,
|
|
383
396
|
disallowedVersions: [],
|
|
384
397
|
}, (schema) => {
|
|
385
398
|
runtime.onSchemaChange(schema);
|
|
@@ -397,10 +410,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
397
410
|
compressionOptions,
|
|
398
411
|
maxBatchSizeInBytes,
|
|
399
412
|
chunkSizeInBytes,
|
|
400
|
-
|
|
401
|
-
enableRuntimeIdCompressor: enableRuntimeIdCompressor,
|
|
413
|
+
enableRuntimeIdCompressor,
|
|
402
414
|
enableGroupedBatching,
|
|
403
415
|
explicitSchemaControl,
|
|
416
|
+
createBlobPayloadPending,
|
|
404
417
|
};
|
|
405
418
|
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks ?? [], aliases ?? [], internalRuntimeOptions, containerScope, logger, existing, blobManagerLoadInfo, context.storage, createIdCompressorFn, documentSchemaController, featureGatesForTelemetry, provideEntryPoint, requestHandler, undefined, // summaryConfiguration
|
|
406
419
|
recentBatchInfo);
|
|
@@ -550,6 +563,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
550
563
|
this.requestHandler = requestHandler;
|
|
551
564
|
this.summaryConfiguration = summaryConfiguration;
|
|
552
565
|
this.imminentClosure = false;
|
|
566
|
+
this.isReadOnly = () => this.deltaManager.readOnlyInfo.readonly === true;
|
|
553
567
|
// We accumulate Id compressor Ops while Id compressor is not loaded yet (only for "delayed" mode)
|
|
554
568
|
// Once it loads, it will process all such ops and we will stop accumulating further ops - ops will be processes as they come in.
|
|
555
569
|
this.pendingIdCompressorOps = [];
|
|
@@ -567,6 +581,51 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
567
581
|
this.snapshotCacheForLoadingGroupIds = new PromiseCache({
|
|
568
582
|
expiry: { policy: "absolute", durationMs: 60000 },
|
|
569
583
|
});
|
|
584
|
+
this.notifyReadOnlyState = (readonly) => this.channelCollection.notifyReadOnlyState(readonly);
|
|
585
|
+
/**
|
|
586
|
+
* Enter Staging Mode, such that ops submitted to the ContainerRuntime will not be sent to the ordering service.
|
|
587
|
+
* To exit Staging Mode, call either discardChanges or commitChanges on the Stage Controls returned from this method.
|
|
588
|
+
*
|
|
589
|
+
* @returns StageControlsExperimental - Controls for exiting Staging Mode.
|
|
590
|
+
*/
|
|
591
|
+
// eslint-disable-next-line import/no-deprecated
|
|
592
|
+
this.enterStagingMode = () => {
|
|
593
|
+
if (this.stageControls !== undefined) {
|
|
594
|
+
throw new Error("already in staging mode");
|
|
595
|
+
}
|
|
596
|
+
// Make sure all BatchManagers are empty before entering staging mode,
|
|
597
|
+
// since we mark whole batches as "staged" or not to indicate whether to submit them.
|
|
598
|
+
this.outbox.flush();
|
|
599
|
+
const exitStagingMode = (discardOrCommit) => () => {
|
|
600
|
+
// Final flush of any last staged changes
|
|
601
|
+
this.outbox.flush(undefined, true /* staged */);
|
|
602
|
+
this.stageControls = undefined;
|
|
603
|
+
discardOrCommit();
|
|
604
|
+
};
|
|
605
|
+
const stageControls = {
|
|
606
|
+
discardChanges: exitStagingMode(() => {
|
|
607
|
+
// Pop all staged batches from the PSM and roll them back in LIFO order
|
|
608
|
+
this.pendingStateManager.popStagedBatches(({ runtimeOp, localOpMetadata }) => {
|
|
609
|
+
assert(runtimeOp !== undefined, 0xb82 /* Staged batches expected to have runtimeOp defined */);
|
|
610
|
+
this.rollback(runtimeOp, localOpMetadata);
|
|
611
|
+
});
|
|
612
|
+
if (this.attachState === AttachState.Attached) {
|
|
613
|
+
this.updateDocumentDirtyState(this.pendingMessagesCount !== 0);
|
|
614
|
+
}
|
|
615
|
+
}),
|
|
616
|
+
commitChanges: exitStagingMode(() => {
|
|
617
|
+
// All staged changes are in the PSM, so just replay them (ignore pre-staging batches)
|
|
618
|
+
// FUTURE: Have this do squash-rebase instead of resubmitting all intermediate changes
|
|
619
|
+
if (this.connected) {
|
|
620
|
+
this.pendingStateManager.replayPendingStates(true /* onlyStagedBatched */);
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
this.pendingStateManager.clearStagingFlags();
|
|
624
|
+
}
|
|
625
|
+
}),
|
|
626
|
+
};
|
|
627
|
+
return (this.stageControls = stageControls);
|
|
628
|
+
};
|
|
570
629
|
this.readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
571
630
|
const { options, clientDetails, connected, baseSnapshot, submitFn, submitBatchFn, submitSummaryFn, submitSignalFn, disposeFn, closeFn, deltaManager, quorum, audience, pendingLocalState, supportedFeatures, snapshotWithContents, } = context;
|
|
572
631
|
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
@@ -686,19 +745,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
686
745
|
isActiveConnection: () => this.innerDeltaManager.active,
|
|
687
746
|
isAttached: () => this.attachState !== AttachState.Detached,
|
|
688
747
|
}, pendingRuntimeState?.pending, this.baseLogger);
|
|
689
|
-
let outerDeltaManager;
|
|
748
|
+
let outerDeltaManager = this.innerDeltaManager;
|
|
690
749
|
this.useDeltaManagerOpsProxy =
|
|
691
750
|
this.mc.config.getBoolean("Fluid.ContainerRuntime.DeltaManagerOpsProxy") === true;
|
|
692
751
|
// The summarizerDeltaManager Proxy is used to lie to the summarizer to convince it is in the right state as a summarizer client.
|
|
693
|
-
|
|
694
|
-
outerDeltaManager = summarizerDeltaManagerProxy;
|
|
752
|
+
outerDeltaManager = DeltaManagerSummarizerProxy.wrapIfSummarizer(outerDeltaManager);
|
|
695
753
|
// The DeltaManagerPendingOpsProxy is used to control the minimum sequence number
|
|
696
754
|
// It allows us to lie to the layers below so that they can maintain enough local state for rebasing ops.
|
|
697
755
|
if (this.useDeltaManagerOpsProxy) {
|
|
698
|
-
const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(
|
|
756
|
+
const pendingOpsDeltaManagerProxy = new DeltaManagerPendingOpsProxy(outerDeltaManager, this.pendingStateManager);
|
|
699
757
|
outerDeltaManager = pendingOpsDeltaManagerProxy;
|
|
700
758
|
}
|
|
701
|
-
|
|
759
|
+
// always wrap the exposed delta manager in at least on layer of proxying
|
|
760
|
+
this._deltaManager =
|
|
761
|
+
outerDeltaManager instanceof BaseDeltaManagerProxy
|
|
762
|
+
? outerDeltaManager
|
|
763
|
+
: new BaseDeltaManagerProxy(outerDeltaManager);
|
|
702
764
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
703
765
|
if (this.summaryConfiguration.state === "enabled") {
|
|
704
766
|
validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
@@ -809,6 +871,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
809
871
|
...props,
|
|
810
872
|
timestampMs: props.timestampMs ?? this.getCurrentReferenceTimestampMs(),
|
|
811
873
|
}), (path) => this.garbageCollector.isNodeDeleted(path), new Map(dataStoreAliasMap), async (runtime) => provideEntryPoint);
|
|
874
|
+
this._deltaManager.on("readonly", this.notifyReadOnlyState);
|
|
812
875
|
this.blobManager = new BlobManager({
|
|
813
876
|
routeContext: this.handleContext,
|
|
814
877
|
blobManagerLoadInfo,
|
|
@@ -829,6 +892,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
829
892
|
isBlobDeleted: (blobPath) => this.garbageCollector.isNodeDeleted(blobPath),
|
|
830
893
|
runtime: this,
|
|
831
894
|
stashedBlobs: pendingRuntimeState?.pendingAttachmentBlobs,
|
|
895
|
+
createBlobPayloadPending: this.sessionSchema.createBlobPayloadPending === true,
|
|
832
896
|
});
|
|
833
897
|
this.deltaScheduler = new DeltaScheduler(this.innerDeltaManager, this, createChildLogger({ logger: this.baseLogger, namespace: "DeltaScheduler" }));
|
|
834
898
|
this.inboundBatchAggregator = new InboundBatchAggregator(this.innerDeltaManager, () => this.clientId, createChildLogger({ logger: this.baseLogger, namespace: "InboundBatchAggregator" }));
|
|
@@ -1080,6 +1144,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1080
1144
|
this.pendingStateManager.dispose();
|
|
1081
1145
|
this.inboundBatchAggregator.dispose();
|
|
1082
1146
|
this.deltaScheduler.dispose();
|
|
1147
|
+
this._deltaManager.dispose();
|
|
1083
1148
|
this.emit("dispose");
|
|
1084
1149
|
this.removeAllListeners();
|
|
1085
1150
|
}
|
|
@@ -1225,7 +1290,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1225
1290
|
return this.resolveHandle(requestParser.createSubRequest(1));
|
|
1226
1291
|
}
|
|
1227
1292
|
if (id === blobManagerBasePath && requestParser.isLeaf(2)) {
|
|
1228
|
-
const
|
|
1293
|
+
const localId = requestParser.pathParts[1];
|
|
1294
|
+
const payloadPending = requestParser.headers?.[RuntimeHeaders.payloadPending] === true;
|
|
1295
|
+
if (!this.blobManager.hasBlob(localId) &&
|
|
1296
|
+
requestParser.headers?.[RuntimeHeaders.wait] === false) {
|
|
1297
|
+
return create404Response(request);
|
|
1298
|
+
}
|
|
1299
|
+
const blob = await this.blobManager.getBlob(localId, payloadPending);
|
|
1229
1300
|
return {
|
|
1230
1301
|
status: 200,
|
|
1231
1302
|
mimeType: "fluid/object",
|
|
@@ -1865,11 +1936,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1865
1936
|
*
|
|
1866
1937
|
* @param resubmittingBatchId - If defined, indicates this is a resubmission of a batch
|
|
1867
1938
|
* with the given Batch ID, which must be preserved
|
|
1939
|
+
* @param resubmittingStagedBatch - If defined, indicates this is a resubmission of a batch that is staged,
|
|
1940
|
+
* meaning it should not be sent to the ordering service yet.
|
|
1868
1941
|
*/
|
|
1869
|
-
flush(resubmittingBatchId) {
|
|
1942
|
+
flush(resubmittingBatchId, resubmittingStagedBatch) {
|
|
1870
1943
|
try {
|
|
1871
1944
|
assert(!this.batchRunner.running, 0x24c /* "Cannot call `flush()` while manually accumulating a batch (e.g. under orderSequentially) */);
|
|
1872
|
-
this.outbox.flush(resubmittingBatchId);
|
|
1945
|
+
this.outbox.flush(resubmittingBatchId, resubmittingStagedBatch);
|
|
1873
1946
|
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1874
1947
|
}
|
|
1875
1948
|
catch (error) {
|
|
@@ -1888,7 +1961,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1888
1961
|
orderSequentially(callback) {
|
|
1889
1962
|
let checkpoint;
|
|
1890
1963
|
const checkpointDirtyState = this.dirtyContainer;
|
|
1964
|
+
// eslint-disable-next-line import/no-deprecated
|
|
1965
|
+
let stageControls;
|
|
1891
1966
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1967
|
+
if (!this.batchRunner.running && !this.inStagingMode) {
|
|
1968
|
+
stageControls = this.enterStagingMode();
|
|
1969
|
+
}
|
|
1892
1970
|
// Note: we are not touching any batches other than mainBatch here, for two reasons:
|
|
1893
1971
|
// 1. It would not help, as other batches are flushed independently from main batch.
|
|
1894
1972
|
// 2. There is no way to undo process of data store creation, blob creation, ID compressor ops, or other things tracked by other batches.
|
|
@@ -1902,11 +1980,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1902
1980
|
if (checkpoint) {
|
|
1903
1981
|
// This will throw and close the container if rollback fails
|
|
1904
1982
|
try {
|
|
1905
|
-
checkpoint.rollback((message) => this.rollback(message.
|
|
1983
|
+
checkpoint.rollback((message) => this.rollback(message.runtimeOp, message.localOpMetadata));
|
|
1906
1984
|
// reset the dirty state after rollback to what it was before to keep it consistent
|
|
1907
1985
|
if (this.dirtyContainer !== checkpointDirtyState) {
|
|
1908
1986
|
this.updateDocumentDirtyState(checkpointDirtyState);
|
|
1909
1987
|
}
|
|
1988
|
+
stageControls?.discardChanges();
|
|
1989
|
+
stageControls = undefined;
|
|
1910
1990
|
}
|
|
1911
1991
|
catch (error_) {
|
|
1912
1992
|
const error2 = wrapError(error_, (message) => {
|
|
@@ -1924,12 +2004,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1924
2004
|
throw error; // throw the original error for the consumer of the runtime
|
|
1925
2005
|
}
|
|
1926
2006
|
});
|
|
2007
|
+
stageControls?.commitChanges();
|
|
1927
2008
|
// We don't flush on TurnBased since we expect all messages in the same JS turn to be part of the same batch
|
|
1928
2009
|
if (this.flushMode !== FlushMode.TurnBased && !this.batchRunner.running) {
|
|
1929
2010
|
this.flush();
|
|
1930
2011
|
}
|
|
1931
2012
|
return result;
|
|
1932
2013
|
}
|
|
2014
|
+
/**
|
|
2015
|
+
* If true, the ContainerRuntime is not submitting any new ops to the ordering service.
|
|
2016
|
+
* Ops submitted to the ContainerRuntime while in Staging Mode will be queued in the PendingStateManager,
|
|
2017
|
+
* either to be discarded or committed later (via the Stage Controls returned from enterStagingMode).
|
|
2018
|
+
*/
|
|
2019
|
+
get inStagingMode() {
|
|
2020
|
+
return this.stageControls !== undefined;
|
|
2021
|
+
}
|
|
1933
2022
|
/**
|
|
1934
2023
|
* Returns the aliased data store's entryPoint, given the alias.
|
|
1935
2024
|
* @param alias - The alias for the data store.
|
|
@@ -2662,8 +2751,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2662
2751
|
contents: idRange,
|
|
2663
2752
|
};
|
|
2664
2753
|
const idAllocationBatchMessage = {
|
|
2665
|
-
|
|
2754
|
+
runtimeOp: idAllocationMessage,
|
|
2666
2755
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2756
|
+
// Note: For now, we will never stage ID Allocation messages.
|
|
2757
|
+
// They won't contain personal info and no harm in extra allocations in case of discarding the staged changes
|
|
2758
|
+
staged: false,
|
|
2667
2759
|
};
|
|
2668
2760
|
this.outbox.submitIdAllocation(idAllocationBatchMessage);
|
|
2669
2761
|
}
|
|
@@ -2705,17 +2797,19 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2705
2797
|
contents: schemaChangeMessage,
|
|
2706
2798
|
};
|
|
2707
2799
|
this.outbox.submit({
|
|
2708
|
-
|
|
2800
|
+
runtimeOp: msg,
|
|
2709
2801
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2802
|
+
staged: this.inStagingMode,
|
|
2710
2803
|
});
|
|
2711
2804
|
}
|
|
2712
2805
|
const message = {
|
|
2713
2806
|
// This will encode any handles present in this op before serializing to string
|
|
2714
2807
|
// Note: handles may already have been encoded by the DDS layer, but encoding handles is idempotent so there's no problem.
|
|
2715
|
-
|
|
2808
|
+
runtimeOp: containerRuntimeMessage,
|
|
2716
2809
|
metadata,
|
|
2717
2810
|
localOpMetadata,
|
|
2718
2811
|
referenceSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2812
|
+
staged: this.inStagingMode,
|
|
2719
2813
|
};
|
|
2720
2814
|
if (type === ContainerMessageType.BlobAttach) {
|
|
2721
2815
|
// BlobAttach ops must have their metadata visible and cannot be grouped (see opGroupingManager.ts)
|
|
@@ -2799,7 +2893,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2799
2893
|
* @remarks - If the "Offline Load" feature is enabled, the batchId is included in the resubmitted messages,
|
|
2800
2894
|
* for correlation to detect container forking.
|
|
2801
2895
|
*/
|
|
2802
|
-
reSubmitBatch(batch, batchId) {
|
|
2896
|
+
reSubmitBatch(batch, batchId, staged) {
|
|
2803
2897
|
this.batchRunner.run(() => {
|
|
2804
2898
|
for (const message of batch) {
|
|
2805
2899
|
this.reSubmit(message);
|
|
@@ -2807,14 +2901,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2807
2901
|
});
|
|
2808
2902
|
// Only include Batch ID if "Offline Load" feature is enabled
|
|
2809
2903
|
// It's only needed to identify batches across container forks arising from misuse of offline load.
|
|
2810
|
-
this.flush(this.offlineEnabled ? batchId : undefined);
|
|
2904
|
+
this.flush(this.offlineEnabled ? batchId : undefined, staged);
|
|
2811
2905
|
}
|
|
2812
2906
|
reSubmit(message) {
|
|
2813
|
-
|
|
2814
|
-
// Note that roundtripping handles through string like this means this parsed contents
|
|
2815
|
-
// contains RemoteFluidObjectHandles, not the original handle.
|
|
2816
|
-
const containerRuntimeMessage = this.parseLocalOpContent(message.content);
|
|
2817
|
-
this.reSubmitCore(containerRuntimeMessage, message.localOpMetadata, message.opMetadata);
|
|
2907
|
+
this.reSubmitCore(message.runtimeOp, message.localOpMetadata, message.opMetadata);
|
|
2818
2908
|
}
|
|
2819
2909
|
/**
|
|
2820
2910
|
* Finds the right store and asks it to resubmit the message. This typically happens when we
|
|
@@ -2869,11 +2959,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
2869
2959
|
}
|
|
2870
2960
|
}
|
|
2871
2961
|
}
|
|
2872
|
-
rollback(
|
|
2873
|
-
|
|
2874
|
-
// Note that roundtripping handles through string like this means this parsed contents
|
|
2875
|
-
// contains RemoteFluidObjectHandles, not the original handle.
|
|
2876
|
-
const { type, contents } = this.parseLocalOpContent(content);
|
|
2962
|
+
rollback(runtimeOp, localOpMetadata) {
|
|
2963
|
+
const { type, contents } = runtimeOp;
|
|
2877
2964
|
switch (type) {
|
|
2878
2965
|
case ContainerMessageType.FluidDataStoreOp: {
|
|
2879
2966
|
// For operations, call rollbackDataStoreOp which will find the right store
|