Skip to content

Commit 12923ca

Browse files
authored
fix(metrics/gauge): implement Atomic<u64> for AtomicU64 (prometheus#226)
Between forcing end users to do endless `as i64` for things that are clearly `u64` and having one error case for rarely used protobuf when a gauge is set to `u64::MAX`, the latter seems like the right choice. Signed-off-by: Ivan Babrou <[email protected]>
1 parent ad05f0f commit 12923ca

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [0.23.0] - unreleased
88

9+
910
### Changed
1011

1112
- `ConstCounter::new` now requires specifying the type of literal arguments, like this: `ConstCounter::new(42u64);`.
@@ -14,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1415
- Update `prost` dependencies to `v0.12`.
1516
See [PR 198].
1617

18+
- Implement `Atomic<u64>` for `AtomicU64` for gauges.
19+
See [PR 226].
20+
21+
[PR 226]: https://github.com/prometheus/client_rust/pull/198
1722
[PR 198]: https://github.com/prometheus/client_rust/pull/198
1823

1924
### Added

src/encoding.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,19 @@ impl EncodeGaugeValue for i64 {
568568
}
569569
}
570570

571+
impl EncodeGaugeValue for u64 {
572+
fn encode(&self, encoder: &mut GaugeValueEncoder) -> Result<(), std::fmt::Error> {
573+
// Between forcing end users to do endless as i64 for things that are
574+
// clearly u64 and having one error case for rarely used protobuf when
575+
// a gauge is set to u64::MAX, the latter seems like the right choice.
576+
if *self == u64::MAX {
577+
return Err(std::fmt::Error);
578+
}
579+
580+
encoder.encode_i64(*self as i64)
581+
}
582+
}
583+
571584
impl EncodeGaugeValue for f64 {
572585
fn encode(&self, encoder: &mut GaugeValueEncoder) -> Result<(), std::fmt::Error> {
573586
encoder.encode_f64(*self)

src/encoding/protobuf.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ mod tests {
453453
use std::borrow::Cow;
454454
use std::collections::HashSet;
455455
use std::sync::atomic::AtomicI64;
456+
use std::sync::atomic::AtomicU64;
456457

457458
#[test]
458459
fn encode_counter_int() {
@@ -600,6 +601,43 @@ mod tests {
600601
}
601602
}
602603

604+
#[test]
605+
fn encode_gauge_u64_normal() {
606+
let mut registry = Registry::default();
607+
let gauge = Gauge::<u64, AtomicU64>::default();
608+
registry.register("my_gauge", "My gauge", gauge.clone());
609+
gauge.set(12345);
610+
611+
let metric_set = encode(&registry).unwrap();
612+
let family = metric_set.metric_families.first().unwrap();
613+
assert_eq!("my_gauge", family.name);
614+
assert_eq!("My gauge.", family.help);
615+
616+
assert_eq!(
617+
openmetrics_data_model::MetricType::Gauge as i32,
618+
extract_metric_type(&metric_set)
619+
);
620+
621+
match extract_metric_point_value(&metric_set) {
622+
openmetrics_data_model::metric_point::Value::GaugeValue(value) => {
623+
let expected = openmetrics_data_model::gauge_value::Value::IntValue(12345);
624+
assert_eq!(Some(expected), value.value);
625+
}
626+
_ => panic!("wrong value type"),
627+
}
628+
}
629+
630+
#[test]
631+
fn encode_gauge_u64_max() {
632+
let mut registry = Registry::default();
633+
let gauge = Gauge::<u64, AtomicU64>::default();
634+
registry.register("my_gauge", "My gauge", gauge.clone());
635+
gauge.set(u64::MAX);
636+
637+
// This expected to fail as protobuf uses i64 and u64::MAX does not fit into it.
638+
assert!(encode(&registry).is_err());
639+
}
640+
603641
#[test]
604642
fn encode_counter_family() {
605643
let mut registry = Registry::default();

src/metrics/gauge.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,33 @@ impl Atomic<i64> for AtomicI64 {
213213
}
214214
}
215215

216+
#[cfg(target_has_atomic = "64")]
217+
impl Atomic<u64> for AtomicU64 {
218+
fn inc(&self) -> u64 {
219+
self.inc_by(1)
220+
}
221+
222+
fn inc_by(&self, v: u64) -> u64 {
223+
self.fetch_add(v, Ordering::Relaxed)
224+
}
225+
226+
fn dec(&self) -> u64 {
227+
self.dec_by(1)
228+
}
229+
230+
fn dec_by(&self, v: u64) -> u64 {
231+
self.fetch_sub(v, Ordering::Relaxed)
232+
}
233+
234+
fn set(&self, v: u64) -> u64 {
235+
self.swap(v, Ordering::Relaxed)
236+
}
237+
238+
fn get(&self) -> u64 {
239+
self.load(Ordering::Relaxed)
240+
}
241+
}
242+
216243
#[cfg(target_has_atomic = "64")]
217244
impl Atomic<f64> for AtomicU64 {
218245
fn inc(&self) -> f64 {

0 commit comments

Comments
 (0)