Skip to content

GVFS.Service: defer and retry telemetry pipe attachment#1986

Merged
tyrielv merged 2 commits into
microsoft:masterfrom
tyrielv:tyrielv/service-telemetry-pipe
May 26, 2026
Merged

GVFS.Service: defer and retry telemetry pipe attachment#1986
tyrielv merged 2 commits into
microsoft:masterfrom
tyrielv:tyrielv/service-telemetry-pipe

Conversation

@tyrielv
Copy link
Copy Markdown
Contributor

@tyrielv tyrielv commented May 22, 2026

Summary

GVFS.Service runs as SYSTEM and cannot read the user's global git config at startup, so TelemetryDaemonEventListener was never created and all service telemetry events were silently lost.

This PR adds deferred telemetry pipe attachment without modifying JsonTracer — the logic lives in two new focused classes that are reusable by other processes (e.g. GVFS.Mount).

Design

BufferingTelemetryListener (GVFS.Common)

An EventListener subclass that buffers TraceEventMessage objects in a bounded ConcurrentQueue. When a real listener becomes available, ReplayAndStop() replays buffered messages and switches to no-op mode.

DeferredTelemetryAttacher (GVFS.Common)

Manages the deferred attachment lifecycle:

  1. Adds a BufferingTelemetryListener to the tracer at construction
  2. Runs a retry timer with exponential backoff (10s, 30s, 1m, 5m steady state)
  3. On each retry, calls TelemetryDaemonEventListener.CreateIfEnabled
  4. On success: adds the real listener, replays buffered messages, stops the timer

Callers can also trigger an explicit attach via TryAttach(gitBinRoot) when conditions change (e.g. user session becomes available).

Service integration (GVFSService.Windows.cs)

  • Creates a DeferredTelemetryAttacher at startup
  • Starts the retry timer for cases where the telemetry collector is installed later
  • On SessionLogon: temporarily overrides HOME to the user's profile (serialized with a lock) so git can read .gitconfig, then calls TryAttach
  • Also checks for an active session at startup (handles service restart during active session)

Files changed

  • GVFS.Common/NativeMethods.csWTSGetActiveConsoleSessionId P/Invoke
  • GVFS.Common/Tracing/BufferingTelemetryListener.cs — new
  • GVFS.Common/Tracing/DeferredTelemetryAttacher.cs — new
  • GVFS.Service/GVFSService.Windows.cs — deferred attach integration
  • GVFS.UnitTests/Common/JsonTracerDeferredTests.cs — tests for buffering, replay, cap, and retry intervals

Testing

  • 8 new unit tests covering buffer/replay, bounded cap, stop-after-replay, retry backoff values, and null handling
  • All 814 existing unit tests pass

@tyrielv tyrielv force-pushed the tyrielv/service-telemetry-pipe branch 2 times, most recently from a39fe39 to 8b149e2 Compare May 26, 2026 17:40
@tyrielv tyrielv marked this pull request as ready for review May 26, 2026 18:30
@tyrielv tyrielv enabled auto-merge May 26, 2026 18:30
Comment thread GVFS/GVFS.Common/Tracing/DeferredTelemetryAttacher.cs
Comment thread GVFS/GVFS.Common/Tracing/DeferredTelemetryAttacher.cs Outdated
Comment thread GVFS/GVFS.Service/GVFSService.Windows.cs Outdated
@tyrielv tyrielv force-pushed the tyrielv/service-telemetry-pipe branch from 8b149e2 to 361e7bc Compare May 26, 2026 20:17
tyrielv added 2 commits May 26, 2026 13:22
GVFS.Service runs as SYSTEM and cannot read the user's global git
config (where gvfs.telemetry-pipe is set) at startup.  This meant
TelemetryDaemonEventListener was never created, and all service
telemetry events (PendingUpgradeHandler, etc.) were silently lost.

Add two new classes in GVFS.Common.Tracing:

- BufferingTelemetryListener: an EventListener that buffers telemetry
  messages in a bounded ConcurrentQueue, then replays them to a real
  listener on demand.

- DeferredTelemetryAttacher: manages the lifecycle of deferred
  telemetry pipe attachment.  Adds a BufferingTelemetryListener to
  the tracer, then periodically retries creating the real daemon
  listener (exponential backoff: 10s, 30s, 1m, 5m steady state).
  On success, replays buffered messages and stops the timer.
  Checks HasTelemetryDaemonListener to prevent duplicate listeners
  when the JsonTracer constructor already attached one.
  Designed for reuse by both GVFS.Service and GVFS.Mount.

TelemetryDaemonEventListener gains a globalConfigPath parameter so
callers can read a specific .gitconfig file via --file instead of
--global.  This lets GVFSService read the logged-on user's config
without mutating the process-wide HOME environment variable.

GVFSService creates a DeferredTelemetryAttacher at startup and calls
TryAttach on session logon events, passing the user's .gitconfig
path resolved via RunImpersonated.

JsonTracer gains only HasTelemetryDaemonListener (one-liner property)
to support the duplicate-listener guard.

Assisted-by: Claude Opus 4.6
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
Test BufferingTelemetryListener: buffer and replay, stop after
replay, bounded cap, second replay returns zero.

Test DeferredTelemetryAttacher: null gitBinRoot handling, retry
interval exponential backoff values.

Assisted-by: Claude Opus 4.6
Signed-off-by: Tyrie Vella <tyrielv@gmail.com>
@tyrielv tyrielv force-pushed the tyrielv/service-telemetry-pipe branch from 361e7bc to b2c81a1 Compare May 26, 2026 20:23
@tyrielv tyrielv merged commit c53aea6 into microsoft:master May 26, 2026
100 of 101 checks passed
@tyrielv tyrielv mentioned this pull request May 26, 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.

2 participants