From 0f75f5af71888cfcea63d17191b2826e99fb8336 Mon Sep 17 00:00:00 2001 From: Bruce Guenter Date: Wed, 14 Jan 2026 13:19:44 -0600 Subject: [PATCH] enhancement(observability)!: Expand internal histogram precision With the introduction of internal timing measurements in seconds as histograms, the lower bound of 2^-6 (~15ms) in the internal histogram storage is no longer precise enough to properly capture the required measurements. This change extends the smallest bucket down to 2^-12 (~244us), which is the inverse of the largest bucket of 2^12 (4096). --- ...d-internal-histogram-precision.breaking.md | 5 ++ lib/vector-core/src/metrics/storage.rs | 52 +++++++++++-------- src/sources/internal_metrics.rs | 6 +-- 3 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 changelog.d/expand-internal-histogram-precision.breaking.md diff --git a/changelog.d/expand-internal-histogram-precision.breaking.md b/changelog.d/expand-internal-histogram-precision.breaking.md new file mode 100644 index 0000000000000..47bfb31245e3a --- /dev/null +++ b/changelog.d/expand-internal-histogram-precision.breaking.md @@ -0,0 +1,5 @@ +Increased the number of buckets in internal histograms to reduce the smallest +bucket down to approximately 0.000244 (2.0^-12). Since this shifts all the +bucket values out, it may break VRL scripts that rely on the previous values. + +authors: bruceg diff --git a/lib/vector-core/src/metrics/storage.rs b/lib/vector-core/src/metrics/storage.rs index 29030e927d7ee..86cf82c1ef7ea 100644 --- a/lib/vector-core/src/metrics/storage.rs +++ b/lib/vector-core/src/metrics/storage.rs @@ -31,15 +31,15 @@ impl Storage for VectorStorage { #[derive(Debug)] pub(super) struct Histogram { - buckets: Box<[(f64, AtomicU32); 20]>, + buckets: Box<[(f64, AtomicU32); 26]>, count: AtomicU64, sum: AtomicF64, } impl Histogram { - const MIN_BUCKET: f64 = 0.015_625; // (-6_f64).exp2() is not const yet - const MIN_BUCKET_EXP: f64 = -6.0; - const BUCKETS: usize = 20; + const MIN_BUCKET: f64 = 1.0 / (1 << 12) as f64; // f64::powi() is not const yet + const MIN_BUCKET_EXP: f64 = -12.0; + const BUCKETS: usize = 26; pub(crate) fn new() -> Self { // Box to avoid having this large array inline to the structure, blowing @@ -52,25 +52,31 @@ impl Histogram { // long-tail. This also lets us find the right bucket to record into using simple // constant-time math operations instead of a loop-and-compare construct. let buckets = Box::new([ - ((-6_f64).exp2(), AtomicU32::new(0)), - ((-5_f64).exp2(), AtomicU32::new(0)), - ((-4_f64).exp2(), AtomicU32::new(0)), - ((-3_f64).exp2(), AtomicU32::new(0)), - ((-2_f64).exp2(), AtomicU32::new(0)), - ((-1_f64).exp2(), AtomicU32::new(0)), - (0_f64.exp2(), AtomicU32::new(0)), - (1_f64.exp2(), AtomicU32::new(0)), - (2_f64.exp2(), AtomicU32::new(0)), - (3_f64.exp2(), AtomicU32::new(0)), - (4_f64.exp2(), AtomicU32::new(0)), - (5_f64.exp2(), AtomicU32::new(0)), - (6_f64.exp2(), AtomicU32::new(0)), - (7_f64.exp2(), AtomicU32::new(0)), - (8_f64.exp2(), AtomicU32::new(0)), - (9_f64.exp2(), AtomicU32::new(0)), - (10_f64.exp2(), AtomicU32::new(0)), - (11_f64.exp2(), AtomicU32::new(0)), - (12_f64.exp2(), AtomicU32::new(0)), + (2.0f64.powi(-12), AtomicU32::new(0)), + (2.0f64.powi(-11), AtomicU32::new(0)), + (2.0f64.powi(-10), AtomicU32::new(0)), + (2.0f64.powi(-9), AtomicU32::new(0)), + (2.0f64.powi(-8), AtomicU32::new(0)), + (2.0f64.powi(-7), AtomicU32::new(0)), + (2.0f64.powi(-6), AtomicU32::new(0)), + (2.0f64.powi(-5), AtomicU32::new(0)), + (2.0f64.powi(-4), AtomicU32::new(0)), + (2.0f64.powi(-3), AtomicU32::new(0)), + (2.0f64.powi(-2), AtomicU32::new(0)), + (2.0f64.powi(-1), AtomicU32::new(0)), + (2.0f64.powi(0), AtomicU32::new(0)), + (2.0f64.powi(1), AtomicU32::new(0)), + (2.0f64.powi(2), AtomicU32::new(0)), + (2.0f64.powi(3), AtomicU32::new(0)), + (2.0f64.powi(4), AtomicU32::new(0)), + (2.0f64.powi(5), AtomicU32::new(0)), + (2.0f64.powi(6), AtomicU32::new(0)), + (2.0f64.powi(7), AtomicU32::new(0)), + (2.0f64.powi(8), AtomicU32::new(0)), + (2.0f64.powi(9), AtomicU32::new(0)), + (2.0f64.powi(10), AtomicU32::new(0)), + (2.0f64.powi(11), AtomicU32::new(0)), + (2.0f64.powi(12), AtomicU32::new(0)), (f64::INFINITY, AtomicU32::new(0)), ]); Self { diff --git a/src/sources/internal_metrics.rs b/src/sources/internal_metrics.rs index 47b5fc7101902..f576efa2a71a1 100644 --- a/src/sources/internal_metrics.rs +++ b/src/sources/internal_metrics.rs @@ -256,7 +256,7 @@ mod tests { // [`metrics::handle::Histogram::new`] are hard-coded. If this // check fails you might look there and see if we've allowed // users to set their own bucket widths. - assert_eq!(buckets[9].count, 2); + assert_eq!(buckets[15].count, 2); assert_eq!(*count, 2); assert_eq!(*sum, 11.0); } @@ -273,8 +273,8 @@ mod tests { // [`metrics::handle::Histogram::new`] are hard-coded. If this // check fails you might look there and see if we've allowed // users to set their own bucket widths. - assert_eq!(buckets[9].count, 1); - assert_eq!(buckets[10].count, 1); + assert_eq!(buckets[15].count, 1); + assert_eq!(buckets[16].count, 1); assert_eq!(*count, 2); assert_eq!(*sum, 16.1); }