@fluidframework/container-runtime 0.57.2 → 0.58.1000
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/batchTracker.d.ts +26 -0
- package/dist/batchTracker.d.ts.map +1 -0
- package/dist/batchTracker.js +59 -0
- package/dist/batchTracker.js.map +1 -0
- package/dist/containerRuntime.d.ts +2 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +63 -27
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.js +1 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.js +1 -1
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts +1 -0
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +6 -4
- package/dist/garbageCollection.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/pendingStateManager.d.ts.map +1 -1
- package/dist/pendingStateManager.js +1 -6
- package/dist/pendingStateManager.js.map +1 -1
- package/dist/runningSummarizer.d.ts +1 -1
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +1 -1
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts +3 -4
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +8 -9
- package/dist/summarizer.js.map +1 -1
- package/dist/summaryGenerator.d.ts +1 -1
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +1 -1
- package/dist/summaryGenerator.js.map +1 -1
- package/dist/summaryManager.d.ts +2 -6
- package/dist/summaryManager.d.ts.map +1 -1
- package/dist/summaryManager.js +4 -10
- package/dist/summaryManager.js.map +1 -1
- package/lib/batchTracker.d.ts +26 -0
- package/lib/batchTracker.d.ts.map +1 -0
- package/lib/batchTracker.js +54 -0
- package/lib/batchTracker.js.map +1 -0
- package/lib/containerRuntime.d.ts +2 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +63 -27
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.js +2 -2
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.js +1 -1
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts +1 -0
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +4 -2
- package/lib/garbageCollection.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/pendingStateManager.d.ts.map +1 -1
- package/lib/pendingStateManager.js +1 -6
- package/lib/pendingStateManager.js.map +1 -1
- package/lib/runningSummarizer.d.ts +1 -1
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +1 -1
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts +3 -4
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +8 -9
- package/lib/summarizer.js.map +1 -1
- package/lib/summaryGenerator.d.ts +1 -1
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +1 -1
- package/lib/summaryGenerator.js.map +1 -1
- package/lib/summaryManager.d.ts +2 -6
- package/lib/summaryManager.d.ts.map +1 -1
- package/lib/summaryManager.js +5 -11
- package/lib/summaryManager.js.map +1 -1
- package/package.json +15 -15
- package/src/batchTracker.ts +80 -0
- package/src/containerRuntime.ts +84 -31
- package/src/dataStoreContext.ts +2 -2
- package/src/dataStores.ts +1 -1
- package/src/garbageCollection.ts +11 -10
- package/src/packageVersion.ts +1 -1
- package/src/pendingStateManager.ts +4 -8
- package/src/runningSummarizer.ts +3 -3
- package/src/summarizer.ts +8 -8
- package/src/summaryGenerator.ts +2 -2
- package/src/summaryManager.ts +5 -20
package/lib/containerRuntime.js
CHANGED
|
@@ -26,6 +26,7 @@ import { formExponentialFn, Throttler } from "./throttler";
|
|
|
26
26
|
import { RunWhileConnectedCoordinator } from "./runWhileConnectedCoordinator";
|
|
27
27
|
import { GarbageCollector, gcTreeKey, } from "./garbageCollection";
|
|
28
28
|
import { channelToDataStore, isDataStoreAliasMessage, } from "./dataStore";
|
|
29
|
+
import { BindBatchTracker } from "./batchTracker";
|
|
29
30
|
export var ContainerMessageType;
|
|
30
31
|
(function (ContainerMessageType) {
|
|
31
32
|
// An op to be delivered to store
|
|
@@ -68,10 +69,16 @@ export var RuntimeHeaders;
|
|
|
68
69
|
/** True if the request is coming from an IFluidHandle. */
|
|
69
70
|
RuntimeHeaders["viaHandle"] = "viaHandle";
|
|
70
71
|
})(RuntimeHeaders || (RuntimeHeaders = {}));
|
|
71
|
-
// Local storage key to set the default flush mode to TurnBased
|
|
72
|
-
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
73
72
|
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
74
73
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
74
|
+
// Feature gate for the max op size. If the value is negative, chunking is enabled
|
|
75
|
+
// and all ops over 16k would be chunked. If the value is positive, all ops with
|
|
76
|
+
// a size strictly larger will be rejected and the container closed with an error.
|
|
77
|
+
const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
|
|
78
|
+
// By default, we should reject any op larger than 768KB,
|
|
79
|
+
// in order to account for some extra overhead from serialization
|
|
80
|
+
// to not reach the 1MB limits in socket.io and Kafka.
|
|
81
|
+
const defaultMaxOpSizeInBytes = 768000;
|
|
75
82
|
export var RuntimeMessage;
|
|
76
83
|
(function (RuntimeMessage) {
|
|
77
84
|
RuntimeMessage["FluidDataStoreOp"] = "component";
|
|
@@ -356,13 +363,13 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
356
363
|
this._storage = _storage;
|
|
357
364
|
this.defaultMaxConsecutiveReconnects = 15;
|
|
358
365
|
this._orderSequentiallyCalls = 0;
|
|
366
|
+
this._flushMode = FlushMode.TurnBased;
|
|
359
367
|
this.needsFlush = false;
|
|
360
368
|
this.flushTrigger = false;
|
|
361
369
|
this.paused = false;
|
|
362
370
|
this.consecutiveReconnects = 0;
|
|
363
371
|
this._disposed = false;
|
|
364
372
|
this.emitDirtyDocumentEvent = true;
|
|
365
|
-
this.summarizerWarning = (warning) => this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
|
|
366
373
|
/**
|
|
367
374
|
* Used to apply stashed ops at their reference sequence number.
|
|
368
375
|
* Normal op processing is synchronous, but applying stashed ops is async since the
|
|
@@ -434,11 +441,10 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
434
441
|
this.chunkMap = new Map(chunks);
|
|
435
442
|
this.handleContext = new ContainerFluidHandleContext("", this);
|
|
436
443
|
this.mc = loggerToMonitoringContext(ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
437
|
-
this._flushMode =
|
|
438
|
-
((_b = this.mc.config.getBoolean(turnBasedFlushModeKey)) !== null && _b !== void 0 ? _b : false) ? FlushMode.TurnBased : FlushMode.Immediate;
|
|
439
444
|
this._aliasingEnabled =
|
|
440
|
-
((
|
|
441
|
-
((
|
|
445
|
+
((_b = this.mc.config.getBoolean(useDataStoreAliasingKey)) !== null && _b !== void 0 ? _b : false) ||
|
|
446
|
+
((_c = runtimeOptions.useDataStoreAliasing) !== null && _c !== void 0 ? _c : false);
|
|
447
|
+
this._maxOpSizeInBytes = ((_d = this.mc.config.getNumber(maxOpSizeInBytesKey)) !== null && _d !== void 0 ? _d : defaultMaxOpSizeInBytes);
|
|
442
448
|
this.maxConsecutiveReconnects = (_e = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _e !== void 0 ? _e : this.defaultMaxConsecutiveReconnects;
|
|
443
449
|
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), (nodePath) => this.dataStores.getNodePackagePath(nodePath),
|
|
444
450
|
/**
|
|
@@ -523,7 +529,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
523
529
|
formExponentialFn({ coefficient: 20, initialDelay: 0 })), {
|
|
524
530
|
initialDelayMs: this.runtimeOptions.summaryOptions.initialSummarizerDelayMs,
|
|
525
531
|
}, this.runtimeOptions.summaryOptions.summarizerOptions);
|
|
526
|
-
this.summaryManager.on("summarizerWarning", this.summarizerWarning);
|
|
527
532
|
this.summaryManager.start();
|
|
528
533
|
}
|
|
529
534
|
}
|
|
@@ -554,6 +559,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
554
559
|
// logging container load stats
|
|
555
560
|
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 }));
|
|
556
561
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
562
|
+
BindBatchTracker(this, this.logger);
|
|
557
563
|
}
|
|
558
564
|
get IContainerRuntime() { return this; }
|
|
559
565
|
get IFluidRouter() { return this; }
|
|
@@ -569,7 +575,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
569
575
|
var _a, _b, _c;
|
|
570
576
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
571
577
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
572
|
-
const
|
|
578
|
+
const backCompatContext = context;
|
|
579
|
+
const passLogger = (_a = backCompatContext.taggedLogger) !== null && _a !== void 0 ? _a : new TaggedLoggerAdapter(backCompatContext.logger);
|
|
573
580
|
const logger = ChildLogger.create(passLogger, undefined, {
|
|
574
581
|
all: {
|
|
575
582
|
runtimeVersion: pkgVersion,
|
|
@@ -630,7 +637,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
630
637
|
// Unless bypass is explicitly set, then take action when sequence numbers mismatch.
|
|
631
638
|
if (loadSequenceNumberVerification !== "bypass" && runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
632
639
|
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
633
|
-
const error = new DataCorruptionError("
|
|
640
|
+
const error = new DataCorruptionError("Summary metadata mismatch", { runtimeSequenceNumber, protocolSequenceNumber });
|
|
634
641
|
if (loadSequenceNumberVerification === "log") {
|
|
635
642
|
logger.sendErrorEvent({ eventName: "SequenceNumberMismatch" }, error);
|
|
636
643
|
}
|
|
@@ -732,7 +739,6 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
732
739
|
attachState: this.attachState,
|
|
733
740
|
}, error);
|
|
734
741
|
if (this.summaryManager !== undefined) {
|
|
735
|
-
this.summaryManager.off("summarizerWarning", this.summarizerWarning);
|
|
736
742
|
this.summaryManager.dispose();
|
|
737
743
|
}
|
|
738
744
|
this.garbageCollector.dispose();
|
|
@@ -896,7 +902,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
896
902
|
if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
|
|
897
903
|
// If we're halfway through the max reconnects, send an event in order
|
|
898
904
|
// to better identify false positives, if any. If the rate of this event
|
|
899
|
-
// matches
|
|
905
|
+
// matches Container Close count below, we can safely cut down
|
|
900
906
|
// maxConsecutiveReconnects to half.
|
|
901
907
|
this.mc.logger.sendTelemetryEvent({
|
|
902
908
|
eventName: "ReconnectsWithNoProgress",
|
|
@@ -961,7 +967,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
961
967
|
this.deltaManager.off("op", this.onOp);
|
|
962
968
|
this.context.pendingLocalState = undefined;
|
|
963
969
|
if (!this.shouldContinueReconnecting()) {
|
|
964
|
-
this.closeFn(new GenericError("
|
|
970
|
+
this.closeFn(new GenericError("Runtime detected too many reconnects with no progress syncing local ops", undefined, // error
|
|
965
971
|
{ attempts: this.consecutiveReconnects }));
|
|
966
972
|
return;
|
|
967
973
|
}
|
|
@@ -1107,7 +1113,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1107
1113
|
callback();
|
|
1108
1114
|
}
|
|
1109
1115
|
catch (error) {
|
|
1110
|
-
this.closeFn(new GenericError("
|
|
1116
|
+
this.closeFn(new GenericError("orderSequentially callback exception", error));
|
|
1111
1117
|
throw error; // throw the original error for the consumer of the runtime
|
|
1112
1118
|
}
|
|
1113
1119
|
finally {
|
|
@@ -1364,7 +1370,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1364
1370
|
* @param options - options controlling how the summary is generated or submitted
|
|
1365
1371
|
*/
|
|
1366
1372
|
async submitSummary(options) {
|
|
1367
|
-
var _a, _b;
|
|
1373
|
+
var _a, _b, _c;
|
|
1368
1374
|
const { fullTree, refreshLatestAck, summaryLogger } = options;
|
|
1369
1375
|
if (refreshLatestAck) {
|
|
1370
1376
|
const latestSummaryRefSeq = await this.refreshLatestSummaryAckFromServer(ChildLogger.create(summaryLogger, undefined, { all: { safeSummary: true } }));
|
|
@@ -1382,6 +1388,14 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1382
1388
|
await this.deltaManager.inbound.pause();
|
|
1383
1389
|
const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
1384
1390
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
1391
|
+
// We should be here is we haven't processed be here. If we are of if the last message's sequence number
|
|
1392
|
+
// doesn't match the last processed sequence number, log an error.
|
|
1393
|
+
if (summaryRefSeqNum !== ((_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.sequenceNumber)) {
|
|
1394
|
+
summaryLogger.sendErrorEvent({
|
|
1395
|
+
eventName: "LastSequenceMismatch",
|
|
1396
|
+
message,
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1385
1399
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryLogger);
|
|
1386
1400
|
// Helper function to check whether we should still continue between each async step.
|
|
1387
1401
|
const checkContinue = () => {
|
|
@@ -1446,7 +1460,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1446
1460
|
const dataStoreTree = this.disableIsolatedChannels ? summaryTree : summaryTree.tree[channelsTreeName];
|
|
1447
1461
|
assert(dataStoreTree.type === SummaryType.Tree, 0x1fc /* "summary is not a tree" */);
|
|
1448
1462
|
const handleCount = Object.values(dataStoreTree.tree).filter((value) => value.type === SummaryType.Handle).length;
|
|
1449
|
-
const summaryStats = Object.assign({ dataStoreCount: this.dataStores.size, summarizedDataStoreCount: this.dataStores.size - handleCount, gcStateUpdatedDataStoreCount: (
|
|
1463
|
+
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 }, partialStats);
|
|
1450
1464
|
const generateSummaryData = {
|
|
1451
1465
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1452
1466
|
summaryTree,
|
|
@@ -1462,7 +1476,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1462
1476
|
const summaryContext = lastAck === undefined
|
|
1463
1477
|
? {
|
|
1464
1478
|
proposalHandle: undefined,
|
|
1465
|
-
ackHandle: (
|
|
1479
|
+
ackHandle: (_c = this.context.getLoadedFromVersion()) === null || _c === void 0 ? void 0 : _c.id,
|
|
1466
1480
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
1467
1481
|
}
|
|
1468
1482
|
: {
|
|
@@ -1593,16 +1607,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1593
1607
|
});
|
|
1594
1608
|
}
|
|
1595
1609
|
}
|
|
1596
|
-
|
|
1597
|
-
// there will be a lot of escape characters that can make it up to 2x bigger!
|
|
1598
|
-
// This is Ok, because DeltaManager.shouldSplit() will have 2 * maxMessageSize limit
|
|
1599
|
-
if (!serializedContent || serializedContent.length <= maxOpSize) {
|
|
1600
|
-
clientSequenceNumber = this.submitRuntimeMessage(type, content,
|
|
1601
|
-
/* batch: */ this._flushMode === FlushMode.TurnBased, opMetadataInternal);
|
|
1602
|
-
}
|
|
1603
|
-
else {
|
|
1604
|
-
clientSequenceNumber = this.submitChunkedMessage(type, serializedContent, maxOpSize);
|
|
1605
|
-
}
|
|
1610
|
+
clientSequenceNumber = this.submitMaybeChunkedMessages(type, content, serializedContent, maxOpSize, this._flushMode === FlushMode.TurnBased, opMetadataInternal);
|
|
1606
1611
|
}
|
|
1607
1612
|
// Let the PendingStateManager know that a message was submitted.
|
|
1608
1613
|
this.pendingStateManager.onSubmitMessage(type, clientSequenceNumber, this.deltaManager.lastSequenceNumber, content, localOpMetadata, opMetadataInternal);
|
|
@@ -1610,6 +1615,35 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1610
1615
|
this.updateDocumentDirtyState(true);
|
|
1611
1616
|
}
|
|
1612
1617
|
}
|
|
1618
|
+
submitMaybeChunkedMessages(type, content, serializedContent, serverMaxOpSize, batch, opMetadataInternal = undefined) {
|
|
1619
|
+
if (this._maxOpSizeInBytes >= 0) {
|
|
1620
|
+
// Chunking disabled
|
|
1621
|
+
if (!serializedContent || serializedContent.length <= this._maxOpSizeInBytes) {
|
|
1622
|
+
return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
|
|
1623
|
+
}
|
|
1624
|
+
// When chunking is disabled, we ignore the server max message size
|
|
1625
|
+
// and if the content length is larger than the client configured message size
|
|
1626
|
+
// instead of splitting the content, we will fail by explicitly close the container
|
|
1627
|
+
this.closeFn(new GenericError("OpTooLarge",
|
|
1628
|
+
/* error */ undefined, {
|
|
1629
|
+
length: {
|
|
1630
|
+
value: serializedContent.length,
|
|
1631
|
+
tag: TelemetryDataTag.PackageData,
|
|
1632
|
+
},
|
|
1633
|
+
limit: {
|
|
1634
|
+
value: this._maxOpSizeInBytes,
|
|
1635
|
+
tag: TelemetryDataTag.PackageData,
|
|
1636
|
+
},
|
|
1637
|
+
}));
|
|
1638
|
+
return -1;
|
|
1639
|
+
}
|
|
1640
|
+
// Chunking enabled, fallback on the server's max message size
|
|
1641
|
+
// and split the content accordingly
|
|
1642
|
+
if (!serializedContent || serializedContent.length <= serverMaxOpSize) {
|
|
1643
|
+
return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
|
|
1644
|
+
}
|
|
1645
|
+
return this.submitChunkedMessage(type, serializedContent, serverMaxOpSize);
|
|
1646
|
+
}
|
|
1613
1647
|
submitChunkedMessage(type, content, maxOpSize) {
|
|
1614
1648
|
const contentLength = content.length;
|
|
1615
1649
|
const chunkN = Math.floor((contentLength - 1) / maxOpSize) + 1;
|
|
@@ -1688,6 +1722,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
1688
1722
|
const readAndParseBlob = async (id) => readAndParse(this.storage, id);
|
|
1689
1723
|
const result = await this.summarizerNode.refreshLatestSummary(proposalHandle, summaryRefSeq, async () => this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
|
|
1690
1724
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
1725
|
+
ackHandle,
|
|
1726
|
+
summaryRefSeq,
|
|
1691
1727
|
fetchLatest: false,
|
|
1692
1728
|
}), readAndParseBlob, summaryLogger);
|
|
1693
1729
|
// Notify the garbage collector so it can update its latest summary state.
|