@fluidframework/container-runtime 0.59.4001 → 0.59.4003
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +45 -19
- package/dist/containerRuntime.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/runningSummarizer.d.ts +14 -0
- package/dist/runningSummarizer.d.ts.map +1 -1
- package/dist/runningSummarizer.js +25 -0
- package/dist/runningSummarizer.js.map +1 -1
- package/dist/summarizer.d.ts.map +1 -1
- package/dist/summarizer.js +14 -3
- package/dist/summarizer.js.map +1 -1
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +45 -19
- package/lib/containerRuntime.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/runningSummarizer.d.ts +14 -0
- package/lib/runningSummarizer.d.ts.map +1 -1
- package/lib/runningSummarizer.js +25 -0
- package/lib/runningSummarizer.js.map +1 -1
- package/lib/summarizer.d.ts.map +1 -1
- package/lib/summarizer.js +15 -4
- package/lib/summarizer.js.map +1 -1
- package/package.json +13 -13
- package/src/containerRuntime.ts +67 -34
- package/src/packageVersion.ts +1 -1
- package/src/runningSummarizer.ts +33 -1
- package/src/summarizer.ts +26 -10
package/src/containerRuntime.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
IQuorumClients,
|
|
62
62
|
ISequencedDocumentMessage,
|
|
63
63
|
ISignalMessage,
|
|
64
|
+
ISnapshotTree,
|
|
64
65
|
ISummaryConfiguration,
|
|
65
66
|
ISummaryContent,
|
|
66
67
|
ISummaryTree,
|
|
@@ -2245,21 +2246,24 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2245
2246
|
},
|
|
2246
2247
|
);
|
|
2247
2248
|
|
|
2249
|
+
let latestSnapshotVersionId: string | undefined;
|
|
2248
2250
|
if (refreshLatestAck) {
|
|
2249
|
-
const
|
|
2251
|
+
const latestSnapshotInfo = await this.refreshLatestSummaryAckFromServer(
|
|
2250
2252
|
ChildLogger.create(summaryNumberLogger, undefined, { all: { safeSummary: true } }));
|
|
2253
|
+
const latestSnapshotRefSeq = latestSnapshotInfo.latestSnapshotRefSeq;
|
|
2254
|
+
latestSnapshotVersionId = latestSnapshotInfo.latestSnapshotVersionId;
|
|
2251
2255
|
|
|
2252
|
-
if (
|
|
2256
|
+
if (latestSnapshotRefSeq > this.deltaManager.lastSequenceNumber) {
|
|
2253
2257
|
// We need to catch up to the latest summary's reference sequence number before pausing.
|
|
2254
2258
|
await PerformanceEvent.timedExecAsync(
|
|
2255
2259
|
summaryNumberLogger,
|
|
2256
2260
|
{
|
|
2257
2261
|
eventName: "WaitingForSeq",
|
|
2258
2262
|
lastSequenceNumber: this.deltaManager.lastSequenceNumber,
|
|
2259
|
-
targetSequenceNumber:
|
|
2263
|
+
targetSequenceNumber: latestSnapshotRefSeq,
|
|
2260
2264
|
lastKnownSeqNumber: this.deltaManager.lastKnownSeqNumber,
|
|
2261
2265
|
},
|
|
2262
|
-
async () => waitForSeq(this.deltaManager,
|
|
2266
|
+
async () => waitForSeq(this.deltaManager, latestSnapshotRefSeq),
|
|
2263
2267
|
{ start: true, end: true, cancel: "error" }, // definitely want start event
|
|
2264
2268
|
);
|
|
2265
2269
|
}
|
|
@@ -2384,19 +2388,33 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2384
2388
|
return { stage: "generate", ...generateSummaryData, error: continueResult.error };
|
|
2385
2389
|
}
|
|
2386
2390
|
|
|
2391
|
+
// It may happen that the lastAck it not correct due to missing summaryAck in case of single commit
|
|
2392
|
+
// summary. So if the previous summarizer closes just after submitting the summary and before
|
|
2393
|
+
// submitting the summaryOp then we can't rely on summaryAck. So in case we have
|
|
2394
|
+
// latestSnapshotVersionId from storage and it does not match with the lastAck ackHandle, then use
|
|
2395
|
+
// the one fetched from storage as parent as that is the latest.
|
|
2387
2396
|
const lastAck = this.summaryCollection.latestAck;
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2397
|
+
let summaryContext: ISummaryContext;
|
|
2398
|
+
if (lastAck?.summaryAck.contents.handle !== latestSnapshotVersionId
|
|
2399
|
+
&& latestSnapshotVersionId !== undefined) {
|
|
2400
|
+
summaryContext = {
|
|
2401
|
+
proposalHandle: undefined,
|
|
2402
|
+
ackHandle: latestSnapshotVersionId,
|
|
2403
|
+
referenceSequenceNumber: summaryRefSeqNum,
|
|
2404
|
+
};
|
|
2405
|
+
} else if (lastAck === undefined) {
|
|
2406
|
+
summaryContext = {
|
|
2391
2407
|
proposalHandle: undefined,
|
|
2392
2408
|
ackHandle: this.context.getLoadedFromVersion()?.id,
|
|
2393
2409
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2394
|
-
}
|
|
2395
|
-
|
|
2410
|
+
};
|
|
2411
|
+
} else {
|
|
2412
|
+
summaryContext = {
|
|
2396
2413
|
proposalHandle: lastAck.summaryOp.contents.handle,
|
|
2397
2414
|
ackHandle: lastAck.summaryAck.contents.handle,
|
|
2398
2415
|
referenceSequenceNumber: summaryRefSeqNum,
|
|
2399
2416
|
};
|
|
2417
|
+
}
|
|
2400
2418
|
|
|
2401
2419
|
let handle: string;
|
|
2402
2420
|
try {
|
|
@@ -2737,20 +2755,29 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2737
2755
|
summaryLogger: ITelemetryLogger,
|
|
2738
2756
|
) {
|
|
2739
2757
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2758
|
+
// The call to fetch the snapshot is very expensive and not always needed.
|
|
2759
|
+
// It should only be done by the summarizerNode, if required.
|
|
2760
|
+
const snapshotTreeFetcher = async () => {
|
|
2761
|
+
const fetchResult = await this.fetchSnapshotFromStorage(
|
|
2762
|
+
ackHandle,
|
|
2763
|
+
summaryLogger,
|
|
2764
|
+
{
|
|
2765
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2766
|
+
ackHandle,
|
|
2767
|
+
summaryRefSeq,
|
|
2768
|
+
fetchLatest: false,
|
|
2769
|
+
});
|
|
2770
|
+
return fetchResult.snapshotTree;
|
|
2771
|
+
};
|
|
2772
|
+
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2773
|
+
proposalHandle,
|
|
2774
|
+
summaryRefSeq,
|
|
2775
|
+
snapshotTreeFetcher,
|
|
2776
|
+
readAndParseBlob,
|
|
2777
|
+
summaryLogger,
|
|
2778
|
+
);
|
|
2779
|
+
|
|
2780
|
+
// Notify the garbage collector so it can update its latest summary state.
|
|
2754
2781
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
2755
2782
|
}
|
|
2756
2783
|
|
|
@@ -2760,19 +2787,22 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2760
2787
|
* @param summaryLogger - logger to use when fetching snapshot from storage
|
|
2761
2788
|
* @returns downloaded snapshot's reference sequence number
|
|
2762
2789
|
*/
|
|
2763
|
-
private async refreshLatestSummaryAckFromServer(
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2790
|
+
private async refreshLatestSummaryAckFromServer(
|
|
2791
|
+
summaryLogger: ITelemetryLogger,
|
|
2792
|
+
): Promise<{ latestSnapshotRefSeq: number; latestSnapshotVersionId: string | undefined; }> {
|
|
2793
|
+
const { snapshotTree, versionId } = await this.fetchSnapshotFromStorage(null, summaryLogger, {
|
|
2794
|
+
eventName: "RefreshLatestSummaryGetSnapshot",
|
|
2795
|
+
fetchLatest: true,
|
|
2796
|
+
},
|
|
2797
|
+
);
|
|
2768
2798
|
|
|
2769
2799
|
const readAndParseBlob = async <T>(id: string) => readAndParse<T>(this.storage, id);
|
|
2770
|
-
const
|
|
2800
|
+
const latestSnapshotRefSeq = await seqFromTree(snapshotTree, readAndParseBlob);
|
|
2771
2801
|
|
|
2772
2802
|
const result = await this.summarizerNode.refreshLatestSummary(
|
|
2773
2803
|
undefined,
|
|
2774
|
-
|
|
2775
|
-
async () =>
|
|
2804
|
+
latestSnapshotRefSeq,
|
|
2805
|
+
async () => snapshotTree,
|
|
2776
2806
|
readAndParseBlob,
|
|
2777
2807
|
summaryLogger,
|
|
2778
2808
|
);
|
|
@@ -2780,11 +2810,14 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2780
2810
|
// Notify the garbage collector so it can update its latest summary state.
|
|
2781
2811
|
await this.garbageCollector.latestSummaryStateRefreshed(result, readAndParseBlob);
|
|
2782
2812
|
|
|
2783
|
-
return
|
|
2813
|
+
return { latestSnapshotRefSeq, latestSnapshotVersionId: versionId };
|
|
2784
2814
|
}
|
|
2785
2815
|
|
|
2786
2816
|
private async fetchSnapshotFromStorage(
|
|
2787
|
-
versionId: string | null,
|
|
2817
|
+
versionId: string | null,
|
|
2818
|
+
logger: ITelemetryLogger,
|
|
2819
|
+
event: ITelemetryGenericEvent,
|
|
2820
|
+
): Promise<{ snapshotTree: ISnapshotTree; versionId: string; }> {
|
|
2788
2821
|
return PerformanceEvent.timedExecAsync(
|
|
2789
2822
|
logger, event, async (perfEvent: {
|
|
2790
2823
|
end: (arg0: {
|
|
@@ -2803,7 +2836,7 @@ export class ContainerRuntime extends TypedEventEmitter<IContainerRuntimeEvents>
|
|
|
2803
2836
|
stats.getSnapshotDuration = trace.trace().duration;
|
|
2804
2837
|
|
|
2805
2838
|
perfEvent.end(stats);
|
|
2806
|
-
return maybeSnapshot;
|
|
2839
|
+
return { snapshotTree: maybeSnapshot, versionId: versions[0].id };
|
|
2807
2840
|
});
|
|
2808
2841
|
}
|
|
2809
2842
|
|
package/src/packageVersion.ts
CHANGED
package/src/runningSummarizer.ts
CHANGED
|
@@ -79,10 +79,10 @@ export class RunningSummarizer implements IDisposable {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
public get disposed() { return this._disposed; }
|
|
82
|
-
|
|
83
82
|
private stopping = false;
|
|
84
83
|
private _disposed = false;
|
|
85
84
|
private summarizingLock: Promise<void> | undefined;
|
|
85
|
+
private refreshSummaryAckLock: Promise<void> | undefined;
|
|
86
86
|
private tryWhileSummarizing = false;
|
|
87
87
|
private readonly pendingAckTimer: PromiseTimer;
|
|
88
88
|
private heuristicRunner?: ISummarizeHeuristicRunner;
|
|
@@ -270,6 +270,31 @@ export class RunningSummarizer implements IDisposable {
|
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Blocks a new summarizer from running in case RefreshSummaryAck is being processed.
|
|
275
|
+
* Assumes that caller checked upfront for lack of concurrent action (this.refreshSummaryAckLock)
|
|
276
|
+
* before calling this API. I.e. caller is responsible for either erroring out or waiting on this promise.
|
|
277
|
+
* Note: The refreshSummaryAckLock makes sure no summarizer gets enqueued or processed
|
|
278
|
+
* until the refresh has completed. One can't rely uniquely on the summarizingLock as the
|
|
279
|
+
* refreshLatestSummaryAck also happens during the time summarizingLock !== undefined.
|
|
280
|
+
* Ex. Summarizer submits a summay + op and then waits for the Summary Ack to proceed
|
|
281
|
+
* with the refreshLatestSummaryAck and complete the summary.
|
|
282
|
+
* @param action - action to perform.
|
|
283
|
+
* @returns - result of action.
|
|
284
|
+
*/
|
|
285
|
+
public async lockedRefreshSummaryAckAction<T>(action: () => Promise<T>) {
|
|
286
|
+
assert(this.refreshSummaryAckLock === undefined,
|
|
287
|
+
"Refresh Summary Ack - Caller is responsible for checking lock");
|
|
288
|
+
|
|
289
|
+
const refreshSummaryAckLock = new Deferred<void>();
|
|
290
|
+
this.refreshSummaryAckLock = refreshSummaryAckLock.promise;
|
|
291
|
+
|
|
292
|
+
return action().finally(() => {
|
|
293
|
+
refreshSummaryAckLock.resolve();
|
|
294
|
+
this.refreshSummaryAckLock = undefined;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
273
298
|
/**
|
|
274
299
|
* Runs single summary action that prevents any other concurrent actions.
|
|
275
300
|
* Assumes that caller checked upfront for lack of concurrent action (this.summarizingLock)
|
|
@@ -284,6 +309,8 @@ export class RunningSummarizer implements IDisposable {
|
|
|
284
309
|
this.summarizingLock = summarizingLock.promise;
|
|
285
310
|
|
|
286
311
|
this.summarizeCount++;
|
|
312
|
+
// Make sure the RefreshLatestSummaryAck is not being executed.
|
|
313
|
+
await this.refreshSummaryAckLock;
|
|
287
314
|
|
|
288
315
|
return action().finally(() => {
|
|
289
316
|
summarizingLock.resolve();
|
|
@@ -383,6 +410,10 @@ export class RunningSummarizer implements IDisposable {
|
|
|
383
410
|
});
|
|
384
411
|
await delay(delaySeconds * 1000);
|
|
385
412
|
}
|
|
413
|
+
|
|
414
|
+
// Make sure the refresh Summary Ack is not being executed.
|
|
415
|
+
await this.refreshSummaryAckLock;
|
|
416
|
+
|
|
386
417
|
// Note: no need to account for cancellationToken.waitCancelled here, as
|
|
387
418
|
// this is accounted SummaryGenerator.summarizeCore that controls receivedSummaryAckOrNack.
|
|
388
419
|
const resultSummarize = this.generator.summarize(summarizeProps, options, cancellationToken);
|
|
@@ -431,6 +462,7 @@ export class RunningSummarizer implements IDisposable {
|
|
|
431
462
|
// The heuristics are blocking concurrent summarize attempts.
|
|
432
463
|
throw new UsageError("Attempted to run an already-running summarizer on demand");
|
|
433
464
|
}
|
|
465
|
+
|
|
434
466
|
const result = this.trySummarizeOnce(
|
|
435
467
|
{ reason: `onDemand/${reason}` },
|
|
436
468
|
options,
|
package/src/summarizer.ts
CHANGED
|
@@ -10,7 +10,12 @@ import { ILoader, LoaderHeader } from "@fluidframework/container-definitions";
|
|
|
10
10
|
import { UsageError } from "@fluidframework/container-utils";
|
|
11
11
|
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
12
12
|
import { requestFluidObject } from "@fluidframework/runtime-utils";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ChildLogger,
|
|
15
|
+
IFluidErrorBase,
|
|
16
|
+
LoggingError,
|
|
17
|
+
wrapErrorAndLog,
|
|
18
|
+
} from "@fluidframework/telemetry-utils";
|
|
14
19
|
import {
|
|
15
20
|
FluidObject,
|
|
16
21
|
IFluidHandleContext,
|
|
@@ -23,7 +28,7 @@ import {
|
|
|
23
28
|
} from "@fluidframework/protocol-definitions";
|
|
24
29
|
import { ICancellableSummarizerController } from "./runWhileConnectedCoordinator";
|
|
25
30
|
import { summarizerClientType } from "./summarizerClientElection";
|
|
26
|
-
import { SummaryCollection } from "./summaryCollection";
|
|
31
|
+
import { IAckedSummary, SummaryCollection } from "./summaryCollection";
|
|
27
32
|
import { SummarizerHandle } from "./summarizerHandle";
|
|
28
33
|
import { RunningSummarizer } from "./runningSummarizer";
|
|
29
34
|
import {
|
|
@@ -374,22 +379,33 @@ export class Summarizer extends EventEmitter implements ISummarizer {
|
|
|
374
379
|
|
|
375
380
|
private async handleSummaryAcks() {
|
|
376
381
|
let refSequenceNumber = this.runtime.deltaManager.initialSequenceNumber;
|
|
382
|
+
let ack: IAckedSummary | undefined;
|
|
377
383
|
while (this.runningSummarizer) {
|
|
378
384
|
const summaryLogger = this.runningSummarizer.tryGetCorrelatedLogger(refSequenceNumber) ?? this.logger;
|
|
379
385
|
try {
|
|
380
|
-
|
|
386
|
+
// Initialize ack with undefined if exception happens inside of waitSummaryAck on second iteration,
|
|
387
|
+
// we record undefined, not previous handles.
|
|
388
|
+
ack = undefined;
|
|
389
|
+
ack = await this.summaryCollection.waitSummaryAck(refSequenceNumber);
|
|
381
390
|
refSequenceNumber = ack.summaryOp.referenceSequenceNumber;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
391
|
+
const summaryOpHandle = ack.summaryOp.contents.handle;
|
|
392
|
+
const summaryAckHandle = ack.summaryAck.contents.handle;
|
|
393
|
+
// Make sure we block any summarizer from being executed/enqueued while
|
|
394
|
+
// executing the refreshLatestSummaryAck.
|
|
395
|
+
// https://dev.azure.com/fluidframework/internal/_workitems/edit/779
|
|
396
|
+
await this.runningSummarizer.lockedRefreshSummaryAckAction(async () =>
|
|
397
|
+
this.internalsProvider.refreshLatestSummaryAck(
|
|
398
|
+
summaryOpHandle,
|
|
399
|
+
summaryAckHandle,
|
|
400
|
+
refSequenceNumber,
|
|
401
|
+
summaryLogger,
|
|
402
|
+
));
|
|
389
403
|
} catch (error) {
|
|
390
404
|
summaryLogger.sendErrorEvent({
|
|
391
405
|
eventName: "HandleSummaryAckError",
|
|
392
406
|
referenceSequenceNumber: refSequenceNumber,
|
|
407
|
+
handle: ack?.summaryOp?.contents?.handle,
|
|
408
|
+
ackHandle: ack?.summaryAck?.contents?.handle,
|
|
393
409
|
}, error);
|
|
394
410
|
}
|
|
395
411
|
refSequenceNumber++;
|