Skip to content

Commit 7350e20

Browse files
authored
refactor(telemetry): memoize get_host_info via @CallOnce (#17669)
## Summary Split out of #17667. Replace the `_host_info` module-level global + `None`-check in `ddtrace/internal/telemetry/data.py` with `@callonce`, the idiomatic no-arg memoization decorator in this codebase (already used by `ddtrace/internal/packages.py` and `ddtrace/internal/debug.py`). - Removes the unsynchronized `None`-check race. `@callonce` also has no explicit lock, but `get_host_info` is deterministic and idempotent, so a benign double-compute on first-call contention produces identical results with no torn state. - `get_host_info()` is still the public entry point and still returns the same dict shape. - Originally this PR used `@cached()` (i.e. `functools.lru_cache`), but `@cached()` requires at least one hashable argument and forced a dummy `_: tuple = ()` parameter plus a wrapper function. `@callonce` is purpose-built for zero-arg functions and can be applied directly to `get_host_info`, eliminating the indirection. ## Performance Microbenchmark on the warm path (A/B reproduction of the two implementations, 10 000 calls per sample, 11 samples): | Per-call (warm) | Before (global None-check) | After (`@callonce`) | Δ | |---|---:|---:|---:| | Best | ~20 ns | ~30-60 ns | negligible | `get_host_info()` is called at most once per telemetry heartbeat (~60 s), so any small constant-factor difference is not observable in practice. The motivation is code clarity and consistency with the rest of the codebase, not speed. ## Test plan - [x] `tests/telemetry/test_data.py::test_get_host_info` passes on Python 3.14 via `scripts/run-tests`. - [x] Existing `tests/telemetry/` suite passes (no behavior change at the function boundary — same dict contents, same call signature). - [x] No release note needed — internal refactor, `changelog/no-changelog` applied. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: alberto.vara <alberto.vara@datadoghq.com>
1 parent d5a12d8 commit 7350e20

1 file changed

Lines changed: 12 additions & 16 deletions

File tree

ddtrace/internal/telemetry/data.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ddtrace.internal.constants import DEFAULT_SERVICE_NAME
77
from ddtrace.internal.runtime.container import get_container_info
88
from ddtrace.internal.utils.cache import cached
9+
from ddtrace.internal.utils.cache import callonce
910
from ddtrace.version import __version__
1011

1112
from ..hostname import get_hostname
@@ -77,23 +78,18 @@ def get_application(service: str, version: str, env: str) -> dict:
7778
return _get_application((service, version, env))
7879

7980

80-
_host_info = None
81-
82-
81+
@callonce
8382
def get_host_info() -> dict:
84-
"""Creates a dictionary to store host data using the platform module"""
85-
global _host_info
86-
if _host_info is None:
87-
_host_info = {
88-
"os": platform.system(),
89-
"hostname": get_hostname(),
90-
"os_version": _get_os_version(),
91-
"kernel_name": platform.system(),
92-
"kernel_release": platform.release(),
93-
"kernel_version": platform.version(),
94-
"container_id": _get_container_id(),
95-
}
96-
return _host_info
83+
"""Creates a dictionary to store host data using the platform module."""
84+
return {
85+
"os": platform.system(),
86+
"hostname": get_hostname(),
87+
"os_version": _get_os_version(),
88+
"kernel_name": platform.system(),
89+
"kernel_release": platform.release(),
90+
"kernel_version": platform.version(),
91+
"container_id": _get_container_id(),
92+
}
9793

9894

9995
def _get_sysconfig_var(key: str) -> str:

0 commit comments

Comments
 (0)