mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(relay): bind single-node probes to loopback
Signed-off-by: npub13n66s06epmqf2kc3v373ez8hj65cuzyvxzjf93vwpervxqn2u7jq2qd9je <8cf5a83f590ec0955b11647d1c88f796a98e088c30a492c58e0e46c3026ae7a4@buzz.block.builderlab.xyz>
This commit is contained in:
parent
024491648d
commit
6b7b533f14
@@ -1,4 +1,5 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -18,7 +19,7 @@ use buzz_db::{Db, DbConfig};
|
||||
use buzz_pubsub::{rate_limiter::AdmissionRateLimiter, InProcessNip98ReplayGuard, PubSubManager};
|
||||
use buzz_search::SearchService;
|
||||
|
||||
use buzz_relay::config::{Config, MAX_DRAIN_JITTER_MS};
|
||||
use buzz_relay::config::{Config, RelayProfile, MAX_DRAIN_JITTER_MS};
|
||||
use buzz_relay::metrics as relay_metrics;
|
||||
use buzz_relay::router::{build_health_router, build_router};
|
||||
use buzz_relay::state::{AppBackends, AppState};
|
||||
@@ -1217,7 +1218,7 @@ async fn run_single_node(config: Config, tracer_init: telemetry::TracerInit) ->
|
||||
));
|
||||
}
|
||||
|
||||
relay_metrics::install(config.metrics_port, usage_metrics_idle_timeout_secs(60));
|
||||
relay_metrics::install_loopback(config.metrics_port, usage_metrics_idle_timeout_secs(60));
|
||||
let db_path =
|
||||
std::env::var("BUZZ_LOCAL_DB").unwrap_or_else(|_| "buzz-local.sqlite".to_string());
|
||||
let media_root =
|
||||
@@ -1334,6 +1335,13 @@ async fn run_single_node(config: Config, tracer_init: telemetry::TracerInit) ->
|
||||
/// roughly the 5s grace plus the ack wait.
|
||||
const GRACEFUL_DRAIN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
|
||||
|
||||
fn health_listener_ip(profile: RelayProfile) -> IpAddr {
|
||||
match profile {
|
||||
RelayProfile::SingleNode => IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
RelayProfile::Production => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
|
||||
}
|
||||
}
|
||||
|
||||
async fn serve(
|
||||
router: axum::Router,
|
||||
health_router: axum::Router,
|
||||
@@ -1341,10 +1349,11 @@ async fn serve(
|
||||
) -> anyhow::Result<()> {
|
||||
let config = &state.config;
|
||||
|
||||
let health_listener = tokio::net::TcpListener::bind(("0.0.0.0", config.health_port))
|
||||
let health_host = health_listener_ip(config.profile);
|
||||
let health_listener = tokio::net::TcpListener::bind((health_host, config.health_port))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Failed to bind health port {}: {e}", config.health_port))?;
|
||||
info!(port = config.health_port, "Health probe listener started");
|
||||
info!(host = %health_host, port = config.health_port, "Health probe listener started");
|
||||
tokio::spawn(async move {
|
||||
axum::serve(health_listener, health_router).await.ok();
|
||||
});
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
//! recorded by [`track_metrics`] middleware on the app router. Buzz-specific
|
||||
//! metrics are recorded inline at their call sites.
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use axum::{
|
||||
@@ -56,6 +57,17 @@ const GIT_PACK_BUCKETS: [f64; 9] = [0.0, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 1
|
||||
/// Integer-count buckets for fan-out recipient histograms.
|
||||
const FANOUT_BUCKETS: [f64; 9] = [0.0, 1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 500.0, 1000.0];
|
||||
|
||||
fn prometheus_listener(port: u16, loopback_only: bool) -> SocketAddr {
|
||||
SocketAddr::new(
|
||||
IpAddr::V4(if loopback_only {
|
||||
Ipv4Addr::LOCALHOST
|
||||
} else {
|
||||
Ipv4Addr::UNSPECIFIED
|
||||
}),
|
||||
port,
|
||||
)
|
||||
}
|
||||
|
||||
/// Install the global metrics recorder and spawn the Prometheus HTTP exporter.
|
||||
///
|
||||
/// `build()` returns the recorder + exporter future and internally spawns
|
||||
@@ -64,8 +76,17 @@ const FANOUT_BUCKETS: [f64; 9] = [0.0, 1.0, 5.0, 10.0, 25.0, 50.0, 100.0, 500.0,
|
||||
/// Must be called from within a Tokio runtime.
|
||||
/// Panics if a recorder is already installed or the port is in use.
|
||||
pub fn install(port: u16, gauge_idle_timeout_secs: u64) {
|
||||
install_on(prometheus_listener(port, false), gauge_idle_timeout_secs);
|
||||
}
|
||||
|
||||
/// Install the metrics exporter on loopback for the single-node profile.
|
||||
pub fn install_loopback(port: u16, gauge_idle_timeout_secs: u64) {
|
||||
install_on(prometheus_listener(port, true), gauge_idle_timeout_secs);
|
||||
}
|
||||
|
||||
fn install_on(listener: SocketAddr, gauge_idle_timeout_secs: u64) {
|
||||
let (recorder, exporter) = PrometheusBuilder::new()
|
||||
.with_http_listener(([0, 0, 0, 0], port))
|
||||
.with_http_listener(listener)
|
||||
// Remove gauge series that the relay intentionally stops emitting.
|
||||
.idle_timeout(
|
||||
MetricKindMask::GAUGE,
|
||||
@@ -205,3 +226,14 @@ pub async fn track_metrics(req: Request, next: Next) -> Response {
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::prometheus_listener;
|
||||
|
||||
#[test]
|
||||
fn single_node_metrics_listener_is_loopback_only() {
|
||||
assert!(prometheus_listener(9102, true).ip().is_loopback());
|
||||
assert!(prometheus_listener(9102, false).ip().is_unspecified());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user