Skip to content

v0.7.0 — Observability

Pre-release
Pre-release

Choose a tag to compare

@jamesgober jamesgober released this 06 Jun 03:29
· 7 commits to main since this release

throttle-net v0.7.0 — Observability

See what the limiters are doing in production. v0.7.0 instruments the whole stack — metrics and tracing events around every acquire and every state transition — and it is feature-gated and genuinely zero-cost when off. No breaking changes.

What is throttle-net?

A general-purpose outbound throttling and resilience library. Where rate-net protects your service from being overwhelmed (inbound), throttle-net protects your service from overwhelming the downstreams it calls — and from being banned by them. It paces outbound work, composes limits across dimensions and scopes, retries, fails fast, queues fairly, adapts its concurrency, syncs to provider headers, and — as of this release — reports what it is doing.

What's new in 0.7.0

Metrics (feature metrics)

Emitted through the metrics facade, so they land in whatever recorder your application installs (Prometheus, StatsD, OTLP, …):

Metric Type Emitted when
throttle_acquired_total counter (label limiter) a waiting acquire is granted
throttle_wait_duration histogram, seconds (label limiter) a waiting acquire completes
throttle_queue_depth gauge the queue's waiter count changes
throttle_circuit_state gauge (0/1/2 = closed/half-open/open) a circuit-breaker transition
throttle_rate_current gauge an adaptive limit changes

Tracing events (feature tracing)

Emitted under the throttle_net target: an acquire event carrying limiter, cost, granted, and wait_secs, plus structured events for every documented state transition — circuit-breaker transitions, adaptive limit changes, queue overflow, and queue deadline exhaustion.

Zero-cost when disabled

The instrumentation routes through a small internal layer of #[inline] hooks. With the features off, each hook compiles to an empty function that the optimizer removes, and — critically — its inputs are never evaluated, so the hot path pays nothing. The wait timer is literally zero-sized in that build, which a test asserts. Each hook is also gated to the feature of the limiter that calls it, so the crate has no dead code in any feature combination.

// Wire up any recorder/subscriber in your app; the limiters emit automatically.
// e.g. with the `metrics` feature and a Prometheus recorder installed:
let throttle = throttle_net::Throttle::per_second(100);
// throttle.acquire().await? now increments `throttle_acquired_total`
//                          and records `throttle_wait_duration`.

Breaking changes

None. Observability is additive and behind the metrics / tracing features.

Verification

Run on Windows x86_64, Rust stable 1.93.1; the same commands run in the CI matrix on Linux, macOS, and Windows across stable and MSRV 1.85:

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo clippy --all-targets --no-default-features -- -D warnings
cargo test --all-features
cargo test --no-default-features
cargo test                  # default: metrics/tracing off (zero-cost path)
cargo doc --no-deps && RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo deny check
cargo audit

All green. Counts at this tag (--all-features): 129 unit tests, 7 property tests, 3 circuit-breaker integration tests, 2 retry integration tests, 1 observability integration test, 67 doctests. The exit criteria are covered directly: a test asserts the wait timer is zero-sized with the features off (zero-cost), and the observability integration test installs a local recorder and asserts the throttle_circuit_state gauge fires on a breaker trip (events fire on transitions). Every transition site routes through a hook, and each is exercised by an existing state-transition test.

This release also confirmed the feature matrix is clean: the lib builds under std-only, circuit-breaker-only, adaptive-only, metrics+tracing-only, and provider-llm-only, with no dead code in any combination.

What's next

  • v0.8.0 — Runtime flexibility + feature freeze. async-std and smol support behind feature flags, a no_std-capable algorithm core, the example set filled out, and the feature surface declared frozen for the run to 1.0.

Installation

[dependencies]
throttle-net = "0.7"

# Observability is behind feature flags:
throttle-net = { version = "0.7", features = ["metrics", "tracing"] }

MSRV: Rust 1.85.

Documentation


Changelog: CHANGELOG.md.

Full Changelog: v0.6.0...v0.7.0