Skip to content

feat(telemetry): extend framework telemetry to ExtendedGcsFileSystem and ZonalFile, add e2e tests - #1140

Open
Mahalaxmibejugam wants to merge 11 commits into
fsspec:mainfrom
Mahalaxmibejugam:feat/telemetry-extended-zonal
Open

Mahalaxmibejugam wants to merge 11 commits into
fsspec:mainfrom
Mahalaxmibejugam:feat/telemetry-extended-zonal

Conversation

@Mahalaxmibejugam

@Mahalaxmibejugam Mahalaxmibejugam commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Summary (follow-up to #1054)

Follow-up to #1054. It extends caller-framework telemetry to ExtendedGcsFileSystem (the class gcsfs.GCSFileSystem exports by default) and to ZonalFile. It also strengthens the telemetry tests and adds real end-to-end telemetry tests to the Cloud Build e2e pipeline.

Changes

Telemetry coverage

  • mirror_gcs_methods(ExtendedGcsFileSystem) wraps Extended's own sync and async overrides (mv, rm, mkdir, makedirs, rmdir, merge, _cat_file, _find, _pipe_file, _put_file, …). These previously went through plain asyn.sync_wrapper, so their requests lacked the framework token and ran stack detection on the fsspecIO thread for every request.
  • _sync_lookup_bucket_layout and sync_process_limits_to_offset_and_length now use _gcs_sync_wrapper.
  • wrap_file_methods(ZonalFile) wraps ZonalFile's own overrides (_fetch_range, _async_fetch_range, write, flush, commit, …).
  • New with_telemetry_context decorator on ExtendedGcsFileSystem._open. The bucket-layout lookup, and for Rapid (zonal) reads the MRD pool's JSON API _info request in ZonalFile.__init__, run before GCSFile.__init__ caches caller_framework. Previously that _info request went out without the token, and detection ran on the fsspecIO thread. The framework is now detected once on the caller thread, and GCSFile.__init__ reuses it.
  • core.py reads telemetry_manager.default_usage_tracker from the module, so tests only need to patch one place.

Tests

  • Coverage test: it no longer pre-sets the telemetry context. Plain fsspec already carries a pre-set context into the loop thread, so the old version passed even without the wrappers. It now uses a detector that only finds a framework on the caller thread and runs on both GCSFileSystem and ExtendedGcsFileSystem. It also asserts detection never runs on the fsspec loop thread.
  • Wrapped-methods test: it derives the methods to check from the class instead of a hardcoded list. Any sync function that wraps a coroutine must be _gcs_sync_wrapper, and every async method mirror_gcs_methods targets, along with its public sync counterpart, must be wrapped.
  • ZonalFile tests:
    • every method defined on ZonalFile is wrapped;
    • _async_fetch_range and write carry the file's framework;
    • over a zonal file's full life (open, read, seek, read, info, close), detection runs exactly once on the caller thread, and every HTTP request and gRPC-side operation carries the framework.
  • Test harness (mock_gcs_harness):
    • parametrized over core/extended;
    • restores the fsspec registry exactly;
    • mocks batch deletes;
    • skips credential refresh, since HTTP is mocked;
    • clears the instance cache on teardown.
  • Real-framework tests (test_upstream_frameworks_real.py): they run on both classes, and Lightning writes to tmp_path.
  • New test_telemetry_e2e.py: real pandas (to_csv/read_csv), torch DataLoader reads, and Lightning save_checkpoint against a GCS endpoint with no mocking. The User-Agent of every request to the GCS host is recorded at the aiohttp layer, and each one must carry the framework token.
    • Runs against real GCS in the Cloud Build standard (regional) and hns suites, which run gcsfs/, and against the fake emulator in GitHub CI.
    • Rapid/zonal buckets are intentionally not covered: their data path uses gRPC bidi APIs, which don't go through the HTTP User-Agent path.

CI / e2e

  • environment_gcsfs.yaml: adds pandas, pytorch-cpu and lightning. Previously the real-framework tests were always skipped in CI; now they actually run.
  • cloudbuild/setup_vm.sh: installs CPU-only torch from download.pytorch.org, plus pandas and lightning. The installs are non-fatal because the tests importorskip these packages.

Docs

  • telemetry.rst now states the 64-frame detection cap, says detection runs on the caller thread, explains that the opt-out env vars are read on every call, and notes that gRPC (zonal) data-path requests don't carry the framework token yet.

Testing

  • Full suite against the fake GCS emulator, rebased on main (after feat(telemetry): integrate upstream framework telemetry into User-Agent header (3/3) #1054 and refactor(extended_gcsfs): replace BucketType with orthogonal BucketLayout and routing helpers #1133):
    • GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT=false: 754 passed.
    • GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT=true: 1104 passed. The only failures are 3 test_hns_unit.py::test_get_control_plane_client_endpoint cases, a local mTLS environment issue that also fails on main.
  • Cloud Build e2e (before the rebase): all suites passed, and test_telemetry_e2e.py passed against real GCS in both the standard and hns suites.
  • Checked that the tests fail when the feature breaks:
    • removing mirror_gcs_methods(ExtendedGcsFileSystem), wrap_file_methods(ZonalFile) or @with_telemetry_context on ExtendedGcsFileSystem._open each makes the corresponding tests fail;
    • with GCSFS_NO_TELEMETRY=1, all e2e tests fail with N/N GCS requests missing fw/<framework>.
  • pre-commit (black, isort, flake8) passes.

Notes for reviewers

  • Not yet verified: conda resolving pytorch-cpu and lightning in GitHub CI. Expect a slower environment setup.
  • Pre-existing CI issue (not changed here): ci.yml uses ${{ matrix.PY }}, but the matrix key is python-version, so every matrix job actually runs Python 3.11 from environment_gcsfs.yaml.
  • Extended on the emulator: GitHub CI only runs with GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT=false. On the emulator, Extended is covered by the mocked harness, not by test_telemetry_e2e.py.
  • Follow-up: adding the framework token to gRPC calls (per-call metadata).

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces lightweight, automatic caller framework detection (telemetry) to identify upstream libraries like Pandas, PyTorch, and Lightning interacting with Google Cloud Storage via gcsfs. It includes documentation, environment updates, and extensive integration and end-to-end tests. The feedback highlights an issue in the VM setup script where a failure to install the CPU-only torch wheel would cause a subsequent lightning installation to pull down the massive CUDA-enabled torch wheel from PyPI, defeating the optimization. A conditional installation approach was suggested to resolve this.

Comment thread cloudbuild/setup_vm.sh Resolved
@Mahalaxmibejugam
Mahalaxmibejugam force-pushed the feat/telemetry-extended-zonal branch from 7502f41 to d9b4f5f Compare November 3, 2026 12:01
@Mahalaxmibejugam Mahalaxmibejugam changed the title feat(telemetry): extend framework telemetry to ExtendedGcsFileSystem and ZonalFile, add e2e tests (stacked on #1054) feat(telemetry): extend framework telemetry to ExtendedGcsFileSystem and ZonalFile, add e2e tests Nov 3, 2026
@Mahalaxmibejugam
Mahalaxmibejugam marked this pull request as ready for review November 3, 2026 12:41
@codecov

codecov Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.59%. Comparing base (76012a9) to head (59f5714).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1140      +/-   ##
==========================================
+ Coverage   92.51%   92.59%   +0.08%     
==========================================
  Files          20       20              
  Lines        4086     4091       +5     
==========================================
+ Hits         3780     3788       +8     
+ Misses        306      303       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Mahalaxmibejugam

Copy link
Copy Markdown
Collaborator Author

/gcbrun

…emetry tests

- Apply mirror_gcs_methods to ExtendedGcsFileSystem (the default exported class) so its
  own sync/async overrides (mv, rm, mkdir, makedirs, rmdir, merge, _cat_file, _open, ...)
  detect the framework on the caller thread; use _gcs_sync_wrapper for
  _sync_lookup_bucket_type and sync_process_limits_to_offset_and_length.
- Apply wrap_file_methods to ZonalFile so its overrides keep the file's caller framework.
- Read the framework value without copying the context dict on every file method call.
- core: resolve default_usage_tracker via the manager module so tests can patch one place;
  move mirror_gcs_methods(GCSFileSystem) directly after the class body.
- Tests:
  - Coverage test no longer pre-sets context; uses a caller-thread-only detector, runs on
    both GCSFileSystem and ExtendedGcsFileSystem, and asserts detection never runs on the
    fsspecIO loop thread (regression guard against per-request stack walks).
  - Add Extended/Zonal wrapping and ZonalFile propagation tests.
  - Replace the vacuous eager-property test with one that mirrors a fresh subclass.
  - Harness: Core/Extended param, exact fsspec registry restore, batch-delete mock,
    skip credential refresh (HTTP is mocked).
  - Real framework tests run on both classes; Lightning writes to tmp_path.
- CI: add pandas, pytorch-cpu and lightning so real framework tests run.
- Docs: 64-frame cap, runtime opt-out, note that gRPC (zonal) requests lack the token.
…ing names

Replace the hardcoded method list with checks derived from the class: any sync
function wrapping a coroutine must be a _gcs_sync_wrapper, and every async method
targeted by mirror_gcs_methods (and its public sync counterpart) must be wrapped.
Runs on both GCSFileSystem and ExtendedGcsFileSystem.
Benchmarking showed it was slower than get_telemetry_context().get() (~205ns vs
~156ns per call; Enum isinstance/.value cost outweighs the saved 1-entry dict copy),
so keep the existing API.
…ild e2e

Add gcsfs/tests/test_telemetry_e2e.py: real pandas (to_csv/read_csv), torch
DataLoader reads, and Lightning save_checkpoint against the GCS endpoint with no
gcsfs mocking. The User-Agent of every request to the GCS host is recorded at the
aiohttp layer and must carry the framework token. A Rapid (zonal) variant checks the
round trip and that any remaining JSON API requests are tagged (gRPC is not yet).

- Runs in GitHub CI against the fake GCS emulator, and in the e2e standard/hns
  suites (which run gcsfs/) against real GCS; added explicitly to the zonal suite.
- setup_vm.sh installs CPU-only torch, pandas and lightning on the e2e VM.
…/HNS)

Rapid (zonal) buckets use gRPC bidi APIs for data and never go through the HTTP
User-Agent path, so the zonal e2e test could not verify anything. Drop it and its
explicit addition to the zonal suite. The regional (standard suite) and HNS e2e
suites already run gcsfs/ and therefore these tests against real GCS.
- Add `with_telemetry_context` and apply it to GCSFileSystem._open and
  ExtendedGcsFileSystem._open (plain sync methods not covered by mirror_gcs_methods).
  Opening a Rapid (zonal) file for read issues a JSON API `_info` request from
  ZonalFile.__init__ (MRD pool setup) before caller_framework is set; it previously
  went out without the framework token and ran detection on the fsspecIO thread.
  Bucket lookup, metadata requests and GCSFile.__init__ now share one detection.
- Test: zonal open sends fw token and detects only on the caller thread.
- Fix the Extended mirror comment (`_open` is not mirrored).
- setup_vm.sh: make framework installs non-fatal (tests importorskip them).
- Harness: clear the fsspec instance cache on teardown; comment the private registry use.
- e2e: guard the private aiohttp `_request` hook with a clear assertion.
- Coverage test: use fsspec's loop thread name instead of hardcoding "fsspecIO".
- Replace the eager-property test with a plain init check (the trap could never fire).
If the CPU torch install failed, installing lightning would resolve torch from PyPI
and download the multi-GB CUDA build. Install pandas separately and chain lightning
on a successful CPU torch install.
Mock _lookup_bucket_layout with gcsfs.tests.layouts instead of the removed
BucketType/_lookup_bucket_type.
…ileSystem._open

GCSFile.__init__ already caches caller_framework before its own info() call (the
wrapped GCSFile.details property reuses it), so the decorator on core
GCSFileSystem._open was redundant. Keep it on ExtendedGcsFileSystem._open, where the
bucket-layout lookup and ZonalFile's MRD pool _info request run before
caller_framework exists.

Extend the zonal test to a full file lifecycle (open, read, seek, read, info, close)
asserting a single caller-thread detection and that every operation carries the
framework.
mv, mkdir, makedirs, rmdir, rm and merge were defined as asyn.sync_wrapper(...) in the
ExtendedGcsFileSystem class body, but mirror_gcs_methods(ExtendedGcsFileSystem)
always overwrites them at import with telemetry-aware _gcs_sync_wrapper(...), so the
class-body values never took effect. mv/mkdir/makedirs/rmdir/rm are still mirrored via
AbstractFileSystem, and merge via the inherited GCSFileSystem.merge; class attributes,
dispatch and docstrings are unchanged.
object.__new__(ZonalFile) raises 'not safe, use ZonalFile.__new__()' on Python
3.10/3.11 for io.IOBase subclasses (allowed from 3.12), which broke the GitHub CI
matrix (all jobs run 3.11).
@Mahalaxmibejugam
Mahalaxmibejugam force-pushed the feat/telemetry-extended-zonal branch from 9727c62 to 59f5714 Compare November 5, 2026 12:55

@zhixiangli zhixiangli left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. One small docs nit inline.

Comment thread docs/source/telemetry.rst
Comment on lines +35 to +36
The framework token is attached to JSON API (HTTP) requests. gRPC data-path requests to Rapid (zonal) buckets
use a ``User-Agent`` fixed when the gRPC client is created and do not yet carry the framework token.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: gRPC requests without the token go beyond Rapid data-path ones. RCU reads also go to ZonalFile/MRD (_open, is_rcu). Storage Control calls (bucket layout lookup, HNS mkdir/rmdir/mv folder ops) use a fixed grpc.primary_user_agent too. Suggest:

Suggested change
The framework token is attached to JSON API (HTTP) requests. gRPC data-path requests to Rapid (zonal) buckets
use a ``User-Agent`` fixed when the gRPC client is created and do not yet carry the framework token.
The framework token is attached to JSON API (HTTP) requests only. gRPC requests use a ``User-Agent`` fixed
when the gRPC client is created and do not yet carry the framework token. This includes Rapid (zonal) reads
and writes, Rapid Cache Ultra reads, and Storage Control API calls (bucket layout lookups and HNS folder
operations such as ``mkdir``, ``rmdir`` and ``mv``).

Comment on lines +11 to +12
Rapid (zonal) buckets are intentionally not covered: their data path uses gRPC
bidi streaming APIs, which don't go through the HTTP User-Agent path at all.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: same here. Rapid Cache Ultra reads also use the gRPC data path, so maybe say "Rapid (zonal) and Rapid Cache Ultra buckets are intentionally not covered".

This branch has not been deployed

No deployments
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