@fluidframework/container-runtime 2.0.0-dev.1.4.5.105745 → 2.0.0-dev.2.2.0.111723
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/batchManager.d.ts +11 -6
- package/dist/batchManager.d.ts.map +1 -1
- package/dist/batchManager.js +23 -13
- package/dist/batchManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +74 -20
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +190 -137
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.d.ts.map +1 -1
- package/dist/dataStore.js +6 -0
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +14 -21
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +71 -57
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStoreContexts.js +1 -1
- package/dist/dataStoreContexts.js.map +1 -1
- package/dist/dataStores.d.ts +11 -10
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +50 -20
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +36 -19
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +207 -121
- package/dist/garbageCollection.js.map +1 -1
- package/dist/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/dist/gcSweepReadyUsageDetection.js +3 -12
- package/dist/gcSweepReadyUsageDetection.js.map +1 -1
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/opCompressor.d.ts +18 -0
- package/dist/opCompressor.d.ts.map +1 -0
- package/dist/opCompressor.js +50 -0
- package/dist/opCompressor.js.map +1 -0
- package/dist/opDecompressor.d.ts +20 -0
- package/dist/opDecompressor.d.ts.map +1 -0
- package/dist/opDecompressor.js +72 -0
- package/dist/opDecompressor.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/pendingStateManager.d.ts +6 -26
- package/dist/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +42 -62
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +3 -2
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +10 -3
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/scheduleManager.js.map +1 -1
- package/dist/summarizer.js +7 -2
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerClientElection.js +1 -1
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +0 -3
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +19 -2
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryFormat.d.ts +4 -2
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +3 -2
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +10 -6
- package/dist/summaryManager.js.map +1 -1
- package/garbageCollection.md +27 -22
- package/lib/batchManager.d.ts +11 -6
- package/lib/batchManager.d.ts.map +1 -1
- package/lib/batchManager.js +23 -13
- package/lib/batchManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +74 -20
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +189 -136
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.d.ts.map +1 -1
- package/lib/dataStore.js +6 -0
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +14 -21
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +75 -61
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStoreContexts.js +1 -1
- package/lib/dataStoreContexts.js.map +1 -1
- package/lib/dataStores.d.ts +11 -10
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +53 -23
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +36 -19
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +208 -122
- package/lib/garbageCollection.js.map +1 -1
- package/lib/gcSweepReadyUsageDetection.d.ts.map +1 -1
- package/lib/gcSweepReadyUsageDetection.js +3 -12
- package/lib/gcSweepReadyUsageDetection.js.map +1 -1
- package/lib/index.d.ts +4 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/opCompressor.d.ts +18 -0
- package/lib/opCompressor.d.ts.map +1 -0
- package/lib/opCompressor.js +46 -0
- package/lib/opCompressor.js.map +1 -0
- package/lib/opDecompressor.d.ts +20 -0
- package/lib/opDecompressor.d.ts.map +1 -0
- package/lib/opDecompressor.js +68 -0
- package/lib/opDecompressor.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/pendingStateManager.d.ts +6 -26
- package/lib/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +42 -62
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +3 -2
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +10 -3
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/scheduleManager.js.map +1 -1
- package/lib/summarizer.js +7 -2
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerClientElection.js +1 -1
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +0 -3
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +19 -2
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryFormat.d.ts +4 -2
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +3 -2
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +10 -6
- package/lib/summaryManager.js.map +1 -1
- package/package.json +37 -63
- package/prettier.config.cjs +8 -0
- package/src/batchManager.ts +32 -15
- package/src/containerRuntime.ts +260 -156
- package/src/dataStore.ts +13 -1
- package/src/dataStoreContext.ts +100 -76
- package/src/dataStoreContexts.ts +1 -1
- package/src/dataStores.ts +61 -23
- package/src/garbageCollection.ts +257 -126
- package/src/gcSweepReadyUsageDetection.ts +2 -10
- package/src/index.ts +4 -4
- package/src/opCompressor.ts +59 -0
- package/src/opDecompressor.ts +82 -0
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +57 -96
- package/src/runningSummarizer.ts +11 -3
- package/src/scheduleManager.ts +1 -0
- package/src/summarizer.ts +6 -6
- package/src/summarizerClientElection.ts +1 -1
- package/src/summarizerHeuristics.ts +0 -3
- package/src/summarizerTypes.ts +20 -7
- package/src/summaryFormat.ts +4 -2
- package/src/summaryGenerator.ts +3 -2
- package/src/summaryManager.ts +18 -7
package/lib/containerRuntime.js
CHANGED
|
@@ -30,6 +30,7 @@ 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 "./opDecompressor";
|
|
33
34
|
export var ContainerMessageType;
|
|
34
35
|
(function (ContainerMessageType) {
|
|
35
36
|
// An op to be delivered to store
|
|
@@ -58,6 +59,7 @@ export const DefaultSummaryConfiguration = {
|
|
|
58
59
|
summarizerClientElection: false,
|
|
59
60
|
nonRuntimeOpWeight: 0.1,
|
|
60
61
|
runtimeOpWeight: 1.0,
|
|
62
|
+
nonRuntimeHeuristicThreshold: 20,
|
|
61
63
|
};
|
|
62
64
|
/**
|
|
63
65
|
* Accepted header keys for requests coming to the runtime.
|
|
@@ -74,8 +76,20 @@ export var RuntimeHeaders;
|
|
|
74
76
|
/** True if the request is coming from an IFluidHandle. */
|
|
75
77
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
76
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 = {}));
|
|
77
86
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
78
87
|
const defaultFlushMode = FlushMode.TurnBased;
|
|
88
|
+
// The actual limit is 1Mb (socket.io and Kafka limits)
|
|
89
|
+
// We can't estimate it fully, as we
|
|
90
|
+
// - do not know what properties relay service will add
|
|
91
|
+
// - we do not stringify final op, thus we do not know how much escaping will be added.
|
|
92
|
+
const defaultMaxBatchSizeInBytes = 950 * 1024;
|
|
79
93
|
/**
|
|
80
94
|
* @deprecated - use ContainerRuntimeMessage instead
|
|
81
95
|
*/
|
|
@@ -93,15 +107,12 @@ export var RuntimeMessage;
|
|
|
93
107
|
* @deprecated - please use version in driver-utils
|
|
94
108
|
*/
|
|
95
109
|
export function isRuntimeMessage(message) {
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
110
|
+
return Object.values(RuntimeMessage).includes(message.type);
|
|
100
111
|
}
|
|
101
112
|
/**
|
|
102
113
|
* Unpacks runtime messages
|
|
103
114
|
*
|
|
104
|
-
* @remarks This API makes no promises regarding backward-
|
|
115
|
+
* @remarks This API makes no promises regarding backward-compatibility. This is internal API.
|
|
105
116
|
* @param message - message (as it observed in storage / service)
|
|
106
117
|
* @returns unpacked runtime message
|
|
107
118
|
*
|
|
@@ -116,7 +127,6 @@ export function unpackRuntimeMessage(message) {
|
|
|
116
127
|
else {
|
|
117
128
|
// new format
|
|
118
129
|
const innerContents = message.contents;
|
|
119
|
-
assert(innerContents.type !== undefined, 0x121 /* "Undefined inner contents type!" */);
|
|
120
130
|
message.type = innerContents.type;
|
|
121
131
|
message.contents = innerContents.contents;
|
|
122
132
|
}
|
|
@@ -156,7 +166,7 @@ export function getDeviceSpec() {
|
|
|
156
166
|
*/
|
|
157
167
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
158
168
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, _storage, requestHandler, summaryConfiguration) {
|
|
159
|
-
var _a, _b, _c, _d;
|
|
169
|
+
var _a, _b, _c, _d, _e, _f;
|
|
160
170
|
if (summaryConfiguration === void 0) { summaryConfiguration = Object.assign(Object.assign({}, DefaultSummaryConfiguration), (_a = runtimeOptions.summaryOptions) === null || _a === void 0 ? void 0 : _a.summaryConfigOverrides); }
|
|
161
171
|
super();
|
|
162
172
|
this.context = context;
|
|
@@ -167,9 +177,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
167
177
|
this._storage = _storage;
|
|
168
178
|
this.requestHandler = requestHandler;
|
|
169
179
|
this.summaryConfiguration = summaryConfiguration;
|
|
180
|
+
this.opDecompressor = new OpDecompressor();
|
|
170
181
|
this.defaultMaxConsecutiveReconnects = 7;
|
|
171
182
|
this._orderSequentiallyCalls = 0;
|
|
172
|
-
this.
|
|
183
|
+
this.flushMicroTaskExists = false;
|
|
173
184
|
this.savedOps = [];
|
|
174
185
|
this.consecutiveReconnects = 0;
|
|
175
186
|
this._disposed = false;
|
|
@@ -181,12 +192,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
181
192
|
signalTimestamp: 0,
|
|
182
193
|
trackingSignalSequenceNumber: undefined,
|
|
183
194
|
};
|
|
184
|
-
// Provide lower soft limit - we want to have some number of ops to get efficiency in compression & bandwidth usage,
|
|
185
|
-
// but at the same time we want to send these ops sooner, to reduce overall latency of processing a batch.
|
|
186
|
-
// So there is some ballance here, that depends on compression algorithm and its efficiency working with smaller
|
|
187
|
-
// payloads. That number represents final (compressed) bits (once compression is implemented).
|
|
188
|
-
this.pendingAttachBatch = new BatchManager(64 * 1024);
|
|
189
|
-
this.pendingBatch = new BatchManager();
|
|
190
195
|
this.summarizeOnDemand = (...args) => {
|
|
191
196
|
if (this.clientDetails.type === summarizerClientType) {
|
|
192
197
|
return this.summarizer.summarizeOnDemand(...args);
|
|
@@ -215,6 +220,26 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
215
220
|
throw new UsageError(`Can't summarize, disableSummaries: ${this.summariesDisabled}`);
|
|
216
221
|
}
|
|
217
222
|
};
|
|
223
|
+
let loadSummaryNumber;
|
|
224
|
+
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
225
|
+
// get the values from the metadata blob.
|
|
226
|
+
if (existing) {
|
|
227
|
+
this.createContainerMetadata = {
|
|
228
|
+
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
229
|
+
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
230
|
+
};
|
|
231
|
+
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
232
|
+
// the count is reset to 0.
|
|
233
|
+
loadSummaryNumber = (_b = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _b !== void 0 ? _b : 0;
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
this.createContainerMetadata = {
|
|
237
|
+
createContainerRuntimeVersion: pkgVersion,
|
|
238
|
+
createContainerTimestamp: Date.now(),
|
|
239
|
+
};
|
|
240
|
+
loadSummaryNumber = 0;
|
|
241
|
+
}
|
|
242
|
+
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
218
243
|
this.messageAtLastSummary = metadata === null || metadata === void 0 ? void 0 : metadata.message;
|
|
219
244
|
this._connected = this.context.connected;
|
|
220
245
|
this.chunkMap = new Map(chunks);
|
|
@@ -229,10 +254,31 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
229
254
|
this.maxOpsSinceLastSummary = this.getMaxOpsSinceLastSummary();
|
|
230
255
|
this.initialSummarizerDelayMs = this.getInitialSummarizerDelayMs();
|
|
231
256
|
this.maxConsecutiveReconnects =
|
|
232
|
-
(
|
|
257
|
+
(_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
233
258
|
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
|
+
});
|
|
234
273
|
const pendingRuntimeState = context.pendingLocalState;
|
|
235
|
-
const baseSnapshot = (
|
|
274
|
+
const baseSnapshot = (_d = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _d !== void 0 ? _d : context.baseSnapshot;
|
|
275
|
+
const maxSnapshotCacheDurationMs = (_f = (_e = this._storage) === null || _e === void 0 ? void 0 : _e.policies) === null || _f === void 0 ? void 0 : _f.maximumCacheDurationMs;
|
|
276
|
+
if (maxSnapshotCacheDurationMs !== undefined && maxSnapshotCacheDurationMs > 5 * 24 * 60 * 60 * 1000) {
|
|
277
|
+
// This is a runtime enforcement of what's already explicit in the policy's type itself,
|
|
278
|
+
// which dictates the value is either undefined or exactly 5 days in ms.
|
|
279
|
+
// As long as the actual value is less than 5 days, the assumptions GC makes here are valid.
|
|
280
|
+
throw new UsageError("Driver's maximumCacheDurationMs policy cannot exceed 5 days");
|
|
281
|
+
}
|
|
236
282
|
this.garbageCollector = GarbageCollector.create({
|
|
237
283
|
runtime: this,
|
|
238
284
|
gcOptions: this.runtimeOptions.gcOptions,
|
|
@@ -240,6 +286,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
240
286
|
baseLogger: this.mc.logger,
|
|
241
287
|
existing,
|
|
242
288
|
metadata,
|
|
289
|
+
createContainerMetadata: this.createContainerMetadata,
|
|
243
290
|
isSummarizerClient: this.context.clientDetails.type === summarizerClientType,
|
|
244
291
|
getNodePackagePath: async (nodePath) => this.getGCNodePackagePath(nodePath),
|
|
245
292
|
getLastSummaryTimestampMs: () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; },
|
|
@@ -265,9 +312,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
265
312
|
gcDisabled: !this.garbageCollector.shouldRunGC,
|
|
266
313
|
});
|
|
267
314
|
if (baseSnapshot) {
|
|
268
|
-
this.summarizerNode.
|
|
315
|
+
this.summarizerNode.updateBaseSummaryState(baseSnapshot);
|
|
269
316
|
}
|
|
270
|
-
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)
|
|
317
|
+
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));
|
|
271
318
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId, localId) => {
|
|
272
319
|
if (!this.disposed) {
|
|
273
320
|
this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId, localId });
|
|
@@ -281,10 +328,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
281
328
|
close: this.closeFn,
|
|
282
329
|
connected: () => this.connected,
|
|
283
330
|
flush: this.flush.bind(this),
|
|
284
|
-
flushMode: () => this.flushMode,
|
|
285
331
|
reSubmit: this.reSubmit.bind(this),
|
|
286
|
-
|
|
287
|
-
|
|
332
|
+
rollback: this.rollback.bind(this),
|
|
333
|
+
orderSequentially: this.orderSequentially.bind(this),
|
|
334
|
+
}, pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.pending);
|
|
288
335
|
this.context.quorum.on("removeMember", (clientId) => {
|
|
289
336
|
this.clearPartialChunks(clientId);
|
|
290
337
|
});
|
|
@@ -308,7 +355,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
308
355
|
// if summaries are enabled and we are not the summarizer client.
|
|
309
356
|
const defaultAction = () => {
|
|
310
357
|
if (this.summaryCollection.opsSinceLastAck > this.maxOpsSinceLastSummary) {
|
|
311
|
-
this.logger.
|
|
358
|
+
this.logger.sendTelemetryEvent({ eventName: "SummaryStatus:Behind" });
|
|
312
359
|
// unregister default to no log on every op after falling behind
|
|
313
360
|
// and register summary ack handler to re-register this handler
|
|
314
361
|
// after successful summary
|
|
@@ -354,26 +401,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
354
401
|
});
|
|
355
402
|
// logging hardware telemetry
|
|
356
403
|
logger.sendTelemetryEvent(Object.assign({ eventName: "DeviceSpec" }, getDeviceSpec()));
|
|
357
|
-
let loadSummaryNumber;
|
|
358
|
-
// Get the container creation metadata. For new container, we initialize these. For existing containers,
|
|
359
|
-
// get the values from the metadata blob.
|
|
360
|
-
if (existing) {
|
|
361
|
-
this.createContainerMetadata = {
|
|
362
|
-
createContainerRuntimeVersion: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerRuntimeVersion,
|
|
363
|
-
createContainerTimestamp: metadata === null || metadata === void 0 ? void 0 : metadata.createContainerTimestamp,
|
|
364
|
-
};
|
|
365
|
-
// summaryNumber was renamed from summaryCount. For older docs that haven't been opened for a long time,
|
|
366
|
-
// the count is reset to 0.
|
|
367
|
-
loadSummaryNumber = (_d = metadata === null || metadata === void 0 ? void 0 : metadata.summaryNumber) !== null && _d !== void 0 ? _d : 0;
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
this.createContainerMetadata = {
|
|
371
|
-
createContainerRuntimeVersion: pkgVersion,
|
|
372
|
-
createContainerTimestamp: Date.now(),
|
|
373
|
-
};
|
|
374
|
-
loadSummaryNumber = 0;
|
|
375
|
-
}
|
|
376
|
-
this.nextSummaryNumber = loadSummaryNumber + 1;
|
|
377
404
|
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 }));
|
|
378
405
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
379
406
|
BindBatchTracker(this, this.logger);
|
|
@@ -399,7 +426,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
399
426
|
runtimeVersion: pkgVersion,
|
|
400
427
|
},
|
|
401
428
|
});
|
|
402
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false,
|
|
429
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", flushMode = defaultFlushMode, enableOfflineLoad = false, compressionOptions = { minimumBatchSizeInBytes: Number.POSITIVE_INFINITY,
|
|
430
|
+
compressionAlgorithm: CompressionAlgorithms.lz4 }, maxBatchSizeInBytes = defaultMaxBatchSizeInBytes, } = runtimeOptions;
|
|
403
431
|
const pendingRuntimeState = context.pendingLocalState;
|
|
404
432
|
const baseSnapshot = (_b = pendingRuntimeState === null || pendingRuntimeState === void 0 ? void 0 : pendingRuntimeState.baseSnapshot) !== null && _b !== void 0 ? _b : context.baseSnapshot;
|
|
405
433
|
const storage = !pendingRuntimeState ?
|
|
@@ -454,13 +482,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
454
482
|
loadSequenceNumberVerification,
|
|
455
483
|
flushMode,
|
|
456
484
|
enableOfflineLoad,
|
|
485
|
+
compressionOptions,
|
|
486
|
+
maxBatchSizeInBytes,
|
|
457
487
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, storage, requestHandler);
|
|
458
488
|
if (pendingRuntimeState) {
|
|
459
489
|
await runtime.processSavedOps(pendingRuntimeState);
|
|
460
490
|
// delete these once runtime has seen them to save space
|
|
461
491
|
pendingRuntimeState.savedOps = [];
|
|
462
492
|
}
|
|
463
|
-
|
|
493
|
+
// Initialize the base state of the runtime before it's returned.
|
|
494
|
+
await runtime.initializeBaseState();
|
|
464
495
|
return runtime;
|
|
465
496
|
}
|
|
466
497
|
get options() {
|
|
@@ -567,6 +598,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
567
598
|
? this.summaryConfiguration.initialSummarizerDelayMs
|
|
568
599
|
: 0;
|
|
569
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Initializes the state from the base snapshot this container runtime loaded from.
|
|
603
|
+
*/
|
|
604
|
+
async initializeBaseState() {
|
|
605
|
+
await this.initializeBaseSnapshotBlobs();
|
|
606
|
+
await this.garbageCollector.initializeBaseState();
|
|
607
|
+
}
|
|
570
608
|
dispose(error) {
|
|
571
609
|
var _a;
|
|
572
610
|
if (this._disposed) {
|
|
@@ -665,13 +703,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
665
703
|
return (_a = this.dataStores.aliases.get(maybeAlias)) !== null && _a !== void 0 ? _a : maybeAlias;
|
|
666
704
|
}
|
|
667
705
|
async getDataStoreFromRequest(id, request) {
|
|
668
|
-
var _a, _b, _c;
|
|
706
|
+
var _a, _b, _c, _d, _e;
|
|
669
707
|
const wait = typeof ((_a = request.headers) === null || _a === void 0 ? void 0 : _a[RuntimeHeaders.wait]) === "boolean"
|
|
670
708
|
? (_b = request.headers) === null || _b === void 0 ? void 0 : _b[RuntimeHeaders.wait]
|
|
671
709
|
: true;
|
|
710
|
+
const viaHandle = typeof ((_c = request.headers) === null || _c === void 0 ? void 0 : _c[RuntimeHeaders.viaHandle]) === "boolean"
|
|
711
|
+
? (_d = request.headers) === null || _d === void 0 ? void 0 : _d[RuntimeHeaders.viaHandle]
|
|
712
|
+
: false;
|
|
672
713
|
await this.dataStores.waitIfPendingAlias(id);
|
|
673
714
|
const internalId = this.internalId(id);
|
|
674
|
-
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait);
|
|
715
|
+
const dataStoreContext = await this.dataStores.getDataStore(internalId, wait, viaHandle);
|
|
675
716
|
/**
|
|
676
717
|
* If GC should run and this an external app request with "externalRequest" header, we need to return
|
|
677
718
|
* an error if the data store being requested is marked as unreferenced as per the data store's base
|
|
@@ -680,7 +721,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
680
721
|
* This is a workaround to handle scenarios where a data store shared with an external app is deleted
|
|
681
722
|
* and marked as unreferenced by GC. Returning an error will fail to load the data store for the app.
|
|
682
723
|
*/
|
|
683
|
-
if (((
|
|
724
|
+
if (((_e = request.headers) === null || _e === void 0 ? void 0 : _e[RuntimeHeaders.externalRequest]) && this.garbageCollector.shouldRunGC) {
|
|
684
725
|
// The data store is referenced if used routes in the base summary has a route to self.
|
|
685
726
|
// Older documents may not have used routes in the summary. They are considered referenced.
|
|
686
727
|
const usedRoutes = (await dataStoreContext.getBaseGCDetails()).usedRoutes;
|
|
@@ -727,11 +768,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
727
768
|
if (Object.keys(blobManagerSummary.summary.tree).length > 0) {
|
|
728
769
|
addTreeToSummary(summaryTree, blobsTreeName, blobManagerSummary);
|
|
729
770
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
734
|
-
}
|
|
771
|
+
const gcSummary = this.garbageCollector.summarize(fullTree, trackState, telemetryContext);
|
|
772
|
+
if (gcSummary !== undefined) {
|
|
773
|
+
addSummarizeResultToSummary(summaryTree, gcTreeKey, gcSummary);
|
|
735
774
|
}
|
|
736
775
|
}
|
|
737
776
|
// Track how many times the container tries to reconnect with pending messages.
|
|
@@ -887,6 +926,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
887
926
|
if (typeof message.contents === "string" && message.contents !== "") {
|
|
888
927
|
message.contents = JSON.parse(message.contents);
|
|
889
928
|
}
|
|
929
|
+
message = this.opDecompressor.processMessage(message);
|
|
890
930
|
// Caveat: This will return false for runtime message in very old format, that are used in snapshot tests
|
|
891
931
|
// This format was not shipped to production workflows.
|
|
892
932
|
const runtimeMessage = unpackRuntimeMessage(message);
|
|
@@ -1004,27 +1044,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1004
1044
|
async getRootDataStoreChannel(id, wait = true) {
|
|
1005
1045
|
await this.dataStores.waitIfPendingAlias(id);
|
|
1006
1046
|
const internalId = this.internalId(id);
|
|
1007
|
-
const context = await this.dataStores.getDataStore(internalId, wait);
|
|
1047
|
+
const context = await this.dataStores.getDataStore(internalId, wait, false /* viaHandle */);
|
|
1008
1048
|
assert(await context.isRoot(), 0x12b /* "did not get root data store" */);
|
|
1009
1049
|
return context.realize();
|
|
1010
1050
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
this.mc.logger.sendTelemetryEvent({
|
|
1016
|
-
eventName: "FlushMode Updated",
|
|
1017
|
-
old: this._flushMode,
|
|
1018
|
-
new: mode,
|
|
1019
|
-
});
|
|
1020
|
-
// Flush any pending batches if switching to immediate
|
|
1021
|
-
if (mode === FlushMode.Immediate) {
|
|
1022
|
-
this.flush();
|
|
1023
|
-
}
|
|
1024
|
-
this._flushMode = mode;
|
|
1025
|
-
// Let the PendingStateManager know that FlushMode has been updated.
|
|
1026
|
-
this.pendingStateManager.onFlushModeUpdated(mode);
|
|
1027
|
-
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Flush the pending ops manually.
|
|
1053
|
+
* This method is expected to be called at the end of a batch.
|
|
1054
|
+
*/
|
|
1028
1055
|
flush() {
|
|
1029
1056
|
assert(this._orderSequentiallyCalls === 0, 0x24c /* "Cannot call `flush()` from `orderSequentially`'s callback" */);
|
|
1030
1057
|
this.flushBatch(this.pendingAttachBatch.popBatch());
|
|
@@ -1032,6 +1059,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1032
1059
|
assert(this.emptyBatch, 0x3cf /* reentrancy */);
|
|
1033
1060
|
}
|
|
1034
1061
|
flushBatch(batch) {
|
|
1062
|
+
var _a;
|
|
1035
1063
|
const length = batch.length;
|
|
1036
1064
|
if (length > 1) {
|
|
1037
1065
|
batch[0].metadata = Object.assign(Object.assign({}, batch[0].metadata), { batch: true });
|
|
@@ -1062,6 +1090,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1062
1090
|
// Legacy path - supporting old loader versions. Can be removed only when LTS moves above
|
|
1063
1091
|
// version that has support for batches (submitBatchFn)
|
|
1064
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
|
+
}
|
|
1065
1097
|
clientSequenceNumber = this.context.submitFn(MessageType.Operation, message.deserializedContent, true, // batch
|
|
1066
1098
|
message.metadata);
|
|
1067
1099
|
}
|
|
@@ -1080,26 +1112,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1080
1112
|
this.pendingStateManager.onFlush();
|
|
1081
1113
|
}
|
|
1082
1114
|
orderSequentially(callback) {
|
|
1083
|
-
// If flush mode is already TurnBased we are either
|
|
1084
|
-
// nested in another orderSequentially, or
|
|
1085
|
-
// the app is flushing manually, in which
|
|
1086
|
-
// case this invocation doesn't own
|
|
1087
|
-
// flushing.
|
|
1088
|
-
if (this.flushMode === FlushMode.TurnBased) {
|
|
1089
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
const savedFlushMode = this.flushMode;
|
|
1093
|
-
this.setFlushMode(FlushMode.TurnBased);
|
|
1094
|
-
try {
|
|
1095
|
-
this.trackOrderSequentiallyCalls(callback);
|
|
1096
|
-
this.flush();
|
|
1097
|
-
}
|
|
1098
|
-
finally {
|
|
1099
|
-
this.setFlushMode(savedFlushMode);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
trackOrderSequentiallyCalls(callback) {
|
|
1103
1115
|
let checkpoint;
|
|
1104
1116
|
if (this.mc.config.getBoolean("Fluid.ContainerRuntime.EnableRollback")) {
|
|
1105
1117
|
// Note: we are not touching this.pendingAttachBatch here, for two reasons:
|
|
@@ -1134,6 +1146,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1134
1146
|
finally {
|
|
1135
1147
|
this._orderSequentiallyCalls--;
|
|
1136
1148
|
}
|
|
1149
|
+
if (this.flushMode === FlushMode.Immediate && this._orderSequentiallyCalls === 0) {
|
|
1150
|
+
this.flush();
|
|
1151
|
+
}
|
|
1137
1152
|
}
|
|
1138
1153
|
async createDataStore(pkg) {
|
|
1139
1154
|
const internalId = uuid();
|
|
@@ -1160,6 +1175,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1160
1175
|
canSendOps() {
|
|
1161
1176
|
return this.connected && !this.deltaManager.readOnlyInfo.readonly;
|
|
1162
1177
|
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Are we in the middle of batching ops together?
|
|
1180
|
+
*/
|
|
1181
|
+
currentlyBatching() {
|
|
1182
|
+
return this.flushMode === FlushMode.TurnBased || this._orderSequentiallyCalls !== 0;
|
|
1183
|
+
}
|
|
1163
1184
|
getQuorum() {
|
|
1164
1185
|
return this.context.quorum;
|
|
1165
1186
|
}
|
|
@@ -1310,10 +1331,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1310
1331
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
1311
1332
|
* After GC has run, called to notify this container's nodes of routes that are used in it.
|
|
1312
1333
|
* @param usedRoutes - The routes that are used in all nodes in this Container.
|
|
1313
|
-
* @param gcTimestamp - The time when GC was run that generated these used routes. If any node node becomes
|
|
1314
|
-
* unreferenced as part of this GC run, this should be used to update the time when it happens.
|
|
1315
1334
|
*/
|
|
1316
|
-
updateUsedRoutes(usedRoutes
|
|
1335
|
+
updateUsedRoutes(usedRoutes) {
|
|
1317
1336
|
// Update our summarizer node's used routes. Updating used routes in summarizer node before
|
|
1318
1337
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1319
1338
|
// always referenced, so the used routes is only self-route (empty string).
|
|
@@ -1324,14 +1343,16 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1324
1343
|
dataStoreUsedRoutes.push(route);
|
|
1325
1344
|
}
|
|
1326
1345
|
}
|
|
1327
|
-
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes
|
|
1346
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes);
|
|
1328
1347
|
}
|
|
1329
1348
|
/**
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
1349
|
+
* This is called to update objects whose routes are unused. The unused objects are either deleted or marked as
|
|
1350
|
+
* tombstones.
|
|
1332
1351
|
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1352
|
+
* @param tombstone - if true, the objects corresponding to unused routes are marked tombstones. Otherwise, they
|
|
1353
|
+
* are deleted.
|
|
1333
1354
|
*/
|
|
1334
|
-
|
|
1355
|
+
updateUnusedRoutes(unusedRoutes, tombstone) {
|
|
1335
1356
|
const blobManagerUnusedRoutes = [];
|
|
1336
1357
|
const dataStoreUnusedRoutes = [];
|
|
1337
1358
|
for (const route of unusedRoutes) {
|
|
@@ -1342,8 +1363,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1342
1363
|
dataStoreUnusedRoutes.push(route);
|
|
1343
1364
|
}
|
|
1344
1365
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1366
|
+
// Todo: Add tombstone for attachment blobs. For now, we ignore attachment blobs that should be tombstoned.
|
|
1367
|
+
if (!tombstone) {
|
|
1368
|
+
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
1369
|
+
}
|
|
1370
|
+
this.dataStores.updateUnusedRoutes(dataStoreUnusedRoutes, tombstone);
|
|
1347
1371
|
}
|
|
1348
1372
|
/**
|
|
1349
1373
|
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
@@ -1429,15 +1453,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1429
1453
|
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
1430
1454
|
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
1431
1455
|
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
await PerformanceEvent.timedExecAsync(summaryNumberLogger, {
|
|
1435
|
-
eventName: "WaitingForSeq",
|
|
1436
|
-
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1437
|
-
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1438
|
-
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1439
|
-
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1440
|
-
}
|
|
1456
|
+
// We might need to catch up to the latest summary's reference sequence number before pausing.
|
|
1457
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryNumberLogger);
|
|
1441
1458
|
}
|
|
1442
1459
|
try {
|
|
1443
1460
|
await this.deltaManager.inbound.pause();
|
|
@@ -1674,6 +1691,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1674
1691
|
return this.blobManager.createBlob(blob);
|
|
1675
1692
|
}
|
|
1676
1693
|
submit(type, contents, localOpMetadata = undefined, metadata = undefined) {
|
|
1694
|
+
var _a, _b, _c, _d;
|
|
1677
1695
|
this.verifyNotClosed();
|
|
1678
1696
|
// There should be no ops in detached container state!
|
|
1679
1697
|
assert(this.attachState !== AttachState.Detached, 0x132 /* "sending ops in detached container" */);
|
|
@@ -1710,8 +1728,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1710
1728
|
// issue than sending.
|
|
1711
1729
|
// Please note that this does not change file format, so it can be disabled in the future if this
|
|
1712
1730
|
// optimization no longer makes sense (for example, batch compression may make it less appealing).
|
|
1713
|
-
if (type === ContainerMessageType.Attach &&
|
|
1714
|
-
this.mc.config.getBoolean("Fluid.ContainerRuntime.
|
|
1731
|
+
if (this.currentlyBatching() && type === ContainerMessageType.Attach &&
|
|
1732
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.enableAttachOpReorder") === true) {
|
|
1715
1733
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1716
1734
|
// BatchManager has two limits - soft limit & hard limit. Soft limit is only engaged
|
|
1717
1735
|
// when queue is not empty.
|
|
@@ -1720,9 +1738,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1720
1738
|
if (!this.pendingAttachBatch.push(message)) {
|
|
1721
1739
|
throw new GenericError("BatchTooLarge",
|
|
1722
1740
|
/* error */ undefined, {
|
|
1723
|
-
opSize: message.contents.length,
|
|
1741
|
+
opSize: (_b = ((_a = message.contents) === null || _a === void 0 ? void 0 : _a.length)) !== null && _b !== void 0 ? _b : 0,
|
|
1724
1742
|
count: this.pendingAttachBatch.length,
|
|
1725
|
-
limit: this.pendingAttachBatch.
|
|
1743
|
+
limit: this.pendingAttachBatch.options.hardLimit,
|
|
1726
1744
|
});
|
|
1727
1745
|
}
|
|
1728
1746
|
}
|
|
@@ -1731,23 +1749,23 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1731
1749
|
if (!this.pendingBatch.push(message)) {
|
|
1732
1750
|
throw new GenericError("BatchTooLarge",
|
|
1733
1751
|
/* error */ undefined, {
|
|
1734
|
-
opSize: message.contents.length,
|
|
1752
|
+
opSize: (_d = ((_c = message.contents) === null || _c === void 0 ? void 0 : _c.length)) !== null && _d !== void 0 ? _d : 0,
|
|
1735
1753
|
count: this.pendingBatch.length,
|
|
1736
|
-
limit: this.pendingBatch.
|
|
1754
|
+
limit: this.pendingBatch.options.hardLimit,
|
|
1737
1755
|
});
|
|
1738
1756
|
}
|
|
1739
|
-
|
|
1740
|
-
if (this._flushMode !== FlushMode.TurnBased) {
|
|
1741
|
-
this.flush();
|
|
1742
|
-
}
|
|
1743
|
-
else if (!this.flushTrigger) {
|
|
1744
|
-
this.flushTrigger = true;
|
|
1745
|
-
// Queue a microtask to detect the end of the turn and force a flush.
|
|
1746
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
1747
|
-
Promise.resolve().then(() => {
|
|
1748
|
-
this.flushTrigger = false;
|
|
1757
|
+
if (!this.currentlyBatching()) {
|
|
1749
1758
|
this.flush();
|
|
1750
|
-
}
|
|
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
|
+
}
|
|
1751
1769
|
}
|
|
1752
1770
|
}
|
|
1753
1771
|
catch (error) {
|
|
@@ -1817,18 +1835,41 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1817
1835
|
throw new Error(`Can't rollback ${type}`);
|
|
1818
1836
|
}
|
|
1819
1837
|
}
|
|
1838
|
+
async waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger) {
|
|
1839
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
1840
|
+
// We need to catch up to the latest summary's reference sequence number before proceeding.
|
|
1841
|
+
await PerformanceEvent.timedExecAsync(summaryLogger, {
|
|
1842
|
+
eventName: "WaitingForSeq",
|
|
1843
|
+
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
1844
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
1845
|
+
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
1846
|
+
}, async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq), { start: true, end: true, cancel: "error" });
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1820
1849
|
/** Implementation of ISummarizerInternalsProvider.refreshLatestSummaryAck */
|
|
1821
|
-
async refreshLatestSummaryAck(
|
|
1850
|
+
async refreshLatestSummaryAck(options) {
|
|
1851
|
+
const { proposalHandle, ackHandle, summaryRefSeq, summaryLogger } = options;
|
|
1822
1852
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1823
1853
|
// The call to fetch the snapshot is very expensive and not always needed.
|
|
1824
1854
|
// It should only be done by the summarizerNode, if required.
|
|
1855
|
+
// When fetching from storage we will always get the latest version and do not use the ackHandle.
|
|
1825
1856
|
const snapshotTreeFetcher = async () => {
|
|
1826
|
-
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
1857
|
+
const fetchResult = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
1827
1858
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1828
1859
|
ackHandle,
|
|
1829
1860
|
summaryRefSeq,
|
|
1830
|
-
fetchLatest:
|
|
1861
|
+
fetchLatest: true,
|
|
1862
|
+
});
|
|
1863
|
+
const latestSnapshotRefSeq = await seqFromTree(fetchResult.snapshotTree, readAndParseBlob);
|
|
1864
|
+
summaryLogger.sendTelemetryEvent({
|
|
1865
|
+
eventName: "LatestSummaryRetrieved",
|
|
1866
|
+
ackHandle,
|
|
1867
|
+
lastSequenceNumber: latestSnapshotRefSeq,
|
|
1868
|
+
targetSequenceNumber: summaryRefSeq,
|
|
1831
1869
|
});
|
|
1870
|
+
// In case we had to retrieve the latest snapshot and it is different than summaryRefSeq,
|
|
1871
|
+
// wait for the delta manager to catch up before refreshing the latest Summary.
|
|
1872
|
+
await this.waitForDeltaManagerToCatchup(latestSnapshotRefSeq, summaryLogger);
|
|
1832
1873
|
return fetchResult.snapshotTree;
|
|
1833
1874
|
};
|
|
1834
1875
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, snapshotTreeFetcher, readAndParseBlob, summaryLogger);
|
|
@@ -1873,7 +1914,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1873
1914
|
this.baseSnapshotBlobs = SerializedSnapshotStorage.serializeTreeWithBlobContents(snapshot);
|
|
1874
1915
|
}
|
|
1875
1916
|
}
|
|
1876
|
-
async
|
|
1917
|
+
async initializeBaseSnapshotBlobs() {
|
|
1877
1918
|
var _a;
|
|
1878
1919
|
if (!((_a = this.mc.config.getBoolean("enableOfflineLoad")) !== null && _a !== void 0 ? _a : this.runtimeOptions.enableOfflineLoad) ||
|
|
1879
1920
|
this.attachState !== AttachState.Attached || this.context.pendingLocalState) {
|
|
@@ -1891,6 +1932,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1891
1932
|
// getPendingLocalState() is only exposed through Container.closeAndGetPendingLocalState(), so it's safe
|
|
1892
1933
|
// to close current batch.
|
|
1893
1934
|
this.flush();
|
|
1935
|
+
if (this._orderSequentiallyCalls !== 0) {
|
|
1936
|
+
throw new UsageError("can't get state during orderSequentially");
|
|
1937
|
+
}
|
|
1894
1938
|
const previousPendingState = this.context.pendingLocalState;
|
|
1895
1939
|
if (previousPendingState) {
|
|
1896
1940
|
return {
|
|
@@ -1958,6 +2002,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1958
2002
|
throw new UsageError(`Summary heuristic configuration property "${prop}" cannot be less than 0`);
|
|
1959
2003
|
}
|
|
1960
2004
|
}
|
|
2005
|
+
if (configuration.minIdleTime > configuration.maxIdleTime) {
|
|
2006
|
+
throw new UsageError(`"minIdleTime" [${configuration.minIdleTime}] cannot be greater than "maxIdleTime" [${configuration.maxIdleTime}]`);
|
|
2007
|
+
}
|
|
1961
2008
|
}
|
|
1962
2009
|
}
|
|
1963
2010
|
/**
|
|
@@ -1967,12 +2014,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1967
2014
|
const waitForSeq = async (deltaManager, targetSeq) => new Promise((resolve, reject) => {
|
|
1968
2015
|
// TODO: remove cast to any when actual event is determined
|
|
1969
2016
|
deltaManager.on("closed", reject);
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2017
|
+
// If we already reached target sequence number, simply resolve the promise.
|
|
2018
|
+
if (deltaManager.lastSequenceNumber >= targetSeq) {
|
|
2019
|
+
resolve();
|
|
2020
|
+
}
|
|
2021
|
+
else {
|
|
2022
|
+
const handleOp = (message) => {
|
|
2023
|
+
if (message.sequenceNumber >= targetSeq) {
|
|
2024
|
+
resolve();
|
|
2025
|
+
deltaManager.off("op", handleOp);
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
deltaManager.on("op", handleOp);
|
|
2029
|
+
}
|
|
1977
2030
|
});
|
|
1978
2031
|
//# sourceMappingURL=containerRuntime.js.map
|