fix(api): make EnvironmentCarrier spec-compliant (#4190)#4264
Conversation
Three changes to align with the OTel env-carriers specification:
1. Empty key normalization (normative, spec#5163):
NormalizeKey('') now returns '_' per the spec requirement that
'An empty environment variable name is non-normalized and
normalizes to _.'
2. Remove caching semantics (non-normative, spec#5179):
Get() previously cached the first getenv() result and served
stale values on subsequent calls for the same key. The carrier
now calls getenv() on every Get() invocation so the current
environment state is always reflected. A value_store_ map is
retained only to own the memory backing the returned string_view
(required by the TextMapCarrier contract), not to serve cached
data.
3. Operational guidance documentation (non-normative):
The class-level doc comment now documents the expected usage
patterns: initialization-time extraction, child process
environment handling via an injected env map, and security
considerations around environment variable visibility.
Tests updated:
- Replaced GetCachesValues with GetReadsCurrentEnvironment to
reflect the no-caching-semantics behavior.
- Replaced GetCacheKeyedByNormalizedName (cache stale-value test)
with NormalizeKeyEmpty (empty-key -> '_' normalization).
- Added GetEmptyKeyNormalizesToUnderscore to cover Get('') path.
Fixes open-telemetry#4190
|
CC @pellared @pranitaurlam: This PR addresses the three items outlined in #4190:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4264 +/- ##
==========================================
+ Coverage 81.31% 81.31% +0.01%
==========================================
Files 445 445
Lines 18857 18859 +2
==========================================
+ Hits 15331 15333 +2
Misses 3526 3526
🚀 New features to boost your workflow:
|
| test_setenv("TRACEPARENT", "changed-value"); | ||
| // Without caching, a second Get reflects the current environment state. | ||
| // (Per the spec, the carrier is intended to be used for a single Extract() at | ||
| // initialization time; repeated Get() calls on a carrier are not a typical usage pattern.) |
There was a problem hiding this comment.
I think this test should change TRACEPARENT before the second Get(). As written, it would still pass with the old caching behavior, so it does not really cover the “no stale cache” change.
There was a problem hiding this comment.
Yes, I verified that the test wasn't actually proving the no-caching behavior since the env var never changed between calls. Updated the test to set TRACEPARENT to a different value before the second Get(), so it now fails with the old caching implementation and only passes with the new behavior.
- GetReadsCurrentEnvironment test: change TRACEPARENT between the two Get() calls so the test actually proves no stale value is served (old caching behavior would now fail this test) - Get() doc comment: narrow string_view lifetime guarantee from 'valid for the lifetime of this carrier' to 'valid until the next Get() call for the same key', which is the accurate bound given value_store_ is overwritten on each call
|
@lalitb I've resolved the comments, please go through the changes once again. |
|
Per spec PR #5179:
I propose to keep the cache here, for the sake of providing a safe storage to support the In practice, caching the result of What matters is that the code can not be abused to cause a crash, so there must be stable memory to back up the |
marcalff
left a comment
There was a problem hiding this comment.
Thanks for the patch.
Ok with the normalization of "" -> "_".
Please keep the caching logic as is.
Per spec PR #5179, language implementations are free to use internal storage when required by their API shape. Revert the cache removal: - Restore cache_ (renamed from value_store_) with first-read-wins semantics, ensuring the returned string_view stays valid for the lifetime of the carrier regardless of subsequent setenv() calls - Restore GetCachesValues and GetCacheKeyedByNormalizedName tests which verify the cache behavior explicitly - Update Get() doc comment to accurately reflect the caching rationale The only spec-normative change retained is empty key -> '_' normalization.
Agreed. Reverted to the original cache_ with first-read-wins semantics. The spec PR #5179 explicitly allows internal storage for API shape reasons, and the cache is exactly the right tool here, stable memory for the returned string_view, with no risk of it being invalidated. |
|
Approved. Please update the change set comments for this PR, to reflect the final code. |
Three changes to align with the OTel env-carriers specification:
Empty key normalization (normative, spec#5163): NormalizeKey('') now returns '_' per the spec requirement that 'An empty environment variable name is non-normalized and normalizes to _.'
Remove caching semantics (non-normative, spec#5179): Get() previously cached the first getenv() result and served stale values on subsequent calls for the same key. The carrier now calls getenv() on every Get() invocation so the current environment state is always reflected. A value_store_ map is retained only to own the memory backing the returned string_view (required by the TextMapCarrier contract), not to serve cached data.
Operational guidance documentation (non-normative): The class-level doc comment now documents the expected usage patterns: initialization-time extraction, child process environment handling via an injected env map, and security considerations around environment variable visibility.
Tests updated:
Fixes #4190
Fixes # (issue)
Changes
Please provide a brief description of the changes here.
For significant contributions please make sure you have completed the following items:
CHANGELOG.mdupdated for non-trivial changes