|
| 1 | +# Native Histograms with Custom Buckets (NHCB) Example |
| 2 | + |
| 3 | +This example demonstrates how to use native histograms with custom bucket boundaries (NHCB) in |
| 4 | +Prometheus Java client. It shows three different types of custom bucket configurations and how |
| 5 | +Prometheus converts them to native histograms with schema -53. |
| 6 | + |
| 7 | +## What are Native Histograms with Custom Buckets? |
| 8 | + |
| 9 | +Native Histograms with Custom Buckets (NHCB) is a Prometheus feature that combines the benefits of: |
| 10 | + |
| 11 | +- **Custom bucket boundaries**: Precisely defined buckets optimized for your specific use case |
| 12 | +- **Native histograms**: Efficient storage and querying capabilities of native histograms |
| 13 | + |
| 14 | +When you configure Prometheus with `convert_classic_histograms_to_nhcb: true`, it converts classic |
| 15 | +histograms with custom buckets into native histograms using schema -53, preserving the custom |
| 16 | +bucket boundaries. |
| 17 | + |
| 18 | +## Example Metrics |
| 19 | + |
| 20 | +This example application generates three different histogram metrics demonstrating different |
| 21 | +bucket configuration strategies: |
| 22 | + |
| 23 | +### 1. API Latency - Arbitrary Custom Boundaries |
| 24 | + |
| 25 | +```java |
| 26 | +Histogram apiLatency = Histogram.builder() |
| 27 | + .name("api_request_duration_seconds") |
| 28 | + .classicUpperBounds(0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0) |
| 29 | + .register(); |
| 30 | +``` |
| 31 | + |
| 32 | +**Use case**: Optimized for typical API response times in seconds. |
| 33 | + |
| 34 | +### 2. Queue Size - Linear Boundaries |
| 35 | + |
| 36 | +```java |
| 37 | +Histogram queueSize = Histogram.builder() |
| 38 | + .name("message_queue_size") |
| 39 | + .classicLinearUpperBounds(10, 10, 10) // 10, 20, 30, ..., 100 |
| 40 | + .register(); |
| 41 | +``` |
| 42 | + |
| 43 | +**Use case**: Equal-width buckets for monitoring queue depth or other discrete values. |
| 44 | + |
| 45 | +### 3. Response Size - Exponential Boundaries |
| 46 | + |
| 47 | +```java |
| 48 | +Histogram responseSize = Histogram.builder() |
| 49 | + .name("http_response_size_bytes") |
| 50 | + .classicExponentialUpperBounds(100, 10, 6) // 100, 1k, 10k, 100k, 1M, 10M |
| 51 | + .register(); |
| 52 | +``` |
| 53 | + |
| 54 | +**Use case**: Data spanning multiple orders of magnitude (bytes, milliseconds, etc). |
| 55 | + |
| 56 | +## Build |
| 57 | + |
| 58 | +This example is built as part of the `client_java` project: |
| 59 | + |
| 60 | +```shell |
| 61 | +./mvnw package |
| 62 | +``` |
| 63 | + |
| 64 | +This creates `./examples/example-custom-buckets/target/example-custom-buckets.jar`. |
| 65 | + |
| 66 | +## Run |
| 67 | + |
| 68 | +With the JAR file present, run: |
| 69 | + |
| 70 | +```shell |
| 71 | +cd ./examples/example-custom-buckets/ |
| 72 | +docker-compose up |
| 73 | +``` |
| 74 | + |
| 75 | +This starts three Docker containers: |
| 76 | + |
| 77 | +- **[http://localhost:9400/metrics](http://localhost:9400/metrics)** - Example application |
| 78 | +- **[http://localhost:9090](http://localhost:9090)** - Prometheus server (with NHCB enabled) |
| 79 | +- **[http://localhost:3000](http://localhost:3000)** - Grafana (user: _admin_, password: _admin_) |
| 80 | + |
| 81 | +You might need to replace `localhost` with `host.docker.internal` on macOS or Windows. |
| 82 | + |
| 83 | +## Verify NHCB Conversion |
| 84 | + |
| 85 | +### 1. Check Prometheus Configuration |
| 86 | + |
| 87 | +The Prometheus configuration enables NHCB conversion: |
| 88 | + |
| 89 | +```yaml |
| 90 | +scrape_configs: |
| 91 | + - job_name: "custom-buckets-demo" |
| 92 | + scrape_protocols: ["PrometheusProto"] |
| 93 | + convert_classic_histograms_to_nhcb: true |
| 94 | + scrape_classic_histograms: true |
| 95 | +``` |
| 96 | +
|
| 97 | +### 2. Verify in Prometheus |
| 98 | +
|
| 99 | +Visit [http://localhost:9090](http://localhost:9090) and run queries: |
| 100 | +
|
| 101 | +```promql |
| 102 | +# View histogram metadata (should show schema -53 for NHCB) |
| 103 | +prometheus_tsdb_head_series |
| 104 | + |
| 105 | +# Calculate quantiles from custom buckets |
| 106 | +histogram_quantile(0.95, rate(api_request_duration_seconds[1m])) |
| 107 | + |
| 108 | +# View raw histogram structure |
| 109 | +api_request_duration_seconds |
| 110 | +``` |
| 111 | + |
| 112 | +### 3. View in Grafana |
| 113 | + |
| 114 | +The Grafana dashboard at [http://localhost:3000](http://localhost:3000) shows: |
| 115 | + |
| 116 | +- p95 and p50 latencies for API endpoints (arbitrary custom buckets) |
| 117 | +- Queue size distribution (linear buckets) |
| 118 | +- Response size distribution (exponential buckets) |
| 119 | + |
| 120 | +## Key Observations |
| 121 | + |
| 122 | +1. **Custom Buckets Preserved**: The custom bucket boundaries you define are preserved when |
| 123 | + converted to NHCB (schema -53). |
| 124 | + |
| 125 | +2. **Dual Representation**: By default, histograms maintain both classic and native |
| 126 | + representations, allowing gradual migration. |
| 127 | + |
| 128 | +3. **Efficient Storage**: Native histograms provide more efficient storage than classic histograms |
| 129 | + while preserving your custom bucket boundaries. |
| 130 | + |
| 131 | +4. **Flexible Bucket Strategies**: You can choose arbitrary, linear, or exponential buckets based |
| 132 | + on your specific monitoring needs. |
| 133 | + |
| 134 | +## When to Use Custom Buckets |
| 135 | + |
| 136 | +Consider using custom buckets (and NHCB) when: |
| 137 | + |
| 138 | +- **Precise boundaries needed**: You know the expected distribution and want specific bucket edges |
| 139 | +- **Migrating from classic histograms**: You want to preserve existing bucket boundaries |
| 140 | +- **Specific use cases**: Default exponential bucketing doesn't fit your distribution well |
| 141 | + - Temperature ranges (might include negative values) |
| 142 | + - Queue depths (discrete values with linear growth) |
| 143 | + - File sizes (exponential growth but with specific thresholds) |
| 144 | + - API latencies (specific SLA boundaries) |
| 145 | + |
| 146 | +## Differences from Standard Native Histograms |
| 147 | + |
| 148 | +| Feature | Standard Native Histograms | NHCB (Schema -53) | |
| 149 | +| ----------------- | ------------------------------- | --------------------------------- | |
| 150 | +| Bucket boundaries | Exponential (base 2^(2^-scale)) | Custom boundaries | |
| 151 | +| Use case | General-purpose | Specific distributions | |
| 152 | +| Mergeability | Can merge with same schema | Cannot merge different boundaries | |
| 153 | +| Configuration | Schema level (0-8) | Explicit boundary list | |
| 154 | + |
| 155 | +## Cleanup |
| 156 | + |
| 157 | +Stop the containers: |
| 158 | + |
| 159 | +```shell |
| 160 | +docker-compose down |
| 161 | +``` |
| 162 | + |
| 163 | +## Further Reading |
| 164 | + |
| 165 | +<!-- editorconfig-checker-disable --> |
| 166 | +<!-- markdownlint-disable MD013 --> |
| 167 | + |
| 168 | +- [Prometheus Native Histograms Specification](https://prometheus.io/docs/specs/native_histograms/) |
| 169 | +- [Prometheus Java Client Documentation](https://prometheus.github.io/client_java/) |
| 170 | +- [OpenTelemetry Exponential Histograms](https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram) |
0 commit comments