Summary
Consider avoiding acquisition of the parent metric's _lock in MetricWrapperBase.labels() when the requested labeled child already exists, while retaining synchronized child creation.
Current behavior
In [prometheus_client/metrics.py, lines 173–203
labels() validates and normalizes label values, then acquires self._lock before checking _metrics. Consequently, repeated lookups of existing children all acquire the same parent lock, including lookups for different labelsets.
`with self._lock:
if str_labelvalues not in self._metrics:
...
CREATE METRIC
return self._metrics[str_labelvalues]
`
In high-contention multithreaded instrumentation that repeatedly calls metric.labels(...).inc() or .observe() for already initialized labelsets, this is a potential source of avoidable synchronization overhead. No benchmark results are supplied, so the magnitude of any improvement remains to be measured.
Proposed approach
- Preserve all existing label validation and normalization.
- Attempt a single dictionary lookup for the existing child before acquiring the parent lock, with a missing-key fallback.
- On a miss, acquire
self._lock and recheck _metrics before constructing and storing a child, preserving the existing constructor arguments and behavior.
- Keep creation and mutation synchronized. This proposal only avoids the explicit parent lock on lookup hits; it does not imply that dictionary internals or subsequent metric updates are lock-free.
`
if metric := self._metrics.get(str_labelvalues):
with self._lock:
if str_labelvalues not in self._metrics:
...
CREATE METRIC
return self._metrics[str_labelvalues]
`
Duplicate search
No matching open issue was found in searches of this repository for labels locking, contention, and labels performance.
Summary
Consider avoiding acquisition of the parent metric's
_lockinMetricWrapperBase.labels()when the requested labeled child already exists, while retaining synchronized child creation.Current behavior
In [prometheus_client/metrics.py, lines 173–203
labels()validates and normalizes label values, then acquiresself._lockbefore checking_metrics. Consequently, repeated lookups of existing children all acquire the same parent lock, including lookups for different labelsets.`with self._lock:
`
In high-contention multithreaded instrumentation that repeatedly calls
metric.labels(...).inc()or.observe()for already initialized labelsets, this is a potential source of avoidable synchronization overhead. No benchmark results are supplied, so the magnitude of any improvement remains to be measured.Proposed approach
self._lockand recheck_metricsbefore constructing and storing a child, preserving the existing constructor arguments and behavior.`
if metric := self._metrics.get(str_labelvalues):
with self._lock:
`
Duplicate search
No matching open issue was found in searches of this repository for labels locking, contention, and labels performance.