Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

[Full changelog](https://github.com/mozilla/glean/compare/v65.2.1...main)

* General
* BREAKING: Remove infill 0 buckets from custom distributions ([#3246](https://github.com/mozilla/glean/pull/3246))
* Swift
* Make `EventMetricType`, `ObjectMetricType`, `URLMetricType` and `Ping` `Sendable` ([#3255](https://github.com/mozilla/glean/pull/3255))
* Glean for iOS is now being built with Xcode 16.4 ([#3270](https://github.com/mozilla/glean/pull/3270))
Expand Down
16 changes: 7 additions & 9 deletions docs/dev/core/internal/payload.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ A [Custom distribution](../../../book/reference/metrics/custom_distribution.md)
| `sum` | Integer | The sum of all recorded values. |
| `values` | Map<String, Integer> | The values in each bucket. The key is the minimum value for the range of that bucket. |

A contiguous range of buckets is always sent, so that the server can aggregate and visualize distributions, without knowing anything about the specific bucketing function used.
This range starts with the first bucket (as specified in the `range_min` parameter), and ends at one bucket beyond the last bucket with a non-zero accumulation (so that the upper bound on the last bucket is retained).
From Glean v66.0.0 on the `values` only includes filled buckets.
Empty buckets are not sent.

For example, suppose you had a custom distribution defined by the following parameters:

Expand All @@ -241,23 +241,21 @@ For example, suppose you had a custom distribution defined by the following para
The following shows the recorded values vs. what is sent in the payload.

```
recorded: 12: 2, 22: 1
sent: 10: 0, 12: 2, 14: 0, 17: 0, 19: 0, 22: 1, 24: 0
recorded: 12: 2, 22: 1
sent: 12: 2, 22: 1
```

Up until Glean v65.0.3 a contiguous range of buckets was always sent, so that the server can aggregate and visualize distributions, without knowing anything about the specific bucketing function used.
This range started with the first bucket (as specified in the `range_min` parameter), and ends at one bucket beyond the last bucket with a non-zero accumulation (so that the upper bound on the last bucket is retained).

#### Example:

```json
{
"sum": 3,
"values": {
"10": 0,
"12": 2,
"14": 0,
"17": 0,
"19": 0,
"22": 1,
"24": 0
}
}
```
Expand Down
10 changes: 9 additions & 1 deletion glean-core/src/histogram/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ fn exponential_range(min: u64, max: u64, bucket_count: usize) -> Vec<u64> {
///
/// Buckets are pre-computed at instantiation with an exponential distribution from `min` to `max`
/// and `bucket_count` buckets.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, MallocSizeOf)]
#[derive(Debug, Clone, Serialize, Deserialize, Eq, MallocSizeOf)]
pub struct PrecomputedExponential {
// Don't serialize the (potentially large) array of ranges, instead compute them on first
// access.
Expand All @@ -68,6 +68,14 @@ pub struct PrecomputedExponential {
pub(crate) bucket_count: usize,
}

impl PartialEq for PrecomputedExponential {
fn eq(&self, other: &Self) -> bool {
// `bucket_ranges` are computed on-demand based on `min`, `max` and `bucket_count`,
// so we don't need to compare it (and thus forcing it to be computed)
self.min == other.min && self.max == other.max && self.bucket_count == other.bucket_count
}
}

impl Bucketing for PrecomputedExponential {
/// Get the bucket for the sample.
///
Expand Down
8 changes: 7 additions & 1 deletion glean-core/src/histogram/linear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn linear_range(min: u64, max: u64, count: usize) -> Vec<u64> {
///
/// Buckets are pre-computed at instantiation with a linear distribution from `min` to `max`
/// and `bucket_count` buckets.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, MallocSizeOf)]
#[derive(Debug, Clone, Serialize, Deserialize, Eq, MallocSizeOf)]
pub struct PrecomputedLinear {
// Don't serialize the (potentially large) array of ranges, instead compute them on first
// access.
Expand All @@ -48,6 +48,12 @@ pub struct PrecomputedLinear {
pub(crate) bucket_count: usize,
}

impl PartialEq for PrecomputedLinear {
fn eq(&self, other: &Self) -> bool {
self.min == other.min && self.max == other.max && self.bucket_count == other.bucket_count
}
}

impl Bucketing for PrecomputedLinear {
/// Get the bucket for the sample.
///
Expand Down
14 changes: 1 addition & 13 deletions glean-core/src/histogram/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,7 @@ impl<B: Bucketing> Histogram<B> {
/// Gets a snapshot of all values from the first bucket until one past the last filled bucket,
/// filling in empty buckets with 0.
pub fn snapshot_values(&self) -> HashMap<u64, u64> {
let mut res = self.values.clone();

let max_bucket = self.values.keys().max().cloned().unwrap_or(0);

for &min_bucket in self.bucketing.ranges() {
// Fill in missing entries.
let _ = res.entry(min_bucket).or_insert(0);
// stop one after the last filled bucket
if min_bucket > max_bucket {
break;
}
}
res
self.values.clone()
}

/// Clear this histogram.
Expand Down