@fluidframework/container-runtime 2.0.0-internal.2.2.1 → 2.0.0-internal.2.3.1
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/.eslintrc.js +19 -8
- package/dist/batchTracker.d.ts +1 -2
- package/dist/batchTracker.d.ts.map +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +45 -34
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +135 -102
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +54 -8
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +143 -72
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +6 -8
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +12 -9
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +41 -35
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +41 -20
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +205 -150
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +7 -3
- package/dist/garbageCollectionConstants.d.ts.map +1 -1
- package/dist/garbageCollectionConstants.js +10 -8
- package/dist/garbageCollectionConstants.js.map +1 -1
- package/dist/garbageCollectionTombstoneUtils.d.ts +14 -0
- package/dist/garbageCollectionTombstoneUtils.d.ts.map +1 -0
- package/dist/garbageCollectionTombstoneUtils.js +23 -0
- package/dist/garbageCollectionTombstoneUtils.js.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +13 -1
- package/dist/opLifecycle/batchManager.d.ts.map +1 -1
- package/dist/opLifecycle/batchManager.js +35 -1
- package/dist/opLifecycle/batchManager.js.map +1 -1
- package/dist/opLifecycle/definitions.d.ts +25 -1
- package/dist/opLifecycle/definitions.d.ts.map +1 -1
- package/dist/opLifecycle/definitions.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +2 -2
- 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/opCompressor.d.ts +1 -1
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opCompressor.js +24 -10
- package/dist/opLifecycle/opCompressor.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts +2 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +30 -17
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opSplitter.d.ts +34 -2
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +114 -5
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +5 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +24 -14
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +17 -2
- package/dist/opLifecycle/remoteMessageProcessor.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/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +0 -1
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.d.ts +0 -1
- package/dist/scheduleManager.d.ts.map +1 -1
- package/dist/scheduleManager.js +9 -20
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizer.d.ts +0 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +2 -1
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerTypes.d.ts +1 -0
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js +1 -2
- package/dist/summaryFormat.js.map +1 -1
- package/lib/batchTracker.d.ts +1 -2
- package/lib/batchTracker.d.ts.map +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +45 -34
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +137 -104
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +54 -8
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +140 -69
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +7 -9
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +12 -9
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +44 -38
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +41 -20
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +201 -146
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +7 -3
- package/lib/garbageCollectionConstants.d.ts.map +1 -1
- package/lib/garbageCollectionConstants.js +9 -7
- package/lib/garbageCollectionConstants.js.map +1 -1
- package/lib/garbageCollectionTombstoneUtils.d.ts +14 -0
- package/lib/garbageCollectionTombstoneUtils.d.ts.map +1 -0
- package/lib/garbageCollectionTombstoneUtils.js +19 -0
- package/lib/garbageCollectionTombstoneUtils.js.map +1 -0
- package/lib/index.d.ts +1 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +13 -1
- package/lib/opLifecycle/batchManager.d.ts.map +1 -1
- package/lib/opLifecycle/batchManager.js +35 -1
- package/lib/opLifecycle/batchManager.js.map +1 -1
- package/lib/opLifecycle/definitions.d.ts +25 -1
- package/lib/opLifecycle/definitions.d.ts.map +1 -1
- package/lib/opLifecycle/definitions.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +2 -2
- 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/opCompressor.d.ts +1 -1
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opCompressor.js +24 -10
- package/lib/opLifecycle/opCompressor.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts +2 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +30 -17
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opSplitter.d.ts +34 -2
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +112 -4
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +5 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +24 -14
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +17 -2
- package/lib/opLifecycle/remoteMessageProcessor.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/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +0 -1
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.d.ts +0 -1
- package/lib/scheduleManager.d.ts.map +1 -1
- package/lib/scheduleManager.js +9 -20
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizer.d.ts +0 -1
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +2 -1
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerTypes.d.ts +1 -0
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js +1 -2
- package/lib/summaryFormat.js.map +1 -1
- package/package.json +20 -19
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +159 -111
- package/src/containerRuntime.ts +202 -73
- package/src/dataStoreContext.ts +15 -16
- package/src/dataStores.ts +61 -45
- package/src/garbageCollection.ts +258 -183
- package/src/garbageCollectionConstants.ts +10 -7
- package/src/garbageCollectionTombstoneUtils.ts +28 -0
- package/src/index.ts +2 -5
- package/src/opLifecycle/batchManager.ts +59 -1
- package/src/opLifecycle/definitions.ts +27 -1
- package/src/opLifecycle/index.ts +2 -1
- package/src/opLifecycle/opCompressor.ts +29 -12
- package/src/opLifecycle/opDecompressor.ts +39 -18
- package/src/opLifecycle/opSplitter.ts +141 -7
- package/src/opLifecycle/outbox.ts +32 -16
- package/src/opLifecycle/remoteMessageProcessor.ts +19 -3
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +0 -1
- package/src/scheduleManager.ts +19 -30
- package/src/summarizer.ts +1 -1
- package/src/summarizerTypes.ts +1 -0
- package/src/summaryFormat.ts +1 -2
package/lib/containerRuntime.js
CHANGED
|
@@ -5,7 +5,7 @@ import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
|
|
|
5
5
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
6
6
|
import { DataCorruptionError, DataProcessingError, GenericError, UsageError, } from "@fluidframework/container-utils";
|
|
7
7
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
8
|
-
import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
8
|
+
import { FlushMode, gcTreeKey, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
9
9
|
import { addBlobToSummary, addSummarizeResultToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, TelemetryContext, } from "@fluidframework/runtime-utils";
|
|
10
10
|
import { GCDataBuilder, trimLeadingAndTrailingSlashes } from "@fluidframework/garbage-collector";
|
|
11
11
|
import { v4 as uuid } from "uuid";
|
|
@@ -25,7 +25,6 @@ import { SummarizerClientElection, summarizerClientType } from "./summarizerClie
|
|
|
25
25
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
26
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
27
27
|
import { GarbageCollector, GCNodeType, } from "./garbageCollection";
|
|
28
|
-
import { gcTreeKey, } from "./garbageCollectionConstants";
|
|
29
28
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
30
29
|
import { BindBatchTracker } from "./batchTracker";
|
|
31
30
|
import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
@@ -76,6 +75,17 @@ export var RuntimeHeaders;
|
|
|
76
75
|
/** True if the request is coming from an IFluidHandle. */
|
|
77
76
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
78
77
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
78
|
+
/** True if a tombstoned object should be returned without erroring */
|
|
79
|
+
export const AllowTombstoneRequestHeaderKey = "allowTombstone"; // Belongs in the enum above, but avoiding the breaking change
|
|
80
|
+
/** Tombstone error responses will have this header set to true */
|
|
81
|
+
export const TombstoneResponseHeaderKey = "isTombstoned";
|
|
82
|
+
/** Default values for Runtime Headers */
|
|
83
|
+
export const defaultRuntimeHeaderData = {
|
|
84
|
+
wait: true,
|
|
85
|
+
externalRequest: false,
|
|
86
|
+
viaHandle: false,
|
|
87
|
+
allowTombstone: false,
|
|
88
|
+
};
|
|
79
89
|
/**
|
|
80
90
|
* Available compression algorithms for op compression.
|
|
81
91
|
*/
|
|
@@ -213,12 +223,17 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
213
223
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
214
224
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
215
225
|
this._connected = this.context.connected;
|
|
216
|
-
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
217
|
-
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
218
226
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
227
|
+
const opSplitter = new OpSplitter(chunks, this.context.submitBatchFn, this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompressionChunking") === true ?
|
|
228
|
+
Number.POSITIVE_INFINITY : runtimeOptions.chunkSizeInBytes, runtimeOptions.maxBatchSizeInBytes, this.mc.logger);
|
|
229
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(opSplitter, new OpDecompressor());
|
|
230
|
+
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
219
231
|
if (this.summaryConfiguration.state === "enabled") {
|
|
220
232
|
this.validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
221
233
|
}
|
|
234
|
+
this.enableOpReentryCheck = runtimeOptions.enableOpReentryCheck === true
|
|
235
|
+
// Allow for a break-glass config to override the options
|
|
236
|
+
&& this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableOpReentryCheck") !== true;
|
|
222
237
|
this.summariesDisabled = this.isSummariesDisabled();
|
|
223
238
|
this.heuristicsDisabled = this.isHeuristicsDisabled();
|
|
224
239
|
this.summarizerClientElectionEnabled = this.isSummarizerClientElectionEnabled();
|
|
@@ -267,16 +282,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
267
282
|
throwOnFailure: true,
|
|
268
283
|
// If GC should not run, let the summarizer node know so that it does not track GC state.
|
|
269
284
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
270
|
-
}
|
|
285
|
+
},
|
|
286
|
+
// Function to get GC data if needed. This will always be called by the root summarizer node to get GC data.
|
|
287
|
+
async (fullGC) => this.getGCDataInternal(fullGC),
|
|
288
|
+
// Function to get the GC details from the base snapshot we loaded from.
|
|
289
|
+
async () => this.garbageCollector.getBaseGCDetails());
|
|
271
290
|
if (baseSnapshot) {
|
|
272
291
|
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
273
292
|
}
|
|
274
293
|
this.dataStores = new DataStores(getSummaryForDatastores(baseSnapshot, metadata), this, (attachMsg) => this.submit(ContainerMessageType.Attach, attachMsg), (id, createParam) => (summarizeInternal, getGCDataFn, getBaseGCDetailsFn) => this.summarizerNode.createChild(summarizeInternal, id, createParam, undefined, getGCDataFn, getBaseGCDetailsFn), (id) => this.summarizerNode.deleteChild(id), this.mc.logger, async () => this.garbageCollector.getBaseGCDetails(), (path, timestampMs, packagePath) => this.garbageCollector.nodeUpdated(path, "Changed", timestampMs, packagePath), new Map(dataStoreAliasMap));
|
|
275
|
-
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (
|
|
294
|
+
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (localId, blobId) => {
|
|
276
295
|
if (!this.disposed) {
|
|
277
|
-
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, {
|
|
296
|
+
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { localId, blobId });
|
|
278
297
|
}
|
|
279
|
-
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
298
|
+
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), (fromPath, toPath) => this.garbageCollector.addedOutboundReference(fromPath, toPath), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
280
299
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
281
300
|
this.pendingStateManager = new PendingStateManager({
|
|
282
301
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
@@ -288,15 +307,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
288
307
|
rollback: this.rollback.bind(this),
|
|
289
308
|
orderSequentially: this.orderSequentially.bind(this),
|
|
290
309
|
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
310
|
+
const compressionOptions = this.mc.config.getBoolean("Fluid.ContainerRuntime.DisableCompression") === true ?
|
|
311
|
+
{
|
|
312
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
313
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
314
|
+
} : runtimeOptions.compressionOptions;
|
|
291
315
|
this.outbox = new Outbox({
|
|
292
316
|
shouldSend: () => this.canSendOps(),
|
|
293
317
|
pendingStateManager: this.pendingStateManager,
|
|
294
318
|
containerContext: this.context,
|
|
295
319
|
compressor: new OpCompressor(this.mc.logger),
|
|
320
|
+
splitter: opSplitter,
|
|
296
321
|
config: {
|
|
297
|
-
compressionOptions
|
|
322
|
+
compressionOptions,
|
|
298
323
|
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
324
|
+
enableOpReentryCheck: this.enableOpReentryCheck,
|
|
299
325
|
},
|
|
326
|
+
logger: this.mc.logger,
|
|
300
327
|
});
|
|
301
328
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
302
329
|
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
@@ -384,7 +411,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
384
411
|
* allows mixin classes to leverage this method to define their own async initializer.
|
|
385
412
|
*/
|
|
386
413
|
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
387
|
-
var _a, _b, _c;
|
|
414
|
+
var _a, _b, _c, _d;
|
|
388
415
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
389
416
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
390
417
|
const backCompatContext = context;
|
|
@@ -397,7 +424,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
397
424
|
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
398
425
|
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
399
426
|
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
400
|
-
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
427
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, chunkSizeInBytes = Number.POSITIVE_INFINITY, enableOpReentryCheck = false, } = runtimeOptions;
|
|
401
428
|
const pendingRuntimeState = context.pendingLocalState;
|
|
402
429
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
403
430
|
const storage = !pendingRuntimeState ?
|
|
@@ -442,7 +469,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
442
469
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
443
470
|
}
|
|
444
471
|
else {
|
|
472
|
+
// Call both close and dispose as close implementation will no longer dispose runtime in future (2.0.0-internal.3.0.0)
|
|
445
473
|
context.closeFn(error);
|
|
474
|
+
(_d = context.disposeFn) === null || _d === void 0 ? void 0 : _d.call(context, error);
|
|
446
475
|
}
|
|
447
476
|
}
|
|
448
477
|
}
|
|
@@ -454,6 +483,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
454
483
|
enableOfflineLoad,
|
|
455
484
|
compressionOptions,
|
|
456
485
|
maxBatchSizeInBytes,
|
|
486
|
+
chunkSizeInBytes,
|
|
487
|
+
enableOpReentryCheck,
|
|
457
488
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
458
489
|
if (pendingRuntimeState) {
|
|
459
490
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
@@ -483,8 +514,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
483
514
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
484
515
|
return this.reSubmit;
|
|
485
516
|
}
|
|
517
|
+
get disposeFn() {
|
|
518
|
+
var _a;
|
|
519
|
+
// In old loaders without dispose functionality, closeFn is equivalent but will also switch container to readonly mode
|
|
520
|
+
return (_a = this.context.disposeFn) !== null && _a !== void 0 ? _a : this.context.closeFn;
|
|
521
|
+
}
|
|
486
522
|
get closeFn() {
|
|
487
|
-
|
|
523
|
+
// Also call disposeFn to retain functionality of runtime being disposed on close
|
|
524
|
+
return (error) => {
|
|
525
|
+
var _a, _b;
|
|
526
|
+
this.context.closeFn(error);
|
|
527
|
+
(_b = (_a = this.context).disposeFn) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
528
|
+
};
|
|
488
529
|
}
|
|
489
530
|
get flushMode() {
|
|
490
531
|
return this._flushMode;
|
|
@@ -670,16 +711,20 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
670
711
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
671
712
|
}
|
|
672
713
|
async getDataStoreFromRequest(id, request) {
|
|
673
|
-
var _a, _b, _c, _d
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
714
|
+
var _a, _b, _c, _d;
|
|
715
|
+
const headerData = {};
|
|
716
|
+
if (typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean") {
|
|
717
|
+
headerData.wait = request.headers[RuntimeHeaders.wait];
|
|
718
|
+
}
|
|
719
|
+
if (typeof ((_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.viaHandle]) === "boolean") {
|
|
720
|
+
headerData.viaHandle = request.headers[RuntimeHeaders.viaHandle];
|
|
721
|
+
}
|
|
722
|
+
if (typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[AllowTombstoneRequestHeaderKey]) === "boolean") {
|
|
723
|
+
headerData.allowTombstone = request.headers[AllowTombstoneRequestHeaderKey];
|
|
724
|
+
}
|
|
680
725
|
await this.dataStores.waitIfPendingAlias(id);
|
|
681
726
|
const internalId = this.internalId(id);
|
|
682
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId,
|
|
727
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, headerData);
|
|
683
728
|
/**
|
|
684
729
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
685
730
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -688,7 +733,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
688
733
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
689
734
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
690
735
|
*/
|
|
691
|
-
if (((
|
|
736
|
+
if (((_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
692
737
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
693
738
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
694
739
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -862,9 +907,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
862
907
|
if (reconnection) {
|
|
863
908
|
this.consecutiveReconnects++;
|
|
864
909
|
if (!this.shouldContinueReconnecting()) {
|
|
865
|
-
this.closeFn(DataProcessingError.create(
|
|
866
|
-
// eslint-disable-next-line max-len
|
|
867
|
-
"Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)", "setConnectionState", undefined, {
|
|
910
|
+
this.closeFn(DataProcessingError.create("Runtime detected too many reconnects with no progress syncing local ops. Batch of ops is likely too large (over 1Mb)", "setConnectionState", undefined, {
|
|
868
911
|
dataLoss: 1,
|
|
869
912
|
attempts: this.consecutiveReconnects,
|
|
870
913
|
pendingMessages: this.pendingStateManager.pendingMessagesCount,
|
|
@@ -898,7 +941,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
898
941
|
this.scheduleManager.beforeOpProcessing(message);
|
|
899
942
|
try {
|
|
900
943
|
let localOpMetadata;
|
|
901
|
-
if (local && runtimeMessage) {
|
|
944
|
+
if (local && runtimeMessage && message.type !== ContainerMessageType.ChunkedOp) {
|
|
902
945
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
903
946
|
}
|
|
904
947
|
// If there are no more pending messages after processing a local message,
|
|
@@ -1000,7 +1043,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1000
1043
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1001
1044
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1002
1045
|
const internalId = this.internalId(id);
|
|
1003
|
-
const context = await this.dataStores.getDataStore(internalId, wait
|
|
1046
|
+
const context = await this.dataStores.getDataStore(internalId, { wait });
|
|
1004
1047
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1005
1048
|
return context.realize();
|
|
1006
1049
|
}
|
|
@@ -1015,6 +1058,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1015
1058
|
}
|
|
1016
1059
|
orderSequentially(callback) {
|
|
1017
1060
|
let checkpoint;
|
|
1061
|
+
let result;
|
|
1018
1062
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1019
1063
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1020
1064
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
@@ -1023,7 +1067,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1023
1067
|
}
|
|
1024
1068
|
try {
|
|
1025
1069
|
this._orderSequentiallyCalls++;
|
|
1026
|
-
callback();
|
|
1070
|
+
result = callback();
|
|
1027
1071
|
}
|
|
1028
1072
|
catch (error) {
|
|
1029
1073
|
if (checkpoint) {
|
|
@@ -1051,6 +1095,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1051
1095
|
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1052
1096
|
this.flush();
|
|
1053
1097
|
}
|
|
1098
|
+
return result;
|
|
1054
1099
|
}
|
|
1055
1100
|
async createDataStore(pkg) {
|
|
1056
1101
|
const internalId = uuid();
|
|
@@ -1216,6 +1261,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1216
1261
|
async updateStateBeforeGC() {
|
|
1217
1262
|
return this.dataStores.updateStateBeforeGC();
|
|
1218
1263
|
}
|
|
1264
|
+
async getGCDataInternal(fullGC) {
|
|
1265
|
+
return this.dataStores.getGCData(fullGC);
|
|
1266
|
+
}
|
|
1219
1267
|
/**
|
|
1220
1268
|
* Implementation of IGarbageCollectionRuntime::getGCData.
|
|
1221
1269
|
* Generates and returns the GC data for this container.
|
|
@@ -1223,7 +1271,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1223
1271
|
*/
|
|
1224
1272
|
async getGCData(fullGC) {
|
|
1225
1273
|
const builder = new GCDataBuilder();
|
|
1226
|
-
const dsGCData = await this.
|
|
1274
|
+
const dsGCData = await this.summarizerNode.getGCData(fullGC);
|
|
1227
1275
|
builder.addNodes(dsGCData.gcNodes);
|
|
1228
1276
|
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
1229
1277
|
builder.addNodes(blobsGCData.gcNodes);
|
|
@@ -1239,39 +1287,26 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1239
1287
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1240
1288
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1241
1289
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1242
|
-
const
|
|
1243
|
-
|
|
1244
|
-
for (const route of usedRoutes) {
|
|
1245
|
-
if (this.isBlobPath(route)) {
|
|
1246
|
-
blobManagerUsedRoutes.push(route);
|
|
1247
|
-
}
|
|
1248
|
-
else {
|
|
1249
|
-
dataStoreUsedRoutes.push(route);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1253
|
-
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1290
|
+
const { dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(usedRoutes);
|
|
1291
|
+
this.dataStores.updateUsedRoutes(dataStoreRoutes);
|
|
1254
1292
|
}
|
|
1255
1293
|
/**
|
|
1256
|
-
* This is called to update objects whose routes are unused.
|
|
1257
|
-
*
|
|
1258
|
-
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1259
|
-
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1260
|
-
* are deleted.
|
|
1294
|
+
* This is called to update objects whose routes are unused.
|
|
1295
|
+
* @param unusedRoutes - Data store and attachment blob routes that are unused in this Container.
|
|
1261
1296
|
*/
|
|
1262
|
-
updateUnusedRoutes(unusedRoutes
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
}
|
|
1273
|
-
this.blobManager.
|
|
1274
|
-
this.dataStores.
|
|
1297
|
+
updateUnusedRoutes(unusedRoutes) {
|
|
1298
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(unusedRoutes);
|
|
1299
|
+
this.blobManager.updateUnusedRoutes(blobManagerRoutes);
|
|
1300
|
+
this.dataStores.updateUnusedRoutes(dataStoreRoutes);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* This is called to update objects that are tombstones.
|
|
1304
|
+
* @param tombstonedRoutes - Data store and attachment blob routes that are tombstones in this Container.
|
|
1305
|
+
*/
|
|
1306
|
+
updateTombstonedRoutes(tombstonedRoutes) {
|
|
1307
|
+
const { blobManagerRoutes, dataStoreRoutes } = this.getDataStoreAndBlobManagerRoutes(tombstonedRoutes);
|
|
1308
|
+
this.blobManager.updateTombstonedRoutes(blobManagerRoutes);
|
|
1309
|
+
this.dataStores.updateTombstonedRoutes(dataStoreRoutes);
|
|
1275
1310
|
}
|
|
1276
1311
|
/**
|
|
1277
1312
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1300,7 +1335,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1300
1335
|
async getGCNodePackagePath(nodePath) {
|
|
1301
1336
|
switch (this.getNodeType(nodePath)) {
|
|
1302
1337
|
case GCNodeType.Blob:
|
|
1303
|
-
return [
|
|
1338
|
+
return [BlobManager.basePath];
|
|
1304
1339
|
case GCNodeType.DataStore:
|
|
1305
1340
|
case GCNodeType.SubDataStore:
|
|
1306
1341
|
return this.dataStores.getDataStorePackagePath(nodePath);
|
|
@@ -1318,6 +1353,25 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1318
1353
|
}
|
|
1319
1354
|
return true;
|
|
1320
1355
|
}
|
|
1356
|
+
/**
|
|
1357
|
+
* From a given list of routes, separate and return routes that belong to blob manager and data stores.
|
|
1358
|
+
* @param routes - A list of routes that can belong to data stores or blob manager.
|
|
1359
|
+
* @returns - Two route lists - One that contains routes for blob manager and another one that contains routes
|
|
1360
|
+
* for data stores.
|
|
1361
|
+
*/
|
|
1362
|
+
getDataStoreAndBlobManagerRoutes(routes) {
|
|
1363
|
+
const blobManagerRoutes = [];
|
|
1364
|
+
const dataStoreRoutes = [];
|
|
1365
|
+
for (const route of routes) {
|
|
1366
|
+
if (this.isBlobPath(route)) {
|
|
1367
|
+
blobManagerRoutes.push(route);
|
|
1368
|
+
}
|
|
1369
|
+
else {
|
|
1370
|
+
dataStoreRoutes.push(route);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return { blobManagerRoutes, dataStoreRoutes };
|
|
1374
|
+
}
|
|
1321
1375
|
/**
|
|
1322
1376
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
1323
1377
|
* @returns the statistics of the garbage collection run; undefined if GC did not run.
|
|
@@ -1389,7 +1443,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1389
1443
|
if (this.deltaManager.lastSequenceNumber !== summaryRefSeqNum) {
|
|
1390
1444
|
return {
|
|
1391
1445
|
continue: false,
|
|
1392
|
-
// eslint-disable-next-line max-len
|
|
1393
1446
|
error: `lastSequenceNumber changed before uploading to storage. ${this.deltaManager.lastSequenceNumber} !== ${summaryRefSeqNum}`,
|
|
1394
1447
|
};
|
|
1395
1448
|
}
|
|
@@ -1397,7 +1450,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1397
1450
|
if (lastAck !== this.summaryCollection.latestAck) {
|
|
1398
1451
|
return {
|
|
1399
1452
|
continue: false,
|
|
1400
|
-
// eslint-disable-next-line max-len
|
|
1401
1453
|
error: `Last summary changed while summarizing. ${this.summaryCollection.latestAck} !== ${lastAck}`,
|
|
1402
1454
|
};
|
|
1403
1455
|
}
|
|
@@ -1612,7 +1664,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1612
1664
|
else if (!this.flushMicroTaskExists) {
|
|
1613
1665
|
this.flushMicroTaskExists = true;
|
|
1614
1666
|
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1615
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1616
1667
|
Promise.resolve().then(() => {
|
|
1617
1668
|
this.flushMicroTaskExists = false;
|
|
1618
1669
|
this.flush();
|
|
@@ -1705,13 +1756,32 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1705
1756
|
// It should only be done by the summarizerNode, if required.
|
|
1706
1757
|
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1707
1758
|
const snapshotTreeFetcher = async () => {
|
|
1708
|
-
const fetchResult = await this.
|
|
1759
|
+
const fetchResult = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1709
1760
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1710
1761
|
ackHandle,
|
|
1711
1762
|
summaryRefSeq,
|
|
1712
1763
|
fetchLatest: true,
|
|
1713
1764
|
});
|
|
1714
1765
|
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
1766
|
+
/**
|
|
1767
|
+
* If the fetched snapshot is older than the one for which the ack was received, close the container.
|
|
1768
|
+
* This should never happen because an ack should be sent after the latest summary is updated in the server.
|
|
1769
|
+
* However, there are couple of scenarios where it's possible:
|
|
1770
|
+
* 1. A file was modified externally resulting in modifying the snapshot's sequence number. This can lead to
|
|
1771
|
+
* the document being unusable and we should not proceed.
|
|
1772
|
+
* 2. The server DB failed after the ack was sent which may delete the corresponding snapshot. Ideally, in
|
|
1773
|
+
* such cases, the file will be rolled back along with the ack and we will eventually reach a consistent
|
|
1774
|
+
* state.
|
|
1775
|
+
*/
|
|
1776
|
+
if (latestSnapshotRefSeq < summaryRefSeq) {
|
|
1777
|
+
const error = DataProcessingError.create("Fetched snapshot is older than the received ack", "RefreshLatestSummaryAck", undefined /* sequencedMessage */, {
|
|
1778
|
+
ackHandle,
|
|
1779
|
+
summaryRefSeq,
|
|
1780
|
+
latestSnapshotRefSeq,
|
|
1781
|
+
});
|
|
1782
|
+
this.closeFn(error);
|
|
1783
|
+
throw error;
|
|
1784
|
+
}
|
|
1715
1785
|
summaryLogger.sendTelemetryEvent({
|
|
1716
1786
|
eventName: "LatestSummaryRetrieved",
|
|
1717
1787
|
ackHandle,
|
|
@@ -1725,7 +1795,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1725
1795
|
};
|
|
1726
1796
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
1727
1797
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1728
|
-
await this.garbageCollector.
|
|
1798
|
+
await this.garbageCollector.refreshLatestSummary(result, proposalHandle, summaryRefSeq, readAndParseBlob);
|
|
1729
1799
|
}
|
|
1730
1800
|
/**
|
|
1731
1801
|
* Fetches the latest snapshot from storage and uses it to refresh SummarizerNode's
|
|
@@ -1734,22 +1804,22 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1734
1804
|
* @returns downloaded snapshot's reference sequence number
|
|
1735
1805
|
*/
|
|
1736
1806
|
async refreshLatestSummaryAckFromServer(summaryLogger) {
|
|
1737
|
-
const { snapshotTree, versionId } = await this.
|
|
1807
|
+
const { snapshotTree, versionId } = await this.fetchLatestSnapshotFromStorage(summaryLogger, {
|
|
1738
1808
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1739
1809
|
fetchLatest: true,
|
|
1740
|
-
}
|
|
1810
|
+
});
|
|
1741
1811
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1742
1812
|
const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
1743
1813
|
const result = await this.summarizerNode.refreshLatestSummary(undefined, latestSnapshotRefSeq, async () => snapshotTree, readAndParseBlob, summaryLogger);
|
|
1744
1814
|
// Notify the garbage collector so it can update its latest summary state.
|
|
1745
|
-
await this.garbageCollector.
|
|
1815
|
+
await this.garbageCollector.refreshLatestSummary(result, undefined, latestSnapshotRefSeq, readAndParseBlob);
|
|
1746
1816
|
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
1747
1817
|
}
|
|
1748
|
-
async
|
|
1818
|
+
async fetchLatestSnapshotFromStorage(logger, event) {
|
|
1749
1819
|
return PerformanceEvent.timedExecAsync(logger, event, async (perfEvent) => {
|
|
1750
1820
|
const stats = {};
|
|
1751
1821
|
const trace = Trace.start();
|
|
1752
|
-
const versions = await this.storage.getVersions(
|
|
1822
|
+
const versions = await this.storage.getVersions(null, 1, "refreshLatestSummaryAckFromServer", FetchSource.noCache);
|
|
1753
1823
|
assert(!!versions && !!versions[0], 0x137 /* "Failed to get version from storage" */);
|
|
1754
1824
|
stats.getVersionDuration = trace.trace().duration;
|
|
1755
1825
|
const maybeSnapshot = await this.storage.getSnapshotTree(versions[0]);
|
|
@@ -1865,6 +1935,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1865
1935
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1866
1936
|
// TODO: remove cast to any when actual event is determined
|
|
1867
1937
|
deltaManager.on("closed", reject);
|
|
1938
|
+
deltaManager.on("disposed", reject);
|
|
1868
1939
|
// If we already reached target sequence number, simply resolve the promise.
|
|
1869
1940
|
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
1870
1941
|
resolve();
|