@fluidframework/container-runtime 0.57.1 → 0.58.0-55561
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 +14 -14
- 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/src/containerRuntime.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
IDeltaManager,
|
|
23
23
|
IDeltaSender,
|
|
24
24
|
IRuntime,
|
|
25
|
-
ContainerWarning,
|
|
26
25
|
ICriticalContainerError,
|
|
27
26
|
AttachState,
|
|
28
27
|
ILoaderOptions,
|
|
@@ -152,6 +151,7 @@ import {
|
|
|
152
151
|
IDataStoreAliasMessage,
|
|
153
152
|
isDataStoreAliasMessage,
|
|
154
153
|
} from "./dataStore";
|
|
154
|
+
import { BindBatchTracker } from "./batchTracker";
|
|
155
155
|
|
|
156
156
|
export enum ContainerMessageType {
|
|
157
157
|
// An op to be delivered to store
|
|
@@ -329,15 +329,24 @@ export enum RuntimeHeaders {
|
|
|
329
329
|
* have the untagged logger, so to accommodate that scenario the below interface is used. It can be removed once
|
|
330
330
|
* its usage is removed from TaggedLoggerAdapter fallback.
|
|
331
331
|
*/
|
|
332
|
-
interface OldContainerContextWithLogger extends IContainerContext {
|
|
332
|
+
interface OldContainerContextWithLogger extends Omit<IContainerContext, "taggedLogger"> {
|
|
333
333
|
logger: ITelemetryBaseLogger;
|
|
334
|
+
taggedLogger: undefined;
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
// Local storage key to set the default flush mode to TurnBased
|
|
337
|
-
const turnBasedFlushModeKey = "Fluid.ContainerRuntime.FlushModeTurnBased";
|
|
338
337
|
const useDataStoreAliasingKey = "Fluid.ContainerRuntime.UseDataStoreAliasing";
|
|
339
338
|
const maxConsecutiveReconnectsKey = "Fluid.ContainerRuntime.MaxConsecutiveReconnects";
|
|
340
339
|
|
|
340
|
+
// Feature gate for the max op size. If the value is negative, chunking is enabled
|
|
341
|
+
// and all ops over 16k would be chunked. If the value is positive, all ops with
|
|
342
|
+
// a size strictly larger will be rejected and the container closed with an error.
|
|
343
|
+
const maxOpSizeInBytesKey = "Fluid.ContainerRuntime.MaxOpSizeInBytes";
|
|
344
|
+
|
|
345
|
+
// By default, we should reject any op larger than 768KB,
|
|
346
|
+
// in order to account for some extra overhead from serialization
|
|
347
|
+
// to not reach the 1MB limits in socket.io and Kafka.
|
|
348
|
+
const defaultMaxOpSizeInBytes = 768000;
|
|
349
|
+
|
|
341
350
|
export enum RuntimeMessage {
|
|
342
351
|
FluidDataStoreOp = "component",
|
|
343
352
|
Attach = "attach",
|
|
@@ -687,8 +696,9 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
687
696
|
): Promise<ContainerRuntime> {
|
|
688
697
|
// If taggedLogger exists, use it. Otherwise, wrap the vanilla logger:
|
|
689
698
|
// back-compat: Remove the TaggedLoggerAdapter fallback once all the host are using loader > 0.45
|
|
690
|
-
const
|
|
691
|
-
|
|
699
|
+
const backCompatContext: IContainerContext | OldContainerContextWithLogger = context;
|
|
700
|
+
const passLogger = backCompatContext.taggedLogger ??
|
|
701
|
+
new TaggedLoggerAdapter((backCompatContext as OldContainerContextWithLogger).logger);
|
|
692
702
|
const logger = ChildLogger.create(passLogger, undefined, {
|
|
693
703
|
all: {
|
|
694
704
|
runtimeVersion: pkgVersion,
|
|
@@ -770,7 +780,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
770
780
|
if (loadSequenceNumberVerification !== "bypass" && runtimeSequenceNumber !== protocolSequenceNumber) {
|
|
771
781
|
// "Load from summary, runtime metadata sequenceNumber !== initialSequenceNumber"
|
|
772
782
|
const error = new DataCorruptionError(
|
|
773
|
-
"
|
|
783
|
+
"Summary metadata mismatch",
|
|
774
784
|
{ runtimeSequenceNumber, protocolSequenceNumber },
|
|
775
785
|
);
|
|
776
786
|
|
|
@@ -892,12 +902,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
892
902
|
|
|
893
903
|
private readonly summarizerNode: IRootSummarizerNodeWithGC;
|
|
894
904
|
private readonly _aliasingEnabled: boolean;
|
|
905
|
+
private readonly _maxOpSizeInBytes: number;
|
|
895
906
|
|
|
896
907
|
private readonly maxConsecutiveReconnects: number;
|
|
897
908
|
private readonly defaultMaxConsecutiveReconnects = 15;
|
|
898
909
|
|
|
899
910
|
private _orderSequentiallyCalls: number = 0;
|
|
900
|
-
private _flushMode: FlushMode;
|
|
911
|
+
private _flushMode: FlushMode = FlushMode.TurnBased;
|
|
901
912
|
private needsFlush = false;
|
|
902
913
|
private flushTrigger = false;
|
|
903
914
|
|
|
@@ -933,8 +944,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
933
944
|
private dirtyContainer: boolean;
|
|
934
945
|
private emitDirtyDocumentEvent = true;
|
|
935
946
|
|
|
936
|
-
private readonly summarizerWarning = (warning: ContainerWarning) =>
|
|
937
|
-
this.mc.logger.sendTelemetryEvent({ eventName: "summarizerWarning" }, warning);
|
|
938
947
|
/**
|
|
939
948
|
* Summarizer is responsible for coordinating when to send generate and send summaries.
|
|
940
949
|
* It is the main entry point for summary work.
|
|
@@ -1019,14 +1028,11 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1019
1028
|
this.mc = loggerToMonitoringContext(
|
|
1020
1029
|
ChildLogger.create(this.logger, "ContainerRuntime"));
|
|
1021
1030
|
|
|
1022
|
-
this._flushMode =
|
|
1023
|
-
this.mc.config.getBoolean(turnBasedFlushModeKey) ?? false
|
|
1024
|
-
? FlushMode.TurnBased : FlushMode.Immediate;
|
|
1025
|
-
|
|
1026
1031
|
this._aliasingEnabled =
|
|
1027
1032
|
(this.mc.config.getBoolean(useDataStoreAliasingKey) ?? false) ||
|
|
1028
1033
|
(runtimeOptions.useDataStoreAliasing ?? false);
|
|
1029
1034
|
|
|
1035
|
+
this._maxOpSizeInBytes = (this.mc.config.getNumber(maxOpSizeInBytesKey) ?? defaultMaxOpSizeInBytes);
|
|
1030
1036
|
this.maxConsecutiveReconnects =
|
|
1031
1037
|
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? this.defaultMaxConsecutiveReconnects;
|
|
1032
1038
|
|
|
@@ -1219,7 +1225,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1219
1225
|
},
|
|
1220
1226
|
this.runtimeOptions.summaryOptions.summarizerOptions,
|
|
1221
1227
|
);
|
|
1222
|
-
this.summaryManager.on("summarizerWarning", this.summarizerWarning);
|
|
1223
1228
|
this.summaryManager.start();
|
|
1224
1229
|
}
|
|
1225
1230
|
}
|
|
@@ -1269,6 +1274,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1269
1274
|
});
|
|
1270
1275
|
|
|
1271
1276
|
ReportOpPerfTelemetry(this.context.clientId, this.deltaManager, this.logger);
|
|
1277
|
+
BindBatchTracker(this, this.logger);
|
|
1272
1278
|
}
|
|
1273
1279
|
|
|
1274
1280
|
public dispose(error?: Error): void {
|
|
@@ -1285,7 +1291,6 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1285
1291
|
}, error);
|
|
1286
1292
|
|
|
1287
1293
|
if (this.summaryManager !== undefined) {
|
|
1288
|
-
this.summaryManager.off("summarizerWarning", this.summarizerWarning);
|
|
1289
1294
|
this.summaryManager.dispose();
|
|
1290
1295
|
}
|
|
1291
1296
|
this.garbageCollector.dispose();
|
|
@@ -1477,7 +1482,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1477
1482
|
if (this.consecutiveReconnects === Math.floor(this.maxConsecutiveReconnects / 2)) {
|
|
1478
1483
|
// If we're halfway through the max reconnects, send an event in order
|
|
1479
1484
|
// to better identify false positives, if any. If the rate of this event
|
|
1480
|
-
// matches
|
|
1485
|
+
// matches Container Close count below, we can safely cut down
|
|
1481
1486
|
// maxConsecutiveReconnects to half.
|
|
1482
1487
|
this.mc.logger.sendTelemetryEvent({
|
|
1483
1488
|
eventName: "ReconnectsWithNoProgress",
|
|
@@ -1574,7 +1579,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1574
1579
|
this.context.pendingLocalState = undefined;
|
|
1575
1580
|
if (!this.shouldContinueReconnecting()) {
|
|
1576
1581
|
this.closeFn(new GenericError(
|
|
1577
|
-
"
|
|
1582
|
+
"Runtime detected too many reconnects with no progress syncing local ops",
|
|
1578
1583
|
undefined, // error
|
|
1579
1584
|
{ attempts: this.consecutiveReconnects }));
|
|
1580
1585
|
return;
|
|
@@ -1757,7 +1762,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
1757
1762
|
this._orderSequentiallyCalls++;
|
|
1758
1763
|
callback();
|
|
1759
1764
|
} catch (error) {
|
|
1760
|
-
this.closeFn(new GenericError("
|
|
1765
|
+
this.closeFn(new GenericError("orderSequentially callback exception", error));
|
|
1761
1766
|
throw error; // throw the original error for the consumer of the runtime
|
|
1762
1767
|
} finally {
|
|
1763
1768
|
this._orderSequentiallyCalls--;
|
|
@@ -2137,6 +2142,15 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2137
2142
|
const summaryRefSeqNum = this.deltaManager.lastSequenceNumber;
|
|
2138
2143
|
const message = `Summary @${summaryRefSeqNum}:${this.deltaManager.minimumSequenceNumber}`;
|
|
2139
2144
|
|
|
2145
|
+
// We should be here is we haven't processed be here. If we are of if the last message's sequence number
|
|
2146
|
+
// doesn't match the last processed sequence number, log an error.
|
|
2147
|
+
if (summaryRefSeqNum !== this.deltaManager.lastMessage?.sequenceNumber) {
|
|
2148
|
+
summaryLogger.sendErrorEvent({
|
|
2149
|
+
eventName: "LastSequenceMismatch",
|
|
2150
|
+
message,
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2140
2154
|
this.summarizerNode.startSummary(summaryRefSeqNum, summaryLogger);
|
|
2141
2155
|
|
|
2142
2156
|
// Helper function to check whether we should still continue between each async step.
|
|
@@ -2406,18 +2420,13 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2406
2420
|
}
|
|
2407
2421
|
}
|
|
2408
2422
|
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
/* batch: */ this._flushMode === FlushMode.TurnBased,
|
|
2417
|
-
opMetadataInternal);
|
|
2418
|
-
} else {
|
|
2419
|
-
clientSequenceNumber = this.submitChunkedMessage(type, serializedContent, maxOpSize);
|
|
2420
|
-
}
|
|
2423
|
+
clientSequenceNumber = this.submitMaybeChunkedMessages(
|
|
2424
|
+
type,
|
|
2425
|
+
content,
|
|
2426
|
+
serializedContent,
|
|
2427
|
+
maxOpSize,
|
|
2428
|
+
this._flushMode === FlushMode.TurnBased,
|
|
2429
|
+
opMetadataInternal);
|
|
2421
2430
|
}
|
|
2422
2431
|
|
|
2423
2432
|
// Let the PendingStateManager know that a message was submitted.
|
|
@@ -2434,6 +2443,48 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2434
2443
|
}
|
|
2435
2444
|
}
|
|
2436
2445
|
|
|
2446
|
+
private submitMaybeChunkedMessages(
|
|
2447
|
+
type: ContainerMessageType,
|
|
2448
|
+
content: any,
|
|
2449
|
+
serializedContent: string,
|
|
2450
|
+
serverMaxOpSize: number,
|
|
2451
|
+
batch: boolean,
|
|
2452
|
+
opMetadataInternal: unknown = undefined,
|
|
2453
|
+
): number {
|
|
2454
|
+
if (this._maxOpSizeInBytes >= 0) {
|
|
2455
|
+
// Chunking disabled
|
|
2456
|
+
if (!serializedContent || serializedContent.length <= this._maxOpSizeInBytes) {
|
|
2457
|
+
return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
|
|
2458
|
+
}
|
|
2459
|
+
|
|
2460
|
+
// When chunking is disabled, we ignore the server max message size
|
|
2461
|
+
// and if the content length is larger than the client configured message size
|
|
2462
|
+
// instead of splitting the content, we will fail by explicitly close the container
|
|
2463
|
+
this.closeFn(new GenericError(
|
|
2464
|
+
"OpTooLarge",
|
|
2465
|
+
/* error */ undefined,
|
|
2466
|
+
{
|
|
2467
|
+
length: {
|
|
2468
|
+
value: serializedContent.length,
|
|
2469
|
+
tag: TelemetryDataTag.PackageData,
|
|
2470
|
+
},
|
|
2471
|
+
limit: {
|
|
2472
|
+
value: this._maxOpSizeInBytes,
|
|
2473
|
+
tag: TelemetryDataTag.PackageData,
|
|
2474
|
+
},
|
|
2475
|
+
}));
|
|
2476
|
+
return -1;
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
// Chunking enabled, fallback on the server's max message size
|
|
2480
|
+
// and split the content accordingly
|
|
2481
|
+
if (!serializedContent || serializedContent.length <= serverMaxOpSize) {
|
|
2482
|
+
return this.submitRuntimeMessage(type, content, batch, opMetadataInternal);
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
return this.submitChunkedMessage(type, serializedContent, serverMaxOpSize);
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2437
2488
|
private submitChunkedMessage(type: ContainerMessageType, content: string, maxOpSize: number): number {
|
|
2438
2489
|
const contentLength = content.length;
|
|
2439
2490
|
const chunkN = Math.floor((contentLength - 1) / maxOpSize) + 1;
|
|
@@ -2549,6 +2600,8 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2549
2600
|
summaryRefSeq,
|
|
2550
2601
|
async () => this.fetchSnapshotFromStorage(ackHandle, summaryLogger, {
|
|
2551
2602
|
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2603
|
+
ackHandle,
|
|
2604
|
+
summaryRefSeq,
|
|
2552
2605
|
fetchLatest: false,
|
|
2553
2606
|
}),
|
|
2554
2607
|
readAndParseBlob,
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
TelemetryDataTag,
|
|
67
67
|
ThresholdCounter,
|
|
68
68
|
} from "@fluidframework/telemetry-utils";
|
|
69
|
-
import {
|
|
69
|
+
import { DataProcessingError } from "@fluidframework/container-utils";
|
|
70
70
|
|
|
71
71
|
import { ContainerRuntime } from "./containerRuntime";
|
|
72
72
|
import {
|
|
@@ -310,7 +310,7 @@ export abstract class FluidDataStoreContext extends TypedEventEmitter<IFluidData
|
|
|
310
310
|
if (!this.channelDeferred) {
|
|
311
311
|
this.channelDeferred = new Deferred<IFluidDataStoreChannel>();
|
|
312
312
|
this.realizeCore(this.existing).catch((error) => {
|
|
313
|
-
const errorWrapped =
|
|
313
|
+
const errorWrapped = DataProcessingError.wrapIfUnrecognized(error, "realizeFluidDataStoreContext");
|
|
314
314
|
errorWrapped.addTelemetryProperties({ fluidDataStoreId: { value: this.id, tag: "PackageData"} });
|
|
315
315
|
this.channelDeferred?.reject(errorWrapped);
|
|
316
316
|
this.logger.sendErrorEvent({ eventName: "RealizeError"}, errorWrapped);
|
package/src/dataStores.ts
CHANGED
|
@@ -194,7 +194,7 @@ export class DataStores implements IDisposable {
|
|
|
194
194
|
if (this.alreadyProcessed(attachMessage.id)) {
|
|
195
195
|
// TODO: dataStoreId may require a different tag from PackageData #7488
|
|
196
196
|
const error = new DataCorruptionError(
|
|
197
|
-
"
|
|
197
|
+
"Duplicate DataStore created with existing id",
|
|
198
198
|
{
|
|
199
199
|
...extractSafePropertiesFromMessage(message),
|
|
200
200
|
dataStoreId: {
|
package/src/garbageCollection.ts
CHANGED
|
@@ -69,7 +69,7 @@ const writeAtRootKey = "Fluid.GarbageCollection.WriteDataAtRoot";
|
|
|
69
69
|
const runSessionExpiry = "Fluid.GarbageCollection.RunSessionExpiry";
|
|
70
70
|
|
|
71
71
|
const defaultDeleteTimeoutMs = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
72
|
-
const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
72
|
+
export const defaultSessionExpiryDurationMs = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
73
73
|
|
|
74
74
|
/** The statistics of the system state after a garbage collection run. */
|
|
75
75
|
export interface IGCStats {
|
|
@@ -545,17 +545,18 @@ export class GarbageCollector implements IGarbageCollector {
|
|
|
545
545
|
// used in the container.
|
|
546
546
|
if (this.shouldRunGC) {
|
|
547
547
|
this.initializeBaseStateP.catch((error) => {
|
|
548
|
-
|
|
549
|
-
error
|
|
548
|
+
const dpe = DataProcessingError.wrapIfUnrecognized(
|
|
549
|
+
error,
|
|
550
550
|
"FailedToInitializeGC",
|
|
551
|
-
{
|
|
552
|
-
gcEnabled: this.gcEnabled,
|
|
553
|
-
runSweep: this.shouldRunSweep,
|
|
554
|
-
writeAtRoot: this._writeDataAtRoot,
|
|
555
|
-
testMode: this.testMode,
|
|
556
|
-
sessionExpiry: this.sessionExpiryTimeoutMs,
|
|
557
|
-
},
|
|
558
551
|
);
|
|
552
|
+
dpe.addTelemetryProperties({
|
|
553
|
+
gcEnabled: this.gcEnabled,
|
|
554
|
+
runSweep: this.shouldRunSweep,
|
|
555
|
+
writeAtRoot: this._writeDataAtRoot,
|
|
556
|
+
testMode: this.testMode,
|
|
557
|
+
sessionExpiry: this.sessionExpiryTimeoutMs,
|
|
558
|
+
});
|
|
559
|
+
throw dpe;
|
|
559
560
|
});
|
|
560
561
|
}
|
|
561
562
|
}
|
package/src/packageVersion.ts
CHANGED
|
@@ -339,15 +339,11 @@ export class PendingStateManager implements IDisposable {
|
|
|
339
339
|
// The clientSequenceNumber of the incoming message must match that of the pending message.
|
|
340
340
|
if (pendingState.clientSequenceNumber !== message.clientSequenceNumber) {
|
|
341
341
|
// Close the container because this could indicate data corruption.
|
|
342
|
-
const error =
|
|
342
|
+
const error = DataProcessingError.create(
|
|
343
|
+
"pending local message clientSequenceNumber mismatch",
|
|
343
344
|
"unexpectedAckReceived",
|
|
344
|
-
|
|
345
|
-
{
|
|
346
|
-
clientId: message.clientId,
|
|
347
|
-
sequenceNumber: message.sequenceNumber,
|
|
348
|
-
clientSequenceNumber: message.clientSequenceNumber,
|
|
349
|
-
expectedClientSequenceNumber: pendingState.clientSequenceNumber,
|
|
350
|
-
},
|
|
345
|
+
message,
|
|
346
|
+
{ expectedClientSequenceNumber: pendingState.clientSequenceNumber },
|
|
351
347
|
);
|
|
352
348
|
|
|
353
349
|
this.containerRuntime.closeFn(error);
|
package/src/runningSummarizer.ts
CHANGED
|
@@ -51,7 +51,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
51
51
|
configuration: ISummaryConfiguration,
|
|
52
52
|
submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
|
|
53
53
|
heuristicData: ISummarizeHeuristicData,
|
|
54
|
-
raiseSummarizingError: (
|
|
54
|
+
raiseSummarizingError: (errorMessage: string) => void,
|
|
55
55
|
summaryCollection: SummaryCollection,
|
|
56
56
|
cancellationToken: ISummaryCancellationToken,
|
|
57
57
|
stopSummarizerCallback: (reason: SummarizerStopReason) => void,
|
|
@@ -101,7 +101,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
101
101
|
private readonly configuration: ISummaryConfiguration,
|
|
102
102
|
private readonly submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
|
|
103
103
|
private readonly heuristicData: ISummarizeHeuristicData,
|
|
104
|
-
private readonly raiseSummarizingError: (
|
|
104
|
+
private readonly raiseSummarizingError: (errorMessage: string) => void,
|
|
105
105
|
private readonly summaryCollection: SummaryCollection,
|
|
106
106
|
private readonly cancellationToken: ISummaryCancellationToken,
|
|
107
107
|
private readonly stopSummarizerCallback: (reason: SummarizerStopReason) => void,
|
|
@@ -131,7 +131,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
131
131
|
this.pendingAckTimer = new PromiseTimer(
|
|
132
132
|
maxAckWaitTime,
|
|
133
133
|
() => {
|
|
134
|
-
this.raiseSummarizingError("
|
|
134
|
+
this.raiseSummarizingError("Pending summary ack not received in time");
|
|
135
135
|
// Note: summarizeCount (from ChildLogger definition) may be 0,
|
|
136
136
|
// since this code path is hit when RunningSummarizer first starts up,
|
|
137
137
|
// before this instance has kicked off a new summarize run.
|
package/src/summarizer.ts
CHANGED
|
@@ -46,20 +46,19 @@ export class SummarizingWarning extends LoggingError implements ISummarizingWarn
|
|
|
46
46
|
|
|
47
47
|
constructor(
|
|
48
48
|
errorMessage: string,
|
|
49
|
-
readonly fluidErrorCode: string,
|
|
50
49
|
readonly logged: boolean = false,
|
|
51
50
|
) {
|
|
52
51
|
super(errorMessage);
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
static wrap(error: any,
|
|
56
|
-
const newErrorFn = (errMsg: string) => new SummarizingWarning(errMsg,
|
|
54
|
+
static wrap(error: any, logged: boolean = false, logger: ITelemetryLogger) {
|
|
55
|
+
const newErrorFn = (errMsg: string) => new SummarizingWarning(errMsg, logged);
|
|
57
56
|
return wrapErrorAndLog<SummarizingWarning>(error, newErrorFn, logger);
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
export const createSummarizingWarning =
|
|
62
|
-
(
|
|
61
|
+
(errorMessage: string, logged: boolean) => new SummarizingWarning(errorMessage, logged);
|
|
63
62
|
|
|
64
63
|
/**
|
|
65
64
|
* Summarizer is responsible for coordinating when to generate and send summaries.
|
|
@@ -144,7 +143,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
144
143
|
return await this.runCore(onBehalfOf, options);
|
|
145
144
|
} catch (error) {
|
|
146
145
|
this.stop("summarizerException");
|
|
147
|
-
throw SummarizingWarning.wrap(error,
|
|
146
|
+
throw SummarizingWarning.wrap(error, false /* logged */, this.logger);
|
|
148
147
|
} finally {
|
|
149
148
|
this.dispose();
|
|
150
149
|
this.runtime.closeFn();
|
|
@@ -257,9 +256,10 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
257
256
|
summaryTime: Date.now(),
|
|
258
257
|
} as const,
|
|
259
258
|
),
|
|
260
|
-
(
|
|
259
|
+
(errorMessage: string) => {
|
|
261
260
|
if (!this._disposed) {
|
|
262
|
-
this.
|
|
261
|
+
this.logger.sendErrorEvent({ eventName: "summarizingError" },
|
|
262
|
+
createSummarizingWarning(errorMessage, true));
|
|
263
263
|
}
|
|
264
264
|
},
|
|
265
265
|
this.summaryCollection,
|
|
@@ -356,7 +356,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
356
356
|
return builder.build();
|
|
357
357
|
}
|
|
358
358
|
catch (error) {
|
|
359
|
-
throw SummarizingWarning.wrap(error,
|
|
359
|
+
throw SummarizingWarning.wrap(error, false /* logged */, this.logger);
|
|
360
360
|
}
|
|
361
361
|
};
|
|
362
362
|
|
package/src/summaryGenerator.ts
CHANGED
|
@@ -152,7 +152,7 @@ export class SummaryGenerator {
|
|
|
152
152
|
private readonly pendingAckTimer: IPromiseTimer,
|
|
153
153
|
private readonly heuristicData: ISummarizeHeuristicData,
|
|
154
154
|
private readonly submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
|
|
155
|
-
private readonly raiseSummarizingError: (
|
|
155
|
+
private readonly raiseSummarizingError: (errorMessage: string) => void,
|
|
156
156
|
private readonly successfulSummaryCallback: () => void,
|
|
157
157
|
private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
|
|
158
158
|
private readonly logger: ITelemetryLogger,
|
|
@@ -394,7 +394,7 @@ export class SummaryGenerator {
|
|
|
394
394
|
const message = summaryNack?.message;
|
|
395
395
|
const retryAfterSeconds = summaryNack?.retryAfter;
|
|
396
396
|
|
|
397
|
-
const error = new LoggingError(`summaryNack: ${message}`, { retryAfterSeconds });
|
|
397
|
+
const error = new LoggingError(`Received summaryNack: ${message}`, { retryAfterSeconds });
|
|
398
398
|
logger.sendErrorEvent(
|
|
399
399
|
{ eventName: "SummaryNack", ...summarizeTelemetryProps, retryAfterSeconds }, error);
|
|
400
400
|
|
package/src/summaryManager.ts
CHANGED
|
@@ -4,16 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IDisposable, IEvent, IEventProvider, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import {
|
|
7
|
+
import { assert } from "@fluidframework/common-utils";
|
|
8
8
|
import { ChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
9
9
|
import { DriverErrorType } from "@fluidframework/driver-definitions";
|
|
10
|
-
import { createSummarizingWarning } from "./summarizer";
|
|
11
10
|
import { ISummarizerClientElection } from "./summarizerClientElection";
|
|
12
11
|
import { IThrottler } from "./throttler";
|
|
13
12
|
import {
|
|
14
13
|
ISummarizer,
|
|
15
14
|
ISummarizerOptions,
|
|
16
|
-
ISummarizingWarning,
|
|
17
15
|
SummarizerStopReason,
|
|
18
16
|
} from "./summarizerTypes";
|
|
19
17
|
import { SummaryCollection } from "./summaryCollection";
|
|
@@ -60,10 +58,6 @@ export interface IConnectedState extends IEventProvider<IConnectedEvents> {
|
|
|
60
58
|
readonly clientId: string | undefined;
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
export interface ISummaryManagerEvents extends IEvent {
|
|
64
|
-
(event: "summarizerWarning", listener: (warning: ISummarizingWarning) => void);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
61
|
export interface ISummaryManagerConfig {
|
|
68
62
|
initialDelayMs: number;
|
|
69
63
|
opsToBypassInitialDelay: number;
|
|
@@ -74,7 +68,7 @@ export interface ISummaryManagerConfig {
|
|
|
74
68
|
* It observes changes in calculated summarizer and reacts to changes by either creating summarizer client or
|
|
75
69
|
* stopping existing summarizer client.
|
|
76
70
|
*/
|
|
77
|
-
export class SummaryManager
|
|
71
|
+
export class SummaryManager implements IDisposable {
|
|
78
72
|
private readonly logger: ITelemetryLogger;
|
|
79
73
|
private readonly opsToBypassInitialDelay: number;
|
|
80
74
|
private readonly initialDelayMs: number;
|
|
@@ -105,7 +99,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
105
99
|
}: Readonly<Partial<ISummaryManagerConfig>> = {},
|
|
106
100
|
private readonly summarizerOptions?: Readonly<Partial<ISummarizerOptions>>,
|
|
107
101
|
) {
|
|
108
|
-
super();
|
|
109
102
|
|
|
110
103
|
this.logger = ChildLogger.create(
|
|
111
104
|
parentLogger,
|
|
@@ -219,8 +212,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
219
212
|
assert(this.state === SummaryManagerState.Starting, 0x263 /* "Expected: starting" */);
|
|
220
213
|
this.state = SummaryManagerState.Running;
|
|
221
214
|
|
|
222
|
-
summarizer.on("summarizingError",
|
|
223
|
-
(warning: ISummarizingWarning) => this.emit("summarizerWarning", warning));
|
|
224
215
|
this.summarizer = summarizer;
|
|
225
216
|
|
|
226
217
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -238,8 +229,8 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
238
229
|
// we ignore blindly, so try to narrow signature we are looking for - skip logging
|
|
239
230
|
// error only if this client should no longer be a summarizer (which in practice
|
|
240
231
|
// means it also lost connection), and error happened on load (we do not have summarizer).
|
|
241
|
-
// We could
|
|
242
|
-
// but that does not seem to be necessary.
|
|
232
|
+
// We could annotate the error raised in Container.load where the container closed during load with no error
|
|
233
|
+
// and check for that case here, but that does not seem to be necessary.
|
|
243
234
|
if (this.getShouldSummarizeState().shouldSummarize || this.summarizer !== undefined) {
|
|
244
235
|
// Report any failure as an error unless it was due to cancellation (like "disconnected" error)
|
|
245
236
|
// If failure happened on container load, we may not yet realized that socket disconnected, so check
|
|
@@ -251,7 +242,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
251
242
|
category,
|
|
252
243
|
},
|
|
253
244
|
error);
|
|
254
|
-
this.emit("summarizerWarning", error);
|
|
255
245
|
|
|
256
246
|
// Note that summarizer may keep going (like doing last summary).
|
|
257
247
|
// Ideally we await stopping process, but this code path is due to a bug
|
|
@@ -296,12 +286,6 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
296
286
|
private async delayBeforeCreatingSummarizer(): Promise<boolean> {
|
|
297
287
|
// throttle creation of new summarizer containers to prevent spamming the server with websocket connections
|
|
298
288
|
let delayMs = this.startThrottler.getDelay();
|
|
299
|
-
if (delayMs > 0 && delayMs > this.startThrottler.maxDelayMs) {
|
|
300
|
-
this.emit(
|
|
301
|
-
"summarizerWarning",
|
|
302
|
-
createSummarizingWarning("summaryManagerCreateSummarizerMaxThrottleDelay", false),
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
289
|
|
|
306
290
|
// We have been elected the summarizer. Some day we may be able to summarize with a live document but for
|
|
307
291
|
// now we play it safe and launch a second copy.
|
|
@@ -309,6 +293,7 @@ export class SummaryManager extends TypedEventEmitter<ISummaryManagerEvents> imp
|
|
|
309
293
|
eventName: "CreatingSummarizer",
|
|
310
294
|
throttlerDelay: delayMs,
|
|
311
295
|
initialDelay: this.initialDelayMs,
|
|
296
|
+
startThrottlerMaxDelayMs: this.startThrottler.maxDelayMs,
|
|
312
297
|
opsSinceLastAck: this.summaryCollection.opsSinceLastAck,
|
|
313
298
|
opsToBypassInitialDelay: this.opsToBypassInitialDelay,
|
|
314
299
|
});
|