ApiServer.get_fast_api_app() unconditionally registered two in-memory
span exporters (ApiServerSpanExporter, InMemoryExporter) on the global
tracer provider. Their retained span/attribute dicts are never pruned,
so any long-lived server serving through ApiServer.get_fast_api_app()
(web=False) leaked memory for the life of the process.
The only reader of this data is DevServer's /debug/trace endpoint
(web=True), so the exporters are now only registered when
_registers_debug_trace_exporters is set, which DevServer overrides to
True. ApiServer no longer registers them at all.
Fixes google#6692
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
Problem:
ApiServer.get_fast_api_app() unconditionally registered two in-memory
span processors (ApiServerSpanExporter, InMemoryExporter) on the global
tracer provider. InMemoryExporter.export() appends every span to an
unbounded list and ApiServerSpanExporter.export() writes every span's
attributes into a dict, neither of which is ever pruned. clear() exists
but has no callers. The only reader of this retained data is DevServer's
/debug/trace endpoint (used by adk web), so any server started with
web=False (e.g. via adk api_server or get_fast_api_app(..., web=False))
leaked memory for the life of the process with no way to opt out.
Solution:
Added a _registers_debug_trace_exporters class attribute on ApiServer
(default False), and only build/register the two debug-trace span
processors when it's set. DevServer overrides it to True, since it's
the only place that reads the retained data (AdkWebServer inherits this
from DevServer). Plain ApiServer (production/web=False) now sets up
telemetry with no internal exporters, so no spans are retained and nothing
observable changes for DevServer/adk web users.
Testing Plan
Unit Tests:
Added test_debug_trace_exporters_only_registered_for_dev_server in
tests/unittests/cli/test_fast_api.py, which patches
google.adk.cli.api_server._setup_telemetry and asserts it is called with
internal_exporters=[] when web=False and with the two debug-trace span
processors when web=True.
Verified the new test fails without the fix (checked out
src/google/adk/cli/api_server.py/dev_server.py from the parent commit
and reran):
With the fix applied:
Also ran isort --check-only and pyink --check on the changed files;
both report no issues.
Manual End-to-End (E2E) Tests:
Followed the repro from the issue: get_fast_api_app(agents_dir=..., web=False)
now sets up the tracer provider with zero internal exporters, so
InMemoryExporter/ApiServerSpanExporter are never instantiated as span
processors and len(memory_exporter._spans) no longer grows as requests
are served. adk web (web=True, DevServer) is unaffected — the
/debug/trace/{event_id} endpoint continues to work exactly as before,
covered by the existing test_debug_trace test.
Checklist
Additional context
This PR was prepared with AI assistance (Claude Code), reviewed and
verified by me before pushing.