@fluidframework/container-runtime 0.56.5 → 0.56.8
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/containerRuntime.d.ts +6 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +49 -7
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -7
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +10 -6
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/containerRuntime.d.ts +6 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +49 -7
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -7
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +10 -6
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +11 -11
- package/src/containerRuntime.ts +63 -1
- package/src/dataStoreContext.ts +10 -13
- package/src/packageVersion.ts +1 -1
package/lib/containerRuntime.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
1
|
import { AttachState, LoaderHeader, } from "@fluidframework/container-definitions";
|
|
6
2
|
import { assert, Trace, TypedEventEmitter, unreachableCase, performance, } from "@fluidframework/common-utils";
|
|
7
3
|
import { ChildLogger, raiseConnectedEvent, PerformanceEvent, normalizeError, TaggedLoggerAdapter, loggerToMonitoringContext, } from "@fluidframework/telemetry-utils";
|
|
@@ -59,6 +55,7 @@ const DefaultSummaryConfiguration = {
|
|
|
59
55
|
;
|
|
60
56
|
// Local storage key to set the default flush mode to TurnBased
|
|
61
57
|
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
58
|
+
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
62
59
|
export var RuntimeMessage;
|
|
63
60
|
(function (RuntimeMessage) {
|
|
64
61
|
RuntimeMessage["FluidDataStoreOp"] = "component";
|
|
@@ -332,7 +329,7 @@ export function getDeviceSpec() {
|
|
|
332
329
|
*/
|
|
333
330
|
export class ContainerRuntime extends TypedEventEmitter {
|
|
334
331
|
constructor(context, registry, metadata, electedSummarizerData, chunks, dataStoreAliasMap, runtimeOptions, containerScope, logger, existing, blobManagerSnapshot, requestHandler, _storage) {
|
|
335
|
-
var _a, _b, _c, _d, _e;
|
|
332
|
+
var _a, _b, _c, _d, _e, _f;
|
|
336
333
|
super();
|
|
337
334
|
this.context = context;
|
|
338
335
|
this.registry = registry;
|
|
@@ -341,10 +338,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
341
338
|
this.logger = logger;
|
|
342
339
|
this.requestHandler = requestHandler;
|
|
343
340
|
this._storage = _storage;
|
|
341
|
+
this.defaultMaxConsecutiveReconnects = 15;
|
|
344
342
|
this._orderSequentiallyCalls = 0;
|
|
345
343
|
this.needsFlush = false;
|
|
346
344
|
this.flushTrigger = false;
|
|
347
345
|
this.paused = false;
|
|
346
|
+
this.consecutiveReconnects = 0;
|
|
348
347
|
this._disposed = false;
|
|
349
348
|
this.dirtyContainer = false;
|
|
350
349
|
this.emitDirtyDocumentEvent = true;
|
|
@@ -435,6 +434,7 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
435
434
|
const timestamp = client === null || client === void 0 ? void 0 : client.timestamp;
|
|
436
435
|
return (_c = (_b = (_a = this.deltaManager.lastMessage) === null || _a === void 0 ? void 0 : _a.timestamp) !== null && _b !== void 0 ? _b : timestamp) !== null && _c !== void 0 ? _c : Date.now();
|
|
437
436
|
};
|
|
437
|
+
this.maxConsecutiveReconnects = (_c = this.mc.config.getNumber(maxConsecutiveReconnectsKey)) !== null && _c !== void 0 ? _c : this.defaultMaxConsecutiveReconnects;
|
|
438
438
|
this.garbageCollector = GarbageCollector.create(this, this.runtimeOptions.gcOptions, (unusedRoutes) => this.dataStores.deleteUnusedRoutes(unusedRoutes), getCurrentTimestamp, this.closeFn, context.baseSnapshot, async (id) => readAndParse(this.storage, id), this.mc.logger, existing, metadata);
|
|
439
439
|
const loadedFromSequenceNumber = this.deltaManager.initialSequenceNumber;
|
|
440
440
|
this.summarizerNode = createRootSummarizerNodeWithGC(ChildLogger.create(this.logger, "SummarizerNode"),
|
|
@@ -476,8 +476,8 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
476
476
|
const orderedClientLogger = ChildLogger.create(this.logger, "OrderedClientElection");
|
|
477
477
|
const orderedClientCollection = new OrderedClientCollection(orderedClientLogger, this.context.deltaManager, this.context.quorum);
|
|
478
478
|
const orderedClientElectionForSummarizer = new OrderedClientElection(orderedClientLogger, orderedClientCollection, electedSummarizerData !== null && electedSummarizerData !== void 0 ? electedSummarizerData : this.context.deltaManager.lastSequenceNumber, SummarizerClientElection.isClientEligible);
|
|
479
|
-
const summarizerClientElectionEnabled = (
|
|
480
|
-
const maxOpsSinceLastSummary = (
|
|
479
|
+
const summarizerClientElectionEnabled = (_d = this.mc.config.getBoolean("Fluid.ContainerRuntime.summarizerClientElection")) !== null && _d !== void 0 ? _d : ((_e = this.runtimeOptions.summaryOptions) === null || _e === void 0 ? void 0 : _e.summarizerClientElection) === true;
|
|
480
|
+
const maxOpsSinceLastSummary = (_f = this.runtimeOptions.summaryOptions.maxOpsSinceLastSummary) !== null && _f !== void 0 ? _f : 7000;
|
|
481
481
|
this.summarizerClientElection = new SummarizerClientElection(orderedClientLogger, this.summaryCollection, orderedClientElectionForSummarizer, maxOpsSinceLastSummary, summarizerClientElectionEnabled);
|
|
482
482
|
if (this.context.clientDetails.type === summarizerClientType) {
|
|
483
483
|
this._summarizer = new Summarizer("/_summarizer", this /* ISummarizerRuntime */, () => this.summaryConfiguration, this /* ISummarizerInternalsProvider */, this.handleContext, this.summaryCollection, async (runtime) => RunWhileConnectedCoordinator.create(runtime));
|
|
@@ -883,6 +883,37 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
883
883
|
}
|
|
884
884
|
}
|
|
885
885
|
}
|
|
886
|
+
// Track how many times the container tries to reconnect with pending messages.
|
|
887
|
+
// This happens when the connection state is changed and we reset the counter
|
|
888
|
+
// when we are able to process a local op or when there are no pending messages.
|
|
889
|
+
// If this counter reaches a max, it's a good indicator that the container
|
|
890
|
+
// is not making progress and it is stuck in a retry loop.
|
|
891
|
+
shouldContinueReconnecting() {
|
|
892
|
+
if (this.maxConsecutiveReconnects <= 0) {
|
|
893
|
+
// Feature disabled, we never stop reconnecting
|
|
894
|
+
return true;
|
|
895
|
+
}
|
|
896
|
+
if (!this.pendingStateManager.hasPendingMessages()) {
|
|
897
|
+
// If there are no pending messages, we can always reconnect
|
|
898
|
+
this.resetReconnectCount();
|
|
899
|
+
return true;
|
|
900
|
+
}
|
|
901
|
+
this.consecutiveReconnects++;
|
|
902
|
+
if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
|
|
903
|
+
// If we're halfway through the max reconnects, send an event in order
|
|
904
|
+
// to better identify false positives, if any. If the rate of this event
|
|
905
|
+
// matches `MaxReconnectsWithNoProgress`, we can safely cut down
|
|
906
|
+
// maxConsecutiveReconnects to half.
|
|
907
|
+
this.mc.logger.sendTelemetryEvent({
|
|
908
|
+
eventName: "ReconnectsWithNoProgress",
|
|
909
|
+
attempts: this.consecutiveReconnects,
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
return this.consecutiveReconnects < this.maxConsecutiveReconnects;
|
|
913
|
+
}
|
|
914
|
+
resetReconnectCount() {
|
|
915
|
+
this.consecutiveReconnects = 0;
|
|
916
|
+
}
|
|
886
917
|
replayPendingStates() {
|
|
887
918
|
// We need to be able to send ops to replay states
|
|
888
919
|
if (!this.canSendOps()) {
|
|
@@ -935,6 +966,11 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
935
966
|
if (changeOfState) {
|
|
936
967
|
this.deltaManager.off("op", this.onOp);
|
|
937
968
|
this.context.pendingLocalState = undefined;
|
|
969
|
+
if (!this.shouldContinueReconnecting()) {
|
|
970
|
+
this.closeFn(new GenericError("MaxReconnectsWithNoProgress", undefined, // error
|
|
971
|
+
{ attempts: this.consecutiveReconnects }));
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
938
974
|
this.replayPendingStates();
|
|
939
975
|
}
|
|
940
976
|
this.dataStores.setConnectionState(connected, clientId);
|
|
@@ -987,6 +1023,12 @@ export class ContainerRuntime extends TypedEventEmitter {
|
|
|
987
1023
|
}
|
|
988
1024
|
this.emit("op", message);
|
|
989
1025
|
this.scheduleManager.afterOpProcessing(undefined, message);
|
|
1026
|
+
if (local) {
|
|
1027
|
+
// If we have processed a local op, this means that the container is
|
|
1028
|
+
// making progress and we can reset the counter for how many times
|
|
1029
|
+
// we have consecutively replayed the pending states
|
|
1030
|
+
this.resetReconnectCount();
|
|
1031
|
+
}
|
|
990
1032
|
}
|
|
991
1033
|
catch (e) {
|
|
992
1034
|
this.scheduleManager.afterOpProcessing(e, message);
|