The server now keeps what it learns in table::RibTable. The Adj-RIB-In actor is gone, and with it the queue every UPDATE from every session went through. - A session takes a number in the table when it is established and writes its UPDATEs into the table itself. Its routes are removed when it ends, on a blocking thread since that looks at every prefix; a session torn down without cleaning up still has them removed. - For every prefix the best route is chosen (RFC 4271 §9.1.2.2) as routes come and go. Nothing acts on the choice yet beyond showing it. - The UPDATE decoder hands over the bytes of the attributes it kept, so the table finds the attribute set from them without writing the attributes out again. On a session with two-octet AS numbers the bytes are not in the table's form, and the parsed attributes are used. - /api/routes/received marks the best route of each prefix and returns at most `limit` routes (default 1000): the table can hold hundreds of millions. /api/status has the totals, and the status page says how many it is showing. The time a route was received is no longer kept or shown. - The event stream reports changes of best route (best_changed, best_withdrawn) in place of every route received. Each client follows the table's journal with a reader of its own, from the moment it connects: table::RibTable::reader_from_now. - Metrics: bgp_rib_routes, bgp_rib_prefixes, bgp_rib_attribute_sets and bgp_rib_journal_entries replace bgp_adj_rib_in_routes. - A line is no longer logged at INFO for every UPDATE sent or received; a full table is a million of them. They are at DEBUG. - A table with no room for another peer ends the session with Cease / Out of Resources. Four peers sending a synthetic full table each over TCP, 4.9 million routes: 0.80 s, against 4.76 s before. One peer: 0.45 s against 0.69 s. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
39 lines
1.5 KiB
Rust
39 lines
1.5 KiB
Rust
//! Prometheus scrape endpoint. Counters are updated by the server crate as
|
|
//! messages flow; table-size gauges are refreshed at scrape time.
|
|
|
|
use axum::extract::State;
|
|
use axum::http::{StatusCode, header};
|
|
use axum::response::IntoResponse;
|
|
use bgp_server::metrics::{
|
|
LOCAL_ROUTES, RIB_ATTRIBUTE_SETS, RIB_JOURNAL_ENTRIES, RIB_PREFIXES, RIB_ROUTES,
|
|
};
|
|
use prometheus::{Encoder, TextEncoder};
|
|
|
|
use crate::AppState;
|
|
|
|
pub async fn metricz(State(state): State<AppState>) -> impl IntoResponse {
|
|
// A scrape that cannot read a table fails. Serving the last known
|
|
// size would hide that the daemon is broken from the one system whose
|
|
// job is to notice.
|
|
let gauge = |count: usize| i64::try_from(count).unwrap_or(i64::MAX);
|
|
match state.local_rib.len().await {
|
|
Ok(local) => LOCAL_ROUTES.set(gauge(local)),
|
|
Err(e) => return (StatusCode::SERVICE_UNAVAILABLE, e.to_string()).into_response(),
|
|
}
|
|
RIB_ROUTES.set(gauge(state.rib.len()));
|
|
RIB_PREFIXES.set(gauge(state.rib.prefix_count()));
|
|
RIB_ATTRIBUTE_SETS.set(gauge(state.rib.attrs().len()));
|
|
RIB_JOURNAL_ENTRIES.set(gauge(state.rib.journal_len()));
|
|
|
|
let mut buf = Vec::new();
|
|
match TextEncoder::new().encode(&prometheus::gather(), &mut buf) {
|
|
Ok(()) => (
|
|
StatusCode::OK,
|
|
[(header::CONTENT_TYPE, "text/plain; version=0.0.4")],
|
|
buf,
|
|
)
|
|
.into_response(),
|
|
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
|
|
}
|
|
}
|