Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.23.9"
version = "0.24.2"
edition = "2024"
license = "Apache-2.0"

Expand Down
42 changes: 33 additions & 9 deletions docs/metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,25 @@ PegaFlow exposes the following metrics for monitoring KV cache operations:

### HLL Reuse Metrics
- **pegaflow_hll_cardinality** (Gauge)
- Estimated distinct block hashes observed in a configured sliding window
- Estimated distinct `(namespace, block hash)` objects classified as misses in a configured sliding window
- Labels: `window` (`15m`, `1h`, `1d` by default)
- Use case: Derive approximate prefix reuse over longer windows without
storing every block hash

- **pegaflow_hll_total_requests** (Gauge)
- Total block hash observations in the same configured sliding window
- Total queried blocks in the same configured sliding window, including ready blocks and duplicates
- Labels: `window` (`15m`, `1h`, `1d` by default)
- Use case: Denominator for HLL-based estimated hit-rate PromQL
- Use case: Denominator for HLL-based reference reuse rate

PegaFlow does not export a separate HLL hit-rate gauge. Use PromQL so the
ratio is computed from values in the same scrape:
- **pegaflow_hll_estimated_hit_rate** (Gauge)
- Server-computed miss-based infinite-cache reuse reference from the same
HLL snapshot as the two gauges above
- Labels: `window`
- Value is clamped to `[0, 1]`

The existing cardinality and total metrics are retained. Existing PromQL
continues to work, but new dashboards should prefer the direct gauge because
it applies the same cardinality clamp as the tracker:

```promql
1 - (
Expand All @@ -162,6 +169,18 @@ ratio is computed from values in the same scrape:
)
```

```promql
pegaflow_hll_estimated_hit_rate{window="1h"}
```

This is a metrics semantic update, not an `/metrics` protocol breaking change:
metric names, types, existing labels, and the HTTP endpoint are unchanged;
the new gauge is additive. The default HLL size changes from 16,384 registers
(`bucket_bits=14`, about 0.8% standard error) to 65,536 registers
(`bucket_bits=16`, about 0.4%). Three default windows use about 192 KiB of
register storage; sliding slots make the live tracker a few MiB per server.
The setting remains configurable with `--metric-hll-bucket-bits`.

### Save Metrics (GPU → CPU)
- **pegaflow_save_bytes_total** (Counter)
- Total bytes saved from GPU to CPU storage
Expand Down Expand Up @@ -245,15 +264,17 @@ tier counters.
- Only used when `--metrics-otel-endpoint` is set

- `--metric-hll-windows`: Comma-separated HLL sliding windows for estimated
prefix reuse (default: `15m,1h,24h`)
prefix reuse (default: `15m,1h,1d`)
- Supported units: `s`, `m`, `h`, `d`
- Each configured duration becomes a canonical `window` label. For example,
the default config exports `window="15m"`, `window="1h"`, and `window="1d"`.
- Empty entries such as `15m,,1h` and duplicate durations such as `1h,60m`
are rejected at startup.

- `--metric-hll-bucket-bits`: HLL bucket index bits (default: `14`)
- Higher values use more memory and lower estimation error.
- `--metric-hll-bucket-bits`: HLL bucket index bits (default: `16`)
- `2^16 = 65,536` registers per window and about 0.4% standard error.
- Higher values use more memory and lower estimation error; `18` remains
the supported maximum.

**Example: Prometheus Metrics**
```bash
Expand Down Expand Up @@ -484,7 +505,10 @@ sum by (le) (
rate(pegaflow_cache_residence_duration_seconds_bucket{reason="pressure"}[5m])
)

# HLL estimated hit rate for the 1h window
# HLL estimated hit rate for the 1h window (preferred)
pegaflow_hll_estimated_hit_rate{window="1h"}

# Backward-compatible derivation from the retained gauges
1 - (
pegaflow_hll_cardinality{window="1h"}
/
Expand Down
4 changes: 3 additions & 1 deletion docs/p2p.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,11 @@ P2P-related Prometheus metrics (on `:9091/metrics` by default):

| Metric | Type | Description |
|---|---|---|
| `pegaflow_rdma_fetch_total` | Counter | Total RDMA fetch operations |
| `pegaflow_rdma_fetch_total` | Counter | Total per-segment RDMA fetch operations |
| `pegaflow_rdma_fetch_duration` | Histogram | RDMA fetch latency distribution |
| `pegaflow_rdma_fetch_bytes` | Counter | Total bytes fetched via RDMA |
| `pegaflow_rdma_fetch_plan_segments` | Histogram | Planned segment count per executed RDMA fetch plan |
| `pegaflow_rdma_fetch_plan_completed_segments` | Histogram | Completed segment count before a plan stops |
| `pegaflow_rdma_qps` | Gauge | Active RDMA queue pairs |
| `pegaflow_transfer_lock_active` | UpDownCounter | Currently held transfer locks |
| `pegaflow_transfer_lock_timeouts_total` | Counter | Transfer lock timeout events |
Expand Down
1 change: 0 additions & 1 deletion examples/ipc_sender.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ class CudaIPCWrapper:
"""Wrapper for CUDA IPC handle with tensor metadata."""

def __init__(self, tensor: torch.Tensor):
assert tensor.storage_offset() == 0, "Tensor must have zero storage offset"
assert tensor.is_contiguous(), "Tensor must be contiguous"

storage = tensor.untyped_storage()
Expand Down
48 changes: 48 additions & 0 deletions pegaflow-common/src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,51 @@ impl BlockKey {
(self.namespace.capacity() + self.hash.capacity() + 48) as u64
}
}

/// Encode a raw content hash with a hybrid-cache group id.
///
/// Group 0 keeps the raw hash byte-for-byte so existing single-group caches
/// (and every current connector) stay bit-identical. Groups >= 1 append the
/// big-endian group id, which cannot collide with a raw content hash because
/// every real hash family is fixed-length.
pub fn group_hash(hash: &[u8], group_id: u32) -> Vec<u8> {
if group_id == 0 {
return hash.to_vec();
}
let mut encoded = Vec::with_capacity(hash.len() + 4);
encoded.extend_from_slice(hash);
encoded.extend_from_slice(&group_id.to_be_bytes());
encoded
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn group_zero_hash_is_bit_identical_to_raw() {
// Backward-compat contract: existing single-group deployments must not
// observe any key change.
let hash = b"sha256-content-hash";
assert_eq!(group_hash(hash, 0), hash.to_vec());
assert_eq!(group_hash(b"", 0), Vec::<u8>::new());
}

#[test]
fn nonzero_group_appends_big_endian_group_id() {
let hash = [0xAA, 0xBB];
assert_eq!(group_hash(&hash, 1), vec![0xAA, 0xBB, 0, 0, 0, 1]);
assert_eq!(group_hash(&hash, 0x01020304), vec![0xAA, 0xBB, 1, 2, 3, 4]);
}

#[test]
fn distinct_groups_do_not_share_keys() {
// The same content hash in two groups must be two different keys, and
// an encoded key must never equal a raw hash of any length (length
// differs, so this holds even across hash families).
let hash = [1, 2, 3, 4];
assert_ne!(group_hash(&hash, 0), group_hash(&hash, 1));
assert_ne!(group_hash(&hash, 1), group_hash(&hash, 2));
assert_ne!(group_hash(&hash, 1).len(), hash.len());
}
}
Loading
Loading