From db836130c0eb108fb567890d1f6a2e5b57413c81 Mon Sep 17 00:00:00 2001 From: Mario Jauregui Gomez Date: Mon, 1 Jun 2026 10:18:32 -0700 Subject: [PATCH] feat: set branch in same state as it used to be --- .../drivers/odsp-driver/src/fetchSnapshot.ts | 472 +++++++++--------- .../src/odspDelayLoadedDeltaStream.ts | 15 +- .../src/odspDeltaStorageService.ts | 3 + .../src/odspDocumentDeltaConnection.ts | 13 +- .../odsp-driver/src/odspDocumentService.ts | 7 +- .../src/odspDocumentStorageManager.ts | 6 + packages/drivers/odsp-driver/src/odspUtils.ts | 3 + packages/drivers/odsp-driver/src/vroom.ts | 4 + .../loader/container-loader/src/container.ts | 6 + .../loader/container-loader/src/loader.ts | 20 +- .../src/channelCollection.ts | 23 +- .../container-runtime/src/deltaScheduler.ts | 27 +- .../src/gc/garbageCollection.ts | 4 + .../summary/summarizerNode/summarizerNode.ts | 3 + 14 files changed, 340 insertions(+), 266 deletions(-) diff --git a/packages/drivers/odsp-driver/src/fetchSnapshot.ts b/packages/drivers/odsp-driver/src/fetchSnapshot.ts index ce1cc9cb65e5..a284a9042830 100644 --- a/packages/drivers/odsp-driver/src/fetchSnapshot.ts +++ b/packages/drivers/odsp-driver/src/fetchSnapshot.ts @@ -4,6 +4,7 @@ */ import { fromUtf8ToBase64 } from "@fluid-internal/client-utils"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import { assert } from "@fluidframework/core-utils/internal"; import { getW3CData } from "@fluidframework/driver-base/internal"; import { @@ -348,264 +349,271 @@ async function fetchLatestSnapshotCore( } } // This event measures only successful cases of getLatest call (no tokens, no retries). - return PerformanceEvent.timedExecAsync(logger, perfEvent, async (event) => { - let controller: AbortController | undefined; - let fetchTimeout: ReturnType | undefined; - if (snapshotOptions?.timeout !== undefined) { - controller = new AbortController(); - fetchTimeout = setTimeout(() => controller!.abort(), snapshotOptions.timeout); - } - - const [response, fetchTime] = await measureP(async () => - snapshotDownloader( - odspResolvedUrl, - getAuthHeader, - tokenFetchOptions, - loadingGroupIds, - snapshotOptions, - controller, - ), - ).finally(() => { - // Clear the fetchTimeout once the response is fetched. - if (fetchTimeout !== undefined) { - clearTimeout(fetchTimeout); - fetchTimeout = undefined; + return PerformanceEvent.timedExecAsync( + logger, + perfEvent, + async (event) => { + let controller: AbortController | undefined; + let fetchTimeout: ReturnType | undefined; + if (snapshotOptions?.timeout !== undefined) { + controller = new AbortController(); + fetchTimeout = setTimeout(() => controller!.abort(), snapshotOptions.timeout); } - }); - const odspResponse = response.odspResponse; - const contentType = odspResponse.headers.get("content-type"); + const [response, fetchTime] = await measureP(async () => + snapshotDownloader( + odspResolvedUrl, + getAuthHeader, + tokenFetchOptions, + loadingGroupIds, + snapshotOptions, + controller, + ), + ).finally(() => { + // Clear the fetchTimeout once the response is fetched. + if (fetchTimeout !== undefined) { + clearTimeout(fetchTimeout); + fetchTimeout = undefined; + } + }); - const propsToLog: DriverErrorTelemetryProps = { - ...odspResponse.propsToLog, - contentType, - accept: response.requestHeaders.accept, - driverVersion: pkgVersion, - }; + const odspResponse = response.odspResponse; + const contentType = odspResponse.headers.get("content-type"); - let parsedSnapshotContents: IOdspResponse | undefined; - let contentTypeToRead: string | undefined; - if (contentType?.includes("application/ms-fluid")) { - contentTypeToRead = "application/ms-fluid"; - } else if (contentType?.includes("application/json")) { - contentTypeToRead = "application/json"; - } + const propsToLog: DriverErrorTelemetryProps = { + ...odspResponse.propsToLog, + contentType, + accept: response.requestHeaders.accept, + driverVersion: pkgVersion, + }; - let parseTime: number; - let receiveContentTime: number; - try { - switch (contentTypeToRead) { - case "application/json": { - let text: string; - [text, receiveContentTime] = await measureP(async () => - odspResponse.content - .text() - .then((res) => { - if (res.length === 0) { + let parsedSnapshotContents: IOdspResponse | undefined; + let contentTypeToRead: string | undefined; + if (contentType?.includes("application/ms-fluid")) { + contentTypeToRead = "application/ms-fluid"; + } else if (contentType?.includes("application/json")) { + contentTypeToRead = "application/json"; + } + + let parseTime: number; + let receiveContentTime: number; + try { + switch (contentTypeToRead) { + case "application/json": { + let text: string; + [text, receiveContentTime] = await measureP(async () => + odspResponse.content + .text() + .then((res) => { + if (res.length === 0) { + throwOdspNetworkError( + "Response from browser is empty", + fetchIncorrectResponse, + odspResponse.content, // response + undefined, // response text + propsToLog, + ); + } + return res; + }) + .catch((error) => + // Parsing can fail and message could contain full request URI, including + // tokens, etc. So do not log error object itself. throwOdspNetworkError( - "Response from browser is empty", + "Error while parsing fetch response", fetchIncorrectResponse, odspResponse.content, // response undefined, // response text propsToLog, - ); - } - return res; - }) - .catch((error) => - // Parsing can fail and message could contain full request URI, including - // tokens, etc. So do not log error object itself. - throwOdspNetworkError( - "Error while parsing fetch response", - fetchIncorrectResponse, - odspResponse.content, // response - undefined, // response text - propsToLog, + ), ), - ), - ); - propsToLog.bodySize = text.length; - let content: IOdspSnapshot; - [content, parseTime] = measure(() => JSON.parse(text) as IOdspSnapshot); - validateBlobsAndTrees(content); - const snapshotContents: ISnapshot = - convertOdspSnapshotToSnapshotTreeAndBlobs(content); - parsedSnapshotContents = { - ...odspResponse, - content: { - ...snapshotContents, - telemetryProps: {}, - }, - }; - break; - } - case "application/ms-fluid": { - let content: ArrayBuffer; - [content, receiveContentTime] = await measureP(async () => - odspResponse.content - .arrayBuffer() - .then((res) => { - if (res.byteLength === 0) { + ); + propsToLog.bodySize = text.length; + let content: IOdspSnapshot; + [content, parseTime] = measure(() => JSON.parse(text) as IOdspSnapshot); + validateBlobsAndTrees(content); + const snapshotContents: ISnapshot = + convertOdspSnapshotToSnapshotTreeAndBlobs(content); + parsedSnapshotContents = { + ...odspResponse, + content: { + ...snapshotContents, + telemetryProps: {}, + }, + }; + break; + } + case "application/ms-fluid": { + let content: ArrayBuffer; + [content, receiveContentTime] = await measureP(async () => + odspResponse.content + .arrayBuffer() + .then((res) => { + if (res.byteLength === 0) { + throwOdspNetworkError( + "Response from browser is empty", + fetchIncorrectResponse, + odspResponse.content, // response + undefined, // response text + propsToLog, + ); + } + return res; + }) + .catch((error) => + // Parsing can fail and message could contain full request URI, including + // tokens, etc. So do not log error object itself. throwOdspNetworkError( - "Response from browser is empty", + "Error while parsing fetch response", fetchIncorrectResponse, odspResponse.content, // response undefined, // response text propsToLog, - ); - } - return res; - }) - .catch((error) => - // Parsing can fail and message could contain full request URI, including - // tokens, etc. So do not log error object itself. - throwOdspNetworkError( - "Error while parsing fetch response", - fetchIncorrectResponse, - odspResponse.content, // response - undefined, // response text - propsToLog, + ), ), - ), - ); - propsToLog.bodySize = content.byteLength; - let snapshotContents: ISnapshotContentsWithProps; - [snapshotContents, parseTime] = measure(() => - parseCompactSnapshotResponse(new Uint8Array(content), logger), - ); - if ( - snapshotContents.snapshotTree.trees === undefined || - snapshotContents.snapshotTree.blobs === undefined - ) { + ); + propsToLog.bodySize = content.byteLength; + let snapshotContents: ISnapshotContentsWithProps; + [snapshotContents, parseTime] = measure(() => + parseCompactSnapshotResponse(new Uint8Array(content), logger), + ); + if ( + snapshotContents.snapshotTree.trees === undefined || + snapshotContents.snapshotTree.blobs === undefined + ) { + throw new NonRetryableError( + "Returned odsp snapshot is malformed. No trees or blobs!", + OdspErrorTypes.incorrectServerResponse, + propsToLog, + ); + } + + const props = snapshotContents.telemetryProps; + const slowTreeParseCodePaths = props.slowTreeStructureCount ?? 0; + const slowBlobParseCodePaths = props.slowBlobStructureCount ?? 0; + const treeStructureCountWithGroupId = props.treeStructureCountWithGroupId ?? 0; + // As trees with groupId go through normal parsing, so exclude them. + if ( + slowTreeParseCodePaths - treeStructureCountWithGroupId > 10 || + slowBlobParseCodePaths > 10 + ) { + logger.sendErrorEvent({ + eventName: "SlowSnapshotParseCodePaths", + slowTreeStructureCount: slowTreeParseCodePaths, + slowBlobStructureCount: slowBlobParseCodePaths, + treeStructureCountWithGroupId, + }); + } + parsedSnapshotContents = { ...odspResponse, content: snapshotContents }; + break; + } + default: { throw new NonRetryableError( - "Returned odsp snapshot is malformed. No trees or blobs!", + "Unknown snapshot content type", OdspErrorTypes.incorrectServerResponse, propsToLog, ); } - - const props = snapshotContents.telemetryProps; - const slowTreeParseCodePaths = props.slowTreeStructureCount ?? 0; - const slowBlobParseCodePaths = props.slowBlobStructureCount ?? 0; - const treeStructureCountWithGroupId = props.treeStructureCountWithGroupId ?? 0; - // As trees with groupId go through normal parsing, so exclude them. - if ( - slowTreeParseCodePaths - treeStructureCountWithGroupId > 10 || - slowBlobParseCodePaths > 10 - ) { - logger.sendErrorEvent({ - eventName: "SlowSnapshotParseCodePaths", - slowTreeStructureCount: slowTreeParseCodePaths, - slowBlobStructureCount: slowBlobParseCodePaths, - treeStructureCountWithGroupId, - }); - } - parsedSnapshotContents = { ...odspResponse, content: snapshotContents }; - break; } - default: { - throw new NonRetryableError( - "Unknown snapshot content type", - OdspErrorTypes.incorrectServerResponse, - propsToLog, - ); + } catch (error) { + if (isFluidError(error)) { + error.addTelemetryProperties(propsToLog); + throw error; } + const enhancedError = wrapError( + error, + (errorMessage) => + new NonRetryableError( + `Error parsing snapshot response: ${errorMessage}`, + OdspErrorTypes.genericError, + propsToLog, + ), + ); + throw enhancedError; } - } catch (error) { - if (isFluidError(error)) { - error.addTelemetryProperties(propsToLog); - throw error; - } - const enhancedError = wrapError( - error, - (errorMessage) => - new NonRetryableError( - `Error parsing snapshot response: ${errorMessage}`, - OdspErrorTypes.genericError, - propsToLog, - ), - ); - throw enhancedError; - } - - assert(parsedSnapshotContents !== undefined, 0x312 /* snapshot should be parsed */); - const snapshot = parsedSnapshotContents.content; - // There are some scenarios in ODSP where we cannot cache, trees/latest will explicitly tell us when we - // cannot cache using an HTTP response header. Only cache snapshot if it is not for a loading group. - const canCache = - odspResponse.headers.get("disablebrowsercachingofusercontent") !== "true" && - !fetchSnapshotForLoadingGroup; - const sequenceNumber: number = snapshot.sequenceNumber ?? 0; - const seqNumberFromOps = - snapshot.ops && snapshot.ops.length > 0 - ? snapshot.ops[0].sequenceNumber - 1 - : undefined; + assert(parsedSnapshotContents !== undefined, 0x312 /* snapshot should be parsed */); + const snapshot = parsedSnapshotContents.content; + + // There are some scenarios in ODSP where we cannot cache, trees/latest will explicitly tell us when we + // cannot cache using an HTTP response header. Only cache snapshot if it is not for a loading group. + const canCache = + odspResponse.headers.get("disablebrowsercachingofusercontent") !== "true" && + !fetchSnapshotForLoadingGroup; + const sequenceNumber: number = snapshot.sequenceNumber ?? 0; + const seqNumberFromOps = + snapshot.ops && snapshot.ops.length > 0 + ? snapshot.ops[0].sequenceNumber - 1 + : undefined; + + if ( + !Number.isInteger(sequenceNumber) || + (seqNumberFromOps !== undefined && seqNumberFromOps !== sequenceNumber) + ) { + logger.sendErrorEvent({ + eventName: "fetchSnapshotError", + sequenceNumber, + seqNumberFromOps, + }); + snapshot.sequenceNumber = undefined; + } else if (canCache) { + const fluidEpoch = odspResponse.headers.get("x-fluid-epoch"); + assert(fluidEpoch !== undefined, 0x1e6 /* "Epoch should be present in response" */); + const value: ISnapshotCachedEntry2 = { + ...snapshot, + cacheEntryTime: Date.now(), + }; + const valueWithEpoch: IVersionedValueWithEpoch = { + value, + fluidEpoch, + version: persistedCacheValueVersion, + }; + // eslint-disable-next-line @typescript-eslint/no-floating-promises + putInCache(valueWithEpoch); + } - if ( - !Number.isInteger(sequenceNumber) || - (seqNumberFromOps !== undefined && seqNumberFromOps !== sequenceNumber) - ) { - logger.sendErrorEvent({ - eventName: "fetchSnapshotError", + event.end({ + // trees, leafTrees, blobNodes, encodedBlobsSize, + // blobNodes - blobs tells us (roughly) how many blobs are deduped by service. + ...getTreeStats(snapshot), + blobs: snapshot.blobContents?.size ?? 0, sequenceNumber, - seqNumberFromOps, + ops: snapshot.ops?.length ?? 0, + fetchSnapshotForLoadingGroup, + useLegacyFlowWithoutGroups: + useLegacyFlowWithoutGroupsForSnapshotFetch(loadingGroupIds), + userOps: snapshot.ops?.filter((op) => isRuntimeMessage(op)).length ?? 0, + fileVersion, + // Measures time to make fetch call. Should be similar to + // fetchStartToResponseEndTime - receiveContentTime, i.e. it looks like it's time till first byte / + // end of response headers + fetchTime, + // time it takes client to parse payload. Same payload as in "SnapshotParse" event, here for + // easier analyzes. + parseTime, + // Time it takes to receive content (text of buffer) from Response object. + // This time likely is very closely correlated with networkTime, i.e. time it takes to receive + // actual content (starting measuring from first bite / end of response header) + receiveContentTime, + ...getW3CData(response.requestUrl, "fetch"), + // Sharing link telemetry regarding sharing link redeem status and performance. Ex: FRL; dur=100, + // Azure Fluid Relay service; desc=S, FRP; desc=False. Here, FRL is the duration taken for redeem, + // Azure Fluid Relay service is the redeem status (S means success), and FRP is a flag to indicate + // if the permission has changed. + sltelemetry: odspResponse.headers.get("x-fluid-sltelemetry"), + // All other props + ...propsToLog, + // Various perf counters and measures collected by binary parsing code: + // slowTreeStructureCount, slowBlobStructureCount, durationStructure, durationStrings, + // durationSnapshotTree, durationBlobs, etc. + ...parsedSnapshotContents.content.telemetryProps, }); - snapshot.sequenceNumber = undefined; - } else if (canCache) { - const fluidEpoch = odspResponse.headers.get("x-fluid-epoch"); - assert(fluidEpoch !== undefined, 0x1e6 /* "Epoch should be present in response" */); - const value: ISnapshotCachedEntry2 = { - ...snapshot, - cacheEntryTime: Date.now(), - }; - const valueWithEpoch: IVersionedValueWithEpoch = { - value, - fluidEpoch, - version: persistedCacheValueVersion, - }; - // eslint-disable-next-line @typescript-eslint/no-floating-promises - putInCache(valueWithEpoch); - } - - event.end({ - // trees, leafTrees, blobNodes, encodedBlobsSize, - // blobNodes - blobs tells us (roughly) how many blobs are deduped by service. - ...getTreeStats(snapshot), - blobs: snapshot.blobContents?.size ?? 0, - sequenceNumber, - ops: snapshot.ops?.length ?? 0, - fetchSnapshotForLoadingGroup, - useLegacyFlowWithoutGroups: - useLegacyFlowWithoutGroupsForSnapshotFetch(loadingGroupIds), - userOps: snapshot.ops?.filter((op) => isRuntimeMessage(op)).length ?? 0, - fileVersion, - // Measures time to make fetch call. Should be similar to - // fetchStartToResponseEndTime - receiveContentTime, i.e. it looks like it's time till first byte / - // end of response headers - fetchTime, - // time it takes client to parse payload. Same payload as in "SnapshotParse" event, here for - // easier analyzes. - parseTime, - // Time it takes to receive content (text of buffer) from Response object. - // This time likely is very closely correlated with networkTime, i.e. time it takes to receive - // actual content (starting measuring from first bite / end of response header) - receiveContentTime, - ...getW3CData(response.requestUrl, "fetch"), - // Sharing link telemetry regarding sharing link redeem status and performance. Ex: FRL; dur=100, - // Azure Fluid Relay service; desc=S, FRP; desc=False. Here, FRL is the duration taken for redeem, - // Azure Fluid Relay service is the redeem status (S means success), and FRP is a flag to indicate - // if the permission has changed. - sltelemetry: odspResponse.headers.get("x-fluid-sltelemetry"), - // All other props - ...propsToLog, - // Various perf counters and measures collected by binary parsing code: - // slowTreeStructureCount, slowBlobStructureCount, durationStructure, durationStrings, - // durationSnapshotTree, durationBlobs, etc. - ...parsedSnapshotContents.content.telemetryProps, - }); - return snapshot; - }).catch((error) => { + return snapshot; + }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, + ).catch((error) => { // We hit these errors in stress tests, under load // It's useful to try one more time in such case. if ( @@ -791,7 +799,11 @@ export const downloadSnapshot = mockify( "downloadSnapshot", ); assert(authHeader !== null, 0x1e5 /* "Storage token should not be null" */); - logger?.sendTelemetryEvent({ eventName: "SnapshotAuthHeaderObtained" }); + logger?.sendTelemetryEvent( + { eventName: "SnapshotAuthHeaderObtained" }, + undefined, // error + LogLevel.info, + ); const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, authHeader, header); const fetchOptions = { body, diff --git a/packages/drivers/odsp-driver/src/odspDelayLoadedDeltaStream.ts b/packages/drivers/odsp-driver/src/odspDelayLoadedDeltaStream.ts index e07442a2c8e8..2a24f8e7a561 100644 --- a/packages/drivers/odsp-driver/src/odspDelayLoadedDeltaStream.ts +++ b/packages/drivers/odsp-driver/src/odspDelayLoadedDeltaStream.ts @@ -5,6 +5,7 @@ import { performanceNow } from "@fluid-internal/client-utils"; import type { ISignalEnvelope } from "@fluidframework/core-interfaces/internal"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import { assert } from "@fluidframework/core-utils/internal"; import type { IClient } from "@fluidframework/driver-definitions"; import type { @@ -158,12 +159,16 @@ export class OdspDelayLoadedDeltaStream { // Log telemetry for join session attempt if (this.firstConnectionAttempt) { - this.mc.logger.sendTelemetryEvent({ - eventName: "FirstJoinSessionAttemptDetails", - details: { - requestWebsocketToken: requestWebsocketTokenFromJoinSession, + this.mc.logger.sendTelemetryEvent( + { + eventName: "FirstJoinSessionAttemptDetails", + details: { + requestWebsocketToken: requestWebsocketTokenFromJoinSession, + }, }, - }); + undefined, // error + LogLevel.info, + ); } const joinSessionPromise = this.joinSession( diff --git a/packages/drivers/odsp-driver/src/odspDeltaStorageService.ts b/packages/drivers/odsp-driver/src/odspDeltaStorageService.ts index e64ec5fe632f..ad4d529cb4a4 100644 --- a/packages/drivers/odsp-driver/src/odspDeltaStorageService.ts +++ b/packages/drivers/odsp-driver/src/odspDeltaStorageService.ts @@ -122,6 +122,9 @@ export class OdspDeltaStorageService { // This may change in the future, if so, we need to adjust and receive "end" value from server in such case. return { messages, partialResult: false }; }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); }); } diff --git a/packages/drivers/odsp-driver/src/odspDocumentDeltaConnection.ts b/packages/drivers/odsp-driver/src/odspDocumentDeltaConnection.ts index 4718d4fdad2b..2e6662448594 100644 --- a/packages/drivers/odsp-driver/src/odspDocumentDeltaConnection.ts +++ b/packages/drivers/odsp-driver/src/odspDocumentDeltaConnection.ts @@ -5,6 +5,7 @@ import { TypedEventEmitter, performanceNow } from "@fluid-internal/client-utils"; import type { IEvent } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces"; import { assert, Deferred } from "@fluidframework/core-utils/internal"; import { DocumentDeltaConnection } from "@fluidframework/driver-base/internal"; import type { IClient } from "@fluidframework/driver-definitions"; @@ -651,10 +652,14 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection { .catch((error) => this.connectionInitializeDeferredP?.reject(error)); await this.connectionInitializeDeferredP.promise.finally(() => { - this.logger.sendTelemetryEvent({ - eventName: "ConnectionAttemptInfo", - ...this.getConnectionDetailsProps(), - }); + this.logger.sendTelemetryEvent( + { + eventName: "ConnectionAttemptInfo", + ...this.getConnectionDetailsProps(), + }, + undefined, // error + LogLevel.info, + ); }); } diff --git a/packages/drivers/odsp-driver/src/odspDocumentService.ts b/packages/drivers/odsp-driver/src/odspDocumentService.ts index 257492e8f98b..d2b3949ce6a8 100644 --- a/packages/drivers/odsp-driver/src/odspDocumentService.ts +++ b/packages/drivers/odsp-driver/src/odspDocumentService.ts @@ -4,6 +4,7 @@ */ import { TypedEventEmitter } from "@fluid-internal/client-utils"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import { assert } from "@fluidframework/core-utils/internal"; import type { IClient } from "@fluidframework/driver-definitions"; import type { @@ -277,7 +278,11 @@ export class OdspDocumentService /* webpackChunkName: "socketModule" */ "./odspDelayLoadedDeltaStream.js" ) .then((m) => { - this.mc.logger.sendTelemetryEvent({ eventName: "SocketModuleLoaded" }); + this.mc.logger.sendTelemetryEvent( + { eventName: "SocketModuleLoaded" }, + undefined, // error + LogLevel.info, + ); return m; }) .catch((error) => { diff --git a/packages/drivers/odsp-driver/src/odspDocumentStorageManager.ts b/packages/drivers/odsp-driver/src/odspDocumentStorageManager.ts index d8995bc07be8..746211c15772 100644 --- a/packages/drivers/odsp-driver/src/odspDocumentStorageManager.ts +++ b/packages/drivers/odsp-driver/src/odspDocumentStorageManager.ts @@ -221,6 +221,9 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase { } return res.content; }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); }); this.blobCache.setBlob(blobId, blob); @@ -442,6 +445,9 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase { }); return retrievedSnapshot; }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); const stTime = performanceNow(); diff --git a/packages/drivers/odsp-driver/src/odspUtils.ts b/packages/drivers/odsp-driver/src/odspUtils.ts index ea346dfdb2ea..dd72e3dc6350 100644 --- a/packages/drivers/odsp-driver/src/odspUtils.ts +++ b/packages/drivers/odsp-driver/src/odspUtils.ts @@ -8,6 +8,7 @@ import type { ITelemetryBaseLogger, ITelemetryBaseProperties, } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import { assert } from "@fluidframework/core-utils/internal"; import type { IResolvedUrl, @@ -457,6 +458,8 @@ export function toInstrumentedOdspTokenFetcher( }, ), { cancel: "generic" }, + undefined, // sampleThreshold + LogLevel.info, ); }; } diff --git a/packages/drivers/odsp-driver/src/vroom.ts b/packages/drivers/odsp-driver/src/vroom.ts index 6747b4828f9b..af8ad6f5f673 100644 --- a/packages/drivers/odsp-driver/src/vroom.ts +++ b/packages/drivers/odsp-driver/src/vroom.ts @@ -4,6 +4,7 @@ */ import type { ITelemetryBaseProperties } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import type { IOdspUrlParts, ISocketStorageDiscovery, @@ -141,6 +142,9 @@ export const fetchJoinSession = mockify( return response.content; }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); }, ); diff --git a/packages/loader/container-loader/src/container.ts b/packages/loader/container-loader/src/container.ts index a32d3fcc4437..8891836bfceb 100644 --- a/packages/loader/container-loader/src/container.ts +++ b/packages/loader/container-loader/src/container.ts @@ -1717,11 +1717,17 @@ export class Container this.mc.logger, { eventName: "WaitOps" }, async () => opsBeforeReturnP, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); await PerformanceEvent.timedExecAsync( this.mc.logger, { eventName: "WaitOpProcessing" }, async () => this._deltaManager.inbound.waitTillProcessingDone(), + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); // eslint-disable-next-line @typescript-eslint/no-floating-promises diff --git a/packages/loader/container-loader/src/loader.ts b/packages/loader/container-loader/src/loader.ts index 934cefad9422..d8ac3c5b1a9d 100644 --- a/packages/loader/container-loader/src/loader.ts +++ b/packages/loader/container-loader/src/loader.ts @@ -19,6 +19,7 @@ import type { IRequest, ITelemetryBaseLogger, } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import type { IClientDetails } from "@fluidframework/driver-definitions"; import type { IDocumentServiceFactory, @@ -312,12 +313,19 @@ export class Loader implements IHostLoader { public async resolve(request: IRequest, pendingLocalState?: string): Promise { const eventName = pendingLocalState === undefined ? "Resolve" : "ResolveWithPendingState"; - return PerformanceEvent.timedExecAsync(this.mc.logger, { eventName }, async () => { - return this.resolveCore( - request, - getAttachedContainerStateFromSerializedContainer(pendingLocalState), - ); - }); + return PerformanceEvent.timedExecAsync( + this.mc.logger, + { eventName }, + async () => { + return this.resolveCore( + request, + getAttachedContainerStateFromSerializedContainer(pendingLocalState), + ); + }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, + ); } private async resolveCore( diff --git a/packages/runtime/container-runtime/src/channelCollection.ts b/packages/runtime/container-runtime/src/channelCollection.ts index a0154267469f..5e6be13e4a86 100644 --- a/packages/runtime/container-runtime/src/channelCollection.ts +++ b/packages/runtime/container-runtime/src/channelCollection.ts @@ -15,6 +15,7 @@ import type { IFluidHandleInternal, ISignalEnvelope, } from "@fluidframework/core-interfaces/internal"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import { assert, Lazy, LazyPromise } from "@fluidframework/core-utils/internal"; import { FluidObjectHandle } from "@fluidframework/datastore/internal"; import type { @@ -469,16 +470,20 @@ export class ChannelCollection // Allows longitudinal tracking of various state (e.g. foundGCData), and some sampled details if (this.shouldSendAttachLog) { this.shouldSendAttachLog = false; - this.mc.logger.sendTelemetryEvent({ - eventName: "dataStoreAttachMessage_sampled", - ...tagCodeArtifacts({ id: attachMessage.id, pkg: attachMessage.type }), - details: { - local, - snapshot: !!attachMessage.snapshot, - foundGCData, + this.mc.logger.sendTelemetryEvent( + { + eventName: "dataStoreAttachMessage_sampled", + ...tagCodeArtifacts({ id: attachMessage.id, pkg: attachMessage.type }), + details: { + local, + snapshot: !!attachMessage.snapshot, + foundGCData, + }, + ...extractSafePropertiesFromMessage(envelope), }, - ...extractSafePropertiesFromMessage(envelope), - }); + undefined, // error + LogLevel.info, + ); } // The local object has already been attached diff --git a/packages/runtime/container-runtime/src/deltaScheduler.ts b/packages/runtime/container-runtime/src/deltaScheduler.ts index 80cba19a9690..bf67f6056eb6 100644 --- a/packages/runtime/container-runtime/src/deltaScheduler.ts +++ b/packages/runtime/container-runtime/src/deltaScheduler.ts @@ -5,6 +5,7 @@ import { performanceNow, type TypedEventEmitter } from "@fluid-internal/client-utils"; import type { IDeltaManagerFull } from "@fluidframework/container-definitions/internal"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import type { ISequencedDocumentMessage } from "@fluidframework/driver-definitions/internal"; import type { IContainerRuntimeBaseEvents } from "@fluidframework/runtime-definitions/internal"; import { @@ -149,17 +150,21 @@ export class DeltaScheduler { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.schedulingLog.totalProcessingTime += currentTime - this.processingStartTime!; - this.logger.sendTelemetryEvent({ - eventName: "InboundOpsProcessingTime", - opsRemainingToProcess: this.schedulingLog.opsRemainingToProcess, - numberOfTurns: this.schedulingLog.numberOfTurns, - processingTime: formatTick(this.schedulingLog.totalProcessingTime), - opsProcessed: - this.schedulingLog.lastSequenceNumber - this.schedulingLog.firstSequenceNumber + 1, - batchesProcessed: this.schedulingLog.numberOfBatchesProcessed, - duration: formatTick(currentTime - this.schedulingLog.startTime), - schedulingCount: this.schedulingCount, - }); + this.logger.sendTelemetryEvent( + { + eventName: "InboundOpsProcessingTime", + opsRemainingToProcess: this.schedulingLog.opsRemainingToProcess, + numberOfTurns: this.schedulingLog.numberOfTurns, + processingTime: formatTick(this.schedulingLog.totalProcessingTime), + opsProcessed: + this.schedulingLog.lastSequenceNumber - this.schedulingLog.firstSequenceNumber + 1, + batchesProcessed: this.schedulingLog.numberOfBatchesProcessed, + duration: formatTick(currentTime - this.schedulingLog.startTime), + schedulingCount: this.schedulingCount, + }, + undefined, // error + LogLevel.info, + ); this.schedulingLog = undefined; } diff --git a/packages/runtime/container-runtime/src/gc/garbageCollection.ts b/packages/runtime/container-runtime/src/gc/garbageCollection.ts index a5df6c2afe2a..b11dfe09381c 100644 --- a/packages/runtime/container-runtime/src/gc/garbageCollection.ts +++ b/packages/runtime/container-runtime/src/gc/garbageCollection.ts @@ -5,6 +5,7 @@ import type { ICriticalContainerError } from "@fluidframework/container-definitions"; import type { IRequest } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces/internal"; import { assert, LazyPromise, Timer } from "@fluidframework/core-utils/internal"; import type { ISnapshotTree } from "@fluidframework/driver-definitions/internal"; import { @@ -448,6 +449,9 @@ export class GarbageCollector implements IGarbageCollector { details: { initialized, unrefNodeCount: this.unreferencedNodesState.size }, }); }, + undefined, // markers + undefined, // sampleThreshold + LogLevel.info, ); } diff --git a/packages/runtime/container-runtime/src/summary/summarizerNode/summarizerNode.ts b/packages/runtime/container-runtime/src/summary/summarizerNode/summarizerNode.ts index 39ec0035ae44..274b2b0118ba 100644 --- a/packages/runtime/container-runtime/src/summary/summarizerNode/summarizerNode.ts +++ b/packages/runtime/container-runtime/src/summary/summarizerNode/summarizerNode.ts @@ -4,6 +4,7 @@ */ import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; +import { LogLevel } from "@fluidframework/core-interfaces"; import { assert, unreachableCase } from "@fluidframework/core-utils/internal"; import { SummaryType } from "@fluidframework/driver-definitions"; import type { @@ -441,6 +442,8 @@ export class SummarizerNode implements IRootSummarizerNode { return { isSummaryTracked, isSummaryNewer }; }, { start: true, end: true, cancel: "error" }, + undefined, // sampleThreshold + LogLevel.info, ); } /**