@fluidframework/container-runtime 2.74.0 → 2.80.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/CHANGELOG.md +4 -0
- package/container-runtime.test-files.tar +0 -0
- package/dist/blobManager/blobManager.d.ts +0 -2
- package/dist/blobManager/blobManager.d.ts.map +1 -1
- package/dist/blobManager/blobManager.js +4 -5
- package/dist/blobManager/blobManager.js.map +1 -1
- package/dist/channelCollection.d.ts +3 -3
- package/dist/channelCollection.d.ts.map +1 -1
- package/dist/channelCollection.js.map +1 -1
- package/dist/containerRuntime.d.ts +5 -0
- package/dist/containerRuntime.d.ts.map +1 -1
- package/dist/containerRuntime.js +13 -7
- package/dist/containerRuntime.js.map +1 -1
- package/dist/dataStoreContext.d.ts +1 -1
- package/dist/dataStoreContext.d.ts.map +1 -1
- package/dist/dataStoreContext.js +1 -1
- package/dist/dataStoreContext.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/runtimeLayerCompatState.d.ts +9 -5
- package/dist/runtimeLayerCompatState.d.ts.map +1 -1
- package/dist/runtimeLayerCompatState.js +16 -6
- package/dist/runtimeLayerCompatState.js.map +1 -1
- package/dist/summary/summarizerClientElection.d.ts.map +1 -1
- package/dist/summary/summarizerClientElection.js +1 -0
- package/dist/summary/summarizerClientElection.js.map +1 -1
- package/dist/summary/summarizerTypes.d.ts +5 -0
- package/dist/summary/summarizerTypes.d.ts.map +1 -1
- package/dist/summary/summarizerTypes.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +3 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js +39 -18
- package/dist/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summarizer.js +13 -11
- package/dist/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +4 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js +30 -9
- package/dist/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/dist/summary/summaryManager.d.ts +1 -1
- package/dist/summary/summaryManager.d.ts.map +1 -1
- package/dist/summary/summaryManager.js +10 -9
- package/dist/summary/summaryManager.js.map +1 -1
- package/lib/blobManager/blobManager.d.ts +0 -2
- package/lib/blobManager/blobManager.d.ts.map +1 -1
- package/lib/blobManager/blobManager.js +4 -5
- package/lib/blobManager/blobManager.js.map +1 -1
- package/lib/channelCollection.d.ts +3 -3
- package/lib/channelCollection.d.ts.map +1 -1
- package/lib/channelCollection.js.map +1 -1
- package/lib/containerRuntime.d.ts +5 -0
- package/lib/containerRuntime.d.ts.map +1 -1
- package/lib/containerRuntime.js +13 -7
- package/lib/containerRuntime.js.map +1 -1
- package/lib/dataStoreContext.d.ts +1 -1
- package/lib/dataStoreContext.d.ts.map +1 -1
- package/lib/dataStoreContext.js +1 -1
- package/lib/dataStoreContext.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/runtimeLayerCompatState.d.ts +9 -5
- package/lib/runtimeLayerCompatState.d.ts.map +1 -1
- package/lib/runtimeLayerCompatState.js +15 -5
- package/lib/runtimeLayerCompatState.js.map +1 -1
- package/lib/summary/summarizerClientElection.d.ts.map +1 -1
- package/lib/summary/summarizerClientElection.js +1 -0
- package/lib/summary/summarizerClientElection.js.map +1 -1
- package/lib/summary/summarizerTypes.d.ts +5 -0
- package/lib/summary/summarizerTypes.d.ts.map +1 -1
- package/lib/summary/summarizerTypes.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts +3 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js +39 -18
- package/lib/summary/summaryDelayLoadedModule/runningSummarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summarizer.js +13 -11
- package/lib/summary/summaryDelayLoadedModule/summarizer.js.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts +4 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.d.ts.map +1 -1
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js +26 -5
- package/lib/summary/summaryDelayLoadedModule/summaryGenerator.js.map +1 -1
- package/lib/summary/summaryManager.d.ts +1 -1
- package/lib/summary/summaryManager.d.ts.map +1 -1
- package/lib/summary/summaryManager.js +10 -9
- package/lib/summary/summaryManager.js.map +1 -1
- package/package.json +26 -26
- package/src/blobManager/blobManager.ts +3 -7
- package/src/channelCollection.ts +3 -3
- package/src/containerRuntime.ts +19 -6
- package/src/dataStoreContext.ts +2 -2
- package/src/packageVersion.ts +1 -1
- package/src/runtimeLayerCompatState.ts +25 -9
- package/src/summary/summarizerClientElection.ts +1 -0
- package/src/summary/summarizerTypes.ts +5 -0
- package/src/summary/summaryDelayLoadedModule/runningSummarizer.ts +54 -26
- package/src/summary/summaryDelayLoadedModule/summarizer.ts +13 -11
- package/src/summary/summaryDelayLoadedModule/summaryGenerator.ts +35 -5
- package/src/summary/summaryManager.ts +11 -10
|
@@ -270,7 +270,6 @@ export class BlobManager {
|
|
|
270
270
|
// blobPath's format - `/<basePath>/<localId>`.
|
|
271
271
|
private readonly isBlobDeleted: (blobPath: string) => boolean;
|
|
272
272
|
private readonly runtime: IBlobManagerRuntime;
|
|
273
|
-
private readonly localIdGenerator: () => string;
|
|
274
273
|
|
|
275
274
|
private readonly createBlobPayloadPending: boolean;
|
|
276
275
|
|
|
@@ -298,7 +297,6 @@ export class BlobManager {
|
|
|
298
297
|
readonly isBlobDeleted: (blobPath: string) => boolean;
|
|
299
298
|
readonly runtime: IBlobManagerRuntime;
|
|
300
299
|
pendingBlobs: IPendingBlobs | undefined;
|
|
301
|
-
readonly localIdGenerator?: (() => string) | undefined;
|
|
302
300
|
readonly createBlobPayloadPending: boolean;
|
|
303
301
|
}) {
|
|
304
302
|
const {
|
|
@@ -310,7 +308,6 @@ export class BlobManager {
|
|
|
310
308
|
isBlobDeleted,
|
|
311
309
|
runtime,
|
|
312
310
|
pendingBlobs,
|
|
313
|
-
localIdGenerator,
|
|
314
311
|
createBlobPayloadPending,
|
|
315
312
|
} = props;
|
|
316
313
|
this.routeContext = routeContext;
|
|
@@ -319,7 +316,6 @@ export class BlobManager {
|
|
|
319
316
|
this.blobRequested = blobRequested;
|
|
320
317
|
this.isBlobDeleted = isBlobDeleted;
|
|
321
318
|
this.runtime = runtime;
|
|
322
|
-
this.localIdGenerator = localIdGenerator ?? uuid;
|
|
323
319
|
this.createBlobPayloadPending = createBlobPayloadPending;
|
|
324
320
|
|
|
325
321
|
this.mc = createChildMonitoringContext({
|
|
@@ -480,7 +476,7 @@ export class BlobManager {
|
|
|
480
476
|
if (signal?.aborted === true) {
|
|
481
477
|
throw createAbortError();
|
|
482
478
|
}
|
|
483
|
-
const localId =
|
|
479
|
+
const localId = uuid();
|
|
484
480
|
this.localBlobCache.set(localId, { state: "uploading", blob });
|
|
485
481
|
// Blobs created while the container is detached are stored in IDetachedBlobStorage.
|
|
486
482
|
// The 'IContainerStorageService.createBlob()' call below will respond with a pseudo storage ID.
|
|
@@ -497,7 +493,7 @@ export class BlobManager {
|
|
|
497
493
|
blob: ArrayBufferLike,
|
|
498
494
|
signal?: AbortSignal,
|
|
499
495
|
): Promise<IFluidHandleInternalPayloadPending<ArrayBufferLike>> {
|
|
500
|
-
const localId =
|
|
496
|
+
const localId = uuid();
|
|
501
497
|
this.localBlobCache.set(localId, { state: "localOnly", blob });
|
|
502
498
|
await this.uploadAndAttach(localId, signal);
|
|
503
499
|
return this.getNonPayloadPendingBlobHandle(localId);
|
|
@@ -507,7 +503,7 @@ export class BlobManager {
|
|
|
507
503
|
blob: ArrayBufferLike,
|
|
508
504
|
signal?: AbortSignal,
|
|
509
505
|
): IFluidHandleInternalPayloadPending<ArrayBufferLike> {
|
|
510
|
-
const localId =
|
|
506
|
+
const localId = uuid();
|
|
511
507
|
this.localBlobCache.set(localId, { state: "localOnly", blob });
|
|
512
508
|
|
|
513
509
|
const blobHandle = new BlobHandle(
|
package/src/channelCollection.ts
CHANGED
|
@@ -793,7 +793,7 @@ export class ChannelCollection
|
|
|
793
793
|
| OutboundContainerRuntimeAttachMessage
|
|
794
794
|
| ContainerRuntimeAliasMessage,
|
|
795
795
|
localOpMetadata: unknown,
|
|
796
|
-
squash: boolean
|
|
796
|
+
squash: boolean,
|
|
797
797
|
): void => {
|
|
798
798
|
switch (message.type) {
|
|
799
799
|
case ContainerMessageType.Attach:
|
|
@@ -813,7 +813,7 @@ export class ChannelCollection
|
|
|
813
813
|
protected readonly resubmitDataStoreOp = (
|
|
814
814
|
envelope: IEnvelope<FluidDataStoreMessage>,
|
|
815
815
|
localOpMetadata: unknown,
|
|
816
|
-
squash: boolean
|
|
816
|
+
squash: boolean,
|
|
817
817
|
): void => {
|
|
818
818
|
const context = this.contexts.get(envelope.address);
|
|
819
819
|
// If the data store has been deleted, log an error and throw an error. If there are local changes for a
|
|
@@ -1820,7 +1820,7 @@ export class ComposableChannelCollection
|
|
|
1820
1820
|
type: string,
|
|
1821
1821
|
content: unknown,
|
|
1822
1822
|
localOpMetadata: unknown,
|
|
1823
|
-
squash
|
|
1823
|
+
squash: boolean,
|
|
1824
1824
|
): void {
|
|
1825
1825
|
// If the cast is incorrect and type is not one of the three supported,
|
|
1826
1826
|
// reSubmitContainerMessage will assert.
|
package/src/containerRuntime.ts
CHANGED
|
@@ -1622,10 +1622,11 @@ export class ContainerRuntime
|
|
|
1622
1622
|
|
|
1623
1623
|
// Validate that the Loader is compatible with this Runtime.
|
|
1624
1624
|
const maybeLoaderCompatDetailsForRuntime = context as FluidObject<ILayerCompatDetails>;
|
|
1625
|
+
|
|
1625
1626
|
validateLoaderCompatibility(
|
|
1626
1627
|
maybeLoaderCompatDetailsForRuntime.ILayerCompatDetails,
|
|
1627
1628
|
this.disposeFn,
|
|
1628
|
-
this.mc
|
|
1629
|
+
this.mc,
|
|
1629
1630
|
);
|
|
1630
1631
|
|
|
1631
1632
|
// If we support multiple algorithms in the future, then we would need to manage it here carefully.
|
|
@@ -1828,7 +1829,9 @@ export class ContainerRuntime
|
|
|
1828
1829
|
validateSummaryHeuristicConfiguration(this.summaryConfiguration);
|
|
1829
1830
|
}
|
|
1830
1831
|
|
|
1831
|
-
this.summariesDisabled =
|
|
1832
|
+
this.summariesDisabled =
|
|
1833
|
+
isSummariesDisabled(this.summaryConfiguration) ||
|
|
1834
|
+
this.mc.config.getBoolean("Fluid.ContainerRuntime.Test.DisableSummaries") === true;
|
|
1832
1835
|
|
|
1833
1836
|
this.maxConsecutiveReconnects =
|
|
1834
1837
|
this.mc.config.getNumber(maxConsecutiveReconnectsKey) ?? defaultMaxConsecutiveReconnects;
|
|
@@ -2294,7 +2297,10 @@ export class ContainerRuntime
|
|
|
2294
2297
|
|
|
2295
2298
|
const defaultAction = (): void => {
|
|
2296
2299
|
if (summaryCollection.opsSinceLastAck > maxOpsSinceLastSummary) {
|
|
2297
|
-
this.mc.logger.sendTelemetryEvent({
|
|
2300
|
+
this.mc.logger.sendTelemetryEvent({
|
|
2301
|
+
eventName: "SummaryStatus:Behind",
|
|
2302
|
+
opsWithoutSummary: summaryCollection.opsSinceLastAck,
|
|
2303
|
+
});
|
|
2298
2304
|
// unregister default to no log on every op after falling behind
|
|
2299
2305
|
// and register summary ack handler to re-register this handler
|
|
2300
2306
|
// after successful summary
|
|
@@ -2336,8 +2342,9 @@ export class ContainerRuntime
|
|
|
2336
2342
|
"summarizerStop",
|
|
2337
2343
|
"summarizerStart",
|
|
2338
2344
|
"summarizerStartupFailed",
|
|
2339
|
-
|
|
2340
|
-
|
|
2345
|
+
"summarizeTimeout",
|
|
2346
|
+
] as const) {
|
|
2347
|
+
this.summaryManager.on(eventName, (...args: unknown[]) => {
|
|
2341
2348
|
this.emit(eventName, ...args);
|
|
2342
2349
|
});
|
|
2343
2350
|
}
|
|
@@ -3930,6 +3937,10 @@ export class ContainerRuntime
|
|
|
3930
3937
|
* True to run GC sweep phase after the mark phase
|
|
3931
3938
|
*/
|
|
3932
3939
|
runSweep?: boolean;
|
|
3940
|
+
/**
|
|
3941
|
+
* Telemetry context to populate during summarization.
|
|
3942
|
+
*/
|
|
3943
|
+
telemetryContext?: TelemetryContext;
|
|
3933
3944
|
}): Promise<ISummaryTreeWithStats> {
|
|
3934
3945
|
this.verifyNotClosed();
|
|
3935
3946
|
|
|
@@ -3940,9 +3951,9 @@ export class ContainerRuntime
|
|
|
3940
3951
|
runGC = this.garbageCollector.shouldRunGC,
|
|
3941
3952
|
runSweep,
|
|
3942
3953
|
fullGC,
|
|
3954
|
+
telemetryContext = new TelemetryContext(),
|
|
3943
3955
|
} = options;
|
|
3944
3956
|
|
|
3945
|
-
const telemetryContext = new TelemetryContext();
|
|
3946
3957
|
// Add the options that are used to generate this summary to the telemetry context.
|
|
3947
3958
|
telemetryContext.setMultiple("fluid_Summarize", "Options", {
|
|
3948
3959
|
fullTree,
|
|
@@ -4183,6 +4194,7 @@ export class ContainerRuntime
|
|
|
4183
4194
|
finalAttempt = false,
|
|
4184
4195
|
summaryLogger,
|
|
4185
4196
|
latestSummaryRefSeqNum,
|
|
4197
|
+
telemetryContext = new TelemetryContext(),
|
|
4186
4198
|
} = options;
|
|
4187
4199
|
// The summary number for this summary. This will be updated during the summary process, so get it now and
|
|
4188
4200
|
// use it for all events logged during this summary.
|
|
@@ -4372,6 +4384,7 @@ export class ContainerRuntime
|
|
|
4372
4384
|
trackState: true,
|
|
4373
4385
|
summaryLogger: summaryNumberLogger,
|
|
4374
4386
|
runGC: this.garbageCollector.shouldRunGC,
|
|
4387
|
+
telemetryContext,
|
|
4375
4388
|
});
|
|
4376
4389
|
} catch (error) {
|
|
4377
4390
|
return {
|
package/src/dataStoreContext.ts
CHANGED
|
@@ -1003,7 +1003,7 @@ export abstract class FluidDataStoreContext
|
|
|
1003
1003
|
validateDatastoreCompatibility(
|
|
1004
1004
|
maybeDataStoreCompatDetails.ILayerCompatDetails,
|
|
1005
1005
|
this.dispose.bind(this),
|
|
1006
|
-
this.mc
|
|
1006
|
+
this.mc,
|
|
1007
1007
|
);
|
|
1008
1008
|
|
|
1009
1009
|
// And now mark the runtime active
|
|
@@ -1096,7 +1096,7 @@ export abstract class FluidDataStoreContext
|
|
|
1096
1096
|
public reSubmit(
|
|
1097
1097
|
message: FluidDataStoreMessage,
|
|
1098
1098
|
localOpMetadata: unknown,
|
|
1099
|
-
squash
|
|
1099
|
+
squash: boolean,
|
|
1100
1100
|
): void {
|
|
1101
1101
|
assert(!!this.channel, 0x14b /* "Channel must exist when resubmitting ops" */);
|
|
1102
1102
|
this.channel.reSubmit(message.type, message.content, localOpMetadata, squash);
|
package/src/packageVersion.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
generation,
|
|
8
|
+
type ILayerCompatDetails,
|
|
9
|
+
type ILayerCompatSupportRequirements,
|
|
9
10
|
} from "@fluid-internal/client-utils";
|
|
10
11
|
import type { ICriticalContainerError } from "@fluidframework/container-definitions";
|
|
11
12
|
import {
|
|
@@ -14,11 +15,17 @@ import {
|
|
|
14
15
|
} from "@fluidframework/runtime-definitions/internal";
|
|
15
16
|
import {
|
|
16
17
|
validateLayerCompatibility,
|
|
17
|
-
type
|
|
18
|
+
type MonitoringContext,
|
|
18
19
|
} from "@fluidframework/telemetry-utils/internal";
|
|
19
20
|
|
|
20
21
|
import { pkgVersion } from "./packageVersion.js";
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The config key to disable strict loader layer compatibility check.
|
|
25
|
+
*/
|
|
26
|
+
export const disableStrictLoaderLayerCompatibilityCheckKey =
|
|
27
|
+
"Fluid.ContainerRuntime.DisableStrictLoaderLayerCompatibilityCheck";
|
|
28
|
+
|
|
22
29
|
/**
|
|
23
30
|
* The core compatibility details of the Runtime layer that is the same across all layer boundaries.
|
|
24
31
|
* @internal
|
|
@@ -31,7 +38,7 @@ export const runtimeCoreCompatDetails = {
|
|
|
31
38
|
/**
|
|
32
39
|
* The current generation of the Runtime layer.
|
|
33
40
|
*/
|
|
34
|
-
generation
|
|
41
|
+
generation,
|
|
35
42
|
} as const;
|
|
36
43
|
|
|
37
44
|
/**
|
|
@@ -97,8 +104,16 @@ export const dataStoreSupportRequirementsForRuntime: ILayerCompatSupportRequirem
|
|
|
97
104
|
export function validateLoaderCompatibility(
|
|
98
105
|
maybeLoaderCompatDetailsForRuntime: ILayerCompatDetails | undefined,
|
|
99
106
|
disposeFn: (error?: ICriticalContainerError) => void,
|
|
100
|
-
|
|
107
|
+
mc: MonitoringContext,
|
|
101
108
|
): void {
|
|
109
|
+
// By default, use strictCompatibilityCheck here - If the Loader doesn't provide compatibility details,
|
|
110
|
+
// assume it's a very old version and should be considered incompatible,
|
|
111
|
+
// since Loader can drift far from the Runtime causing issues.
|
|
112
|
+
// Can be disabled via config `disableStrictLoaderLayerCompatibilityCheckKey`.
|
|
113
|
+
const disableStrictLoaderLayerCompatibilityCheck = mc.config.getBoolean(
|
|
114
|
+
disableStrictLoaderLayerCompatibilityCheckKey,
|
|
115
|
+
);
|
|
116
|
+
|
|
102
117
|
validateLayerCompatibility(
|
|
103
118
|
"runtime",
|
|
104
119
|
"loader",
|
|
@@ -106,7 +121,8 @@ export function validateLoaderCompatibility(
|
|
|
106
121
|
loaderSupportRequirementsForRuntime,
|
|
107
122
|
maybeLoaderCompatDetailsForRuntime,
|
|
108
123
|
disposeFn,
|
|
109
|
-
|
|
124
|
+
mc,
|
|
125
|
+
disableStrictLoaderLayerCompatibilityCheck !== true /* strictCompatibilityCheck */,
|
|
110
126
|
);
|
|
111
127
|
}
|
|
112
128
|
|
|
@@ -117,7 +133,7 @@ export function validateLoaderCompatibility(
|
|
|
117
133
|
export function validateDatastoreCompatibility(
|
|
118
134
|
maybeDataStoreCompatDetailsForRuntime: ILayerCompatDetails | undefined,
|
|
119
135
|
disposeFn: () => void,
|
|
120
|
-
|
|
136
|
+
mc: MonitoringContext,
|
|
121
137
|
): void {
|
|
122
138
|
validateLayerCompatibility(
|
|
123
139
|
"runtime",
|
|
@@ -126,6 +142,6 @@ export function validateDatastoreCompatibility(
|
|
|
126
142
|
dataStoreSupportRequirementsForRuntime,
|
|
127
143
|
maybeDataStoreCompatDetailsForRuntime,
|
|
128
144
|
disposeFn,
|
|
129
|
-
|
|
145
|
+
mc,
|
|
130
146
|
);
|
|
131
147
|
}
|
|
@@ -91,6 +91,7 @@ export class SummarizerClientElection
|
|
|
91
91
|
lastSummaryAckSeqForClient: this.lastSummaryAckSeqForClient,
|
|
92
92
|
electionSequenceNumber,
|
|
93
93
|
nextElectedClientId: this.clientElection.peekNextElectedClient()?.clientId,
|
|
94
|
+
opsWithoutSummary,
|
|
94
95
|
});
|
|
95
96
|
this.lastReportedSeq = sequenceNumber;
|
|
96
97
|
}
|
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
ISequencedDocumentMessage,
|
|
23
23
|
} from "@fluidframework/driver-definitions/internal";
|
|
24
24
|
import type { ISummaryStats } from "@fluidframework/runtime-definitions/internal";
|
|
25
|
+
import type { TelemetryContext } from "@fluidframework/runtime-utils/internal";
|
|
25
26
|
import type {
|
|
26
27
|
ITelemetryLoggerExt,
|
|
27
28
|
ITelemetryLoggerPropertyBag,
|
|
@@ -167,6 +168,10 @@ export interface ISubmitSummaryOptions extends ISummarizeOptions {
|
|
|
167
168
|
* The sequence number of the latest summary used to validate if summary state is correct before summarizing
|
|
168
169
|
*/
|
|
169
170
|
readonly latestSummaryRefSeqNum: number;
|
|
171
|
+
/**
|
|
172
|
+
* Shared telemetry context for the current summarize attempt.
|
|
173
|
+
*/
|
|
174
|
+
telemetryContext?: TelemetryContext;
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
/**
|
|
@@ -177,11 +177,6 @@ export class RunningSummarizer
|
|
|
177
177
|
private totalSuccessfulAttempts = 0;
|
|
178
178
|
private initialized = false;
|
|
179
179
|
|
|
180
|
-
private readonly runtimeListener: (
|
|
181
|
-
op: ISequencedDocumentMessage,
|
|
182
|
-
runtimeMessage?: boolean,
|
|
183
|
-
) => void;
|
|
184
|
-
|
|
185
180
|
/**
|
|
186
181
|
* The maximum number of summary attempts to do when submit summary fails.
|
|
187
182
|
*/
|
|
@@ -295,12 +290,6 @@ export class RunningSummarizer
|
|
|
295
290
|
this.mc.logger,
|
|
296
291
|
);
|
|
297
292
|
|
|
298
|
-
// Listen to runtime for ops
|
|
299
|
-
this.runtimeListener = (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => {
|
|
300
|
-
this.handleOp(op, runtimeMessage === true);
|
|
301
|
-
};
|
|
302
|
-
this.runtime.on("op", this.runtimeListener);
|
|
303
|
-
|
|
304
293
|
// The max attempts for submit failures can be overridden via a feature flag. This allows us to
|
|
305
294
|
// tweak this as per telemetry data until we arrive at a stable number.
|
|
306
295
|
// If its set to a number higher than `defaultMaxAttemptsForSubmitFailures`, it will be ignored.
|
|
@@ -312,6 +301,8 @@ export class RunningSummarizer
|
|
|
312
301
|
overrideMaxAttempts < defaultMaxAttemptsForSubmitFailures
|
|
313
302
|
? overrideMaxAttempts
|
|
314
303
|
: defaultMaxAttemptsForSubmitFailures;
|
|
304
|
+
|
|
305
|
+
this.setupEventListeners();
|
|
315
306
|
}
|
|
316
307
|
|
|
317
308
|
private async handleSummaryAck(ack: IAckedSummary): Promise<void> {
|
|
@@ -397,7 +388,7 @@ export class RunningSummarizer
|
|
|
397
388
|
}
|
|
398
389
|
|
|
399
390
|
public dispose(): void {
|
|
400
|
-
this.
|
|
391
|
+
this.cleanupEventListeners();
|
|
401
392
|
this.summaryWatcher.dispose();
|
|
402
393
|
this.heuristicRunner?.dispose();
|
|
403
394
|
this.heuristicRunner = undefined;
|
|
@@ -408,6 +399,33 @@ export class RunningSummarizer
|
|
|
408
399
|
this.stopping = true;
|
|
409
400
|
}
|
|
410
401
|
|
|
402
|
+
private readonly eventsCleanup: (() => void)[] = [];
|
|
403
|
+
|
|
404
|
+
private setupEventListeners(): void {
|
|
405
|
+
const runtimeListener: (op: ISequencedDocumentMessage, runtimeMessage?: boolean) => void =
|
|
406
|
+
(op: ISequencedDocumentMessage, runtimeMessage?: boolean) => {
|
|
407
|
+
this.handleOp(op, runtimeMessage === true);
|
|
408
|
+
};
|
|
409
|
+
this.runtime.on("op", runtimeListener);
|
|
410
|
+
this.eventsCleanup.push(() => this.runtime.off("op", runtimeListener));
|
|
411
|
+
|
|
412
|
+
// Forward events
|
|
413
|
+
for (const event of ["summarizeTimeout"] as const) {
|
|
414
|
+
const listener = (...args: unknown[]): void => {
|
|
415
|
+
this.emit(event, ...args);
|
|
416
|
+
};
|
|
417
|
+
this.generator.on(event, listener);
|
|
418
|
+
this.eventsCleanup.push(() => this.generator.off(event, listener));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private cleanupEventListeners(): void {
|
|
423
|
+
for (const cleanup of this.eventsCleanup) {
|
|
424
|
+
cleanup();
|
|
425
|
+
}
|
|
426
|
+
this.eventsCleanup.length = 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
411
429
|
/**
|
|
412
430
|
* RunningSummarizer's logger includes the sequenced index of the current summary on each event.
|
|
413
431
|
* If some other Summarizer code wants that event on their logs they can get it here,
|
|
@@ -655,11 +673,12 @@ export class RunningSummarizer
|
|
|
655
673
|
numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
|
|
656
674
|
isLastSummary,
|
|
657
675
|
});
|
|
658
|
-
|
|
676
|
+
summaryLogger.sendErrorEvent(
|
|
659
677
|
{
|
|
660
678
|
eventName: "SummarizeFailed",
|
|
661
679
|
maxAttempts: 1,
|
|
662
680
|
summaryAttempts: 1,
|
|
681
|
+
isLastSummary,
|
|
663
682
|
},
|
|
664
683
|
result.error,
|
|
665
684
|
);
|
|
@@ -708,7 +727,10 @@ export class RunningSummarizer
|
|
|
708
727
|
this.afterSummaryAction();
|
|
709
728
|
},
|
|
710
729
|
).catch((error) => {
|
|
711
|
-
this.mc.logger.sendErrorEvent(
|
|
730
|
+
this.mc.logger.sendErrorEvent(
|
|
731
|
+
{ eventName: "UnexpectedSummarizeError", summarizeReason: reason },
|
|
732
|
+
error,
|
|
733
|
+
);
|
|
712
734
|
});
|
|
713
735
|
}
|
|
714
736
|
|
|
@@ -718,6 +740,7 @@ export class RunningSummarizer
|
|
|
718
740
|
*/
|
|
719
741
|
private async trySummarizeWithRetries(
|
|
720
742
|
reason: SummarizeReason,
|
|
743
|
+
summarizeOptions: ISummarizeOptions = {},
|
|
721
744
|
): Promise<ISummarizeResults | undefined> {
|
|
722
745
|
// Helper to set summarize options, telemetry properties and call summarize.
|
|
723
746
|
const attemptSummarize = (
|
|
@@ -727,13 +750,13 @@ export class RunningSummarizer
|
|
|
727
750
|
summarizeProps: ISummarizeTelemetryProperties;
|
|
728
751
|
summarizeResult: ISummarizeResults;
|
|
729
752
|
} => {
|
|
730
|
-
const
|
|
731
|
-
fullTree: false,
|
|
753
|
+
const attemptSummarizeOptions: ISummarizeOptions = {
|
|
754
|
+
fullTree: summarizeOptions.fullTree ?? false,
|
|
732
755
|
};
|
|
733
756
|
const summarizeProps: ISummarizeTelemetryProperties = {
|
|
734
757
|
summarizeReason: reason,
|
|
735
758
|
summaryAttempts: attemptNumber,
|
|
736
|
-
...
|
|
759
|
+
...attemptSummarizeOptions,
|
|
737
760
|
finalAttempt,
|
|
738
761
|
};
|
|
739
762
|
const summaryLogger = createChildLogger({
|
|
@@ -742,7 +765,7 @@ export class RunningSummarizer
|
|
|
742
765
|
});
|
|
743
766
|
|
|
744
767
|
const summaryOptions: ISubmitSummaryOptions = {
|
|
745
|
-
...
|
|
768
|
+
...attemptSummarizeOptions,
|
|
746
769
|
summaryLogger,
|
|
747
770
|
cancellationToken: this.cancellationToken,
|
|
748
771
|
finalAttempt,
|
|
@@ -872,6 +895,8 @@ export class RunningSummarizer
|
|
|
872
895
|
eventName: "SummarizeFailed",
|
|
873
896
|
maxAttempts,
|
|
874
897
|
summaryAttempts: currentAttempt,
|
|
898
|
+
summarizeReason: reason,
|
|
899
|
+
isLastSummary: reason === "lastSummary",
|
|
875
900
|
},
|
|
876
901
|
error,
|
|
877
902
|
);
|
|
@@ -893,8 +918,9 @@ export class RunningSummarizer
|
|
|
893
918
|
private async summarizeOnDemandWithRetries(
|
|
894
919
|
reason: SummarizeReason,
|
|
895
920
|
resultsBuilder: SummarizeResultBuilder,
|
|
921
|
+
summarizeOptions: ISummarizeOptions = {},
|
|
896
922
|
): Promise<ISummarizeResults> {
|
|
897
|
-
const results = await this.trySummarizeWithRetries(reason);
|
|
923
|
+
const results = await this.trySummarizeWithRetries(reason, summarizeOptions);
|
|
898
924
|
if (results === undefined) {
|
|
899
925
|
resultsBuilder.fail(
|
|
900
926
|
"Summarization was canceled",
|
|
@@ -932,13 +958,15 @@ export class RunningSummarizer
|
|
|
932
958
|
throw new UsageError("Attempted to run an already-running summarizer on demand");
|
|
933
959
|
}
|
|
934
960
|
|
|
935
|
-
const { reason, ...summarizeOptions } = options;
|
|
936
|
-
if (
|
|
937
|
-
this.summarizeOnDemandWithRetries(
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
)
|
|
961
|
+
const { reason, retryOnFailure, ...summarizeOptions } = options;
|
|
962
|
+
if (retryOnFailure === true) {
|
|
963
|
+
this.summarizeOnDemandWithRetries(
|
|
964
|
+
`onDemand;${reason}`,
|
|
965
|
+
resultsBuilder,
|
|
966
|
+
summarizeOptions,
|
|
967
|
+
).catch((error: IRetriableFailureError) => {
|
|
968
|
+
resultsBuilder.fail("summarize failed", error);
|
|
969
|
+
});
|
|
942
970
|
} else {
|
|
943
971
|
this.trySummarizeOnce(
|
|
944
972
|
{ summarizeReason: `onDemand/${reason}` },
|
|
@@ -301,7 +301,7 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
|
|
|
301
301
|
this.runtime,
|
|
302
302
|
);
|
|
303
303
|
this.runningSummarizer = runningSummarizer;
|
|
304
|
-
this.setupForwardedEvents();
|
|
304
|
+
this.setupForwardedEvents(runningSummarizer);
|
|
305
305
|
this.starting = false;
|
|
306
306
|
return runningSummarizer;
|
|
307
307
|
}
|
|
@@ -399,24 +399,26 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
|
|
|
399
399
|
this._heuristicData?.recordAttempt(summaryRefSeqNum);
|
|
400
400
|
}
|
|
401
401
|
|
|
402
|
-
private readonly
|
|
402
|
+
private readonly forwardedEventsCleanup: (() => void)[] = [];
|
|
403
403
|
|
|
404
|
-
private setupForwardedEvents(): void {
|
|
405
|
-
for (const event of [
|
|
404
|
+
private setupForwardedEvents(runningSummarizer: RunningSummarizer): void {
|
|
405
|
+
for (const event of [
|
|
406
|
+
"summarize",
|
|
407
|
+
"summarizeAllAttemptsFailed",
|
|
408
|
+
"summarizeTimeout",
|
|
409
|
+
] as const) {
|
|
406
410
|
const listener = (...args: unknown[]): void => {
|
|
407
411
|
this.emit(event, ...args);
|
|
408
412
|
};
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
this.runningSummarizer?.on(event as any, listener);
|
|
412
|
-
this.forwardedEvents.set(event, listener);
|
|
413
|
+
runningSummarizer.on(event, listener);
|
|
414
|
+
this.forwardedEventsCleanup.push(() => runningSummarizer.off(event, listener));
|
|
413
415
|
}
|
|
414
416
|
}
|
|
415
417
|
|
|
416
418
|
private cleanupForwardedEvents(): void {
|
|
417
|
-
for (const
|
|
418
|
-
|
|
419
|
+
for (const cleanup of this.forwardedEventsCleanup) {
|
|
420
|
+
cleanup();
|
|
419
421
|
}
|
|
420
|
-
this.
|
|
422
|
+
this.forwardedEventsCleanup.length = 0;
|
|
421
423
|
}
|
|
422
424
|
}
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { TypedEventEmitter } from "@fluid-internal/client-utils";
|
|
7
|
+
import type { ISummarizerEvents } from "@fluidframework/container-runtime-definitions/internal";
|
|
6
8
|
import { assert, type IPromiseTimer, Timer } from "@fluidframework/core-utils/internal";
|
|
7
9
|
import { DriverErrorTypes, MessageType } from "@fluidframework/driver-definitions/internal";
|
|
8
10
|
import { getRetryDelaySecondsFromError } from "@fluidframework/driver-utils/internal";
|
|
11
|
+
import { TelemetryContext } from "@fluidframework/runtime-utils/internal";
|
|
9
12
|
import {
|
|
10
13
|
isFluidError,
|
|
11
14
|
type ITelemetryLoggerExt,
|
|
@@ -40,8 +43,9 @@ const maxSummarizeTimeoutCount = 5; // Double and resend 5 times
|
|
|
40
43
|
/**
|
|
41
44
|
* This class generates and tracks a summary attempt.
|
|
42
45
|
*/
|
|
43
|
-
export class SummaryGenerator {
|
|
46
|
+
export class SummaryGenerator extends TypedEventEmitter<ISummarizerEvents> {
|
|
44
47
|
private readonly summarizeTimer: Timer;
|
|
48
|
+
private activeTelemetryContext?: TelemetryContext;
|
|
45
49
|
constructor(
|
|
46
50
|
private readonly pendingAckTimer: IPromiseTimer,
|
|
47
51
|
private readonly heuristicData: ISummarizeHeuristicData,
|
|
@@ -55,6 +59,7 @@ export class SummaryGenerator {
|
|
|
55
59
|
private readonly summaryWatcher: Pick<IClientSummaryWatcher, "watchSummary">,
|
|
56
60
|
private readonly logger: ITelemetryLoggerExt,
|
|
57
61
|
) {
|
|
62
|
+
super();
|
|
58
63
|
this.summarizeTimer = new Timer(maxSummarizeTimeoutTime, () =>
|
|
59
64
|
this.summarizeTimerHandler(maxSummarizeTimeoutTime, 1),
|
|
60
65
|
);
|
|
@@ -85,7 +90,16 @@ export class SummaryGenerator {
|
|
|
85
90
|
submitSummaryOptions: ISubmitSummaryOptions,
|
|
86
91
|
resultsBuilder: SummarizeResultBuilder,
|
|
87
92
|
): Promise<void> {
|
|
88
|
-
const {
|
|
93
|
+
const {
|
|
94
|
+
summaryLogger,
|
|
95
|
+
cancellationToken,
|
|
96
|
+
telemetryContext = new TelemetryContext(),
|
|
97
|
+
...summarizeOptions
|
|
98
|
+
} = submitSummaryOptions;
|
|
99
|
+
|
|
100
|
+
telemetryContext.setCurrentSummarizeStep("submitSummary");
|
|
101
|
+
const submitOptions = { ...submitSummaryOptions, telemetryContext };
|
|
102
|
+
this.activeTelemetryContext = telemetryContext;
|
|
89
103
|
|
|
90
104
|
// Note: timeSinceLastAttempt and timeSinceLastSummary for the
|
|
91
105
|
// first summary are basically the time since the summarizer was loaded.
|
|
@@ -97,6 +111,8 @@ export class SummaryGenerator {
|
|
|
97
111
|
fullTree: summarizeOptions.fullTree ?? false,
|
|
98
112
|
timeSinceLastAttempt,
|
|
99
113
|
timeSinceLastSummary,
|
|
114
|
+
nonRuntimeOpsSinceLastSummary: this.heuristicData.numNonRuntimeOps,
|
|
115
|
+
runtimeOpsSinceLastSummary: this.heuristicData.numRuntimeOps,
|
|
100
116
|
};
|
|
101
117
|
|
|
102
118
|
const summarizeEvent = PerformanceEvent.start(
|
|
@@ -148,10 +164,12 @@ export class SummaryGenerator {
|
|
|
148
164
|
// Wait to generate and send summary
|
|
149
165
|
this.summarizeTimer.start();
|
|
150
166
|
try {
|
|
167
|
+
telemetryContext.setCurrentSummarizeStep("generateSummary");
|
|
151
168
|
// Need to save refSeqNum before we record new attempt (happens as part of submitSummaryCallback)
|
|
152
169
|
const lastAttemptRefSeqNum = this.heuristicData.lastAttempt.refSequenceNumber;
|
|
153
170
|
|
|
154
|
-
summaryData = await this.submitSummaryCallback(
|
|
171
|
+
summaryData = await this.submitSummaryCallback(submitOptions);
|
|
172
|
+
telemetryContext.setCurrentSummarizeStep("submitSummaryOp");
|
|
155
173
|
|
|
156
174
|
// Cumulatively add telemetry properties based on how far generateSummary went.
|
|
157
175
|
const referenceSequenceNumber = summaryData.referenceSequenceNumber;
|
|
@@ -189,6 +207,7 @@ export class SummaryGenerator {
|
|
|
189
207
|
* exceed the number of ops since last summary + number of data store whose reference state changed.
|
|
190
208
|
*/
|
|
191
209
|
if (submitSummaryOptions.fullTree !== true) {
|
|
210
|
+
telemetryContext.setCurrentSummarizeStep("watchSummary");
|
|
192
211
|
const { summarizedDataStoreCount, gcStateUpdatedDataStoreCount = 0 } =
|
|
193
212
|
summaryData.summaryStats;
|
|
194
213
|
if (
|
|
@@ -227,6 +246,8 @@ export class SummaryGenerator {
|
|
|
227
246
|
this.summarizeTimer.clear();
|
|
228
247
|
}
|
|
229
248
|
|
|
249
|
+
telemetryContext.setCurrentSummarizeStep("waitForSummaryAck");
|
|
250
|
+
|
|
230
251
|
try {
|
|
231
252
|
const pendingTimeoutP = this.pendingAckTimer.start();
|
|
232
253
|
const summary = this.summaryWatcher.watchSummary(summaryData.clientSequenceNumber);
|
|
@@ -300,6 +321,7 @@ export class SummaryGenerator {
|
|
|
300
321
|
...summarizeTelemetryProps,
|
|
301
322
|
};
|
|
302
323
|
if (ackNackOp.type === MessageType.SummaryAck) {
|
|
324
|
+
telemetryContext.setCurrentSummarizeStep("ackReceived");
|
|
303
325
|
this.heuristicData.markLastAttemptAsSuccessful();
|
|
304
326
|
this.successfulSummaryCallback();
|
|
305
327
|
summarizeEvent.end({
|
|
@@ -324,6 +346,7 @@ export class SummaryGenerator {
|
|
|
324
346
|
} else {
|
|
325
347
|
// Check for retryDelay in summaryNack response.
|
|
326
348
|
assert(ackNackOp.type === MessageType.SummaryNack, 0x274 /* "type check" */);
|
|
349
|
+
telemetryContext.setCurrentSummarizeStep("nackReceived");
|
|
327
350
|
const summaryNack = ackNackOp.contents;
|
|
328
351
|
const errorMessage = summaryNack?.message;
|
|
329
352
|
const retryAfterSeconds = summaryNack?.retryAfter;
|
|
@@ -350,6 +373,7 @@ export class SummaryGenerator {
|
|
|
350
373
|
}
|
|
351
374
|
} finally {
|
|
352
375
|
this.pendingAckTimer.clear();
|
|
376
|
+
this.activeTelemetryContext = undefined;
|
|
353
377
|
}
|
|
354
378
|
}
|
|
355
379
|
|
|
@@ -390,8 +414,6 @@ export class SummaryGenerator {
|
|
|
390
414
|
clientSequenceNumber: summaryData.clientSequenceNumber,
|
|
391
415
|
hasMissingOpData: this.heuristicData.hasMissingOpData,
|
|
392
416
|
opsSizesSinceLastSummary: this.heuristicData.totalOpsSize,
|
|
393
|
-
nonRuntimeOpsSinceLastSummary: this.heuristicData.numNonRuntimeOps,
|
|
394
|
-
runtimeOpsSinceLastSummary: this.heuristicData.numRuntimeOps,
|
|
395
417
|
};
|
|
396
418
|
}
|
|
397
419
|
|
|
@@ -408,6 +430,13 @@ export class SummaryGenerator {
|
|
|
408
430
|
eventName: "SummarizeTimeout",
|
|
409
431
|
timeoutTime: time,
|
|
410
432
|
timeoutCount: count,
|
|
433
|
+
currentSummarizeStep: this.activeTelemetryContext?.getCurrentSummarizeStep(),
|
|
434
|
+
});
|
|
435
|
+
this.emit("summarizeTimeout", {
|
|
436
|
+
timeoutCount: count,
|
|
437
|
+
currentSummarizeStep: this.activeTelemetryContext?.getCurrentSummarizeStep(),
|
|
438
|
+
numUnsummarizedRuntimeOps: this.heuristicData.numRuntimeOps,
|
|
439
|
+
numUnsummarizedNonRuntimeOps: this.heuristicData.numNonRuntimeOps,
|
|
411
440
|
});
|
|
412
441
|
if (count < maxSummarizeTimeoutCount) {
|
|
413
442
|
// Double and start a new timer
|
|
@@ -420,5 +449,6 @@ export class SummaryGenerator {
|
|
|
420
449
|
|
|
421
450
|
public dispose(): void {
|
|
422
451
|
this.summarizeTimer.clear();
|
|
452
|
+
this.activeTelemetryContext = undefined;
|
|
423
453
|
}
|
|
424
454
|
}
|