aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-05 00:56:39 +0100
committertv <tv@krebsco.de>2026-11-05 00:56:39 +0100
commit4da17a5e40e34aea27a34241b4f692ae0c827f57 (patch)
treedf4260a9b912957a3f5e7908dca31810c9a3a88f
parent4c6604db27f26998b72fd815b6bd1de5a3b08780 (diff)
neigbours by transport
-rw-r--r--docs/measurements.md1
-rw-r--r--docs/openapi.yaml7
-rw-r--r--src/api/stats.rs3
-rw-r--r--src/api/stats/prometheus.rs12
-rw-r--r--src/swarm/topology/mod.rs22
5 files changed, 43 insertions, 2 deletions
diff --git a/docs/measurements.md b/docs/measurements.md
index 81ca0fc..cff7add 100644
--- a/docs/measurements.md
+++ b/docs/measurements.md
@@ -98,6 +98,7 @@ or caught up over many small messages.
| `topology.view.stable` | `stable_members` | Members whose registry entry says `stable`. |
| `topology.view.neighbours` | `neighbours` | Current neighbours: peers this node keeps alive and syncs with on its own. At least `k` (links chosen by either end count); well above `k` on a stable member that many others chose, bounded at about 8 × k. |
| `topology.view.stableNeighbours` | `stable_neighbours` | Of those, stable members. |
+| `topology.view.neighboursByTransport` | `neighbours_on{transport}` | Of those, by this node's transports (address schemes): the ones the member list gives an address on it. One on two transports counts on both; one it gives none for (not listed yet, or only connecting out), on none. About `k` on each ([`peers.md`](peers.md)); few on one means that transport reaches the swarm through few members. |
| `topology.view.ownTree.members` | `own_tree_members` | Other members in this node's own writer's tree: what its writes go along ([`peers.md`](peers.md)), as this node computes it (what members beyond its neighbours reach is taken from the member list's graph). |
| `topology.view.ownTree.depth`, `.meanDepth` | `own_tree_depth`, `own_tree_mean_depth` | The most hops along it from this node to one of them, and the mean. |
| `topology.view.ownTree.offTree` | `own_tree_off_members` | Members it doesn't reach: no member they reach comes before them, they are below one such, or the member list's graph doesn't connect them to this node. They get its writes by announcement. |
diff --git a/docs/openapi.yaml b/docs/openapi.yaml
index 3d74746..db73ede 100644
--- a/docs/openapi.yaml
+++ b/docs/openapi.yaml
@@ -2069,7 +2069,7 @@ components:
oneOf:
- type: 'null'
- type: object
- required: [members, reachable, stable, neighbours, stableNeighbours, ownTree]
+ required: [members, reachable, stable, neighbours, stableNeighbours, neighboursByTransport, ownTree]
properties:
members:
type: integer
@@ -2083,6 +2083,11 @@ components:
type: integer
stableNeighbours:
type: integer
+ neighboursByTransport:
+ type: object
+ additionalProperties:
+ type: integer
+ description: Neighbours the member list gives an address on each of this node's transports (address schemes); one on two counts on both.
ownTree:
description: This node's own writer's tree; null while it isn't in the member list's graph.
oneOf:
diff --git a/src/api/stats.rs b/src/api/stats.rs
index 3851ef7..306b6a2 100644
--- a/src/api/stats.rs
+++ b/src/api/stats.rs
@@ -187,6 +187,8 @@ struct TopologyView {
stable: usize,
neighbours: usize,
stable_neighbours: usize,
+ /// Neighbours by our transports (`topology::Summary`).
+ neighbours_by_transport: std::collections::BTreeMap<String, usize>,
/// Our own writer's tree (`swarm::tree::own_shape`); absent while we aren't in the
/// member list's graph.
own_tree: Option<OwnTree>,
@@ -382,6 +384,7 @@ pub(crate) async fn collect(state: &AppState, list: &PeerListResponse) -> StatsR
stable: s.stable,
neighbours: s.neighbours,
stable_neighbours: s.stable_neighbours,
+ neighbours_by_transport: s.neighbours_by_transport,
own_tree,
}),
},
diff --git a/src/api/stats/prometheus.rs b/src/api/stats/prometheus.rs
index db3cef3..ffe6593 100644
--- a/src/api/stats/prometheus.rs
+++ b/src/api/stats/prometheus.rs
@@ -713,6 +713,18 @@ pub(super) fn render(stats: &StatsResponse, list: &PeerListResponse) -> String {
"Current neighbours with the stable tier.",
view.stable_neighbours as f64,
);
+ out.family(
+ "neighbours_on",
+ "gauge",
+ "Current neighbours with an address on one of this node's transports, by transport.",
+ );
+ for (transport, neighbours) in &view.neighbours_by_transport {
+ out.sample(
+ "neighbours_on",
+ &[("transport", transport)],
+ *neighbours as f64,
+ );
+ }
if let Some(tree) = &view.own_tree {
out.single(
"own_tree_members",
diff --git a/src/swarm/topology/mod.rs b/src/swarm/topology/mod.rs
index afa13ee..37b2f71 100644
--- a/src/swarm/topology/mod.rs
+++ b/src/swarm/topology/mod.rs
@@ -117,7 +117,7 @@ pub(crate) fn is_full_mesh(state: &AppState) -> bool {
}
/// The current view in numbers, once known.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
pub(crate) struct Summary {
/// Registered members, us included.
pub(crate) members: usize,
@@ -126,6 +126,10 @@ pub(crate) struct Summary {
pub(crate) stable: usize,
pub(crate) neighbours: usize,
pub(crate) stable_neighbours: usize,
+ /// For each of our transports (address scheme), the neighbours the member list
+ /// gives an address on it: one on two transports counts on both, one it gives none
+ /// for (not listed yet, or only connecting out) on none.
+ pub(crate) neighbours_by_transport: BTreeMap<String, usize>,
}
pub(crate) fn summary(state: &AppState) -> Option<Summary> {
@@ -148,6 +152,22 @@ pub(crate) fn summary(state: &AppState) -> Option<Summary> {
.iter()
.filter(|m| view.stable.contains(*m))
.count(),
+ neighbours_by_transport: view
+ .local_transports
+ .iter()
+ .map(|transport| {
+ let on_it = view
+ .neighbours
+ .iter()
+ .filter(|m| {
+ view.transports
+ .get(*m)
+ .is_some_and(|theirs| theirs.contains(transport))
+ })
+ .count();
+ (transport.clone(), on_it)
+ })
+ .collect(),
})
}