@fluidframework/container-runtime 0.58.2001 → 0.59.2000-61729
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 +15 -2
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +65 -9
- package/dist/blobManager.js.map +1 -1
- package/dist/connectionTelemetry.d.ts.map +1 -1
- package/dist/connectionTelemetry.js +63 -23
- package/dist/connectionTelemetry.js.map +1 -1
- package/dist/containerRuntime.d.ts +39 -7
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +161 -29
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +8 -1
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +9 -3
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +22 -6
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +13 -5
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +39 -18
- package/dist/dataStores.js.map +1 -1
- package/dist/deltaScheduler.d.ts +4 -5
- package/dist/deltaScheduler.d.ts.map +1 -1
- package/dist/deltaScheduler.js +54 -35
- package/dist/deltaScheduler.js.map +1 -1
- package/dist/garbageCollection.d.ts +31 -27
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +76 -75
- package/dist/garbageCollection.js.map +1 -1
- package/dist/opTelemetry.d.ts +22 -0
- package/dist/opTelemetry.d.ts.map +1 -0
- package/dist/opTelemetry.js +59 -0
- package/dist/opTelemetry.js.map +1 -0
- package/dist/orderedClientElection.d.ts +57 -6
- package/dist/orderedClientElection.d.ts.map +1 -1
- package/dist/orderedClientElection.js +141 -26
- package/dist/orderedClientElection.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/summarizerClientElection.d.ts +2 -0
- package/dist/summarizerClientElection.d.ts.map +1 -1
- package/dist/summarizerClientElection.js +15 -2
- package/dist/summarizerClientElection.js.map +1 -1
- package/dist/summarizerTypes.d.ts +9 -0
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +3 -4
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +14 -3
- package/dist/summaryManager.js.map +1 -1
- package/lib/blobManager.d.ts +15 -2
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +66 -10
- package/lib/blobManager.js.map +1 -1
- package/lib/connectionTelemetry.d.ts.map +1 -1
- package/lib/connectionTelemetry.js +63 -23
- package/lib/connectionTelemetry.js.map +1 -1
- package/lib/containerRuntime.d.ts +39 -7
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +163 -31
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +8 -1
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +9 -3
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +22 -6
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +13 -5
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +39 -18
- package/lib/dataStores.js.map +1 -1
- package/lib/deltaScheduler.d.ts +4 -5
- package/lib/deltaScheduler.d.ts.map +1 -1
- package/lib/deltaScheduler.js +54 -35
- package/lib/deltaScheduler.js.map +1 -1
- package/lib/garbageCollection.d.ts +31 -27
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +75 -74
- package/lib/garbageCollection.js.map +1 -1
- package/lib/opTelemetry.d.ts +22 -0
- package/lib/opTelemetry.d.ts.map +1 -0
- package/lib/opTelemetry.js +55 -0
- package/lib/opTelemetry.js.map +1 -0
- package/lib/orderedClientElection.d.ts +57 -6
- package/lib/orderedClientElection.d.ts.map +1 -1
- package/lib/orderedClientElection.js +141 -26
- package/lib/orderedClientElection.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/summarizerClientElection.d.ts +2 -0
- package/lib/summarizerClientElection.d.ts.map +1 -1
- package/lib/summarizerClientElection.js +15 -2
- package/lib/summarizerClientElection.js.map +1 -1
- package/lib/summarizerTypes.d.ts +9 -0
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +3 -4
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +14 -3
- package/lib/summaryManager.js.map +1 -1
- package/package.json +63 -19
- package/src/blobManager.ts +78 -11
- package/src/connectionTelemetry.ts +110 -19
- package/src/containerRuntime.ts +191 -36
- package/src/dataStore.ts +7 -1
- package/src/dataStoreContext.ts +22 -7
- package/src/dataStores.ts +40 -19
- package/src/deltaScheduler.ts +65 -39
- package/src/garbageCollection.ts +92 -78
- package/src/opTelemetry.ts +71 -0
- package/src/orderedClientElection.ts +155 -25
- package/src/packageVersion.ts +1 -1
- package/src/summarizerClientElection.ts +15 -2
- package/src/summarizerTypes.ts +9 -0
- package/src/summaryGenerator.ts +10 -8
- package/src/summaryManager.ts +15 -4
package/lib/containerRuntime.js
CHANGED
|
@@ -6,7 +6,8 @@ import { readAndParse, BlobAggregationStorage } from "@fluidframework/driver-uti
|
|
|
6
6
|
import { DataCorruptionError, GenericError, UsageError, extractSafePropertiesFromMessage, } from "@fluidframework/container-utils";
|
|
7
7
|
import { MessageType, SummaryType, } from "@fluidframework/protocol-definitions";
|
|
8
8
|
import { FlushMode, channelsTreeName, } from "@fluidframework/runtime-definitions";
|
|
9
|
-
import { addBlobToSummary, addTreeToSummary,
|
|
9
|
+
import { addBlobToSummary, addTreeToSummary, createRootSummarizerNodeWithGC, RequestParser, create404Response, exceptionToResponse, requestFluidObject, responseToException, seqFromTree, calculateStats, } from "@fluidframework/runtime-utils";
|
|
10
|
+
import { GCDataBuilder } from "@fluidframework/garbage-collector";
|
|
10
11
|
import { v4 as uuid } from "uuid";
|
|
11
12
|
import { ContainerFluidHandleContext } from "./containerHandleContext";
|
|
12
13
|
import { FluidDataStoreRegistry } from "./dataStoreRegistry";
|
|
@@ -24,9 +25,10 @@ import { OrderedClientCollection, OrderedClientElection } from "./orderedClientE
|
|
|
24
25
|
import { SummarizerClientElection, summarizerClientType } from "./summarizerClientElection";
|
|
25
26
|
import { formExponentialFn, Throttler } from "./throttler";
|
|
26
27
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
27
|
-
import { GarbageCollector, gcTreeKey, } from "./garbageCollection";
|
|
28
|
+
import { GarbageCollector, GCNodeType, gcTreeKey, } from "./garbageCollection";
|
|
28
29
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
29
30
|
import { BindBatchTracker } from "./batchTracker";
|
|
31
|
+
import { OpTracker } from "./opTelemetry";
|
|
30
32
|
export var ContainerMessageType;
|
|
31
33
|
(function (ContainerMessageType) {
|
|
32
34
|
// An op to be delivered to store
|
|
@@ -79,6 +81,11 @@ const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
|
|
|
79
81
|
// in order to account for some extra overhead from serialization
|
|
80
82
|
// to not reach the 1MB limits in socket.io and Kafka.
|
|
81
83
|
const defaultMaxOpSizeInBytes = 768000;
|
|
84
|
+
// By default, the size of the contents for the incoming ops is tracked.
|
|
85
|
+
// However, in certain situations, this may incur a performance hit.
|
|
86
|
+
// The feature-gate below can be used to disable this feature.
|
|
87
|
+
const disableOpTrackingKey = "Fluid.ContainerRuntime.DisableOpTracking";
|
|
88
|
+
const defaultFlushMode = FlushMode.TurnBased;
|
|
82
89
|
export var RuntimeMessage;
|
|
83
90
|
(function (RuntimeMessage) {
|
|
84
91
|
RuntimeMessage["FluidDataStoreOp"] = "component";
|
|
@@ -127,6 +134,7 @@ class ScheduleManagerCore {
|
|
|
127
134
|
this.logger = logger;
|
|
128
135
|
this.localPaused = false;
|
|
129
136
|
this.timePaused = 0;
|
|
137
|
+
this.batchCount = 0;
|
|
130
138
|
// Listen for delta manager sends and add batch metadata to messages
|
|
131
139
|
this.deltaManager.on("prepareSend", (messages) => {
|
|
132
140
|
if (messages.length === 0) {
|
|
@@ -193,13 +201,26 @@ class ScheduleManagerCore {
|
|
|
193
201
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
194
202
|
this.deltaManager.inbound.pause();
|
|
195
203
|
}
|
|
196
|
-
resumeQueue(startBatch,
|
|
204
|
+
resumeQueue(startBatch, messageEndBatch) {
|
|
205
|
+
const endBatch = messageEndBatch.sequenceNumber;
|
|
206
|
+
const duration = performance.now() - this.timePaused;
|
|
207
|
+
this.batchCount++;
|
|
208
|
+
if (this.batchCount % 1000 === 1) {
|
|
209
|
+
this.logger.sendTelemetryEvent({
|
|
210
|
+
eventName: "BatchStats",
|
|
211
|
+
sequenceNumber: endBatch,
|
|
212
|
+
length: endBatch - startBatch + 1,
|
|
213
|
+
msnDistance: endBatch - messageEndBatch.minimumSequenceNumber,
|
|
214
|
+
duration,
|
|
215
|
+
batchCount: this.batchCount,
|
|
216
|
+
interrupted: this.localPaused,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
197
219
|
// Return early if no change in value
|
|
198
220
|
if (!this.localPaused) {
|
|
199
221
|
return;
|
|
200
222
|
}
|
|
201
223
|
this.localPaused = false;
|
|
202
|
-
const duration = performance.now() - this.timePaused;
|
|
203
224
|
// Random round number - we want to know when batch waiting paused op processing.
|
|
204
225
|
if (duration > latencyThreshold) {
|
|
205
226
|
this.logger.sendErrorEvent({
|
|
@@ -260,7 +281,7 @@ class ScheduleManagerCore {
|
|
|
260
281
|
else if (batchMetadata === false) {
|
|
261
282
|
assert(this.pauseSequenceNumber !== undefined, 0x2a0 /* "batch presence was validated above" */);
|
|
262
283
|
// Batch is complete, we can process it!
|
|
263
|
-
this.resumeQueue(this.pauseSequenceNumber, message
|
|
284
|
+
this.resumeQueue(this.pauseSequenceNumber, message);
|
|
264
285
|
this.pauseSequenceNumber = undefined;
|
|
265
286
|
this.currentBatchClientId = undefined;
|
|
266
287
|
}
|
|
@@ -292,7 +313,7 @@ export class ScheduleManager {
|
|
|
292
313
|
assert(this.batchClientId === undefined, 0x2a2 /* "Batch is interrupted by other client op. Should be caught by trackPending()" */);
|
|
293
314
|
// This could be the beginning of a new batch or an individual message.
|
|
294
315
|
this.emitter.emit("batchBegin", message);
|
|
295
|
-
this.deltaScheduler.batchBegin();
|
|
316
|
+
this.deltaScheduler.batchBegin(message);
|
|
296
317
|
const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
|
|
297
318
|
if (batch) {
|
|
298
319
|
this.batchClientId = message.clientId;
|
|
@@ -312,7 +333,7 @@ export class ScheduleManager {
|
|
|
312
333
|
this.hitError = true;
|
|
313
334
|
this.batchClientId = undefined;
|
|
314
335
|
this.emitter.emit("batchEnd", error, message);
|
|
315
|
-
this.deltaScheduler.batchEnd();
|
|
336
|
+
this.deltaScheduler.batchEnd(message);
|
|
316
337
|
return;
|
|
317
338
|
}
|
|
318
339
|
const batch = (_a = message === null || message === void 0 ? void 0 : message.metadata) === null || _a === void 0 ? void 0 : _a.batch;
|
|
@@ -321,7 +342,7 @@ export class ScheduleManager {
|
|
|
321
342
|
if (this.batchClientId === undefined || batch === false) {
|
|
322
343
|
this.batchClientId = undefined;
|
|
323
344
|
this.emitter.emit("batchEnd", undefined, message);
|
|
324
|
-
this.deltaScheduler.batchEnd();
|
|
345
|
+
this.deltaScheduler.batchEnd(message);
|
|
325
346
|
return;
|
|
326
347
|
}
|
|
327
348
|
}
|
|
@@ -363,7 +384,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
363
384
|
this._storage = _storage;
|
|
364
385
|
this.defaultMaxConsecutiveReconnects = 15;
|
|
365
386
|
this._orderSequentiallyCalls = 0;
|
|
366
|
-
this._flushMode = FlushMode.TurnBased;
|
|
367
387
|
this.needsFlush = false;
|
|
368
388
|
this.flushTrigger = false;
|
|
369
389
|
this.paused = false;
|
|
@@ -446,12 +466,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
446
466
|
((_c = runtimeOptions.useDataStoreAliasing) !== null && _c !== void 0 ? _c : false);
|
|
447
467
|
this._maxOpSizeInBytes = ((_d = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _d !== void 0 ? _d : defaultMaxOpSizeInBytes);
|
|
448
468
|
this.maxConsecutiveReconnects = (_e = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _e !== void 0 ? _e : this.defaultMaxConsecutiveReconnects;
|
|
449
|
-
this.
|
|
450
|
-
|
|
451
|
-
* Returns the timestamp of the last message seen by this client. This is used by garbage collector as
|
|
452
|
-
* the current reference timestamp for tracking unreferenced objects.
|
|
453
|
-
*/
|
|
454
|
-
() => { var _a, _b, _c; return (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : (_c = this.messageAtLastSummary) === null || _c === void 0 ? void 0 : _c.timestamp; }, () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
469
|
+
this._flushMode = runtimeOptions.flushMode;
|
|
470
|
+
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (nodePath) => this.getGCNodePackagePath(nodePath), () => { var _a; return (_a = this.messageAtLastSummary) === null || _a === void 0 ? void 0 : _a.timestamp; }, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
455
471
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
456
472
|
this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
|
|
457
473
|
// Summarize function to call when summarize is called. Summarizer node always tracks summary state.
|
|
@@ -472,7 +488,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
472
488
|
if (this.context.baseSnapshot) {
|
|
473
489
|
this.summarizerNode.loadBaseSummaryWithoutDifferential(this.context.baseSnapshot);
|
|
474
490
|
}
|
|
475
|
-
this.dataStores = new DataStores(getSummaryForDatastores(context.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.
|
|
491
|
+
this.dataStores = new DataStores(getSummaryForDatastores(context.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), this.garbageCollector.writeDataAtRoot);
|
|
476
492
|
this.blobManager = new BlobManager(this.handleContext, blobManagerSnapshot, () => this.storage, (blobId) => this.submit(ContainerMessageType.BlobAttach, undefined, undefined, { blobId }), this, this.logger);
|
|
477
493
|
this.scheduleManager = new ScheduleManager(context.deltaManager, this, ChildLogger.create(this.logger, "ScheduleManager"));
|
|
478
494
|
this.deltaSender = this.deltaManager;
|
|
@@ -560,6 +576,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
560
576
|
this.logger.sendTelemetryEvent(Object.assign(Object.assign(Object.assign({ eventName: "ContainerLoadStats" }, this.createContainerMetadata), this.dataStores.containerLoadStats), { summaryCount: this.summaryCount, 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 }));
|
|
561
577
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
562
578
|
BindBatchTracker(this, this.logger);
|
|
579
|
+
this.opTracker = new OpTracker(this.deltaManager, this.mc.config.getBoolean(disableOpTrackingKey) === true);
|
|
563
580
|
}
|
|
564
581
|
get IContainerRuntime() { return this; }
|
|
565
582
|
get IFluidRouter() { return this; }
|
|
@@ -582,7 +599,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
582
599
|
runtimeVersion: pkgVersion,
|
|
583
600
|
},
|
|
584
601
|
});
|
|
585
|
-
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, } = runtimeOptions;
|
|
602
|
+
const { summaryOptions = {}, gcOptions = {}, loadSequenceNumberVerification = "close", useDataStoreAliasing = false, flushMode = defaultFlushMode, } = runtimeOptions;
|
|
586
603
|
// We pack at data store level only. If isolated channels are disabled,
|
|
587
604
|
// then there are no .channel layers, we pack at level 1, otherwise we pack at level 2
|
|
588
605
|
const packingLevel = summaryOptions.disableIsolatedChannels ? 1 : 2;
|
|
@@ -653,6 +670,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
653
670
|
gcOptions,
|
|
654
671
|
loadSequenceNumberVerification,
|
|
655
672
|
useDataStoreAliasing,
|
|
673
|
+
flushMode,
|
|
656
674
|
}, containerScope, logger, loadExisting, blobManagerSnapshot, requestHandler, storage);
|
|
657
675
|
return runtime;
|
|
658
676
|
}
|
|
@@ -871,12 +889,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
871
889
|
const electedSummarizerContent = JSON.stringify((_a = this.summarizerClientElection) === null || _a === void 0 ? void 0 : _a.serialize());
|
|
872
890
|
addBlobToSummary(summaryTree, electedSummarizerBlobName, electedSummarizerContent);
|
|
873
891
|
}
|
|
874
|
-
const
|
|
892
|
+
const summary = this.blobManager.summarize();
|
|
875
893
|
// Some storage (like git) doesn't allow empty tree, so we can omit it.
|
|
876
894
|
// and the blob manager can handle the tree not existing when loading
|
|
877
|
-
if (
|
|
878
|
-
|
|
879
|
-
addTreeToSummary(summaryTree, blobsTreeName, blobsTree);
|
|
895
|
+
if (Object.keys(summary.summary.tree).length > 0) {
|
|
896
|
+
addTreeToSummary(summaryTree, blobsTreeName, summary);
|
|
880
897
|
}
|
|
881
898
|
if (this.garbageCollector.writeDataAtRoot) {
|
|
882
899
|
const gcSummary = this.garbageCollector.summarize();
|
|
@@ -1065,6 +1082,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1065
1082
|
if (mode === this._flushMode) {
|
|
1066
1083
|
return;
|
|
1067
1084
|
}
|
|
1085
|
+
this.mc.logger.sendTelemetryEvent({
|
|
1086
|
+
eventName: "FlushMode Updated",
|
|
1087
|
+
old: this._flushMode,
|
|
1088
|
+
new: mode,
|
|
1089
|
+
});
|
|
1068
1090
|
// Flush any pending batches if switching to immediate
|
|
1069
1091
|
if (mode === FlushMode.Immediate) {
|
|
1070
1092
|
this.flush();
|
|
@@ -1137,7 +1159,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1137
1159
|
*/
|
|
1138
1160
|
async createRootDataStoreLegacy(pkg, rootDataStoreId) {
|
|
1139
1161
|
const fluidDataStore = await this._createDataStore(pkg, true /* isRoot */, rootDataStoreId);
|
|
1140
|
-
|
|
1162
|
+
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel. For
|
|
1163
|
+
// older versions, we still have to call bindToContext.
|
|
1164
|
+
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
1165
|
+
fluidDataStore.makeVisibleAndAttachGraph();
|
|
1166
|
+
}
|
|
1167
|
+
else {
|
|
1168
|
+
fluidDataStore.bindToContext();
|
|
1169
|
+
}
|
|
1141
1170
|
return fluidDataStore;
|
|
1142
1171
|
}
|
|
1143
1172
|
async createRootDataStore(pkg, rootDataStoreId) {
|
|
@@ -1191,7 +1220,18 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1191
1220
|
async _createDataStoreWithPropsLegacy(pkg, props, id = uuid(), isRoot = false) {
|
|
1192
1221
|
const fluidDataStore = await this.dataStores._createFluidDataStoreContext(Array.isArray(pkg) ? pkg : [pkg], id, isRoot, props).realize();
|
|
1193
1222
|
if (isRoot) {
|
|
1194
|
-
|
|
1223
|
+
// back-compat 0.59.1000 - makeVisibleAndAttachGraph was added in this version to IFluidDataStoreChannel.
|
|
1224
|
+
// For older versions, we still have to call bindToContext.
|
|
1225
|
+
if (fluidDataStore.makeVisibleAndAttachGraph !== undefined) {
|
|
1226
|
+
fluidDataStore.makeVisibleAndAttachGraph();
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
fluidDataStore.bindToContext();
|
|
1230
|
+
}
|
|
1231
|
+
this.logger.sendTelemetryEvent({
|
|
1232
|
+
eventName: "Root datastore with props",
|
|
1233
|
+
hasProps: props !== undefined,
|
|
1234
|
+
});
|
|
1195
1235
|
}
|
|
1196
1236
|
return channelToDataStore(fluidDataStore, id, this, this.dataStores, this.mc.logger);
|
|
1197
1237
|
}
|
|
@@ -1315,9 +1355,9 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1315
1355
|
if (runGC) {
|
|
1316
1356
|
gcStats = await this.collectGarbage({ logger: summaryLogger, runSweep, fullGC });
|
|
1317
1357
|
}
|
|
1318
|
-
const
|
|
1319
|
-
assert(
|
|
1320
|
-
return
|
|
1358
|
+
const { stats, summary } = await this.summarizerNode.summarize(fullTree, trackState);
|
|
1359
|
+
assert(summary.type === SummaryType.Tree, 0x12f /* "Container Runtime's summarize should always return a tree" */);
|
|
1360
|
+
return { stats, summary, gcStats };
|
|
1321
1361
|
}
|
|
1322
1362
|
/**
|
|
1323
1363
|
* Implementation of IGarbageCollectionRuntime::updateStateBeforeGC.
|
|
@@ -1334,7 +1374,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1334
1374
|
* @param fullGC - true to bypass optimizations and force full generation of GC data.
|
|
1335
1375
|
*/
|
|
1336
1376
|
async getGCData(fullGC) {
|
|
1337
|
-
|
|
1377
|
+
const builder = new GCDataBuilder();
|
|
1378
|
+
const dsGCData = await this.dataStores.getGCData(fullGC);
|
|
1379
|
+
builder.addNodes(dsGCData.gcNodes);
|
|
1380
|
+
const blobsGCData = this.blobManager.getGCData(fullGC);
|
|
1381
|
+
builder.addNodes(blobsGCData.gcNodes);
|
|
1382
|
+
return builder.getGCData();
|
|
1338
1383
|
}
|
|
1339
1384
|
/**
|
|
1340
1385
|
* Implementation of IGarbageCollectionRuntime::updateUsedRoutes.
|
|
@@ -1348,7 +1393,78 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1348
1393
|
// summarizing is required and asserted by the the summarizer node. We are the root and are
|
|
1349
1394
|
// always referenced, so the used routes is only self-route (empty string).
|
|
1350
1395
|
this.summarizerNode.updateUsedRoutes([""]);
|
|
1351
|
-
|
|
1396
|
+
const dataStoreUsedRoutes = [];
|
|
1397
|
+
for (const route of usedRoutes) {
|
|
1398
|
+
if (route.split("/")[1] !== BlobManager.basePath) {
|
|
1399
|
+
dataStoreUsedRoutes.push(route);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
return this.dataStores.updateUsedRoutes(dataStoreUsedRoutes, gcTimestamp);
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* When running GC in test mode, this is called to delete objects whose routes are unused. This enables testing
|
|
1406
|
+
* scenarios with accessing deleted content.
|
|
1407
|
+
* @param unusedRoutes - The routes that are unused in all data stores in this Container.
|
|
1408
|
+
*/
|
|
1409
|
+
deleteUnusedRoutes(unusedRoutes) {
|
|
1410
|
+
const blobManagerUnusedRoutes = [];
|
|
1411
|
+
const dataStoreUnusedRoutes = [];
|
|
1412
|
+
for (const route of unusedRoutes) {
|
|
1413
|
+
if (this.isBlobPath(route)) {
|
|
1414
|
+
blobManagerUnusedRoutes.push(route);
|
|
1415
|
+
}
|
|
1416
|
+
else {
|
|
1417
|
+
dataStoreUnusedRoutes.push(route);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
this.blobManager.deleteUnusedRoutes(blobManagerUnusedRoutes);
|
|
1421
|
+
this.dataStores.deleteUnusedRoutes(dataStoreUnusedRoutes);
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Returns a server generated referenced timestamp to be used to track unreferenced nodes by GC.
|
|
1425
|
+
*/
|
|
1426
|
+
getCurrentReferenceTimestampMs() {
|
|
1427
|
+
var _a, _b, _c;
|
|
1428
|
+
// Use the timestamp of the last message seen by this client as that is server generated. If no messages have
|
|
1429
|
+
// been processed, use the timestamp of the message from the last summary.
|
|
1430
|
+
return (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : (_c = this.messageAtLastSummary) === null || _c === void 0 ? void 0 : _c.timestamp;
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Returns the type of the GC node. Currently, there are nodes that belong to data store and nodes that belong
|
|
1434
|
+
* to the blob manager.
|
|
1435
|
+
*/
|
|
1436
|
+
getNodeType(nodePath) {
|
|
1437
|
+
if (this.isBlobPath(nodePath)) {
|
|
1438
|
+
return GCNodeType.Blob;
|
|
1439
|
+
}
|
|
1440
|
+
if (this.dataStores.isDataStoreNode(nodePath)) {
|
|
1441
|
+
return GCNodeType.DataStore;
|
|
1442
|
+
}
|
|
1443
|
+
// Root node ("/") and DDS nodes belong to "Other" node types.
|
|
1444
|
+
return GCNodeType.Other;
|
|
1445
|
+
}
|
|
1446
|
+
/**
|
|
1447
|
+
* Called by GC to retrieve the package path of the node with the given path. The node should belong to a
|
|
1448
|
+
* data store or an attachment blob.
|
|
1449
|
+
*/
|
|
1450
|
+
getGCNodePackagePath(nodePath) {
|
|
1451
|
+
// If the node is a blob, return "_blobs" as the package path.
|
|
1452
|
+
if (this.isBlobPath(nodePath)) {
|
|
1453
|
+
return ["_blobs"];
|
|
1454
|
+
}
|
|
1455
|
+
const dataStorePkgPath = this.dataStores.getDataStorePackagePath(nodePath);
|
|
1456
|
+
assert(dataStorePkgPath !== undefined, 0x2d6 /* "Package path requested for unknown node type." */);
|
|
1457
|
+
return dataStorePkgPath;
|
|
1458
|
+
}
|
|
1459
|
+
/**
|
|
1460
|
+
* Returns whether a given path is for attachment blobs that are in the format - "/BlobManager.basePath/...".
|
|
1461
|
+
*/
|
|
1462
|
+
isBlobPath(path) {
|
|
1463
|
+
const pathParts = path.split("/");
|
|
1464
|
+
if (pathParts.length < 2 || pathParts[1] !== BlobManager.basePath) {
|
|
1465
|
+
return false;
|
|
1466
|
+
}
|
|
1467
|
+
return true;
|
|
1352
1468
|
}
|
|
1353
1469
|
/**
|
|
1354
1470
|
* Runs garbage collection and updates the reference / used state of the nodes in the container.
|
|
@@ -1392,6 +1508,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1392
1508
|
try {
|
|
1393
1509
|
await this.deltaManager.inbound.pause();
|
|
1394
1510
|
const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
1511
|
+
const minimumSequenceNumber = this.deltaManager.minimumSequenceNumber;
|
|
1395
1512
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
1396
1513
|
// We should be here is we haven't processed be here. If we are of if the last message's sequence number
|
|
1397
1514
|
// doesn't match the last processed sequence number, log an error.
|
|
@@ -1431,7 +1548,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1431
1548
|
};
|
|
1432
1549
|
let continueResult = checkContinue();
|
|
1433
1550
|
if (!continueResult.continue) {
|
|
1434
|
-
return {
|
|
1551
|
+
return {
|
|
1552
|
+
stage: "base",
|
|
1553
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
1554
|
+
minimumSequenceNumber,
|
|
1555
|
+
error: continueResult.error,
|
|
1556
|
+
};
|
|
1435
1557
|
}
|
|
1436
1558
|
// increment summary count
|
|
1437
1559
|
if (this.summaryCount !== undefined) {
|
|
@@ -1454,7 +1576,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1454
1576
|
});
|
|
1455
1577
|
}
|
|
1456
1578
|
catch (error) {
|
|
1457
|
-
return {
|
|
1579
|
+
return {
|
|
1580
|
+
stage: "base",
|
|
1581
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
1582
|
+
minimumSequenceNumber,
|
|
1583
|
+
error,
|
|
1584
|
+
};
|
|
1458
1585
|
}
|
|
1459
1586
|
const { summary: summaryTree, stats: partialStats } = summarizeResult;
|
|
1460
1587
|
// Now that we have generated the summary, update the message at last summary to the last message processed.
|
|
@@ -1465,9 +1592,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1465
1592
|
const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
|
|
1466
1593
|
assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1467
1594
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
|
|
1468
|
-
const
|
|
1595
|
+
const gcSummaryTreeStats = summaryTree.tree[gcTreeKey]
|
|
1596
|
+
? calculateStats(summaryTree.tree[gcTreeKey])
|
|
1597
|
+
: undefined;
|
|
1598
|
+
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (_b = summarizeResult.gcStats) === null || _b === void 0 ? void 0 : _b.updatedDataStoreCount, gcBlobNodeCount: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.blobNodeCount, gcTotalBlobsSize: gcSummaryTreeStats === null || gcSummaryTreeStats === void 0 ? void 0 : gcSummaryTreeStats.totalBlobSize, opsSizesSinceLastSummary: this.opTracker.opsSizeAccumulator, nonSystemOpsSinceLastSummary: this.opTracker.nonSystemOpCount }, partialStats);
|
|
1469
1599
|
const generateSummaryData = {
|
|
1470
1600
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1601
|
+
minimumSequenceNumber,
|
|
1471
1602
|
summaryTree,
|
|
1472
1603
|
summaryStats,
|
|
1473
1604
|
generateDuration: trace.trace().duration,
|
|
@@ -1518,6 +1649,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1518
1649
|
}
|
|
1519
1650
|
const submitData = Object.assign(Object.assign({ stage: "submit" }, uploadData), { clientSequenceNumber, submitOpDuration: trace.trace().duration });
|
|
1520
1651
|
this.summarizerNode.completeSummary(handle);
|
|
1652
|
+
this.opTracker.reset();
|
|
1521
1653
|
return submitData;
|
|
1522
1654
|
}
|
|
1523
1655
|
finally {
|