@fluidframework/container-runtime 2.0.0-internal.2.1.1 → 2.0.0-internal.2.2.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/.eslintrc.js +1 -1
- package/dist/blobManager.d.ts +20 -5
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +57 -15
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +39 -40
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +115 -278
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +21 -3
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +8 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +26 -13
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +15 -17
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +92 -106
- package/dist/garbageCollection.js.map +1 -1
- package/dist/garbageCollectionConstants.d.ts +19 -0
- package/dist/garbageCollectionConstants.d.ts.map +1 -0
- package/dist/garbageCollectionConstants.js +34 -0
- package/dist/garbageCollectionConstants.js.map +1 -0
- package/dist/gcSweepReadyUsageDetection.js +2 -2
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/opLifecycle/batchManager.d.ts +30 -0
- package/dist/opLifecycle/batchManager.d.ts.map +1 -0
- package/dist/{batchManager.js → opLifecycle/batchManager.js} +25 -10
- package/dist/opLifecycle/batchManager.js.map +1 -0
- package/dist/opLifecycle/definitions.d.ts +40 -0
- package/dist/opLifecycle/definitions.d.ts.map +1 -0
- package/dist/opLifecycle/definitions.js +7 -0
- package/dist/opLifecycle/definitions.js.map +1 -0
- package/dist/opLifecycle/index.d.ts +12 -0
- package/dist/opLifecycle/index.d.ts.map +1 -0
- package/dist/opLifecycle/index.js +21 -0
- package/dist/opLifecycle/index.js.map +1 -0
- package/dist/opLifecycle/opCompressor.d.ts +18 -0
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opCompressor.js +53 -0
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/opLifecycle/opDecompressor.d.ts +20 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/opLifecycle/opDecompressor.js +72 -0
- package/dist/opLifecycle/opDecompressor.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +17 -0
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -0
- package/dist/opLifecycle/opSplitter.js +61 -0
- package/dist/opLifecycle/opSplitter.js.map +1 -0
- package/dist/opLifecycle/outbox.d.ts +47 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -0
- package/dist/opLifecycle/outbox.js +153 -0
- package/dist/opLifecycle/outbox.js.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/dist/opLifecycle/remoteMessageProcessor.js +81 -0
- package/dist/opLifecycle/remoteMessageProcessor.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summaryFormat.js +2 -2
- package/dist/summaryFormat.js.map +1 -1
- package/lib/blobManager.d.ts +20 -5
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +59 -17
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +39 -40
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +113 -275
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +23 -5
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +8 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +28 -15
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +15 -17
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +72 -86
- package/lib/garbageCollection.js.map +1 -1
- package/lib/garbageCollectionConstants.d.ts +19 -0
- package/lib/garbageCollectionConstants.d.ts.map +1 -0
- package/lib/garbageCollectionConstants.js +31 -0
- package/lib/garbageCollectionConstants.js.map +1 -0
- package/lib/gcSweepReadyUsageDetection.js +1 -1
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -2
- package/lib/index.js.map +1 -1
- package/lib/opLifecycle/batchManager.d.ts +30 -0
- package/lib/opLifecycle/batchManager.d.ts.map +1 -0
- package/lib/{batchManager.js → opLifecycle/batchManager.js} +25 -10
- package/lib/opLifecycle/batchManager.js.map +1 -0
- package/lib/opLifecycle/definitions.d.ts +40 -0
- package/lib/opLifecycle/definitions.d.ts.map +1 -0
- package/lib/opLifecycle/definitions.js +6 -0
- package/lib/opLifecycle/definitions.js.map +1 -0
- package/lib/opLifecycle/index.d.ts +12 -0
- package/lib/opLifecycle/index.d.ts.map +1 -0
- package/lib/opLifecycle/index.js +11 -0
- package/lib/opLifecycle/index.js.map +1 -0
- package/lib/opLifecycle/opCompressor.d.ts +18 -0
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opCompressor.js +49 -0
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/opLifecycle/opDecompressor.d.ts +20 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/opLifecycle/opDecompressor.js +68 -0
- package/lib/opLifecycle/opDecompressor.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +17 -0
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -0
- package/lib/opLifecycle/opSplitter.js +57 -0
- package/lib/opLifecycle/opSplitter.js.map +1 -0
- package/lib/opLifecycle/outbox.d.ts +47 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -0
- package/lib/opLifecycle/outbox.js +149 -0
- package/lib/opLifecycle/outbox.js.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +26 -0
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -0
- package/lib/opLifecycle/remoteMessageProcessor.js +76 -0
- package/lib/opLifecycle/remoteMessageProcessor.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summaryFormat.js +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/package.json +35 -21
- package/prettier.config.cjs +8 -0
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +144 -341
- package/src/dataStoreContext.ts +33 -5
- package/src/dataStores.ts +32 -16
- package/src/garbageCollection.ts +106 -82
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +1 -1
- package/src/index.ts +6 -4
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +41 -23
- package/src/opLifecycle/definitions.ts +44 -0
- package/src/opLifecycle/index.ts +17 -0
- package/src/opLifecycle/opCompressor.ts +64 -0
- package/src/opLifecycle/opDecompressor.ts +84 -0
- package/src/opLifecycle/opSplitter.ts +78 -0
- package/src/opLifecycle/outbox.ts +204 -0
- package/src/opLifecycle/remoteMessageProcessor.ts +90 -0
- package/src/packageVersion.ts +1 -1
- package/src/summaryFormat.ts +1 -1
- package/dist/batchManager.d.ts +0 -36
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/lib/batchManager.d.ts +0 -36
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js.map +0 -1
package/lib/containerRuntime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
2
|
-
import { assert, Trace, TypedEventEmitter, unreachableCase,
|
|
2
|
+
import { assert, Trace, TypedEventEmitter, unreachableCase, } from "@fluidframework/common-utils";
|
|
3
3
|
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, TaggedLoggerAdapter, loggerToMonitoringContext, wrapError, } from "@fluidframework/telemetry-utils";
|
|
4
4
|
import { DriverHeader, FetchSource, } from "@fluidframework/driver-definitions";
|
|
5
5
|
import { readAndParse } from "@fluidframework/driver-utils";
|
|
@@ -9,14 +9,12 @@ import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definition
|
|
|
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";
|
|
12
|
-
import { compress, decompress } from "lz4js";
|
|
13
12
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
14
13
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
15
14
|
import { Summarizer } from "./summarizer";
|
|
16
15
|
import { SummaryManager } from "./summaryManager";
|
|
17
16
|
import { ReportOpPerfTelemetry, } from "./connectionTelemetry";
|
|
18
17
|
import { PendingStateManager, } from "./pendingStateManager";
|
|
19
|
-
import { BatchManager } from "./batchManager";
|
|
20
18
|
import { pkgVersion } from "./packageVersion";
|
|
21
19
|
import { BlobManager } from "./blobManager";
|
|
22
20
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
@@ -26,11 +24,13 @@ import { OrderedClientCollection, OrderedClientElection } from "./orderedClientE
|
|
|
26
24
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
27
25
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
28
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
29
|
-
import { GarbageCollector, GCNodeType,
|
|
27
|
+
import { GarbageCollector, GCNodeType, } from "./garbageCollection";
|
|
28
|
+
import { gcTreeKey, } from "./garbageCollectionConstants";
|
|
30
29
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
31
30
|
import { BindBatchTracker } from "./batchTracker";
|
|
32
31
|
import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
33
32
|
import { ScheduleManager } from "./scheduleManager";
|
|
33
|
+
import { OpCompressor, OpDecompressor, Outbox, OpSplitter, RemoteMessageProcessor, } from "./opLifecycle";
|
|
34
34
|
export var ContainerMessageType;
|
|
35
35
|
(function (ContainerMessageType) {
|
|
36
36
|
// An op to be delivered to store
|
|
@@ -76,6 +76,13 @@ export var RuntimeHeaders;
|
|
|
76
76
|
/** True if the request is coming from an IFluidHandle. */
|
|
77
77
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
78
78
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
79
|
+
/**
|
|
80
|
+
* Available compression algorithms for op compression.
|
|
81
|
+
*/
|
|
82
|
+
export var CompressionAlgorithms;
|
|
83
|
+
(function (CompressionAlgorithms) {
|
|
84
|
+
CompressionAlgorithms["lz4"] = "lz4";
|
|
85
|
+
})(CompressionAlgorithms || (CompressionAlgorithms = {}));
|
|
79
86
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
80
87
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
81
88
|
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
@@ -102,47 +109,6 @@ export var RuntimeMessage;
|
|
|
102
109
|
export function isRuntimeMessage(message) {
|
|
103
110
|
return Object.values(RuntimeMessage).includes(message.type);
|
|
104
111
|
}
|
|
105
|
-
/**
|
|
106
|
-
* Unpacks runtime messages
|
|
107
|
-
*
|
|
108
|
-
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
109
|
-
* @param message - message (as it observed in storage / service)
|
|
110
|
-
* @returns unpacked runtime message
|
|
111
|
-
*
|
|
112
|
-
* @internal
|
|
113
|
-
*/
|
|
114
|
-
export function unpackRuntimeMessage(message) {
|
|
115
|
-
var _a;
|
|
116
|
-
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
117
|
-
const contents = IsoBuffer.from(message.contents.contents, "base64");
|
|
118
|
-
const decompressedMessage = decompress(contents);
|
|
119
|
-
const intoString = new TextDecoder().decode(decompressedMessage);
|
|
120
|
-
const asObj = JSON.parse(intoString);
|
|
121
|
-
message.contents.contents = asObj;
|
|
122
|
-
message.metadata.compressed = false;
|
|
123
|
-
}
|
|
124
|
-
if (message.type === MessageType.Operation) {
|
|
125
|
-
// legacy op format?
|
|
126
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
127
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
// new format
|
|
131
|
-
const innerContents = message.contents;
|
|
132
|
-
assert(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
133
|
-
message.type = innerContents.type;
|
|
134
|
-
message.contents = innerContents.contents;
|
|
135
|
-
}
|
|
136
|
-
return true;
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
// Legacy format, but it's already "unpacked",
|
|
140
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
141
|
-
// Or it's non-runtime message.
|
|
142
|
-
// Nothing to do in such case.
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
112
|
/**
|
|
147
113
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
148
114
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
@@ -168,6 +134,9 @@ export function getDeviceSpec() {
|
|
|
168
134
|
* It will define the store level mappings.
|
|
169
135
|
*/
|
|
170
136
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
137
|
+
/**
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
171
140
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
172
141
|
var _a, _b, _c, _d, _e, _f;
|
|
173
142
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
@@ -185,7 +154,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
185
154
|
this.flushMicroTaskExists = false;
|
|
186
155
|
this.savedOps = [];
|
|
187
156
|
this.consecutiveReconnects = 0;
|
|
188
|
-
this.compressedOpCount = 0;
|
|
189
157
|
this._disposed = false;
|
|
190
158
|
this.emitDirtyDocumentEvent = true;
|
|
191
159
|
this.defaultTelemetrySignalSampleCount = 100;
|
|
@@ -223,9 +191,29 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
223
191
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
224
192
|
}
|
|
225
193
|
};
|
|
194
|
+
let loadSummaryNumber;
|
|
195
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
196
|
+
// get the values from the metadata blob.
|
|
197
|
+
if (existing) {
|
|
198
|
+
this.createContainerMetadata = {
|
|
199
|
+
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
200
|
+
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
201
|
+
};
|
|
202
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
203
|
+
// the count is reset to 0.
|
|
204
|
+
loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
this.createContainerMetadata = {
|
|
208
|
+
createContainerRuntimeVersion: pkgVersion,
|
|
209
|
+
createContainerTimestamp: Date.now(),
|
|
210
|
+
};
|
|
211
|
+
loadSummaryNumber = 0;
|
|
212
|
+
}
|
|
213
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
226
214
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
227
215
|
this._connected = this.context.connected;
|
|
228
|
-
this.
|
|
216
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
229
217
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
230
218
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
231
219
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -237,18 +225,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
237
225
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
238
226
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
239
227
|
this.maxConsecutiveReconnects =
|
|
240
|
-
(
|
|
228
|
+
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
241
229
|
this._flushMode = runtimeOptions.flushMode;
|
|
242
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
243
|
-
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
244
|
-
// latency of processing a batch.
|
|
245
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
246
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
247
|
-
this.pendingAttachBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes, 64 * 1024);
|
|
248
|
-
this.pendingBatch = new BatchManager(runtimeOptions.maxBatchSizeInBytes);
|
|
249
230
|
const pendingRuntimeState = context.pendingLocalState;
|
|
250
|
-
const baseSnapshot = (
|
|
251
|
-
const maxSnapshotCacheDurationMs = (
|
|
231
|
+
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
232
|
+
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
252
233
|
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
253
234
|
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
254
235
|
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
@@ -262,6 +243,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
262
243
|
baseLogger: this.mc.logger,
|
|
263
244
|
existing,
|
|
264
245
|
metadata,
|
|
246
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
265
247
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
|
|
266
248
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
267
249
|
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
@@ -296,7 +278,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
296
278
|
}
|
|
297
279
|
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
298
280
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
299
|
-
this.deltaSender = this.deltaManager;
|
|
300
281
|
this.pendingStateManager = new PendingStateManager({
|
|
301
282
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
302
283
|
clientId: () => this.clientId,
|
|
@@ -307,8 +288,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
307
288
|
rollback: this.rollback.bind(this),
|
|
308
289
|
orderSequentially: this.orderSequentially.bind(this),
|
|
309
290
|
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
291
|
+
this.outbox = new Outbox({
|
|
292
|
+
shouldSend: () => this.canSendOps(),
|
|
293
|
+
pendingStateManager: this.pendingStateManager,
|
|
294
|
+
containerContext: this.context,
|
|
295
|
+
compressor: new OpCompressor(this.mc.logger),
|
|
296
|
+
config: {
|
|
297
|
+
compressionOptions: runtimeOptions.compressionOptions,
|
|
298
|
+
maxBatchSizeInBytes: runtimeOptions.maxBatchSizeInBytes,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
310
301
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
311
|
-
this.
|
|
302
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
312
303
|
});
|
|
313
304
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
314
305
|
this.dirtyContainer = this.context.attachState !== AttachState.Attached
|
|
@@ -376,26 +367,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
376
367
|
});
|
|
377
368
|
// logging hardware telemetry
|
|
378
369
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
379
|
-
let loadSummaryNumber;
|
|
380
|
-
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
381
|
-
// get the values from the metadata blob.
|
|
382
|
-
if (existing) {
|
|
383
|
-
this.createContainerMetadata = {
|
|
384
|
-
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
385
|
-
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
386
|
-
};
|
|
387
|
-
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
388
|
-
// the count is reset to 0.
|
|
389
|
-
loadSummaryNumber = (_f = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _f !== void 0 ? _f : 0;
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
this.createContainerMetadata = {
|
|
393
|
-
createContainerRuntimeVersion: pkgVersion,
|
|
394
|
-
createContainerTimestamp: Date.now(),
|
|
395
|
-
};
|
|
396
|
-
loadSummaryNumber = 0;
|
|
397
|
-
}
|
|
398
|
-
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
399
370
|
this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryNumber: loadSummaryNumber, summaryFormatVersion: metadata === null || metadata === void 0 ? void 0 : metadata.summaryFormatVersion, disableIsolatedChannels: metadata === null || metadata === void 0 ? void 0 : metadata.disableIsolatedChannels, gcVersion: metadata === null || metadata === void 0 ? void 0 : metadata.gcFeature }));
|
|
400
371
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
401
372
|
BindBatchTracker(this, this.logger);
|
|
@@ -409,8 +380,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
409
380
|
* @param requestHandler - Request handlers for the container runtime
|
|
410
381
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
411
382
|
* @param existing - (optional) When loading from an existing snapshot. Precedes context.existing if provided
|
|
383
|
+
* @param containerRuntimeCtor - (optional) Constructor to use to create the ContainerRuntime instance. This
|
|
384
|
+
* allows mixin classes to leverage this method to define their own async initializer.
|
|
412
385
|
*/
|
|
413
|
-
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing) {
|
|
386
|
+
static async load(context, registryEntries, requestHandler, runtimeOptions = {}, containerScope = context.scope, existing, containerRuntimeCtor = ContainerRuntime) {
|
|
414
387
|
var _a, _b, _c;
|
|
415
388
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
416
389
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
@@ -421,7 +394,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
421
394
|
runtimeVersion: pkgVersion,
|
|
422
395
|
},
|
|
423
396
|
});
|
|
424
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
397
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
398
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
399
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
400
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
425
401
|
const pendingRuntimeState = context.pendingLocalState;
|
|
426
402
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
427
403
|
const storage = !pendingRuntimeState ?
|
|
@@ -470,7 +446,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
470
446
|
}
|
|
471
447
|
}
|
|
472
448
|
}
|
|
473
|
-
const runtime = new
|
|
449
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
474
450
|
summaryOptions,
|
|
475
451
|
gcOptions,
|
|
476
452
|
loadSequenceNumberVerification,
|
|
@@ -484,7 +460,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
484
460
|
// delete these once runtime has seen them to save space
|
|
485
461
|
pendingRuntimeState.savedOps = [];
|
|
486
462
|
}
|
|
487
|
-
|
|
463
|
+
// Initialize the base state of the runtime before it's returned.
|
|
464
|
+
await runtime.initializeBaseState();
|
|
488
465
|
return runtime;
|
|
489
466
|
}
|
|
490
467
|
get options() {
|
|
@@ -533,9 +510,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
533
510
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
534
511
|
}
|
|
535
512
|
get disposed() { return this._disposed; }
|
|
536
|
-
get emptyBatch() {
|
|
537
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
538
|
-
}
|
|
539
513
|
get summarizer() {
|
|
540
514
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
541
515
|
return this._summarizer;
|
|
@@ -591,6 +565,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
591
565
|
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
592
566
|
: 0;
|
|
593
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
570
|
+
*/
|
|
571
|
+
async initializeBaseState() {
|
|
572
|
+
await this.initializeBaseSnapshotBlobs();
|
|
573
|
+
await this.garbageCollector.initializeBaseState();
|
|
574
|
+
}
|
|
594
575
|
dispose(error) {
|
|
595
576
|
var _a;
|
|
596
577
|
if (this._disposed) {
|
|
@@ -736,8 +717,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
736
717
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
737
718
|
var _a;
|
|
738
719
|
this.addMetadataToSummary(summaryTree);
|
|
739
|
-
if (this.
|
|
740
|
-
const content = JSON.stringify([...this.
|
|
720
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
721
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
741
722
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
742
723
|
}
|
|
743
724
|
const dataStoreAliases = this.dataStores.aliases;
|
|
@@ -901,31 +882,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
901
882
|
process(messageArg, local) {
|
|
902
883
|
var _a;
|
|
903
884
|
this.verifyNotClosed();
|
|
904
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
905
|
-
// There might be multiple container instances receiving same message
|
|
906
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
907
|
-
// but would not modify contents details
|
|
908
|
-
let message = Object.assign({}, messageArg);
|
|
909
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
910
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
911
|
-
// Old ops may contain empty string (I assume noops).
|
|
912
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
913
|
-
message.contents = JSON.parse(message.contents);
|
|
914
|
-
}
|
|
915
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
916
|
-
// This format was not shipped to production workflows.
|
|
917
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
918
885
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
919
886
|
this.savedOps.push(messageArg);
|
|
920
887
|
}
|
|
888
|
+
// Whether or not the message is actually a runtime message.
|
|
889
|
+
// It may be a legacy runtime message (ie already unpacked and ContainerMessageType)
|
|
890
|
+
// or something different, like a system message.
|
|
891
|
+
const runtimeMessage = messageArg.type === MessageType.Operation;
|
|
892
|
+
// Do shallow copy of message, as the processing flow will modify it.
|
|
893
|
+
const messageCopy = Object.assign({}, messageArg);
|
|
894
|
+
const message = this.remoteMessageProcessor.process(messageCopy);
|
|
921
895
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
922
896
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
923
897
|
// messages once a batch has been fully processed.
|
|
924
898
|
this.scheduleManager.beforeOpProcessing(message);
|
|
925
899
|
try {
|
|
926
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
927
|
-
// once all pieces are available
|
|
928
|
-
message = this.processRemoteChunkedMessage(message);
|
|
929
900
|
let localOpMetadata;
|
|
930
901
|
if (local && runtimeMessage) {
|
|
931
902
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
@@ -1039,83 +1010,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1039
1010
|
*/
|
|
1040
1011
|
flush() {
|
|
1041
1012
|
assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1042
|
-
this.
|
|
1043
|
-
|
|
1044
|
-
assert(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1045
|
-
}
|
|
1046
|
-
flushBatch(batch) {
|
|
1047
|
-
const length = batch.length;
|
|
1048
|
-
if (length > 1) {
|
|
1049
|
-
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
1050
|
-
batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
|
|
1051
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1052
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1053
|
-
// i.e. in the middle of op processing!
|
|
1054
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1055
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1056
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1057
|
-
// Tracked via ADO #1834
|
|
1058
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1059
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1060
|
-
}
|
|
1061
|
-
let clientSequenceNumber = -1;
|
|
1062
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1063
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1064
|
-
if (this.canSendOps()) {
|
|
1065
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1066
|
-
const batchToSend = [];
|
|
1067
|
-
for (const message of batch) {
|
|
1068
|
-
let contents = message.contents;
|
|
1069
|
-
let metadata = message.metadata;
|
|
1070
|
-
if (this.runtimeOptions.compressionOptions.minimumSize &&
|
|
1071
|
-
this.runtimeOptions.compressionOptions.minimumSize < message.contents.length) {
|
|
1072
|
-
this.compressedOpCount++;
|
|
1073
|
-
const copiedMessage = Object.assign({}, message.deserializedContent);
|
|
1074
|
-
const compressionStart = Date.now();
|
|
1075
|
-
const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(copiedMessage.contents));
|
|
1076
|
-
const compressedContents = compress(contentsAsBuffer);
|
|
1077
|
-
const compressedContent = IsoBuffer.from(compressedContents).toString("base64");
|
|
1078
|
-
const duration = Date.now() - compressionStart;
|
|
1079
|
-
if (this.compressedOpCount % 100) {
|
|
1080
|
-
this.mc.logger.sendPerformanceEvent({
|
|
1081
|
-
eventName: "compressedOp",
|
|
1082
|
-
duration,
|
|
1083
|
-
sizeBeforeCompression: message.contents.length,
|
|
1084
|
-
sizeAfterCompression: compressedContent.length,
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
copiedMessage.contents = compressedContent;
|
|
1088
|
-
const stringifiedContents = JSON.stringify(copiedMessage);
|
|
1089
|
-
if (stringifiedContents.length < message.contents.length) {
|
|
1090
|
-
contents = JSON.stringify(copiedMessage);
|
|
1091
|
-
metadata = Object.assign(Object.assign({}, message.metadata), { compressed: true });
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
batchToSend.push({ contents, metadata });
|
|
1095
|
-
}
|
|
1096
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1097
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1098
|
-
}
|
|
1099
|
-
else {
|
|
1100
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1101
|
-
// version that has support for batches (submitBatchFn)
|
|
1102
|
-
for (const message of batch) {
|
|
1103
|
-
clientSequenceNumber = this.context.submitFn(MessageType.Operation, message.deserializedContent, true, // batch
|
|
1104
|
-
message.metadata);
|
|
1105
|
-
}
|
|
1106
|
-
this.deltaSender.flush();
|
|
1107
|
-
}
|
|
1108
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1109
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1110
|
-
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1111
|
-
}
|
|
1112
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1113
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1114
|
-
for (const message of batch) {
|
|
1115
|
-
this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
|
|
1116
|
-
clientSequenceNumber++;
|
|
1117
|
-
}
|
|
1118
|
-
this.pendingStateManager.onFlush();
|
|
1013
|
+
this.outbox.flush();
|
|
1014
|
+
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1119
1015
|
}
|
|
1120
1016
|
orderSequentially(callback) {
|
|
1121
1017
|
let checkpoint;
|
|
@@ -1123,7 +1019,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1123
1019
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1124
1020
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1125
1021
|
// 2. There is no way to undo process of data store creation.
|
|
1126
|
-
checkpoint = this.
|
|
1022
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1127
1023
|
}
|
|
1128
1024
|
try {
|
|
1129
1025
|
this._orderSequentiallyCalls++;
|
|
@@ -1343,33 +1239,27 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1343
1239
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1344
1240
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1345
1241
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1242
|
+
const blobManagerUsedRoutes = [];
|
|
1346
1243
|
const dataStoreUsedRoutes = [];
|
|
1347
1244
|
for (const route of usedRoutes) {
|
|
1348
|
-
if (
|
|
1245
|
+
if (this.isBlobPath(route)) {
|
|
1246
|
+
blobManagerUsedRoutes.push(route);
|
|
1247
|
+
}
|
|
1248
|
+
else {
|
|
1349
1249
|
dataStoreUsedRoutes.push(route);
|
|
1350
1250
|
}
|
|
1351
1251
|
}
|
|
1352
|
-
|
|
1252
|
+
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1253
|
+
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1353
1254
|
}
|
|
1354
1255
|
/**
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1357
|
-
* @param unusedRoutes - The routes that are unused in all data stores and blobs in this Container.
|
|
1256
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1257
|
+
* tombstones.
|
|
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.
|
|
1358
1261
|
*/
|
|
1359
|
-
|
|
1360
|
-
var _a;
|
|
1361
|
-
/**
|
|
1362
|
-
* When running GC in tombstone mode, this is called to tombstone datastore routes that are unused. This
|
|
1363
|
-
* enables testing scenarios without actually deleting content. The content acts as if it's deleted to the
|
|
1364
|
-
* external user, but the internal runtime does not delete it in summarizes, etc.
|
|
1365
|
-
*/
|
|
1366
|
-
const tombstone = (_a = this.mc.config.getBoolean(testTombstoneKey)) !== null && _a !== void 0 ? _a : false;
|
|
1367
|
-
// TODO: add blobs
|
|
1368
|
-
if (tombstone) {
|
|
1369
|
-
// If blob routes are passed in here, tombstone will fail and hit an assert
|
|
1370
|
-
this.dataStores.deleteUnusedRoutes(unusedRoutes, tombstone);
|
|
1371
|
-
return;
|
|
1372
|
-
}
|
|
1262
|
+
updateUnusedRoutes(unusedRoutes, tombstone) {
|
|
1373
1263
|
const blobManagerUnusedRoutes = [];
|
|
1374
1264
|
const dataStoreUnusedRoutes = [];
|
|
1375
1265
|
for (const route of unusedRoutes) {
|
|
@@ -1380,8 +1270,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1380
1270
|
dataStoreUnusedRoutes.push(route);
|
|
1381
1271
|
}
|
|
1382
1272
|
}
|
|
1383
|
-
this.blobManager.
|
|
1384
|
-
this.dataStores.
|
|
1273
|
+
this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
|
|
1274
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1385
1275
|
}
|
|
1386
1276
|
/**
|
|
1387
1277
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1461,7 +1351,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1461
1351
|
const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
|
|
1462
1352
|
all: { summaryNumber },
|
|
1463
1353
|
});
|
|
1464
|
-
assert(this.
|
|
1354
|
+
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1465
1355
|
let latestSnapshotVersionId;
|
|
1466
1356
|
if (refreshLatestAck) {
|
|
1467
1357
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1634,40 +1524,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1634
1524
|
this.deltaManager.inbound.resume();
|
|
1635
1525
|
}
|
|
1636
1526
|
}
|
|
1637
|
-
processRemoteChunkedMessage(message) {
|
|
1638
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1639
|
-
return message;
|
|
1640
|
-
}
|
|
1641
|
-
const clientId = message.clientId;
|
|
1642
|
-
const chunkedContent = message.contents;
|
|
1643
|
-
this.addChunk(clientId, chunkedContent);
|
|
1644
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
1645
|
-
const newMessage = Object.assign({}, message);
|
|
1646
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1647
|
-
const serializedContent = this.chunkMap.get(clientId).join("");
|
|
1648
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
1649
|
-
newMessage.type = chunkedContent.originalType;
|
|
1650
|
-
this.clearPartialChunks(clientId);
|
|
1651
|
-
return newMessage;
|
|
1652
|
-
}
|
|
1653
|
-
return message;
|
|
1654
|
-
}
|
|
1655
|
-
addChunk(clientId, chunkedContent) {
|
|
1656
|
-
let map = this.chunkMap.get(clientId);
|
|
1657
|
-
if (map === undefined) {
|
|
1658
|
-
map = [];
|
|
1659
|
-
this.chunkMap.set(clientId, map);
|
|
1660
|
-
}
|
|
1661
|
-
assert(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
1662
|
-
map.push(chunkedContent.contents);
|
|
1663
|
-
}
|
|
1664
|
-
clearPartialChunks(clientId) {
|
|
1665
|
-
if (this.chunkMap.has(clientId)) {
|
|
1666
|
-
this.chunkMap.delete(clientId);
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
1527
|
hasPendingMessages() {
|
|
1670
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
1528
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
1671
1529
|
}
|
|
1672
1530
|
updateDocumentDirtyState(dirty) {
|
|
1673
1531
|
if (this.attachState !== AttachState.Attached) {
|
|
@@ -1711,7 +1569,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1711
1569
|
const deserializedContent = { type, contents };
|
|
1712
1570
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1713
1571
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1714
|
-
this.logger.
|
|
1572
|
+
this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
|
|
1715
1573
|
}
|
|
1716
1574
|
const message = {
|
|
1717
1575
|
contents: serializedContent,
|
|
@@ -1742,43 +1600,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1742
1600
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1743
1601
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1744
1602
|
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1745
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1746
|
-
|
|
1747
|
-
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1748
|
-
// when queue is not empty.
|
|
1749
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
1750
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1751
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
1752
|
-
throw new GenericError("BatchTooLarge",
|
|
1753
|
-
/* error */ undefined, {
|
|
1754
|
-
opSize: message.contents.length,
|
|
1755
|
-
count: this.pendingAttachBatch.length,
|
|
1756
|
-
limit: this.pendingAttachBatch.limit,
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1603
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1604
|
+
this.outbox.submitAttach(message);
|
|
1760
1605
|
}
|
|
1761
1606
|
else {
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1607
|
+
this.outbox.submit(message);
|
|
1608
|
+
}
|
|
1609
|
+
if (!this.currentlyBatching()) {
|
|
1610
|
+
this.flush();
|
|
1611
|
+
}
|
|
1612
|
+
else if (!this.flushMicroTaskExists) {
|
|
1613
|
+
this.flushMicroTaskExists = true;
|
|
1614
|
+
// 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
|
+
Promise.resolve().then(() => {
|
|
1617
|
+
this.flushMicroTaskExists = false;
|
|
1771
1618
|
this.flush();
|
|
1772
|
-
}
|
|
1773
|
-
else if (!this.flushMicroTaskExists) {
|
|
1774
|
-
this.flushMicroTaskExists = true;
|
|
1775
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1776
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1777
|
-
Promise.resolve().then(() => {
|
|
1778
|
-
this.flushMicroTaskExists = false;
|
|
1779
|
-
this.flush();
|
|
1780
|
-
});
|
|
1781
|
-
}
|
|
1619
|
+
}).catch((error) => { this.closeFn(error); });
|
|
1782
1620
|
}
|
|
1783
1621
|
}
|
|
1784
1622
|
catch (error) {
|
|
@@ -1793,7 +1631,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1793
1631
|
this.verifyNotClosed();
|
|
1794
1632
|
assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
1795
1633
|
// System message should not be sent in the middle of the batch.
|
|
1796
|
-
assert(this.
|
|
1634
|
+
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1797
1635
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1798
1636
|
return this.context.submitSummaryFn !== undefined
|
|
1799
1637
|
? this.context.submitSummaryFn(contents)
|
|
@@ -1927,7 +1765,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1927
1765
|
this.baseSnapshotBlobs = SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1928
1766
|
}
|
|
1929
1767
|
}
|
|
1930
|
-
async
|
|
1768
|
+
async initializeBaseSnapshotBlobs() {
|
|
1931
1769
|
var _a;
|
|
1932
1770
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1933
1771
|
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|