@fluidframework/container-runtime 1.2.3-83900 → 2.0.0-internal.1.0.0
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.js +1 -1
- package/dist/batchTracker.js.map +1 -1
- package/dist/blobManager.d.ts +81 -25
- package/dist/blobManager.d.ts.map +1 -1
- package/dist/blobManager.js +301 -100
- package/dist/blobManager.js.map +1 -1
- package/dist/containerRuntime.d.ts +66 -49
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +129 -164
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStore.js +29 -24
- package/dist/dataStore.js.map +1 -1
- package/dist/dataStoreContext.d.ts +3 -4
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +16 -23
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/dataStores.d.ts +6 -3
- package/dist/dataStores.d.ts.map +1 -1
- package/dist/dataStores.js +13 -5
- package/dist/dataStores.js.map +1 -1
- package/dist/garbageCollection.d.ts.map +1 -1
- package/dist/garbageCollection.js +17 -12
- package/dist/garbageCollection.js.map +1 -1
- package/dist/opProperties.d.ts +7 -0
- package/dist/opProperties.d.ts.map +1 -0
- package/dist/opProperties.js +20 -0
- package/dist/opProperties.js.map +1 -0
- 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/runningSummarizer.d.ts +28 -4
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +93 -26
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts +0 -2
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +34 -15
- package/dist/summarizer.js.map +1 -1
- package/dist/summarizerHeuristics.d.ts +26 -4
- package/dist/summarizerHeuristics.d.ts.map +1 -1
- package/dist/summarizerHeuristics.js +95 -18
- package/dist/summarizerHeuristics.js.map +1 -1
- package/dist/summarizerTypes.d.ts +30 -10
- package/dist/summarizerTypes.d.ts.map +1 -1
- package/dist/summarizerTypes.js.map +1 -1
- package/dist/summaryCollection.js +1 -1
- package/dist/summaryCollection.js.map +1 -1
- package/dist/summaryFormat.d.ts +0 -5
- package/dist/summaryFormat.d.ts.map +1 -1
- package/dist/summaryFormat.js.map +1 -1
- package/dist/summaryGenerator.d.ts +1 -0
- package/dist/summaryGenerator.d.ts.map +1 -1
- package/dist/summaryGenerator.js +11 -9
- package/dist/summaryGenerator.js.map +1 -1
- package/lib/batchTracker.js +1 -1
- package/lib/batchTracker.js.map +1 -1
- package/lib/blobManager.d.ts +81 -25
- package/lib/blobManager.d.ts.map +1 -1
- package/lib/blobManager.js +302 -101
- package/lib/blobManager.js.map +1 -1
- package/lib/containerRuntime.d.ts +66 -49
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +131 -166
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStore.js +29 -24
- package/lib/dataStore.js.map +1 -1
- package/lib/dataStoreContext.d.ts +3 -4
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +17 -24
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/dataStores.d.ts +6 -3
- package/lib/dataStores.d.ts.map +1 -1
- package/lib/dataStores.js +13 -5
- package/lib/dataStores.js.map +1 -1
- package/lib/garbageCollection.d.ts.map +1 -1
- package/lib/garbageCollection.js +17 -12
- package/lib/garbageCollection.js.map +1 -1
- package/lib/opProperties.d.ts +7 -0
- package/lib/opProperties.d.ts.map +1 -0
- package/lib/opProperties.js +16 -0
- package/lib/opProperties.js.map +1 -0
- 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/runningSummarizer.d.ts +28 -4
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +93 -26
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts +0 -2
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +36 -17
- package/lib/summarizer.js.map +1 -1
- package/lib/summarizerHeuristics.d.ts +26 -4
- package/lib/summarizerHeuristics.d.ts.map +1 -1
- package/lib/summarizerHeuristics.js +95 -18
- package/lib/summarizerHeuristics.js.map +1 -1
- package/lib/summarizerTypes.d.ts +30 -10
- package/lib/summarizerTypes.d.ts.map +1 -1
- package/lib/summarizerTypes.js.map +1 -1
- package/lib/summaryCollection.js +1 -1
- package/lib/summaryCollection.js.map +1 -1
- package/lib/summaryFormat.d.ts +0 -5
- package/lib/summaryFormat.d.ts.map +1 -1
- package/lib/summaryFormat.js.map +1 -1
- package/lib/summaryGenerator.d.ts +1 -0
- package/lib/summaryGenerator.d.ts.map +1 -1
- package/lib/summaryGenerator.js +11 -9
- package/lib/summaryGenerator.js.map +1 -1
- package/package.json +55 -21
- package/src/batchTracker.ts +1 -1
- package/src/blobManager.ts +364 -119
- package/src/containerRuntime.ts +232 -216
- package/src/dataStore.ts +49 -37
- package/src/dataStoreContext.ts +16 -23
- package/src/dataStores.ts +27 -16
- package/src/garbageCollection.ts +13 -7
- package/src/opProperties.ts +19 -0
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +108 -23
- package/src/summarizer.ts +47 -28
- package/src/summarizerHeuristics.ts +133 -19
- package/src/summarizerTypes.ts +37 -10
- package/src/summaryCollection.ts +1 -1
- package/src/summaryFormat.ts +0 -6
- package/src/summaryGenerator.ts +40 -22
- package/dist/opTelemetry.d.ts +0 -22
- package/dist/opTelemetry.d.ts.map +0 -1
- package/dist/opTelemetry.js +0 -59
- package/dist/opTelemetry.js.map +0 -1
- package/lib/opTelemetry.d.ts +0 -22
- package/lib/opTelemetry.d.ts.map +0 -1
- package/lib/opTelemetry.js +0 -55
- package/lib/opTelemetry.js.map +0 -1
- package/src/opTelemetry.ts +0 -71
package/src/runningSummarizer.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { IDisposable, ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { assert, delay, Deferred, PromiseTimer } from "@fluidframework/common-utils";
|
|
8
8
|
import { UsageError } from "@fluidframework/container-utils";
|
|
9
|
+
import { isRuntimeMessage } from "@fluidframework/driver-utils";
|
|
9
10
|
import {
|
|
10
11
|
ISequencedDocumentMessage,
|
|
11
12
|
MessageType,
|
|
@@ -14,6 +15,7 @@ import { ChildLogger } from "@fluidframework/telemetry-utils";
|
|
|
14
15
|
import {
|
|
15
16
|
ISummaryConfiguration,
|
|
16
17
|
} from "./containerRuntime";
|
|
18
|
+
import { opSize } from "./opProperties";
|
|
17
19
|
import { SummarizeHeuristicRunner } from "./summarizerHeuristics";
|
|
18
20
|
import {
|
|
19
21
|
IEnqueueSummarizeOptions,
|
|
@@ -28,6 +30,7 @@ import {
|
|
|
28
30
|
ISummaryCancellationToken,
|
|
29
31
|
ISummarizeResults,
|
|
30
32
|
ISummarizeTelemetryProperties,
|
|
33
|
+
ISummarizerRuntime,
|
|
31
34
|
ISummarizeRunnerTelemetry,
|
|
32
35
|
} from "./summarizerTypes";
|
|
33
36
|
import { IClientSummaryWatcher, SummaryCollection } from "./summaryCollection";
|
|
@@ -58,6 +61,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
58
61
|
summaryCollection: SummaryCollection,
|
|
59
62
|
cancellationToken: ISummaryCancellationToken,
|
|
60
63
|
stopSummarizerCallback: (reason: SummarizerStopReason) => void,
|
|
64
|
+
runtime: ISummarizerRuntime,
|
|
61
65
|
): Promise<RunningSummarizer> {
|
|
62
66
|
const summarizer = new RunningSummarizer(
|
|
63
67
|
logger,
|
|
@@ -68,20 +72,44 @@ export class RunningSummarizer implements IDisposable {
|
|
|
68
72
|
raiseSummarizingError,
|
|
69
73
|
summaryCollection,
|
|
70
74
|
cancellationToken,
|
|
71
|
-
stopSummarizerCallback
|
|
75
|
+
stopSummarizerCallback,
|
|
76
|
+
runtime);
|
|
72
77
|
|
|
73
78
|
await summarizer.waitStart();
|
|
74
79
|
|
|
75
|
-
//
|
|
80
|
+
// Update heuristic counts
|
|
81
|
+
// By the time we get here, there are potentially ops missing from the heuristic summary counts
|
|
82
|
+
// Examples of where this could happen:
|
|
83
|
+
// 1. Op is processed during the time that we are initiating the RunningSummarizer instance but before we
|
|
84
|
+
// listen for the op events (will get missed by the handlers in the current workflow)
|
|
85
|
+
// 2. Op was sequenced after the last time we summarized (op sequence number > summarize ref sequence number)
|
|
86
|
+
const diff = runtime.deltaManager.lastSequenceNumber - (
|
|
87
|
+
heuristicData.lastSuccessfulSummary.refSequenceNumber
|
|
88
|
+
+ heuristicData.numNonRuntimeOps
|
|
89
|
+
+ heuristicData.numRuntimeOps);
|
|
90
|
+
heuristicData.hasMissingOpData = diff > 0;
|
|
91
|
+
|
|
92
|
+
if (heuristicData.hasMissingOpData) {
|
|
93
|
+
// Split the diff 50-50 and increment the counts appropriately
|
|
94
|
+
heuristicData.numNonRuntimeOps += Math.ceil(diff / 2);
|
|
95
|
+
heuristicData.numRuntimeOps += Math.floor(diff / 2);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Update last seq number (in case the handlers haven't processed anything yet)
|
|
99
|
+
heuristicData.lastOpSequenceNumber = runtime.deltaManager.lastSequenceNumber;
|
|
100
|
+
|
|
101
|
+
// Start heuristics
|
|
102
|
+
summarizer.heuristicRunner?.start();
|
|
76
103
|
summarizer.heuristicRunner?.run();
|
|
104
|
+
|
|
77
105
|
return summarizer;
|
|
78
106
|
}
|
|
79
107
|
|
|
80
108
|
public get disposed() { return this._disposed; }
|
|
81
|
-
|
|
82
109
|
private stopping = false;
|
|
83
110
|
private _disposed = false;
|
|
84
111
|
private summarizingLock: Promise<void> | undefined;
|
|
112
|
+
private refreshSummaryAckLock: Promise<void> | undefined;
|
|
85
113
|
private tryWhileSummarizing = false;
|
|
86
114
|
private readonly pendingAckTimer: PromiseTimer;
|
|
87
115
|
private heuristicRunner?: ISummarizeHeuristicRunner;
|
|
@@ -95,6 +123,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
95
123
|
} | undefined;
|
|
96
124
|
private summarizeCount = 0;
|
|
97
125
|
private totalSuccessfulAttempts = 0;
|
|
126
|
+
private initialized = false;
|
|
98
127
|
|
|
99
128
|
private constructor(
|
|
100
129
|
baseLogger: ITelemetryLogger,
|
|
@@ -106,6 +135,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
106
135
|
private readonly summaryCollection: SummaryCollection,
|
|
107
136
|
private readonly cancellationToken: ISummaryCancellationToken,
|
|
108
137
|
private readonly stopSummarizerCallback: (reason: SummarizerStopReason) => void,
|
|
138
|
+
private readonly runtime: ISummarizerRuntime,
|
|
109
139
|
) {
|
|
110
140
|
const telemetryProps: ISummarizeRunnerTelemetry = {
|
|
111
141
|
summarizeCount: () => this.summarizeCount,
|
|
@@ -175,9 +205,13 @@ export class RunningSummarizer implements IDisposable {
|
|
|
175
205
|
this.summaryWatcher,
|
|
176
206
|
this.logger,
|
|
177
207
|
);
|
|
208
|
+
|
|
209
|
+
// Listen for ops
|
|
210
|
+
this.runtime.deltaManager.on("op", (op) => { this.handleOp(op); });
|
|
178
211
|
}
|
|
179
212
|
|
|
180
213
|
public dispose(): void {
|
|
214
|
+
this.runtime.deltaManager.off("op", (op) => { this.handleOp(op); });
|
|
181
215
|
this.summaryWatcher.dispose();
|
|
182
216
|
this.heuristicRunner?.dispose();
|
|
183
217
|
this.heuristicRunner = undefined;
|
|
@@ -199,30 +233,48 @@ export class RunningSummarizer implements IDisposable {
|
|
|
199
233
|
? this.logger
|
|
200
234
|
: undefined;
|
|
201
235
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
case MessageType.ClientLeave:
|
|
205
|
-
case MessageType.ClientJoin:
|
|
206
|
-
case MessageType.Propose: {
|
|
207
|
-
// Synchronously handle quorum ops like regular ops
|
|
208
|
-
this.handleOp(undefined, op);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
default: {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
236
|
+
/** We only want a single heuristic runner micro-task (will provide better optimized grouping of ops) */
|
|
237
|
+
private heuristicRunnerMicroTaskExists = false;
|
|
216
238
|
|
|
217
|
-
public handleOp(
|
|
218
|
-
|
|
219
|
-
|
|
239
|
+
public handleOp(op: ISequencedDocumentMessage) {
|
|
240
|
+
this.heuristicData.lastOpSequenceNumber = op.sequenceNumber;
|
|
241
|
+
|
|
242
|
+
if (op.type !== MessageType.Summarize && isRuntimeMessage(op)) {
|
|
243
|
+
this.heuristicData.numRuntimeOps++;
|
|
244
|
+
} else {
|
|
245
|
+
this.heuristicData.numNonRuntimeOps++;
|
|
220
246
|
}
|
|
221
|
-
|
|
247
|
+
|
|
248
|
+
this.heuristicData.totalOpsSize += opSize(op);
|
|
222
249
|
|
|
223
250
|
// Check for enqueued on-demand summaries; Intentionally do nothing otherwise
|
|
224
|
-
if (
|
|
225
|
-
this.
|
|
251
|
+
if (this.initialized
|
|
252
|
+
&& this.opCanTriggerSummary(op)
|
|
253
|
+
&& !this.tryRunEnqueuedSummary()
|
|
254
|
+
&& !this.heuristicRunnerMicroTaskExists) {
|
|
255
|
+
this.heuristicRunnerMicroTaskExists = true;
|
|
256
|
+
Promise.resolve().then(() => {
|
|
257
|
+
this.heuristicRunner?.run();
|
|
258
|
+
}).finally(() => {
|
|
259
|
+
this.heuristicRunnerMicroTaskExists = false;
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Can the given op trigger a summary?
|
|
266
|
+
* # Currently only prevents summaries for Summarize and SummaryAck ops
|
|
267
|
+
* @param op - op to check
|
|
268
|
+
* @returns true if this type of op can trigger a summary
|
|
269
|
+
*/
|
|
270
|
+
private opCanTriggerSummary(op: ISequencedDocumentMessage): boolean {
|
|
271
|
+
switch (op.type) {
|
|
272
|
+
case MessageType.Summarize:
|
|
273
|
+
case MessageType.SummaryAck:
|
|
274
|
+
case MessageType.SummaryNack:
|
|
275
|
+
return false;
|
|
276
|
+
default:
|
|
277
|
+
return true;
|
|
226
278
|
}
|
|
227
279
|
}
|
|
228
280
|
|
|
@@ -274,6 +326,32 @@ export class RunningSummarizer implements IDisposable {
|
|
|
274
326
|
summarySequenceNumber: waitStartResult.value.summaryOp.sequenceNumber,
|
|
275
327
|
});
|
|
276
328
|
}
|
|
329
|
+
this.initialized = true;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Blocks a new summarizer from running in case RefreshSummaryAck is being processed.
|
|
334
|
+
* Assumes that caller checked upfront for lack of concurrent action (this.refreshSummaryAckLock)
|
|
335
|
+
* before calling this API. I.e. caller is responsible for either erroring out or waiting on this promise.
|
|
336
|
+
* Note: The refreshSummaryAckLock makes sure no summarizer gets enqueued or processed
|
|
337
|
+
* until the refresh has completed. One can't rely uniquely on the summarizingLock as the
|
|
338
|
+
* refreshLatestSummaryAck also happens during the time summarizingLock !== undefined.
|
|
339
|
+
* Ex. Summarizer submits a summay + op and then waits for the Summary Ack to proceed
|
|
340
|
+
* with the refreshLatestSummaryAck and complete the summary.
|
|
341
|
+
* @param action - action to perform.
|
|
342
|
+
* @returns - result of action.
|
|
343
|
+
*/
|
|
344
|
+
public async lockedRefreshSummaryAckAction<T>(action: () => Promise<T>) {
|
|
345
|
+
assert(this.refreshSummaryAckLock === undefined,
|
|
346
|
+
0x396 /* Refresh Summary Ack - Caller is responsible for checking lock */);
|
|
347
|
+
|
|
348
|
+
const refreshSummaryAckLock = new Deferred<void>();
|
|
349
|
+
this.refreshSummaryAckLock = refreshSummaryAckLock.promise;
|
|
350
|
+
|
|
351
|
+
return action().finally(() => {
|
|
352
|
+
refreshSummaryAckLock.resolve();
|
|
353
|
+
this.refreshSummaryAckLock = undefined;
|
|
354
|
+
});
|
|
277
355
|
}
|
|
278
356
|
|
|
279
357
|
/**
|
|
@@ -290,6 +368,8 @@ export class RunningSummarizer implements IDisposable {
|
|
|
290
368
|
this.summarizingLock = summarizingLock.promise;
|
|
291
369
|
|
|
292
370
|
this.summarizeCount++;
|
|
371
|
+
// Make sure the RefreshLatestSummaryAck is not being executed.
|
|
372
|
+
await this.refreshSummaryAckLock;
|
|
293
373
|
|
|
294
374
|
return action().finally(() => {
|
|
295
375
|
summarizingLock.resolve();
|
|
@@ -393,6 +473,10 @@ export class RunningSummarizer implements IDisposable {
|
|
|
393
473
|
});
|
|
394
474
|
await delay(delaySeconds * 1000);
|
|
395
475
|
}
|
|
476
|
+
|
|
477
|
+
// Make sure the refresh Summary Ack is not being executed.
|
|
478
|
+
await this.refreshSummaryAckLock;
|
|
479
|
+
|
|
396
480
|
// Note: no need to account for cancellationToken.waitCancelled here, as
|
|
397
481
|
// this is accounted SummaryGenerator.summarizeCore that controls receivedSummaryAckOrNack.
|
|
398
482
|
const resultSummarize = this.generator.summarize(summarizeProps, options, cancellationToken);
|
|
@@ -441,6 +525,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
441
525
|
// The heuristics are blocking concurrent summarize attempts.
|
|
442
526
|
throw new UsageError("Attempted to run an already-running summarizer on demand");
|
|
443
527
|
}
|
|
528
|
+
|
|
444
529
|
const result = this.trySummarizeOnce(
|
|
445
530
|
{ reason: `onDemand/${reason}` },
|
|
446
531
|
options,
|
package/src/summarizer.ts
CHANGED
|
@@ -8,22 +8,25 @@ import { Deferred } from "@fluidframework/common-utils";
|
|
|
8
8
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
9
9
|
import { ILoader, LoaderHeader } from "@fluidframework/container-definitions";
|
|
10
10
|
import { UsageError } from "@fluidframework/container-utils";
|
|
11
|
-
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
11
|
+
import { DriverErrorType, DriverHeader } from "@fluidframework/driver-definitions";
|
|
12
12
|
import { requestFluidObject } from "@fluidframework/runtime-utils";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ChildLogger,
|
|
15
|
+
IFluidErrorBase,
|
|
16
|
+
isFluidError,
|
|
17
|
+
LoggingError,
|
|
18
|
+
wrapErrorAndLog,
|
|
19
|
+
} from "@fluidframework/telemetry-utils";
|
|
14
20
|
import {
|
|
15
21
|
FluidObject,
|
|
16
22
|
IFluidHandleContext,
|
|
17
23
|
IFluidHandle,
|
|
18
24
|
IRequest,
|
|
19
25
|
} from "@fluidframework/core-interfaces";
|
|
20
|
-
import {
|
|
21
|
-
ISequencedDocumentMessage,
|
|
22
|
-
} from "@fluidframework/protocol-definitions";
|
|
23
26
|
import { ISummaryConfiguration } from "./containerRuntime";
|
|
24
27
|
import { ICancellableSummarizerController } from "./runWhileConnectedCoordinator";
|
|
25
28
|
import { summarizerClientType } from "./summarizerClientElection";
|
|
26
|
-
import { SummaryCollection } from "./summaryCollection";
|
|
29
|
+
import { IAckedSummary, SummaryCollection } from "./summaryCollection";
|
|
27
30
|
import { SummarizerHandle } from "./summarizerHandle";
|
|
28
31
|
import { RunningSummarizer } from "./runningSummarizer";
|
|
29
32
|
import {
|
|
@@ -70,8 +73,6 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
70
73
|
|
|
71
74
|
private readonly logger: ITelemetryLogger;
|
|
72
75
|
private runningSummarizer?: RunningSummarizer;
|
|
73
|
-
private systemOpListener?: (op: ISequencedDocumentMessage) => void;
|
|
74
|
-
private opListener?: (error: any, op: ISequencedDocumentMessage) => void;
|
|
75
76
|
private _disposed: boolean = false;
|
|
76
77
|
private starting: boolean = false;
|
|
77
78
|
|
|
@@ -277,6 +278,7 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
277
278
|
this.summaryCollection,
|
|
278
279
|
runCoordinator /* cancellationToken */,
|
|
279
280
|
(reason) => runCoordinator.stop(reason), /* stopSummarizerCallback */
|
|
281
|
+
this.runtime,
|
|
280
282
|
);
|
|
281
283
|
this.runningSummarizer = runningSummarizer;
|
|
282
284
|
this.starting = false;
|
|
@@ -287,13 +289,6 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
287
289
|
this.logger.sendErrorEvent({ eventName: "HandleSummaryAckFatalError" }, error);
|
|
288
290
|
});
|
|
289
291
|
|
|
290
|
-
// Listen for ops
|
|
291
|
-
this.systemOpListener = (op: ISequencedDocumentMessage) => runningSummarizer.handleSystemOp(op);
|
|
292
|
-
this.runtime.deltaManager.inbound.on("op", this.systemOpListener);
|
|
293
|
-
|
|
294
|
-
this.opListener = (error: any, op: ISequencedDocumentMessage) => runningSummarizer.handleOp(error, op);
|
|
295
|
-
this.runtime.on("batchEnd", this.opListener);
|
|
296
|
-
|
|
297
292
|
return runningSummarizer;
|
|
298
293
|
}
|
|
299
294
|
|
|
@@ -312,12 +307,6 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
312
307
|
this.runningSummarizer.dispose();
|
|
313
308
|
this.runningSummarizer = undefined;
|
|
314
309
|
}
|
|
315
|
-
if (this.systemOpListener) {
|
|
316
|
-
this.runtime.deltaManager.inbound.off("op", this.systemOpListener);
|
|
317
|
-
}
|
|
318
|
-
if (this.opListener) {
|
|
319
|
-
this.runtime.removeListener("batchEnd", this.opListener);
|
|
320
|
-
}
|
|
321
310
|
}
|
|
322
311
|
|
|
323
312
|
public readonly summarizeOnDemand: ISummarizer["summarizeOnDemand"] = (...args) => {
|
|
@@ -378,22 +367,52 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
378
367
|
|
|
379
368
|
private async handleSummaryAcks() {
|
|
380
369
|
let refSequenceNumber = this.runtime.deltaManager.initialSequenceNumber;
|
|
370
|
+
let ack: IAckedSummary | undefined;
|
|
381
371
|
while (this.runningSummarizer) {
|
|
382
372
|
const summaryLogger = this.runningSummarizer.tryGetCorrelatedLogger(refSequenceNumber) ?? this.logger;
|
|
383
373
|
try {
|
|
384
|
-
|
|
374
|
+
// Initialize ack with undefined if exception happens inside of waitSummaryAck on second iteration,
|
|
375
|
+
// we record undefined, not previous handles.
|
|
376
|
+
ack = undefined;
|
|
377
|
+
ack = await this.summaryCollection.waitSummaryAck(refSequenceNumber);
|
|
385
378
|
refSequenceNumber = ack.summaryOp.referenceSequenceNumber;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
379
|
+
const summaryOpHandle = ack.summaryOp.contents.handle;
|
|
380
|
+
const summaryAckHandle = ack.summaryAck.contents.handle;
|
|
381
|
+
// Make sure we block any summarizer from being executed/enqueued while
|
|
382
|
+
// executing the refreshLatestSummaryAck.
|
|
383
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/779
|
|
384
|
+
await this.runningSummarizer.lockedRefreshSummaryAckAction(async () =>
|
|
385
|
+
this.internalsProvider.refreshLatestSummaryAck(
|
|
386
|
+
summaryOpHandle,
|
|
387
|
+
summaryAckHandle,
|
|
388
|
+
refSequenceNumber,
|
|
389
|
+
summaryLogger,
|
|
390
|
+
).catch(async (error) => {
|
|
391
|
+
// If the error is 404, so maybe the fetched version no longer exists on server. We just
|
|
392
|
+
// ignore this error in that case, as that means we will have another summaryAck for the
|
|
393
|
+
// latest version with which we will refresh the state. However in case of single commit
|
|
394
|
+
// summary, we might me missing a summary ack, so in that case we are still fine as the
|
|
395
|
+
// code in `submitSummary` function in container runtime, will refresh the latest state
|
|
396
|
+
// by calling `refreshLatestSummaryAckFromServer` and we will be fine.
|
|
397
|
+
if (isFluidError(error)
|
|
398
|
+
&& error.errorType === DriverErrorType.fileNotFoundOrAccessDeniedError) {
|
|
399
|
+
summaryLogger.sendTelemetryEvent({
|
|
400
|
+
eventName: "HandleSummaryAckErrorIgnored",
|
|
401
|
+
referenceSequenceNumber: refSequenceNumber,
|
|
402
|
+
proposalHandle: summaryOpHandle,
|
|
403
|
+
ackHandle: summaryAckHandle,
|
|
404
|
+
}, error);
|
|
405
|
+
} else {
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
}),
|
|
392
409
|
);
|
|
393
410
|
} catch (error) {
|
|
394
411
|
summaryLogger.sendErrorEvent({
|
|
395
412
|
eventName: "HandleSummaryAckError",
|
|
396
413
|
referenceSequenceNumber: refSequenceNumber,
|
|
414
|
+
handle: ack?.summaryOp?.contents?.handle,
|
|
415
|
+
ackHandle: ack?.summaryAck?.contents?.handle,
|
|
397
416
|
}, error);
|
|
398
417
|
}
|
|
399
418
|
refSequenceNumber++;
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { Timer } from "@fluidframework/common-utils";
|
|
8
8
|
import { ISummaryConfigurationHeuristics } from "./containerRuntime";
|
|
9
|
-
|
|
10
9
|
import {
|
|
11
10
|
ISummarizeHeuristicData,
|
|
12
11
|
ISummarizeHeuristicRunner,
|
|
13
12
|
ISummarizeAttempt,
|
|
13
|
+
ISummaryHeuristicStrategy,
|
|
14
14
|
} from "./summarizerTypes";
|
|
15
15
|
import { SummarizeReason } from "./summaryGenerator";
|
|
16
16
|
|
|
@@ -26,6 +26,30 @@ export class SummarizeHeuristicData implements ISummarizeHeuristicData {
|
|
|
26
26
|
return this._lastSuccessfulSummary;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
public numNonRuntimeOps: number = 0;
|
|
30
|
+
public totalOpsSize: number = 0;
|
|
31
|
+
public hasMissingOpData: boolean = false;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Cumulative size in bytes of all the ops at the beginning of the summarization attempt.
|
|
35
|
+
* Is used to adjust totalOpsSize appropriately after successful summarization.
|
|
36
|
+
*/
|
|
37
|
+
/** */
|
|
38
|
+
private totalOpsSizeBefore: number = 0;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Number of system ops at beginning of attempting to summarize.
|
|
42
|
+
* Is used to adjust numSystemOps appropriately after successful summarization.
|
|
43
|
+
*/
|
|
44
|
+
private numSystemOpsBefore: number = 0;
|
|
45
|
+
|
|
46
|
+
public numRuntimeOps: number = 0;
|
|
47
|
+
/**
|
|
48
|
+
* Number of non-system ops at beginning of attempting to summarize.
|
|
49
|
+
* Is used to adjust numNonSystemOps appropriately after successful summarization.
|
|
50
|
+
*/
|
|
51
|
+
private numNonSystemOpsBefore: number = 0;
|
|
52
|
+
|
|
29
53
|
constructor(
|
|
30
54
|
public lastOpSequenceNumber: number,
|
|
31
55
|
/** Baseline attempt data used for comparisons with subsequent attempts/calculations. */
|
|
@@ -45,10 +69,23 @@ export class SummarizeHeuristicData implements ISummarizeHeuristicData {
|
|
|
45
69
|
refSequenceNumber: refSequenceNumber ?? this.lastOpSequenceNumber,
|
|
46
70
|
summaryTime: Date.now(),
|
|
47
71
|
};
|
|
72
|
+
|
|
73
|
+
this.numSystemOpsBefore = this.numNonRuntimeOps;
|
|
74
|
+
this.numNonSystemOpsBefore = this.numRuntimeOps;
|
|
75
|
+
this.totalOpsSizeBefore = this.totalOpsSize;
|
|
48
76
|
}
|
|
49
77
|
|
|
50
78
|
public markLastAttemptAsSuccessful() {
|
|
51
79
|
this._lastSuccessfulSummary = { ...this.lastAttempt };
|
|
80
|
+
|
|
81
|
+
this.numNonRuntimeOps -= this.numSystemOpsBefore;
|
|
82
|
+
this.numSystemOpsBefore = 0;
|
|
83
|
+
|
|
84
|
+
this.numRuntimeOps -= this.numNonSystemOpsBefore;
|
|
85
|
+
this.numNonSystemOpsBefore = 0;
|
|
86
|
+
|
|
87
|
+
this.totalOpsSize -= this.totalOpsSizeBefore;
|
|
88
|
+
this.totalOpsSizeBefore = 0;
|
|
52
89
|
}
|
|
53
90
|
}
|
|
54
91
|
|
|
@@ -56,42 +93,71 @@ export class SummarizeHeuristicData implements ISummarizeHeuristicData {
|
|
|
56
93
|
* This class contains the heuristics for when to summarize.
|
|
57
94
|
*/
|
|
58
95
|
export class SummarizeHeuristicRunner implements ISummarizeHeuristicRunner {
|
|
59
|
-
private readonly idleTimer: Timer;
|
|
60
|
-
private readonly
|
|
96
|
+
private readonly idleTimer: Timer | undefined;
|
|
97
|
+
private readonly runSummarize: (reason: SummarizeReason) => void;
|
|
61
98
|
|
|
62
99
|
public constructor(
|
|
63
100
|
private readonly heuristicData: ISummarizeHeuristicData,
|
|
64
101
|
private readonly configuration: ISummaryConfigurationHeuristics,
|
|
65
|
-
|
|
102
|
+
trySummarize: (reason: SummarizeReason) => void,
|
|
66
103
|
private readonly logger: ITelemetryLogger,
|
|
104
|
+
private readonly summarizeStrategies: ISummaryHeuristicStrategy[] = getDefaultSummaryHeuristicStrategies(),
|
|
67
105
|
) {
|
|
68
106
|
this.idleTimer = new Timer(
|
|
69
|
-
this.
|
|
70
|
-
() => this.
|
|
71
|
-
|
|
107
|
+
this.idleTime,
|
|
108
|
+
() => this.runSummarize("idle"));
|
|
109
|
+
|
|
110
|
+
this.runSummarize = (reason: SummarizeReason) => {
|
|
111
|
+
this.idleTimer?.clear();
|
|
112
|
+
|
|
113
|
+
// We shouldn't attempt a summary if there are no new processed ops
|
|
114
|
+
const opsSinceLastAck = this.opsSinceLastAck;
|
|
115
|
+
if (opsSinceLastAck > 0) {
|
|
116
|
+
trySummarize(reason);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
public get idleTime(): number {
|
|
122
|
+
const maxIdleTime = this.configuration.maxIdleTime;
|
|
123
|
+
const minIdleTime = this.configuration.minIdleTime;
|
|
124
|
+
const weightedNumOfOps = getWeightedNumberOfOps(
|
|
125
|
+
this.heuristicData.numRuntimeOps,
|
|
126
|
+
this.heuristicData.numNonRuntimeOps,
|
|
127
|
+
this.configuration.runtimeOpWeight,
|
|
128
|
+
this.configuration.nonRuntimeOpWeight,
|
|
129
|
+
);
|
|
130
|
+
const pToMaxOps = weightedNumOfOps * 1.0 / this.configuration.maxOps;
|
|
131
|
+
|
|
132
|
+
if (pToMaxOps >= 1) {
|
|
133
|
+
return minIdleTime;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Return a ratioed idle time based on the percentage of ops
|
|
137
|
+
return maxIdleTime - ((maxIdleTime - minIdleTime) * pToMaxOps);
|
|
72
138
|
}
|
|
73
139
|
|
|
74
140
|
public get opsSinceLastAck(): number {
|
|
75
141
|
return this.heuristicData.lastOpSequenceNumber - this.heuristicData.lastSuccessfulSummary.refSequenceNumber;
|
|
76
142
|
}
|
|
77
143
|
|
|
144
|
+
public start() {
|
|
145
|
+
this.idleTimer?.start(this.idleTime);
|
|
146
|
+
}
|
|
147
|
+
|
|
78
148
|
public run() {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
this.trySummarize("maxTime");
|
|
84
|
-
} else if (opsSinceLastAck > this.configuration.maxOps) {
|
|
85
|
-
this.idleTimer.clear();
|
|
86
|
-
this.trySummarize("maxOps");
|
|
87
|
-
} else {
|
|
88
|
-
this.idleTimer.restart();
|
|
149
|
+
for (const strategy of this.summarizeStrategies) {
|
|
150
|
+
if (strategy.shouldRunSummary(this.configuration, this.heuristicData)) {
|
|
151
|
+
return this.runSummarize(strategy.summarizeReason);
|
|
152
|
+
}
|
|
89
153
|
}
|
|
154
|
+
|
|
155
|
+
this.idleTimer?.restart(this.idleTime);
|
|
90
156
|
}
|
|
91
157
|
|
|
92
158
|
public shouldRunLastSummary(): boolean {
|
|
93
159
|
const opsSinceLastAck = this.opsSinceLastAck;
|
|
94
|
-
const minOpsForLastSummaryAttempt = this.minOpsForLastSummaryAttempt;
|
|
160
|
+
const minOpsForLastSummaryAttempt = this.configuration.minOpsForLastSummaryAttempt;
|
|
95
161
|
|
|
96
162
|
this.logger.sendTelemetryEvent({
|
|
97
163
|
eventName: "ShouldRunLastSummary",
|
|
@@ -103,6 +169,54 @@ export class SummarizeHeuristicRunner implements ISummarizeHeuristicRunner {
|
|
|
103
169
|
}
|
|
104
170
|
|
|
105
171
|
public dispose() {
|
|
106
|
-
this.idleTimer
|
|
172
|
+
this.idleTimer?.clear();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Strategy used to run a summary when it's been a while since our last successful summary */
|
|
177
|
+
class MaxTimeSummaryHeuristicStrategy implements ISummaryHeuristicStrategy {
|
|
178
|
+
public readonly summarizeReason: Readonly<SummarizeReason> = "maxTime";
|
|
179
|
+
|
|
180
|
+
public shouldRunSummary(
|
|
181
|
+
configuration: ISummaryConfigurationHeuristics,
|
|
182
|
+
heuristicData: ISummarizeHeuristicData,
|
|
183
|
+
): boolean {
|
|
184
|
+
const timeSinceLastSummary = Date.now() - heuristicData.lastSuccessfulSummary.summaryTime;
|
|
185
|
+
return timeSinceLastSummary > configuration.maxTime;
|
|
107
186
|
}
|
|
108
187
|
}
|
|
188
|
+
|
|
189
|
+
function getWeightedNumberOfOps(
|
|
190
|
+
runtimeOpCount: number,
|
|
191
|
+
nonRuntimeOpCount: number,
|
|
192
|
+
runtimeOpWeight: number,
|
|
193
|
+
nonRuntimeOpWeight: number,
|
|
194
|
+
): number {
|
|
195
|
+
return (runtimeOpWeight * runtimeOpCount)
|
|
196
|
+
+ (nonRuntimeOpWeight * nonRuntimeOpCount);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Strategy used to do a weighted analysis on the ops we've processed since the last successful summary */
|
|
200
|
+
class WeightedOpsSummaryHeuristicStrategy implements ISummaryHeuristicStrategy {
|
|
201
|
+
public readonly summarizeReason: Readonly<SummarizeReason> = "maxOps";
|
|
202
|
+
|
|
203
|
+
public shouldRunSummary(
|
|
204
|
+
configuration: ISummaryConfigurationHeuristics,
|
|
205
|
+
heuristicData: ISummarizeHeuristicData,
|
|
206
|
+
): boolean {
|
|
207
|
+
const weightedNumOfOps = getWeightedNumberOfOps(
|
|
208
|
+
heuristicData.numRuntimeOps,
|
|
209
|
+
heuristicData.numNonRuntimeOps,
|
|
210
|
+
configuration.runtimeOpWeight,
|
|
211
|
+
configuration.nonRuntimeOpWeight,
|
|
212
|
+
);
|
|
213
|
+
return weightedNumOfOps > configuration.maxOps;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function getDefaultSummaryHeuristicStrategies() {
|
|
218
|
+
return [
|
|
219
|
+
new MaxTimeSummaryHeuristicStrategy(),
|
|
220
|
+
new WeightedOpsSummaryHeuristicStrategy(),
|
|
221
|
+
];
|
|
222
|
+
}
|
package/src/summarizerTypes.ts
CHANGED
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
} from "@fluidframework/protocol-definitions";
|
|
22
22
|
import { ISummaryStats } from "@fluidframework/runtime-definitions";
|
|
23
23
|
import { ISummaryAckMessage, ISummaryNackMessage, ISummaryOpMessage } from "./summaryCollection";
|
|
24
|
+
import { SummarizeReason } from "./summaryGenerator";
|
|
25
|
+
import { ISummaryConfigurationHeuristics } from ".";
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* @deprecated - This will be removed in a later release.
|
|
@@ -98,7 +100,9 @@ export interface ISummarizerRuntime extends IConnectableRuntime {
|
|
|
98
100
|
/** clientId of parent (non-summarizing) container that owns summarizer container */
|
|
99
101
|
readonly summarizerClientId: string | undefined;
|
|
100
102
|
closeFn(): void;
|
|
103
|
+
/** @deprecated 1.0, please remove all implementations and usage */
|
|
101
104
|
on(event: "batchEnd", listener: (error: any, op: ISequencedDocumentMessage) => void): this;
|
|
105
|
+
/** @deprecated 1.0, please remove all implementations and usage */
|
|
102
106
|
removeListener(event: "batchEnd", listener: (error: any, op: ISequencedDocumentMessage) => void): this;
|
|
103
107
|
}
|
|
104
108
|
|
|
@@ -150,10 +154,6 @@ export interface IGeneratedSummaryStats extends ISummaryStats {
|
|
|
150
154
|
readonly gcTotalBlobsSize?: number;
|
|
151
155
|
/** The number of gc blobs in this summary. */
|
|
152
156
|
readonly gcBlobNodeCount?: number;
|
|
153
|
-
/** Sum of the sizes of all op contents since the last summary */
|
|
154
|
-
readonly opsSizesSinceLastSummary: number;
|
|
155
|
-
/** Number of non-system ops since the last summary. See {@link @fluidframework/protocol-base#isSystemMessage} */
|
|
156
|
-
readonly nonSystemOpsSinceLastSummary: number;
|
|
157
157
|
/** The summary number for a container's summary. Incremented on summaries throughout its lifetime. */
|
|
158
158
|
readonly summaryNumber: number;
|
|
159
159
|
}
|
|
@@ -361,6 +361,18 @@ export interface ISummarizeHeuristicData {
|
|
|
361
361
|
/** Most recent summary that received an ack */
|
|
362
362
|
readonly lastSuccessfulSummary: Readonly<ISummarizeAttempt>;
|
|
363
363
|
|
|
364
|
+
/** Number of runtime ops since last summary */
|
|
365
|
+
numRuntimeOps: number;
|
|
366
|
+
|
|
367
|
+
/** Number of non-runtime ops since last summary */
|
|
368
|
+
numNonRuntimeOps: number;
|
|
369
|
+
|
|
370
|
+
/** Cumulative size in bytes of all the ops since the last summary */
|
|
371
|
+
totalOpsSize: number;
|
|
372
|
+
|
|
373
|
+
/** Wether or not this instance contains adjusted metrics due to missing op data */
|
|
374
|
+
hasMissingOpData: boolean;
|
|
375
|
+
|
|
364
376
|
/**
|
|
365
377
|
* Updates lastAttempt and lastSuccessfulAttempt based on the last summary.
|
|
366
378
|
* @param lastSummary - last ack summary
|
|
@@ -381,7 +393,10 @@ export interface ISummarizeHeuristicData {
|
|
|
381
393
|
|
|
382
394
|
/** Responsible for running heuristics determining when to summarize. */
|
|
383
395
|
export interface ISummarizeHeuristicRunner {
|
|
384
|
-
/**
|
|
396
|
+
/** Start specific heuristic trackers (ex: idle timer) */
|
|
397
|
+
start(): void;
|
|
398
|
+
|
|
399
|
+
/** Runs the heuristics to determine if it should try to summarize */
|
|
385
400
|
run(): void;
|
|
386
401
|
|
|
387
402
|
/** Runs a different heuristic to check if it should summarize before closing */
|
|
@@ -408,6 +423,19 @@ export type ISummarizeTelemetryProperties =
|
|
|
408
423
|
Pick<ITelemetryProperties, ISummarizeTelemetryRequiredProperties> &
|
|
409
424
|
Partial<Pick<ITelemetryProperties, ISummarizeTelemetryOptionalProperties>>;
|
|
410
425
|
|
|
426
|
+
/** Strategy used to heuristically determine when we should run a summary */
|
|
427
|
+
export interface ISummaryHeuristicStrategy {
|
|
428
|
+
/** Summarize reason for this summarize heuristic strategy (ex: "maxTime") */
|
|
429
|
+
summarizeReason: Readonly<SummarizeReason>;
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Determines if this strategy's summarize criteria been met
|
|
433
|
+
* @param configuration - summary configuration we are to check against
|
|
434
|
+
* @param heuristicData - heuristic data used to confirm conditions are met
|
|
435
|
+
*/
|
|
436
|
+
shouldRunSummary(configuration: ISummaryConfigurationHeuristics, heuristicData: ISummarizeHeuristicData): boolean;
|
|
437
|
+
}
|
|
438
|
+
|
|
411
439
|
type SummaryGeneratorRequiredTelemetryProperties =
|
|
412
440
|
/** True to generate the full tree with no handle reuse optimizations */
|
|
413
441
|
"fullTree" |
|
|
@@ -428,11 +456,10 @@ type SummaryGeneratorOptionalTelemetryProperties =
|
|
|
428
456
|
/** Delta in sum of op sizes between the current reference sequence number and the reference
|
|
429
457
|
* sequence number of the last summary */
|
|
430
458
|
"opsSizesSinceLastSummary" |
|
|
431
|
-
/**
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
"nonSystemOpsSinceLastSummary" |
|
|
459
|
+
/** Delta between the number of non-runtime ops since the last summary */
|
|
460
|
+
"nonRuntimeOpsSinceLastSummary" |
|
|
461
|
+
/** Wether or not this instance contains adjusted metrics due to missing op data */
|
|
462
|
+
"hasMissingOpData" |
|
|
436
463
|
/** Time it took to generate the summary tree and stats. */
|
|
437
464
|
"generateDuration" |
|
|
438
465
|
/** The handle returned by storage pointing to the uploaded summary tree. */
|