Skip to main content

connectrpc_health/
lib.rs

1//! gRPC health-checking service for `connectrpc`.
2//!
3//! Wire-compatible with [`grpc.health.v1.Health`], so `grpc_health_probe`,
4//! `grpcurl`, Kubernetes' gRPC liveness probes, and any other client of the
5//! standard gRPC health protocol just work.
6//!
7//! Non-empty unregistered services return `Err(ConnectError::not_found(_))`
8//! from both `Check` and `Watch`; the empty service auto-subscribes on
9//! `Watch` and returns `Serving` on `Check` by default — see
10//! [`HealthService`]'s `# Unknown services` section for how this relates
11//! to the gRPC Health spec.
12//!
13//! # Cargo features
14//!
15//! * **`client`** (on by default) — re-exports the generated
16//!   `HealthClient` for in-process probes, integration tests, and
17//!   sidecar tooling. Pulls in `connectrpc`'s `client` feature (the
18//!   HTTP/2 transport stack). Server-only deployments drop it with
19//!   `connectrpc-health = { version = "0.7", default-features = false }`;
20//!   `use connectrpc_health::HealthClient` then becomes an unresolved
21//!   import (the type is gone), but the dependency graph loses
22//!   `connectrpc/client`.
23//!
24//! # Writing a custom `Checker`
25//!
26//! [`StaticChecker`] covers most servers. If you implement [`Checker`]
27//! yourself (e.g. report `NotServing` while a database connection is
28//! down), note that the **default `watch` implementation returns
29//! `Unimplemented`** — fine for kubelet / `grpc_health_probe` (they only
30//! call `Check`), but service meshes (Linkerd, Istio) and gRPC clients
31//! with health-based load balancing call `Watch` too. Override the
32//! method if your probes need it. See [`Checker::watch`] for details.
33//!
34//! # Quick start
35//!
36//! ```no_run
37//! use connectrpc::Router;
38//! use connectrpc_health::{install_static, Status};
39//!
40//! // In real code, pass the generated `*_SERVICE_NAME` constant —
41//! // the literal below is a stand-in.
42//! let (router, health) = install_static(Router::new(), [
43//!     "acme.user.v1.UserService",
44//! ]);
45//!
46//! // Later, when something goes wrong. `set_status` errors on unknown
47//! // names; here the name was just registered above, so `.expect`
48//! // documents the invariant.
49//! health
50//!     .set_status("acme.user.v1.UserService", Status::NotServing)
51//!     .expect("registered above");
52//!
53//! // ...and at shutdown. `shutdown()` flips every registered service,
54//! // including the empty whole-process entry seeded on construction.
55//! health.shutdown();
56//! # drop(router);
57//! ```
58//!
59//! For custom logic (probing a database, propagating dependency state),
60//! implement [`Checker`] directly and wrap it in [`HealthService::new`]
61//! / [`HealthService::from_arc`]; see the next section for the one extra
62//! call that path needs.
63//!
64//! # Request limits
65//!
66//! A `HealthCheckRequest` is one service name, so the health routes do not
67//! need the multi-megabyte request ceiling a `connectrpc` service allows by
68//! default. This crate sizes `Check` and `Watch` to [`MAX_REQUEST_BYTES`]
69//! (16 KiB) per request through per-route
70//! [`Limits`](connectrpc::Limits) — see [`request_limits`] for the exact
71//! profile — and a larger request is refused with `resource_exhausted`
72//! before it reaches the [`Checker`]. The profile *replaces* the
73//! service-wide limits on these two routes, whether those are looser or
74//! tighter.
75//!
76//! * [`install_static`] applies [`request_limits`] for you.
77//! * Registering a [`HealthService`] any other way — the generated
78//!   [`HealthExt::register`](HealthExt) or
79//!   [`Router::add_service`](connectrpc::Router::add_service) — does not, so
80//!   follow it with [`apply_request_limits`]`(router, `[`request_limits`]`())`.
81//! * To tune the health routes specifically, call [`apply_request_limits`]
82//!   with your own `Limits` after either path; the later call wins.
83//!
84//! ```no_run
85//! use connectrpc::{Limits, Router};
86//! use connectrpc_health::{apply_request_limits, install_static};
87//!
88//! let (router, health) = install_static(Router::new(), ["acme.user.v1.UserService"]);
89//! // Optional: hold the health routes to 1 KiB instead of the bundled 16 KiB.
90//! let router = apply_request_limits(
91//!     router,
92//!     Limits::default()
93//!         .with_max_request_body_size(1024)
94//!         .with_max_message_size(1024),
95//! );
96//! # drop((router, health));
97//! ```
98//!
99//! [`grpc.health.v1.Health`]: https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
100#![cfg_attr(docsrs, feature(doc_cfg))]
101
102mod checker;
103mod service;
104mod static_checker;
105mod status;
106
107#[path = "generated/connect/mod.rs"]
108mod connect;
109#[path = "generated/buffa/mod.rs"]
110mod proto;
111
112pub use checker::{Checker, StatusStream};
113pub use service::{
114    HealthService, MAX_REQUEST_BYTES, apply_request_limits, install_static, request_limits,
115};
116pub use static_checker::{StaticChecker, UnknownServiceError};
117pub use status::Status;
118
119/// Generated client for calling a `grpc.health.v1.Health` server.
120///
121/// Gated on the `client` Cargo feature (on by default). Server-only
122/// deployments turn off default features to drop the `connectrpc/client`
123/// transport stack from their dependency graph.
124#[cfg(feature = "client")]
125#[cfg_attr(docsrs, doc(cfg(feature = "client")))]
126pub use connect::grpc::health::v1::HealthClient;
127
128/// Generated extension trait that adds `.register(router)` to any
129/// `Arc<S> where S: Health`. Import it to register a [`HealthService`].
130pub use connect::grpc::health::v1::HealthExt;
131
132/// Fully-qualified protobuf service name: `"grpc.health.v1.Health"`.
133pub use connect::grpc::health::v1::HEALTH_SERVICE_NAME;
134
135/// Re-exports of the generated `grpc.health.v1` wire types — request and
136/// response messages, `ServingStatus`, the `*_SPEC` constants. Downstream
137/// crates can build probe loops without regenerating the proto.
138pub mod wire {
139    pub use crate::connect::grpc::health::v1::{HEALTH_CHECK_SPEC, HEALTH_WATCH_SPEC};
140    pub use crate::proto::grpc::health::v1::health_check_response::ServingStatus;
141    pub use crate::proto::grpc::health::v1::{HealthCheckRequest, HealthCheckResponse};
142}