@fluidframework/container-runtime 2.0.0-dev.4.1.0.148229 → 2.0.0-dev.4.2.0.153917
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/CHANGELOG.md +58 -0
- package/README.md +69 -0
- package/dist/blobManager.d.ts +6 -14
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +50 -37
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +14 -1
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +37 -12
- package/dist/containerRuntime.js.map +1 -1
- package/dist/gc/gcHelpers.d.ts.map +1 -1
- package/dist/gc/gcHelpers.js +6 -6
- package/dist/gc/gcHelpers.js.map +1 -1
- package/dist/opLifecycle/index.d.ts +1 -0
- package/dist/opLifecycle/index.d.ts.map +1 -1
- package/dist/opLifecycle/index.js +3 -1
- package/dist/opLifecycle/index.js.map +1 -1
- package/dist/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/dist/opLifecycle/opDecompressor.js +2 -1
- package/dist/opLifecycle/opDecompressor.js.map +1 -1
- package/dist/opLifecycle/opGroupingManager.d.ts +14 -0
- package/dist/opLifecycle/opGroupingManager.d.ts.map +1 -0
- package/dist/opLifecycle/opGroupingManager.js +56 -0
- package/dist/opLifecycle/opGroupingManager.js.map +1 -0
- package/dist/opLifecycle/opSplitter.d.ts +1 -1
- package/dist/opLifecycle/opSplitter.d.ts.map +1 -1
- package/dist/opLifecycle/opSplitter.js +5 -6
- package/dist/opLifecycle/opSplitter.js.map +1 -1
- package/dist/opLifecycle/outbox.d.ts +2 -0
- package/dist/opLifecycle/outbox.d.ts.map +1 -1
- package/dist/opLifecycle/outbox.js +3 -3
- package/dist/opLifecycle/outbox.js.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.d.ts +4 -2
- package/dist/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/dist/opLifecycle/remoteMessageProcessor.js +30 -20
- package/dist/opLifecycle/remoteMessageProcessor.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/dist/summary/index.d.ts +1 -1
- package/dist/summary/index.d.ts.map +1 -1
- package/dist/summary/index.js +3 -1
- package/dist/summary/index.js.map +1 -1
- package/dist/summary/runningSummarizer.d.ts +5 -3
- package/dist/summary/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/runningSummarizer.js +82 -67
- package/dist/summary/runningSummarizer.js.map +1 -1
- package/dist/summary/summarizer.d.ts.map +1 -1
- package/dist/summary/summarizer.js +1 -5
- package/dist/summary/summarizer.js.map +1 -1
- package/dist/summary/summarizerHeuristics.d.ts +1 -0
- package/dist/summary/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summary/summarizerHeuristics.js +3 -0
- package/dist/summary/summarizerHeuristics.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js +1 -1
- package/dist/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts +128 -2
- package/dist/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js +4 -3
- package/dist/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +14 -2
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryGenerator.d.ts +28 -2
- package/dist/summary/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryGenerator.js +19 -16
- package/dist/summary/summaryGenerator.js.map +1 -1
- package/lib/blobManager.d.ts +6 -14
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +50 -37
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +14 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +38 -13
- package/lib/containerRuntime.js.map +1 -1
- package/lib/gc/gcHelpers.d.ts.map +1 -1
- package/lib/gc/gcHelpers.js +6 -6
- package/lib/gc/gcHelpers.js.map +1 -1
- package/lib/opLifecycle/index.d.ts +1 -0
- package/lib/opLifecycle/index.d.ts.map +1 -1
- package/lib/opLifecycle/index.js +1 -0
- package/lib/opLifecycle/index.js.map +1 -1
- package/lib/opLifecycle/opDecompressor.d.ts.map +1 -1
- package/lib/opLifecycle/opDecompressor.js +2 -1
- package/lib/opLifecycle/opDecompressor.js.map +1 -1
- package/lib/opLifecycle/opGroupingManager.d.ts +14 -0
- package/lib/opLifecycle/opGroupingManager.d.ts.map +1 -0
- package/lib/opLifecycle/opGroupingManager.js +52 -0
- package/lib/opLifecycle/opGroupingManager.js.map +1 -0
- package/lib/opLifecycle/opSplitter.d.ts +1 -1
- package/lib/opLifecycle/opSplitter.d.ts.map +1 -1
- package/lib/opLifecycle/opSplitter.js +5 -6
- package/lib/opLifecycle/opSplitter.js.map +1 -1
- package/lib/opLifecycle/outbox.d.ts +2 -0
- package/lib/opLifecycle/outbox.d.ts.map +1 -1
- package/lib/opLifecycle/outbox.js +3 -3
- package/lib/opLifecycle/outbox.js.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.d.ts +4 -2
- package/lib/opLifecycle/remoteMessageProcessor.d.ts.map +1 -1
- package/lib/opLifecycle/remoteMessageProcessor.js +30 -20
- package/lib/opLifecycle/remoteMessageProcessor.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/lib/summary/index.d.ts +1 -1
- package/lib/summary/index.d.ts.map +1 -1
- package/lib/summary/index.js +1 -0
- package/lib/summary/index.js.map +1 -1
- package/lib/summary/runningSummarizer.d.ts +5 -3
- package/lib/summary/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/runningSummarizer.js +82 -67
- package/lib/summary/runningSummarizer.js.map +1 -1
- package/lib/summary/summarizer.d.ts.map +1 -1
- package/lib/summary/summarizer.js +1 -5
- package/lib/summary/summarizer.js.map +1 -1
- package/lib/summary/summarizerHeuristics.d.ts +1 -0
- package/lib/summary/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summary/summarizerHeuristics.js +3 -0
- package/lib/summary/summarizerHeuristics.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js +1 -1
- package/lib/summary/summarizerNode/summarizerNode.js.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts +128 -2
- package/lib/summary/summarizerNode/summarizerNodeWithGc.d.ts.map +1 -1
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js +3 -3
- package/lib/summary/summarizerNode/summarizerNodeWithGc.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +14 -2
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryGenerator.d.ts +28 -2
- package/lib/summary/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryGenerator.js +17 -15
- package/lib/summary/summaryGenerator.js.map +1 -1
- package/package.json +19 -16
- package/src/blobManager.ts +64 -41
- package/src/containerRuntime.ts +70 -9
- package/src/gc/gcHelpers.ts +9 -6
- package/src/opLifecycle/README.md +106 -0
- package/src/opLifecycle/index.ts +1 -0
- package/src/opLifecycle/opDecompressor.ts +1 -0
- package/src/opLifecycle/opGroupingManager.ts +78 -0
- package/src/opLifecycle/opSplitter.ts +1 -5
- package/src/opLifecycle/outbox.ts +7 -3
- package/src/opLifecycle/remoteMessageProcessor.ts +38 -22
- package/src/packageVersion.ts +1 -1
- package/src/summary/index.ts +1 -1
- package/src/summary/runningSummarizer.ts +102 -80
- package/src/summary/summarizer.ts +0 -8
- package/src/summary/summarizerHeuristics.ts +4 -0
- package/src/summary/summarizerNode/summarizerNode.ts +1 -1
- package/src/summary/summarizerNode/summarizerNodeWithGc.ts +3 -3
- package/src/summary/summarizerTypes.ts +20 -3
- package/src/summary/summaryGenerator.ts +22 -16
|
@@ -135,7 +135,7 @@ export class OpSplitter {
|
|
|
135
135
|
* @param batch - the compressed batch which needs to be processed
|
|
136
136
|
* @returns A new adjusted batch which can be sent over the wire
|
|
137
137
|
*/
|
|
138
|
-
public
|
|
138
|
+
public splitFirstBatchMessage(batch: IBatch): IBatch {
|
|
139
139
|
assert(this.isBatchChunkingEnabled, 0x513 /* Chunking needs to be enabled */);
|
|
140
140
|
assert(
|
|
141
141
|
batch.contentSizeInBytes > 0 && batch.content.length > 0,
|
|
@@ -152,10 +152,6 @@ export class OpSplitter {
|
|
|
152
152
|
);
|
|
153
153
|
|
|
154
154
|
const firstMessage = batch.content[0]; // we expect this to be the large compressed op, which needs to be split
|
|
155
|
-
assert(
|
|
156
|
-
firstMessage.metadata?.compressed === true || firstMessage.compression !== undefined,
|
|
157
|
-
0x517 /* Batch needs to be compressed */,
|
|
158
|
-
);
|
|
159
155
|
assert(
|
|
160
156
|
(firstMessage.contents?.length ?? 0) >= this.chunkSizeInBytes,
|
|
161
157
|
0x518 /* First message in the batch needs to be chunkable */,
|
|
@@ -18,6 +18,7 @@ import { PendingStateManager } from "../pendingStateManager";
|
|
|
18
18
|
import { BatchManager, estimateSocketSize } from "./batchManager";
|
|
19
19
|
import { BatchMessage, IBatch } from "./definitions";
|
|
20
20
|
import { OpCompressor } from "./opCompressor";
|
|
21
|
+
import { OpGroupingManager } from "./opGroupingManager";
|
|
21
22
|
import { OpSplitter } from "./opSplitter";
|
|
22
23
|
|
|
23
24
|
export interface IOutboxConfig {
|
|
@@ -35,6 +36,7 @@ export interface IOutboxParameters {
|
|
|
35
36
|
readonly compressor: OpCompressor;
|
|
36
37
|
readonly splitter: OpSplitter;
|
|
37
38
|
readonly logger: ITelemetryLogger;
|
|
39
|
+
readonly groupingManager: OpGroupingManager;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
export class Outbox {
|
|
@@ -180,15 +182,17 @@ export class Outbox {
|
|
|
180
182
|
this.params.containerContext.submitBatchFn === undefined
|
|
181
183
|
) {
|
|
182
184
|
// Nothing to do if the batch is empty or if compression is disabled or not supported, or if we don't need to compress
|
|
183
|
-
return batch;
|
|
185
|
+
return this.params.groupingManager.groupBatch(batch);
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
const compressedBatch = this.params.
|
|
188
|
+
const compressedBatch = this.params.groupingManager.groupBatch(
|
|
189
|
+
this.params.compressor.compressBatch(batch),
|
|
190
|
+
);
|
|
187
191
|
|
|
188
192
|
if (this.params.splitter.isBatchChunkingEnabled) {
|
|
189
193
|
return compressedBatch.contentSizeInBytes <= this.params.splitter.chunkSizeInBytes
|
|
190
194
|
? compressedBatch
|
|
191
|
-
: this.params.splitter.
|
|
195
|
+
: this.params.splitter.splitFirstBatchMessage(compressedBatch);
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
if (compressedBatch.contentSizeInBytes >= this.params.config.maxBatchSizeInBytes) {
|
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
import { ISequencedDocumentMessage, MessageType } from "@fluidframework/protocol-definitions";
|
|
7
7
|
import { ContainerMessageType, ContainerRuntimeMessage } from "../containerRuntime";
|
|
8
8
|
import { OpDecompressor } from "./opDecompressor";
|
|
9
|
+
import { OpGroupingManager } from "./opGroupingManager";
|
|
9
10
|
import { OpSplitter } from "./opSplitter";
|
|
10
11
|
|
|
11
12
|
export class RemoteMessageProcessor {
|
|
12
13
|
constructor(
|
|
13
14
|
private readonly opSplitter: OpSplitter,
|
|
14
15
|
private readonly opDecompressor: OpDecompressor,
|
|
16
|
+
private readonly opGroupingManager: OpGroupingManager,
|
|
15
17
|
) {}
|
|
16
18
|
|
|
17
19
|
public get partialMessages(): ReadonlyMap<string, string[]> {
|
|
@@ -22,30 +24,44 @@ export class RemoteMessageProcessor {
|
|
|
22
24
|
this.opSplitter.clearPartialChunks(clientId);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
public process(remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage {
|
|
26
|
-
|
|
27
|
-
message = this.opDecompressor.processMessage(message).message;
|
|
28
|
-
unpackRuntimeMessage(message);
|
|
29
|
-
|
|
30
|
-
const chunkProcessingResult = this.opSplitter.processRemoteMessage(message);
|
|
31
|
-
message = chunkProcessingResult.message;
|
|
32
|
-
if (chunkProcessingResult.state !== "Processed") {
|
|
33
|
-
// If the message is not chunked or if the splitter is still rebuilding the original message,
|
|
34
|
-
// there is no need to continue processing
|
|
35
|
-
return message;
|
|
36
|
-
}
|
|
27
|
+
public process(remoteMessage: ISequencedDocumentMessage): ISequencedDocumentMessage[] {
|
|
28
|
+
const result: ISequencedDocumentMessage[] = [];
|
|
37
29
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// there is no need to continue processing
|
|
43
|
-
return message;
|
|
44
|
-
}
|
|
30
|
+
// Ungroup before processing chunks
|
|
31
|
+
for (let ungroupedMessage of this.opGroupingManager.ungroupOp(copy(remoteMessage))) {
|
|
32
|
+
ungroupedMessage = this.opDecompressor.processMessage(ungroupedMessage).message;
|
|
33
|
+
unpackRuntimeMessage(ungroupedMessage);
|
|
45
34
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
const chunkProcessingResult = this.opSplitter.processRemoteMessage(ungroupedMessage);
|
|
36
|
+
ungroupedMessage = chunkProcessingResult.message;
|
|
37
|
+
if (chunkProcessingResult.state !== "Processed") {
|
|
38
|
+
// If the message is not chunked or if the splitter is still rebuilding the original message,
|
|
39
|
+
// there is no need to continue processing
|
|
40
|
+
result.push(ungroupedMessage);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Ungroup the chunked message before decompressing
|
|
45
|
+
for (let ungroupedMessageAfterChunking of this.opGroupingManager.ungroupOp(
|
|
46
|
+
ungroupedMessage,
|
|
47
|
+
)) {
|
|
48
|
+
const decompressionAfterChunking = this.opDecompressor.processMessage(
|
|
49
|
+
ungroupedMessageAfterChunking,
|
|
50
|
+
);
|
|
51
|
+
ungroupedMessageAfterChunking = decompressionAfterChunking.message;
|
|
52
|
+
if (decompressionAfterChunking.state === "Skipped") {
|
|
53
|
+
// After chunking, if the original message was not compressed,
|
|
54
|
+
// there is no need to continue processing
|
|
55
|
+
result.push(ungroupedMessageAfterChunking);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The message needs to be unpacked after chunking + decompression
|
|
60
|
+
unpack(ungroupedMessageAfterChunking);
|
|
61
|
+
result.push(ungroupedMessageAfterChunking);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
49
65
|
}
|
|
50
66
|
}
|
|
51
67
|
|
package/src/packageVersion.ts
CHANGED
package/src/summary/index.ts
CHANGED
|
@@ -95,7 +95,7 @@ export {
|
|
|
95
95
|
WriteFluidDataStoreAttributes,
|
|
96
96
|
wrapSummaryInChannelsTree,
|
|
97
97
|
} from "./summaryFormat";
|
|
98
|
-
export { SummarizeReason } from "./summaryGenerator";
|
|
98
|
+
export { getFailMessage, SummarizeReason } from "./summaryGenerator";
|
|
99
99
|
export {
|
|
100
100
|
IConnectedEvents,
|
|
101
101
|
IConnectedState,
|
|
@@ -46,6 +46,8 @@ import {
|
|
|
46
46
|
const maxSummarizeAckWaitTime = 10 * 60 * 1000; // 10 minutes
|
|
47
47
|
|
|
48
48
|
const defaultNumberSummarizationAttempts = 2; // only up to 2 attempts
|
|
49
|
+
const numberOfAttemptsOnRestartAsRecovery = 1; // Only summarize once
|
|
50
|
+
|
|
49
51
|
/**
|
|
50
52
|
* An instance of RunningSummarizer manages the heuristics for summarizing.
|
|
51
53
|
* Until disposed, the instance of RunningSummarizer can assume that it is
|
|
@@ -61,7 +63,6 @@ export class RunningSummarizer implements IDisposable {
|
|
|
61
63
|
submitSummaryCallback: (options: ISubmitSummaryOptions) => Promise<SubmitSummaryResult>,
|
|
62
64
|
refreshLatestSummaryAckCallback: (options: IRefreshSummaryAckOptions) => Promise<void>,
|
|
63
65
|
heuristicData: ISummarizeHeuristicData,
|
|
64
|
-
raiseSummarizingError: (errorMessage: string) => void,
|
|
65
66
|
summaryCollection: SummaryCollection,
|
|
66
67
|
cancellationToken: ISummaryCancellationToken,
|
|
67
68
|
stopSummarizerCallback: (reason: SummarizerStopReason) => void,
|
|
@@ -74,7 +75,6 @@ export class RunningSummarizer implements IDisposable {
|
|
|
74
75
|
submitSummaryCallback,
|
|
75
76
|
refreshLatestSummaryAckCallback,
|
|
76
77
|
heuristicData,
|
|
77
|
-
raiseSummarizingError,
|
|
78
78
|
summaryCollection,
|
|
79
79
|
cancellationToken,
|
|
80
80
|
stopSummarizerCallback,
|
|
@@ -133,6 +133,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
133
133
|
private heuristicRunner?: ISummarizeHeuristicRunner;
|
|
134
134
|
private readonly generator: SummaryGenerator;
|
|
135
135
|
private readonly mc: MonitoringContext;
|
|
136
|
+
private readonly shouldAbortOnSummaryFailure: boolean;
|
|
136
137
|
|
|
137
138
|
private enqueuedSummary:
|
|
138
139
|
| {
|
|
@@ -146,6 +147,9 @@ export class RunningSummarizer implements IDisposable {
|
|
|
146
147
|
private totalSuccessfulAttempts = 0;
|
|
147
148
|
private initialized = false;
|
|
148
149
|
|
|
150
|
+
private readonly deltaManagerListener;
|
|
151
|
+
private readonly runtimeListener;
|
|
152
|
+
|
|
149
153
|
private constructor(
|
|
150
154
|
baseLogger: ITelemetryLogger,
|
|
151
155
|
private readonly summaryWatcher: IClientSummaryWatcher,
|
|
@@ -157,7 +161,6 @@ export class RunningSummarizer implements IDisposable {
|
|
|
157
161
|
options: IRefreshSummaryAckOptions,
|
|
158
162
|
) => Promise<void>,
|
|
159
163
|
private readonly heuristicData: ISummarizeHeuristicData,
|
|
160
|
-
private readonly raiseSummarizingError: (errorMessage: string) => void,
|
|
161
164
|
private readonly summaryCollection: SummaryCollection,
|
|
162
165
|
private readonly cancellationToken: ISummaryCancellationToken,
|
|
163
166
|
private readonly stopSummarizerCallback: (reason: SummarizerStopReason) => void,
|
|
@@ -174,6 +177,10 @@ export class RunningSummarizer implements IDisposable {
|
|
|
174
177
|
}),
|
|
175
178
|
);
|
|
176
179
|
|
|
180
|
+
this.shouldAbortOnSummaryFailure =
|
|
181
|
+
this.mc.config.getString("Fluid.ContainerRuntime.Test.SummarizationRecoveryMethod") ===
|
|
182
|
+
"restart";
|
|
183
|
+
|
|
177
184
|
if (configuration.state !== "disableHeuristics") {
|
|
178
185
|
assert(
|
|
179
186
|
this.configuration.state === "enabled",
|
|
@@ -198,13 +205,12 @@ export class RunningSummarizer implements IDisposable {
|
|
|
198
205
|
const maxAckWaitTime = Math.min(this.configuration.maxAckWaitTime, maxSummarizeAckWaitTime);
|
|
199
206
|
|
|
200
207
|
this.pendingAckTimer = new PromiseTimer(maxAckWaitTime, () => {
|
|
201
|
-
// pre-0.58 error message: summaryAckWaitTimeout
|
|
202
|
-
this.raiseSummarizingError("Pending summary ack not received in time");
|
|
203
208
|
// Note: summarizeCount (from ChildLogger definition) may be 0,
|
|
204
209
|
// since this code path is hit when RunningSummarizer first starts up,
|
|
205
210
|
// before this instance has kicked off a new summarize run.
|
|
206
211
|
this.mc.logger.sendErrorEvent({
|
|
207
212
|
eventName: "SummaryAckWaitTimeout",
|
|
213
|
+
message: "Pending summary ack not received in time",
|
|
208
214
|
maxAckWaitTime,
|
|
209
215
|
referenceSequenceNumber: this.heuristicData.lastAttempt.refSequenceNumber,
|
|
210
216
|
summarySequenceNumber: this.heuristicData.lastAttempt.summarySequenceNumber,
|
|
@@ -227,7 +233,6 @@ export class RunningSummarizer implements IDisposable {
|
|
|
227
233
|
this.pendingAckTimer,
|
|
228
234
|
this.heuristicData,
|
|
229
235
|
this.submitSummaryCallback,
|
|
230
|
-
this.raiseSummarizingError,
|
|
231
236
|
() => {
|
|
232
237
|
this.totalSuccessfulAttempts++;
|
|
233
238
|
},
|
|
@@ -235,10 +240,25 @@ export class RunningSummarizer implements IDisposable {
|
|
|
235
240
|
this.mc.logger,
|
|
236
241
|
);
|
|
237
242
|
|
|
238
|
-
// Listen for ops
|
|
239
|
-
this.
|
|
240
|
-
|
|
241
|
-
|
|
243
|
+
// Listen to deltaManager for non-runtime ops
|
|
244
|
+
this.deltaManagerListener = (op) => {
|
|
245
|
+
if (!isRuntimeMessage(op)) {
|
|
246
|
+
this.handleOp(op, false);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// Listen to runtime for runtime ops
|
|
251
|
+
this.runtimeListener = (op, runtimeMessage) => {
|
|
252
|
+
if (runtimeMessage) {
|
|
253
|
+
this.handleOp(op, true);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Purpose of listening to deltaManager is for back-compat
|
|
258
|
+
// Can remove and only listen to runtime once loader version is past 2.0.0-internal.1.2.0 (https://github.com/microsoft/FluidFramework/pull/11832)
|
|
259
|
+
// Tracked by AB#3883
|
|
260
|
+
this.runtime.deltaManager.on("op", this.deltaManagerListener);
|
|
261
|
+
this.runtime.on?.("op", this.runtimeListener);
|
|
242
262
|
}
|
|
243
263
|
|
|
244
264
|
private async handleSummaryAck(): Promise<number> {
|
|
@@ -248,67 +268,54 @@ export class RunningSummarizer implements IDisposable {
|
|
|
248
268
|
if (lastAck !== undefined) {
|
|
249
269
|
refSequenceNumber = lastAck.summaryOp.referenceSequenceNumber;
|
|
250
270
|
const summaryLogger = this.tryGetCorrelatedLogger(refSequenceNumber) ?? this.mc.logger;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
271
|
+
const summaryOpHandle = lastAck.summaryOp.contents.handle;
|
|
272
|
+
const summaryAckHandle = lastAck.summaryAck.contents.handle;
|
|
273
|
+
while (this.summarizingLock !== undefined) {
|
|
274
|
+
summaryLogger.sendTelemetryEvent({
|
|
275
|
+
eventName: "RefreshAttemptWithSummarizerRunning",
|
|
276
|
+
referenceSequenceNumber: refSequenceNumber,
|
|
277
|
+
proposalHandle: summaryOpHandle,
|
|
278
|
+
ackHandle: summaryAckHandle,
|
|
279
|
+
});
|
|
280
|
+
await this.summarizingLock;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Make sure we block any summarizer from being executed/enqueued while
|
|
284
|
+
// executing the refreshLatestSummaryAck.
|
|
285
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/779
|
|
286
|
+
await this.lockedSummaryAction(
|
|
287
|
+
() => {},
|
|
288
|
+
async () =>
|
|
289
|
+
this.refreshLatestSummaryAckCallback({
|
|
258
290
|
proposalHandle: summaryOpHandle,
|
|
259
291
|
ackHandle: summaryAckHandle,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
{
|
|
288
|
-
eventName: "HandleSummaryAckErrorIgnored",
|
|
289
|
-
referenceSequenceNumber: refSequenceNumber,
|
|
290
|
-
proposalHandle: summaryOpHandle,
|
|
291
|
-
ackHandle: summaryAckHandle,
|
|
292
|
-
},
|
|
293
|
-
error,
|
|
294
|
-
);
|
|
295
|
-
} else {
|
|
296
|
-
throw error;
|
|
297
|
-
}
|
|
298
|
-
}),
|
|
299
|
-
() => {},
|
|
300
|
-
);
|
|
301
|
-
} catch (error) {
|
|
302
|
-
summaryLogger.sendErrorEvent(
|
|
303
|
-
{
|
|
304
|
-
eventName: "HandleLastSummaryAckError",
|
|
305
|
-
referenceSequenceNumber: refSequenceNumber,
|
|
306
|
-
handle: lastAck?.summaryOp?.contents?.handle,
|
|
307
|
-
ackHandle: lastAck?.summaryAck?.contents?.handle,
|
|
308
|
-
},
|
|
309
|
-
error,
|
|
310
|
-
);
|
|
311
|
-
}
|
|
292
|
+
summaryRefSeq: refSequenceNumber,
|
|
293
|
+
summaryLogger,
|
|
294
|
+
}).catch(async (error) => {
|
|
295
|
+
// If the error is 404, so maybe the fetched version no longer exists on server. We just
|
|
296
|
+
// ignore this error in that case, as that means we will have another summaryAck for the
|
|
297
|
+
// latest version with which we will refresh the state. However in case of single commit
|
|
298
|
+
// summary, we might me missing a summary ack, so in that case we are still fine as the
|
|
299
|
+
// code in `submitSummary` function in container runtime, will refresh the latest state
|
|
300
|
+
// by calling `refreshLatestSummaryAckFromServer` and we will be fine.
|
|
301
|
+
const isIgnoredError =
|
|
302
|
+
isFluidError(error) &&
|
|
303
|
+
error.errorType === DriverErrorType.fileNotFoundOrAccessDeniedError;
|
|
304
|
+
|
|
305
|
+
summaryLogger.sendTelemetryEvent(
|
|
306
|
+
{
|
|
307
|
+
eventName: isIgnoredError
|
|
308
|
+
? "HandleSummaryAckErrorIgnored"
|
|
309
|
+
: "HandleLastSummaryAckError",
|
|
310
|
+
referenceSequenceNumber: refSequenceNumber,
|
|
311
|
+
proposalHandle: summaryOpHandle,
|
|
312
|
+
ackHandle: summaryAckHandle,
|
|
313
|
+
},
|
|
314
|
+
error,
|
|
315
|
+
);
|
|
316
|
+
}),
|
|
317
|
+
() => {},
|
|
318
|
+
);
|
|
312
319
|
refSequenceNumber++;
|
|
313
320
|
}
|
|
314
321
|
return refSequenceNumber;
|
|
@@ -345,9 +352,8 @@ export class RunningSummarizer implements IDisposable {
|
|
|
345
352
|
}
|
|
346
353
|
|
|
347
354
|
public dispose(): void {
|
|
348
|
-
this.runtime.deltaManager.off("op",
|
|
349
|
-
|
|
350
|
-
});
|
|
355
|
+
this.runtime.deltaManager.off("op", this.deltaManagerListener);
|
|
356
|
+
this.runtime.off?.("op", this.runtimeListener);
|
|
351
357
|
this.summaryWatcher.dispose();
|
|
352
358
|
this.heuristicRunner?.dispose();
|
|
353
359
|
this.heuristicRunner = undefined;
|
|
@@ -372,10 +378,10 @@ export class RunningSummarizer implements IDisposable {
|
|
|
372
378
|
/** We only want a single heuristic runner micro-task (will provide better optimized grouping of ops) */
|
|
373
379
|
private heuristicRunnerMicroTaskExists = false;
|
|
374
380
|
|
|
375
|
-
public handleOp(op: ISequencedDocumentMessage) {
|
|
381
|
+
public handleOp(op: ISequencedDocumentMessage, runtimeMessage: boolean) {
|
|
376
382
|
this.heuristicData.lastOpSequenceNumber = op.sequenceNumber;
|
|
377
383
|
|
|
378
|
-
if (
|
|
384
|
+
if (runtimeMessage) {
|
|
379
385
|
this.heuristicData.numRuntimeOps++;
|
|
380
386
|
} else {
|
|
381
387
|
this.heuristicData.numNonRuntimeOps++;
|
|
@@ -386,7 +392,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
386
392
|
// Check for enqueued on-demand summaries; Intentionally do nothing otherwise
|
|
387
393
|
if (
|
|
388
394
|
this.initialized &&
|
|
389
|
-
this.opCanTriggerSummary(op) &&
|
|
395
|
+
this.opCanTriggerSummary(op, runtimeMessage) &&
|
|
390
396
|
!this.tryRunEnqueuedSummary() &&
|
|
391
397
|
!this.heuristicRunnerMicroTaskExists
|
|
392
398
|
) {
|
|
@@ -407,14 +413,14 @@ export class RunningSummarizer implements IDisposable {
|
|
|
407
413
|
* @param op - op to check
|
|
408
414
|
* @returns true if this op can trigger a summary
|
|
409
415
|
*/
|
|
410
|
-
private opCanTriggerSummary(op: ISequencedDocumentMessage): boolean {
|
|
416
|
+
private opCanTriggerSummary(op: ISequencedDocumentMessage, runtimeMessage: boolean): boolean {
|
|
411
417
|
switch (op.type) {
|
|
412
418
|
case MessageType.Summarize:
|
|
413
419
|
case MessageType.SummaryAck:
|
|
414
420
|
case MessageType.SummaryNack:
|
|
415
421
|
return false;
|
|
416
422
|
default:
|
|
417
|
-
return
|
|
423
|
+
return runtimeMessage || this.nonRuntimeOpCanTriggerSummary();
|
|
418
424
|
}
|
|
419
425
|
}
|
|
420
426
|
|
|
@@ -595,9 +601,10 @@ export class RunningSummarizer implements IDisposable {
|
|
|
595
601
|
let summaryAttempts = 0;
|
|
596
602
|
let summaryAttemptsPerPhase = 0;
|
|
597
603
|
// Reducing the default number of attempts to defaultNumberofSummarizationAttempts.
|
|
598
|
-
let totalAttempts =
|
|
599
|
-
|
|
600
|
-
|
|
604
|
+
let totalAttempts = this.shouldAbortOnSummaryFailure
|
|
605
|
+
? numberOfAttemptsOnRestartAsRecovery
|
|
606
|
+
: this.mc.config.getNumber("Fluid.Summarizer.Attempts") ??
|
|
607
|
+
defaultNumberSummarizationAttempts;
|
|
601
608
|
|
|
602
609
|
if (totalAttempts > attempts.length) {
|
|
603
610
|
this.mc.logger.sendTelemetryEvent({
|
|
@@ -646,6 +653,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
646
653
|
if (result.success) {
|
|
647
654
|
return;
|
|
648
655
|
}
|
|
656
|
+
|
|
649
657
|
// Check for retryDelay that can come from summaryNack or upload summary flow.
|
|
650
658
|
// Retry the same step only once per retryAfter response.
|
|
651
659
|
overrideDelaySeconds = result.retryAfterSeconds;
|
|
@@ -668,6 +676,20 @@ export class RunningSummarizer implements IDisposable {
|
|
|
668
676
|
}
|
|
669
677
|
}
|
|
670
678
|
|
|
679
|
+
if (this.shouldAbortOnSummaryFailure) {
|
|
680
|
+
this.mc.logger.sendTelemetryEvent(
|
|
681
|
+
{
|
|
682
|
+
eventName: "ClosingSummarizerOnSummaryStale",
|
|
683
|
+
reason,
|
|
684
|
+
message: lastResult?.message,
|
|
685
|
+
},
|
|
686
|
+
lastResult?.error,
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
this.stopSummarizerCallback("latestSummaryStateStale");
|
|
690
|
+
this.runtime.closeFn();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
671
693
|
// If all attempts failed, log error (with last attempt info) and close the summarizer container
|
|
672
694
|
this.mc.logger.sendErrorEvent(
|
|
673
695
|
{
|
|
@@ -263,14 +263,6 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
263
263
|
refSequenceNumber: this.runtime.deltaManager.initialSequenceNumber,
|
|
264
264
|
summaryTime: Date.now(),
|
|
265
265
|
} as const),
|
|
266
|
-
(errorMessage: string) => {
|
|
267
|
-
if (!this._disposed) {
|
|
268
|
-
this.logger.sendErrorEvent(
|
|
269
|
-
{ eventName: "summarizingError" },
|
|
270
|
-
createSummarizingWarning(errorMessage, true),
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
266
|
this.summaryCollection,
|
|
275
267
|
runCoordinator /* cancellationToken */,
|
|
276
268
|
(reason) => runCoordinator.stop(reason) /* stopSummarizerCallback */,
|
|
@@ -26,6 +26,10 @@ export class SummarizeHeuristicData implements ISummarizeHeuristicData {
|
|
|
26
26
|
return this._lastSuccessfulSummary;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
public get opsSinceLastSummary(): number {
|
|
30
|
+
return this.numSystemOpsBefore + this.numNonSystemOpsBefore;
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
public numNonRuntimeOps: number = 0;
|
|
30
34
|
public totalOpsSize: number = 0;
|
|
31
35
|
public hasMissingOpData: boolean = false;
|
|
@@ -164,7 +164,7 @@ export class SummarizerNode implements IRootSummarizerNode {
|
|
|
164
164
|
// complains if this assert isn't done this way
|
|
165
165
|
assert(
|
|
166
166
|
this.wipReferenceSequenceNumber !== undefined,
|
|
167
|
-
|
|
167
|
+
0x5df /* Summarize should not be called when not tracking the summary */,
|
|
168
168
|
);
|
|
169
169
|
const incrementalSummaryContext: IExperimentalIncrementalSummaryContext | undefined =
|
|
170
170
|
this._latestSummary !== undefined
|
|
@@ -69,7 +69,7 @@ class SummaryNodeWithGC extends SummaryNode {
|
|
|
69
69
|
* - Adds trackState param to summarize. If trackState is false, it bypasses the SummarizerNode and calls
|
|
70
70
|
* directly into summarizeInternal method.
|
|
71
71
|
*/
|
|
72
|
-
class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNodeWithGC {
|
|
72
|
+
export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNodeWithGC {
|
|
73
73
|
// Tracks the work-in-progress used routes during summary.
|
|
74
74
|
private wipSerializedUsedRoutes: string | undefined;
|
|
75
75
|
|
|
@@ -536,8 +536,8 @@ class SummarizerNodeWithGC extends SummarizerNode implements IRootSummarizerNode
|
|
|
536
536
|
JSON.stringify(newSerializedRoutes),
|
|
537
537
|
{
|
|
538
538
|
referenceSequenceNumber: value.referenceSequenceNumber,
|
|
539
|
-
basePath:
|
|
540
|
-
localPath:
|
|
539
|
+
basePath: child.latestSummary.basePath,
|
|
540
|
+
localPath: child.latestSummary.localPath,
|
|
541
541
|
},
|
|
542
542
|
);
|
|
543
543
|
child.addPendingSummary(key, newLatestSummaryNode);
|
|
@@ -78,6 +78,14 @@ export interface ISummarizerRuntime extends IConnectableRuntime {
|
|
|
78
78
|
readonly deltaManager: IDeltaManager<ISequencedDocumentMessage, IDocumentMessage>;
|
|
79
79
|
disposeFn?(): void;
|
|
80
80
|
closeFn(): void;
|
|
81
|
+
on?(
|
|
82
|
+
event: "op",
|
|
83
|
+
listener: (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => void,
|
|
84
|
+
): this;
|
|
85
|
+
off?(
|
|
86
|
+
event: "op",
|
|
87
|
+
listener: (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => void,
|
|
88
|
+
): this;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
/** Options affecting summarize behavior. */
|
|
@@ -294,8 +302,13 @@ export type SummarizerStopReason =
|
|
|
294
302
|
| "notElectedClient"
|
|
295
303
|
/** Summarizer client was disconnected */
|
|
296
304
|
| "summarizerClientDisconnected"
|
|
297
|
-
|
|
298
|
-
| "summarizerException"
|
|
305
|
+
/** running summarizer threw an exception */
|
|
306
|
+
| "summarizerException"
|
|
307
|
+
/**
|
|
308
|
+
* The previous summary state on the summarizer is not the most recently acked summary. this also happens when the
|
|
309
|
+
* first submitSummary attempt fails for any reason and there's a 2nd summary attempt without an ack
|
|
310
|
+
*/
|
|
311
|
+
| "latestSummaryStateStale";
|
|
299
312
|
|
|
300
313
|
export interface ISummarizerEvents extends IEvent {
|
|
301
314
|
/**
|
|
@@ -398,6 +411,8 @@ export interface ISummarizeHeuristicData {
|
|
|
398
411
|
|
|
399
412
|
/** Mark that the last sent summary attempt has received an ack */
|
|
400
413
|
markLastAttemptAsSuccessful(): void;
|
|
414
|
+
|
|
415
|
+
opsSinceLastSummary: number;
|
|
401
416
|
}
|
|
402
417
|
|
|
403
418
|
/** Responsible for running heuristics determining when to summarize. */
|
|
@@ -491,7 +506,9 @@ type SummaryGeneratorOptionalTelemetryProperties =
|
|
|
491
506
|
/** Actual sequence number of the summary op proposal. */
|
|
492
507
|
| "summarySequenceNumber"
|
|
493
508
|
/** Optional Retry-After time in seconds. If specified, the client should wait this many seconds before retrying. */
|
|
494
|
-
| "nackRetryAfter"
|
|
509
|
+
| "nackRetryAfter"
|
|
510
|
+
/** The stage at which the submit summary method failed at. This can help determine what type of failure we have */
|
|
511
|
+
| "stage";
|
|
495
512
|
|
|
496
513
|
export type SummaryGeneratorTelemetry = Pick<
|
|
497
514
|
ITelemetryProperties,
|