Skip to content

Populate telemetry metadata on proactive-refresh results (3707191) - #6157

Merged
Neha Bhargava (neha-bhargava) merged 3 commits into
mainfrom
nebharg/fix-proactive-refresh-telemetry-3707191
Aug 6, 2026
Merged

Neha Bhargava (neha-bhargava) merged 3 commits into
mainfrom
nebharg/fix-proactive-refresh-telemetry-3707191

Conversation

@neha-bhargava

Copy link
Copy Markdown
Contributor

Summary

Proactive (background) token refresh produced AuthenticationResults with stripped telemetry metadata. Fixes AB#3707191.

The proactive-refresh path (SilentRequestHelper.ProcessFetchInBackground) runs the fetch outside RequestBase.RunAsync, so the AuthenticationResultMetadata that RunAsync -> UpdateTelemetry normally fills in was left at its constructor defaults:

  • TokenEndpoint was null
  • DurationTotalInMs was always 0
  • DurationInHttpInMs, CacheRefreshReason, RegionDetails, CacheLevel, CachedAccessTokenCount were also unset

Affected consumers:

  1. The OnBackgroundTokenRefreshCompleted callback (ExecutionResult.Result).
  2. The LogSuccessHttpDuration OTel emission inside ProcessFetchInBackground, which recorded DurationInHttpInMs == 0 for every proactive refresh.

Note: the token is cached, not the AuthenticationResult, so a later foreground read still rebuilds correct metadata via RunAsync. The bug affects the background result object specifically.

Fix

  • Extract the metadata-population core of UpdateTelemetry into a shared internal static RequestBase.PopulateSuccessMetadata.
  • Call it from the background success path, backfilling from the shared apiEvent (whose HTTP fields the background call populates) and using the background operation's own elapsed time as DurationTotalInMs.
  • This mirrors the existing failure-path treatment (CreateFailureMetadata) and avoids a third divergent field-copy.
  • Foreground behavior is unchanged: GetCacheLevel and the global Metrics.IncrementTotalDurationInMs increment stay on the foreground path only, so background latency does not inflate the user-facing aggregate.

Why background duration (not the parent request's)?

DurationTotalInMs means "time to produce this result." The parent request returned a cached token in ~0ms; stamping that onto the background result would re-introduce the ~0 symptom and make Total < Http (since apiEvent.DurationInHttpInMs is overwritten by the background call) - internally inconsistent.

Tests

  • Strengthened ClientCredentials_BackgroundRefresh_Success_InvokesCallback_Async to assert the result carries populated metadata (CacheRefreshReason == ProactivelyRefreshed, non-empty TokenEndpoint, TokenSource == IdentityProvider).
  • Verified the strengthened assertions fail against the pre-fix implementation (regression proof).
  • RefreshInTests (13 passed) and TelemetryTests (28 passed) green on net8.0.

DurationTotalInMs > 0 is intentionally not asserted - a sub-millisecond in-memory mock round-trip makes it flaky. The deterministic backfilled fields prove the fix.

Proactive (background) refresh runs the fetch outside RequestBase.RunAsync,
so the AuthenticationResultMetadata that RunAsync normally fills in
(TokenEndpoint, DurationTotalInMs, DurationInHttpInMs, CacheRefreshReason,
RegionDetails, etc.) was left at its constructor defaults. Consumers of the
OnBackgroundTokenRefreshCompleted callback, and the LogSuccessHttpDuration
OTel emission, therefore saw stripped telemetry (null TokenEndpoint, 0
duration).

Extract the metadata-population core of UpdateTelemetry into a shared static
RequestBase.PopulateSuccessMetadata and call it from the background success
path in SilentRequestHelper.ProcessFetchInBackground, backfilling from the
shared apiEvent and using the background operation's own elapsed time as the
total duration. Mirrors the existing failure-path treatment via
CreateFailureMetadata. The global Metrics total-duration counter stays
foreground-only so background latency does not inflate the user-facing
aggregate.

Strengthen the background-refresh success test to assert the metadata is
populated (fails against the pre-fix implementation).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 766dbf9f-f979-44b5-9362-a1a1efae90ea
Copilot AI lite review requested due to automatic review settings August 3, 2026 23:45
@neha-bhargava
Neha Bhargava (neha-bhargava) requested a review from a team as a code owner August 3, 2026 23:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes missing telemetry metadata on proactive (background) token refresh AuthenticationResults by sharing the same metadata-population logic used by the foreground (RequestBase.RunAsync) path.

Changes:

  • Refactors the “success metadata” portion of RequestBase.UpdateTelemetry into a shared internal static RequestBase.PopulateSuccessMetadata.
  • Populates proactive-refresh success results’ metadata in SilentRequestHelper.ProcessFetchInBackground, using the background operation’s own elapsed time for DurationTotalInMs.
  • Strengthens the unit test to assert deterministic metadata fields are populated on proactive-refresh results (regression coverage for AB#3707191).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
tests/Microsoft.Identity.Test.Unit/PublicApiTests/RefreshInTests.cs Adds regression assertions ensuring proactive-refresh results contain populated telemetry metadata.
src/client/Microsoft.Identity.Client/Internal/Requests/SilentRequestHelper.cs Measures background refresh duration and backfills success metadata onto the background AuthenticationResult.
src/client/Microsoft.Identity.Client/Internal/Requests/RequestBase.cs Extracts shared success-metadata copy logic into PopulateSuccessMetadata and reuses it from UpdateTelemetry.

Make the added comments concise. Also strengthen the managed identity
background-refresh test to assert the proactive-refresh result carries
backfilled telemetry (CacheRefreshReason, TokenSource) - MI is the other
primary OnBackgroundTokenRefreshCompleted consumer and flows through the same
ProcessFetchInBackground path. TokenEndpoint stays null for MI (no TokenClient),
so only the applicable fields are asserted.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 766dbf9f-f979-44b5-9362-a1a1efae90ea
Copilot AI review requested due to automatic review settings August 4, 2026 06:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 766dbf9f-f979-44b5-9362-a1a1efae90ea
Copilot AI review requested due to automatic review settings August 4, 2026 06:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

This was referenced Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants