@fluidframework/container-runtime 2.0.0-dev.2.2.0.111723 → 2.0.0-dev.2.3.0.115467
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/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 +16 -33
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +71 -219
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.js +2 -2
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +2 -1
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +7 -16
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +41 -61
- 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 +7 -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} +17 -17
- 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/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +2 -2
- package/dist/opLifecycle/opCompressor.d.ts.map +1 -0
- package/dist/{opCompressor.js → opLifecycle/opCompressor.js} +16 -13
- package/dist/opLifecycle/opCompressor.js.map +1 -0
- package/dist/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +0 -0
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/dist/{opDecompressor.js → opLifecycle/opDecompressor.js} +5 -5
- 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 +16 -33
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +68 -215
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.js +1 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +2 -1
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +7 -16
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +19 -39
- 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} +17 -17
- 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/{opCompressor.d.ts → opLifecycle/opCompressor.d.ts} +2 -2
- package/lib/opLifecycle/opCompressor.d.ts.map +1 -0
- package/lib/{opCompressor.js → opLifecycle/opCompressor.js} +16 -13
- package/lib/opLifecycle/opCompressor.js.map +1 -0
- package/lib/{opDecompressor.d.ts → opLifecycle/opDecompressor.d.ts} +0 -0
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -0
- package/lib/{opDecompressor.js → opLifecycle/opDecompressor.js} +4 -4
- 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 +21 -34
- package/src/blobManager.ts +74 -19
- package/src/containerRuntime.ts +91 -278
- package/src/dataStoreContext.ts +1 -1
- package/src/dataStores.ts +2 -1
- package/src/garbageCollection.ts +33 -43
- package/src/garbageCollectionConstants.ts +35 -0
- package/src/gcSweepReadyUsageDetection.ts +1 -1
- package/src/index.ts +5 -4
- package/src/{batchManager.ts → opLifecycle/batchManager.ts} +30 -33
- package/src/opLifecycle/definitions.ts +44 -0
- package/src/opLifecycle/index.ts +17 -0
- package/src/{opCompressor.ts → opLifecycle/opCompressor.ts} +21 -16
- package/src/{opDecompressor.ts → opLifecycle/opDecompressor.ts} +8 -6
- 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 -42
- package/dist/batchManager.d.ts.map +0 -1
- package/dist/batchManager.js.map +0 -1
- package/dist/opCompressor.d.ts.map +0 -1
- package/dist/opCompressor.js.map +0 -1
- package/dist/opDecompressor.d.ts.map +0 -1
- package/dist/opDecompressor.js.map +0 -1
- package/lib/batchManager.d.ts +0 -42
- package/lib/batchManager.d.ts.map +0 -1
- package/lib/batchManager.js.map +0 -1
- package/lib/opCompressor.d.ts.map +0 -1
- package/lib/opCompressor.js.map +0 -1
- package/lib/opDecompressor.d.ts.map +0 -1
- package/lib/opDecompressor.js.map +0 -1
package/lib/containerRuntime.js
CHANGED
|
@@ -15,7 +15,6 @@ import { Summarizer } from "./summarizer";
|
|
|
15
15
|
import { SummaryManager } from "./summaryManager";
|
|
16
16
|
import { ReportOpPerfTelemetry, } from "./connectionTelemetry";
|
|
17
17
|
import { PendingStateManager, } from "./pendingStateManager";
|
|
18
|
-
import { BatchManager } from "./batchManager";
|
|
19
18
|
import { pkgVersion } from "./packageVersion";
|
|
20
19
|
import { BlobManager } from "./blobManager";
|
|
21
20
|
import { DataStores, getSummaryForDatastores } from "./dataStores";
|
|
@@ -25,12 +24,13 @@ import { OrderedClientCollection, OrderedClientElection } from "./orderedClientE
|
|
|
25
24
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
26
25
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
27
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
28
|
-
import { GarbageCollector, GCNodeType,
|
|
27
|
+
import { GarbageCollector, GCNodeType, } from "./garbageCollection";
|
|
28
|
+
import { gcTreeKey, } from "./garbageCollectionConstants";
|
|
29
29
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
30
30
|
import { BindBatchTracker } from "./batchTracker";
|
|
31
31
|
import { SerializedSnapshotStorage } from "./serializedSnapshotStorage";
|
|
32
32
|
import { ScheduleManager } from "./scheduleManager";
|
|
33
|
-
import { OpDecompressor } from "./
|
|
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
|
|
@@ -109,37 +109,6 @@ export var RuntimeMessage;
|
|
|
109
109
|
export function isRuntimeMessage(message) {
|
|
110
110
|
return Object.values(RuntimeMessage).includes(message.type);
|
|
111
111
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Unpacks runtime messages
|
|
114
|
-
*
|
|
115
|
-
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
116
|
-
* @param message - message (as it observed in storage / service)
|
|
117
|
-
* @returns unpacked runtime message
|
|
118
|
-
*
|
|
119
|
-
* @internal
|
|
120
|
-
*/
|
|
121
|
-
export function unpackRuntimeMessage(message) {
|
|
122
|
-
if (message.type === MessageType.Operation) {
|
|
123
|
-
// legacy op format?
|
|
124
|
-
if (message.contents.address !== undefined && message.contents.type === undefined) {
|
|
125
|
-
message.type = ContainerMessageType.FluidDataStoreOp;
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
// new format
|
|
129
|
-
const innerContents = message.contents;
|
|
130
|
-
message.type = innerContents.type;
|
|
131
|
-
message.contents = innerContents.contents;
|
|
132
|
-
}
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
// Legacy format, but it's already "unpacked",
|
|
137
|
-
// i.e. message.type is actually ContainerMessageType.
|
|
138
|
-
// Or it's non-runtime message.
|
|
139
|
-
// Nothing to do in such case.
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
112
|
/**
|
|
144
113
|
* Legacy ID for the built-in AgentScheduler. To minimize disruption while removing it, retaining this as a
|
|
145
114
|
* special-case for document dirty state. Ultimately we should have no special-cases from the
|
|
@@ -165,6 +134,9 @@ export function getDeviceSpec() {
|
|
|
165
134
|
* It will define the store level mappings.
|
|
166
135
|
*/
|
|
167
136
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
137
|
+
/**
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
168
140
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
169
141
|
var _a, _b, _c, _d, _e, _f;
|
|
170
142
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
@@ -177,7 +149,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
177
149
|
this._storage = _storage;
|
|
178
150
|
this.requestHandler = requestHandler;
|
|
179
151
|
this.summaryConfiguration = summaryConfiguration;
|
|
180
|
-
this.opDecompressor = new OpDecompressor();
|
|
181
152
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
182
153
|
this._orderSequentiallyCalls = 0;
|
|
183
154
|
this.flushMicroTaskExists = false;
|
|
@@ -242,7 +213,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
242
213
|
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
243
214
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
244
215
|
this._connected = this.context.connected;
|
|
245
|
-
this.
|
|
216
|
+
this.remoteMessageProcessor = new RemoteMessageProcessor(new OpSplitter(chunks), new OpDecompressor());
|
|
246
217
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
247
218
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
248
219
|
if (this.summaryConfiguration.state === "enabled") {
|
|
@@ -256,20 +227,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
256
227
|
this.maxConsecutiveReconnects =
|
|
257
228
|
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
258
229
|
this._flushMode = runtimeOptions.flushMode;
|
|
259
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression
|
|
260
|
-
// & bandwidth usage, but at the same time we want to send these ops sooner, to reduce overall
|
|
261
|
-
// latency of processing a batch.
|
|
262
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
263
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
264
|
-
this.pendingAttachBatch = new BatchManager(this.mc.logger, {
|
|
265
|
-
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
266
|
-
softLimit: 64 * 1024,
|
|
267
|
-
compressionOptions: runtimeOptions.compressionOptions
|
|
268
|
-
});
|
|
269
|
-
this.pendingBatch = new BatchManager(this.mc.logger, {
|
|
270
|
-
hardLimit: runtimeOptions.maxBatchSizeInBytes,
|
|
271
|
-
compressionOptions: runtimeOptions.compressionOptions
|
|
272
|
-
});
|
|
273
230
|
const pendingRuntimeState = context.pendingLocalState;
|
|
274
231
|
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
275
232
|
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
@@ -321,7 +278,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
321
278
|
}
|
|
322
279
|
}, (blobPath) => this.garbageCollector.nodeUpdated(blobPath, "Loaded"), this, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pendingAttachmentBlobs);
|
|
323
280
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, () => this.clientId, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
324
|
-
this.deltaSender = this.deltaManager;
|
|
325
281
|
this.pendingStateManager = new PendingStateManager({
|
|
326
282
|
applyStashedOp: this.applyStashedOp.bind(this),
|
|
327
283
|
clientId: () => this.clientId,
|
|
@@ -332,8 +288,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
332
288
|
rollback: this.rollback.bind(this),
|
|
333
289
|
orderSequentially: this.orderSequentially.bind(this),
|
|
334
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
|
+
});
|
|
335
301
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
336
|
-
this.
|
|
302
|
+
this.remoteMessageProcessor.clearPartialMessagesFor(clientId);
|
|
337
303
|
});
|
|
338
304
|
this.summaryCollection = new SummaryCollection(this.deltaManager, this.logger);
|
|
339
305
|
this.dirtyContainer = this.context.attachState !== AttachState.Attached
|
|
@@ -414,8 +380,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
414
380
|
* @param requestHandler - Request handlers for the container runtime
|
|
415
381
|
* @param runtimeOptions - Additional options to be passed to the runtime
|
|
416
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.
|
|
417
385
|
*/
|
|
418
|
-
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) {
|
|
419
387
|
var _a, _b, _c;
|
|
420
388
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
421
389
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
@@ -426,8 +394,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
426
394
|
runtimeVersion: pkgVersion,
|
|
427
395
|
},
|
|
428
396
|
});
|
|
429
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
430
|
-
|
|
397
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = {
|
|
398
|
+
minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
399
|
+
compressionAlgorithm: CompressionAlgorithms.lz4
|
|
400
|
+
}, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
431
401
|
const pendingRuntimeState = context.pendingLocalState;
|
|
432
402
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
433
403
|
const storage = !pendingRuntimeState ?
|
|
@@ -476,7 +446,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
476
446
|
}
|
|
477
447
|
}
|
|
478
448
|
}
|
|
479
|
-
const runtime = new
|
|
449
|
+
const runtime = new containerRuntimeCtor(context, registry, metadata, electedSummarizerData, chunks !== null && chunks !== void 0 ? chunks : [], aliases !== null && aliases !== void 0 ? aliases : [], {
|
|
480
450
|
summaryOptions,
|
|
481
451
|
gcOptions,
|
|
482
452
|
loadSequenceNumberVerification,
|
|
@@ -540,9 +510,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
540
510
|
return (_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.electedClientId;
|
|
541
511
|
}
|
|
542
512
|
get disposed() { return this._disposed; }
|
|
543
|
-
get emptyBatch() {
|
|
544
|
-
return this.pendingBatch.empty && this.pendingAttachBatch.empty;
|
|
545
|
-
}
|
|
546
513
|
get summarizer() {
|
|
547
514
|
assert(this._summarizer !== undefined, 0x257 /* "This is not summarizing container" */);
|
|
548
515
|
return this._summarizer;
|
|
@@ -750,8 +717,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
750
717
|
addContainerStateToSummary(summaryTree, fullTree, trackState, telemetryContext) {
|
|
751
718
|
var _a;
|
|
752
719
|
this.addMetadataToSummary(summaryTree);
|
|
753
|
-
if (this.
|
|
754
|
-
const content = JSON.stringify([...this.
|
|
720
|
+
if (this.remoteMessageProcessor.partialMessages.size > 0) {
|
|
721
|
+
const content = JSON.stringify([...this.remoteMessageProcessor.partialMessages]);
|
|
755
722
|
addBlobToSummary(summaryTree, chunksBlobName, content);
|
|
756
723
|
}
|
|
757
724
|
const dataStoreAliases = this.dataStores.aliases;
|
|
@@ -915,32 +882,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
915
882
|
process(messageArg, local) {
|
|
916
883
|
var _a;
|
|
917
884
|
this.verifyNotClosed();
|
|
918
|
-
// Do shallow copy of message, as methods below will modify it.
|
|
919
|
-
// There might be multiple container instances receiving same message
|
|
920
|
-
// We do not need to make deep copy, as each layer will just replace message.content itself,
|
|
921
|
-
// but would not modify contents details
|
|
922
|
-
let message = Object.assign({}, messageArg);
|
|
923
|
-
// back-compat: ADO #1385: eventually should become unconditional, but only for runtime messages!
|
|
924
|
-
// System message may have no contents, or in some cases (mostly for back-compat) they may have actual objects.
|
|
925
|
-
// Old ops may contain empty string (I assume noops).
|
|
926
|
-
if (typeof message.contents === "string" && message.contents !== "") {
|
|
927
|
-
message.contents = JSON.parse(message.contents);
|
|
928
|
-
}
|
|
929
|
-
message = this.opDecompressor.processMessage(message);
|
|
930
|
-
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
931
|
-
// This format was not shipped to production workflows.
|
|
932
|
-
const runtimeMessage = unpackRuntimeMessage(message);
|
|
933
885
|
if ((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) {
|
|
934
886
|
this.savedOps.push(messageArg);
|
|
935
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);
|
|
936
895
|
// Surround the actual processing of the operation with messages to the schedule manager indicating
|
|
937
896
|
// the beginning and end. This allows it to emit appropriate events and/or pause the processing of new
|
|
938
897
|
// messages once a batch has been fully processed.
|
|
939
898
|
this.scheduleManager.beforeOpProcessing(message);
|
|
940
899
|
try {
|
|
941
|
-
// Chunk processing must come first given that we will transform the message to the unchunked version
|
|
942
|
-
// once all pieces are available
|
|
943
|
-
message = this.processRemoteChunkedMessage(message);
|
|
944
900
|
let localOpMetadata;
|
|
945
901
|
if (local && runtimeMessage) {
|
|
946
902
|
localOpMetadata = this.pendingStateManager.processPendingLocalMessage(message);
|
|
@@ -1054,74 +1010,21 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1054
1010
|
*/
|
|
1055
1011
|
flush() {
|
|
1056
1012
|
assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1057
|
-
this.
|
|
1058
|
-
|
|
1059
|
-
assert(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1060
|
-
}
|
|
1061
|
-
flushBatch(batch) {
|
|
1062
|
-
var _a;
|
|
1063
|
-
const length = batch.length;
|
|
1064
|
-
if (length > 1) {
|
|
1065
|
-
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
1066
|
-
batch[length - 1].metadata = Object.assign(Object.assign({}, batch[length - 1].metadata), { batch: false });
|
|
1067
|
-
// This assert fires for the following reason (there might be more cases like that):
|
|
1068
|
-
// AgentScheduler will send ops in response to ConsensusRegisterCollection's "atomicChanged" event handler,
|
|
1069
|
-
// i.e. in the middle of op processing!
|
|
1070
|
-
// Sending ops while processing ops is not good idea - it's not defined when
|
|
1071
|
-
// referenceSequenceNumber changes in op processing sequence (at the beginning or end of op processing),
|
|
1072
|
-
// If we send ops in response to processing multiple ops, then we for sure hit this assert!
|
|
1073
|
-
// Tracked via ADO #1834
|
|
1074
|
-
// assert(batch[0].referenceSequenceNumber === batch[length - 1].referenceSequenceNumber,
|
|
1075
|
-
// "Batch should be generated synchronously, without processing ops in the middle!");
|
|
1076
|
-
}
|
|
1077
|
-
let clientSequenceNumber = -1;
|
|
1078
|
-
// Did we disconnect in the middle of turn-based batch?
|
|
1079
|
-
// If so, do nothing, as pending state manager will resubmit it correctly on reconnect.
|
|
1080
|
-
if (this.canSendOps()) {
|
|
1081
|
-
if (this.context.submitBatchFn !== undefined) {
|
|
1082
|
-
const batchToSend = [];
|
|
1083
|
-
for (const message of batch) {
|
|
1084
|
-
batchToSend.push({ contents: message.contents, metadata: message.metadata });
|
|
1085
|
-
}
|
|
1086
|
-
// returns clientSequenceNumber of last message in a batch
|
|
1087
|
-
clientSequenceNumber = this.context.submitBatchFn(batchToSend);
|
|
1088
|
-
}
|
|
1089
|
-
else {
|
|
1090
|
-
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1091
|
-
// version that has support for batches (submitBatchFn)
|
|
1092
|
-
for (const message of batch) {
|
|
1093
|
-
// Legacy path doesn't support compressed payloads and will submit uncompressed payload anyways
|
|
1094
|
-
if ((_a = message.metadata) === null || _a === void 0 ? void 0 : _a.compressed) {
|
|
1095
|
-
delete message.metadata.compressed;
|
|
1096
|
-
}
|
|
1097
|
-
clientSequenceNumber = this.context.submitFn(MessageType.Operation, message.deserializedContent, true, // batch
|
|
1098
|
-
message.metadata);
|
|
1099
|
-
}
|
|
1100
|
-
this.deltaSender.flush();
|
|
1101
|
-
}
|
|
1102
|
-
// Convert from clientSequenceNumber of last message in the batch to clientSequenceNumber of first message.
|
|
1103
|
-
clientSequenceNumber -= batch.length - 1;
|
|
1104
|
-
assert(clientSequenceNumber >= 0, 0x3d0 /* clientSequenceNumber can't be negative */);
|
|
1105
|
-
}
|
|
1106
|
-
// Let the PendingStateManager know that a message was submitted.
|
|
1107
|
-
// In future, need to shift toward keeping batch as a whole!
|
|
1108
|
-
for (const message of batch) {
|
|
1109
|
-
this.pendingStateManager.onSubmitMessage(message.deserializedContent.type, clientSequenceNumber, message.referenceSequenceNumber, message.deserializedContent.contents, message.localOpMetadata, message.metadata);
|
|
1110
|
-
clientSequenceNumber++;
|
|
1111
|
-
}
|
|
1112
|
-
this.pendingStateManager.onFlush();
|
|
1013
|
+
this.outbox.flush();
|
|
1014
|
+
assert(this.outbox.isEmpty, 0x3cf /* reentrancy */);
|
|
1113
1015
|
}
|
|
1114
1016
|
orderSequentially(callback) {
|
|
1115
1017
|
let checkpoint;
|
|
1018
|
+
let result;
|
|
1116
1019
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1117
1020
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
1118
1021
|
// 1. It would not help, as we flush attach ops as they become available.
|
|
1119
1022
|
// 2. There is no way to undo process of data store creation.
|
|
1120
|
-
checkpoint = this.
|
|
1023
|
+
checkpoint = this.outbox.checkpoint().mainBatch;
|
|
1121
1024
|
}
|
|
1122
1025
|
try {
|
|
1123
1026
|
this._orderSequentiallyCalls++;
|
|
1124
|
-
callback();
|
|
1027
|
+
result = callback();
|
|
1125
1028
|
}
|
|
1126
1029
|
catch (error) {
|
|
1127
1030
|
if (checkpoint) {
|
|
@@ -1149,6 +1052,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1149
1052
|
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1150
1053
|
this.flush();
|
|
1151
1054
|
}
|
|
1055
|
+
return result;
|
|
1152
1056
|
}
|
|
1153
1057
|
async createDataStore(pkg) {
|
|
1154
1058
|
const internalId = uuid();
|
|
@@ -1337,18 +1241,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1337
1241
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1338
1242
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1339
1243
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1244
|
+
const blobManagerUsedRoutes = [];
|
|
1340
1245
|
const dataStoreUsedRoutes = [];
|
|
1341
1246
|
for (const route of usedRoutes) {
|
|
1342
|
-
if (
|
|
1247
|
+
if (this.isBlobPath(route)) {
|
|
1248
|
+
blobManagerUsedRoutes.push(route);
|
|
1249
|
+
}
|
|
1250
|
+
else {
|
|
1343
1251
|
dataStoreUsedRoutes.push(route);
|
|
1344
1252
|
}
|
|
1345
1253
|
}
|
|
1346
|
-
|
|
1254
|
+
this.blobManager.updateUsedRoutes(blobManagerUsedRoutes);
|
|
1255
|
+
this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1347
1256
|
}
|
|
1348
1257
|
/**
|
|
1349
1258
|
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1350
1259
|
* tombstones.
|
|
1351
|
-
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1260
|
+
* @param unusedRoutes - The routes that are unused in all data stores and attachment blobs in this Container.
|
|
1352
1261
|
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1353
1262
|
* are deleted.
|
|
1354
1263
|
*/
|
|
@@ -1363,10 +1272,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1363
1272
|
dataStoreUnusedRoutes.push(route);
|
|
1364
1273
|
}
|
|
1365
1274
|
}
|
|
1366
|
-
|
|
1367
|
-
if (!tombstone) {
|
|
1368
|
-
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
1369
|
-
}
|
|
1275
|
+
this.blobManager.updateUnusedRoutes(blobManagerUnusedRoutes, tombstone);
|
|
1370
1276
|
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1371
1277
|
}
|
|
1372
1278
|
/**
|
|
@@ -1447,7 +1353,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1447
1353
|
const summaryNumberLogger = ChildLogger.create(summaryLogger, undefined, {
|
|
1448
1354
|
all: { summaryNumber },
|
|
1449
1355
|
});
|
|
1450
|
-
assert(this.
|
|
1356
|
+
assert(this.outbox.isEmpty, 0x3d1 /* Can't trigger summary in the middle of a batch */);
|
|
1451
1357
|
let latestSnapshotVersionId;
|
|
1452
1358
|
if (refreshLatestAck) {
|
|
1453
1359
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1620,40 +1526,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1620
1526
|
this.deltaManager.inbound.resume();
|
|
1621
1527
|
}
|
|
1622
1528
|
}
|
|
1623
|
-
processRemoteChunkedMessage(message) {
|
|
1624
|
-
if (message.type !== ContainerMessageType.ChunkedOp) {
|
|
1625
|
-
return message;
|
|
1626
|
-
}
|
|
1627
|
-
const clientId = message.clientId;
|
|
1628
|
-
const chunkedContent = message.contents;
|
|
1629
|
-
this.addChunk(clientId, chunkedContent);
|
|
1630
|
-
if (chunkedContent.chunkId === chunkedContent.totalChunks) {
|
|
1631
|
-
const newMessage = Object.assign({}, message);
|
|
1632
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1633
|
-
const serializedContent = this.chunkMap.get(clientId).join("");
|
|
1634
|
-
newMessage.contents = JSON.parse(serializedContent);
|
|
1635
|
-
newMessage.type = chunkedContent.originalType;
|
|
1636
|
-
this.clearPartialChunks(clientId);
|
|
1637
|
-
return newMessage;
|
|
1638
|
-
}
|
|
1639
|
-
return message;
|
|
1640
|
-
}
|
|
1641
|
-
addChunk(clientId, chunkedContent) {
|
|
1642
|
-
let map = this.chunkMap.get(clientId);
|
|
1643
|
-
if (map === undefined) {
|
|
1644
|
-
map = [];
|
|
1645
|
-
this.chunkMap.set(clientId, map);
|
|
1646
|
-
}
|
|
1647
|
-
assert(chunkedContent.chunkId === map.length + 1, 0x131 /* "Mismatch between new chunkId and expected chunkMap" */); // 1-based indexing
|
|
1648
|
-
map.push(chunkedContent.contents);
|
|
1649
|
-
}
|
|
1650
|
-
clearPartialChunks(clientId) {
|
|
1651
|
-
if (this.chunkMap.has(clientId)) {
|
|
1652
|
-
this.chunkMap.delete(clientId);
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
1529
|
hasPendingMessages() {
|
|
1656
|
-
return this.pendingStateManager.hasPendingMessages() || !this.
|
|
1530
|
+
return this.pendingStateManager.hasPendingMessages() || !this.outbox.isEmpty;
|
|
1657
1531
|
}
|
|
1658
1532
|
updateDocumentDirtyState(dirty) {
|
|
1659
1533
|
if (this.attachState !== AttachState.Attached) {
|
|
@@ -1691,14 +1565,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1691
1565
|
return this.blobManager.createBlob(blob);
|
|
1692
1566
|
}
|
|
1693
1567
|
submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
|
|
1694
|
-
var _a, _b, _c, _d;
|
|
1695
1568
|
this.verifyNotClosed();
|
|
1696
1569
|
// There should be no ops in detached container state!
|
|
1697
1570
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
1698
1571
|
const deserializedContent = { type, contents };
|
|
1699
1572
|
const serializedContent = JSON.stringify(deserializedContent);
|
|
1700
1573
|
if (this.deltaManager.readOnlyInfo.readonly) {
|
|
1701
|
-
this.logger.
|
|
1574
|
+
this.logger.sendTelemetryEvent({ eventName: "SubmitOpInReadonly", connected: this.connected });
|
|
1702
1575
|
}
|
|
1703
1576
|
const message = {
|
|
1704
1577
|
contents: serializedContent,
|
|
@@ -1729,43 +1602,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1729
1602
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1730
1603
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1731
1604
|
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1732
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1733
|
-
|
|
1734
|
-
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1735
|
-
// when queue is not empty.
|
|
1736
|
-
// Flush queue & retry. Failure on retry would mean - single message is bigger than hard limit
|
|
1737
|
-
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
1738
|
-
if (!this.pendingAttachBatch.push(message)) {
|
|
1739
|
-
throw new GenericError("BatchTooLarge",
|
|
1740
|
-
/* error */ undefined, {
|
|
1741
|
-
opSize: (_b = ((_a = message.contents) === null || _a === void 0 ? void 0 : _a.length)) !== null && _b !== void 0 ? _b : 0,
|
|
1742
|
-
count: this.pendingAttachBatch.length,
|
|
1743
|
-
limit: this.pendingAttachBatch.options.hardLimit,
|
|
1744
|
-
});
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1605
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.disableAttachOpReorder") !== true) {
|
|
1606
|
+
this.outbox.submitAttach(message);
|
|
1747
1607
|
}
|
|
1748
1608
|
else {
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1609
|
+
this.outbox.submit(message);
|
|
1610
|
+
}
|
|
1611
|
+
if (!this.currentlyBatching()) {
|
|
1612
|
+
this.flush();
|
|
1613
|
+
}
|
|
1614
|
+
else if (!this.flushMicroTaskExists) {
|
|
1615
|
+
this.flushMicroTaskExists = true;
|
|
1616
|
+
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1617
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1618
|
+
Promise.resolve().then(() => {
|
|
1619
|
+
this.flushMicroTaskExists = false;
|
|
1758
1620
|
this.flush();
|
|
1759
|
-
}
|
|
1760
|
-
else if (!this.flushMicroTaskExists) {
|
|
1761
|
-
this.flushMicroTaskExists = true;
|
|
1762
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1763
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1764
|
-
Promise.resolve().then(() => {
|
|
1765
|
-
this.flushMicroTaskExists = false;
|
|
1766
|
-
this.flush();
|
|
1767
|
-
});
|
|
1768
|
-
}
|
|
1621
|
+
}).catch((error) => { this.closeFn(error); });
|
|
1769
1622
|
}
|
|
1770
1623
|
}
|
|
1771
1624
|
catch (error) {
|
|
@@ -1780,7 +1633,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1780
1633
|
this.verifyNotClosed();
|
|
1781
1634
|
assert(this.connected, 0x133 /* "Container disconnected when trying to submit system message" */);
|
|
1782
1635
|
// System message should not be sent in the middle of the batch.
|
|
1783
|
-
assert(this.
|
|
1636
|
+
assert(this.outbox.isEmpty, 0x3d4 /* System op in the middle of a batch */);
|
|
1784
1637
|
// back-compat: ADO #1385: Make this call unconditional in the future
|
|
1785
1638
|
return this.context.submitSummaryFn !== undefined
|
|
1786
1639
|
? this.context.submitSummaryFn(contents)
|