Status: Accepted Date: 2026-02-27
At the time of this ADR, the proto file defined five gRPC services —
GlobalService, NeighborService, RibService, InjectionService, and
ControlService. Three of these were
implemented (M1–M4), but GlobalService and ControlService had no server-side
implementation. Clients generated from the proto would get UNIMPLEMENTED for 5 RPCs.
Additionally, shutdown was not coordinated. The run() function sent
PeerManagerCommand::Shutdown to the PeerManager then returned immediately,
allowing the tokio runtime to drop and abort tasks mid-shutdown — before peers
could receive NOTIFICATION messages.
Options considered for shutdown coordination:
- tokio-util
CancellationToken— clean API but adds a new dependency. tokio::sync::watch<bool>— no new dep, but heavier than needed for a one-shot signal.- Two
oneshotchannels — minimal, no new deps, clear ownership. One for ctrl-c-initiated gRPC shutdown, one for RPC-initiated shutdown.
Implement both services and use two oneshot channels for shutdown coordination.
GetGlobal— returns read-only ASN, router_id, listen_port set at construction.SetGlobal— returnsUNIMPLEMENTED. Runtime ASN/router-id mutation is complex (requires re-negotiating all sessions) and deferred to post-v1.
GetHealth— queries PeerManager viaListPeersand filters to Established state foractive_peers. Queries Loc-RIB count viaQueryLocRibCountfortotal_routes. Reports uptime fromInstantcaptured at daemon start. (M8: previously counted all configured peers and summed per-peer prefix counts.)GetMetrics— gathers Prometheus text from the explicitBgpMetricsregistry, reusing the same pattern asmetrics_server.rs.Shutdown— sendsPeerManagerCommand::Shutdown, then fires the gRPC shutdown oneshot. The shutdown sequence is spawned so the RPC can return a response before the server stops.
Two oneshot channels:
grpc_shutdown_tx/rx— main fires this after PeerManager drains, stopping tonic.rpc_shutdown_tx/rx— ControlService fires this from the Shutdown RPC.
Shutdown flow (ctrl-c path):
tokio::select!onctrl_c()andrpc_shutdown_rx- Send
PeerManagerCommand::Shutdownto PeerManager - Await PeerManager
JoinHandle(peers send NOTIFICATIONs, close TCP) - Send
grpc_shutdown_tx— tonic'sserve_with_shutdownexits run()returns cleanly
Shutdown flow (RPC path):
- Shutdown RPC spawns: send
PeerManagerCommand::Shutdown, then firerpc_shutdown_tx - Main's
select!detectsrpc_shutdown_rx, enters the same drain sequence
Positive:
- All 5 proto services are now implemented — no more UNIMPLEMENTED surprises.
- Peers receive proper Cease NOTIFICATIONs on shutdown.
- gRPC server exits gracefully — in-flight RPCs can complete.
- No new dependencies.
Negative:
SetGlobalreturns UNIMPLEMENTED — callers must handle this. Documented as deferred; the proto already exists so the surface is stable.ShutdownRPC has no authentication — any gRPC client can shut down the daemon. Access control is a post-v1 concern (same as all other RPCs).