aboutsummaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/api')
-rw-r--r--src/api/dashboard/dashboard.html13
-rw-r--r--src/api/peers.rs25
-rw-r--r--src/api/stats.rs8
-rw-r--r--src/api/stats/prometheus.rs6
4 files changed, 49 insertions, 3 deletions
diff --git a/src/api/dashboard/dashboard.html b/src/api/dashboard/dashboard.html
index 601b578..fa4d436 100644
--- a/src/api/dashboard/dashboard.html
+++ b/src/api/dashboard/dashboard.html
@@ -284,6 +284,7 @@
#peers td.via { max-width: 0; min-width: 8em; width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* A peer's status (not the legend's "dormant"). */
#peers .active { color: var(--ok); } #peers .dormant { color: var(--bad); }
+ #peers .refused { color: var(--bad); font-weight: 600; }
#peers .unknown { color: var(--muted); }
.tree { font-family: ui-monospace, monospace; font-size: 12px; }
/* Every entry indented alike: a branch's triangle sits in the indentation, left of
@@ -658,9 +659,15 @@ function renderPeers() {
el("td", { title: id + (peer.neighbour ? ""
: "\nnot a neighbour: this node only answers it, and syncs with it on its own only as a neighbour") },
peer.alias || el("span", { class: "id" }, short(id))),
- // How long dormant is about how long unseen ("seen"): in the tooltip only.
- el("td", { class: peer.status, title: peer.status === "dormant"
- ? "dormant for " + age(aged("dormantForSecs", peer.dormantForSecs)) : "" }, peer.status),
+ // How long dormant is about how long unseen ("seen"): in the tooltip only. Refused
+ // as a whole (its own change broke the swarm's rules) above all.
+ peer.refused
+ ? el("td", { class: "refused", title: "refused as a whole for " +
+ age(aged("secsAgo", peer.refused.secsAgo)) + ": its change " +
+ short(peer.refused.change) + " broke the swarm's rules (" + peer.refused.why +
+ "), so this node sends it nothing" }, "refused")
+ : el("td", { class: peer.status, title: peer.status === "dormant"
+ ? "dormant for " + age(aged("dormantForSecs", peer.dormantForSecs)) : "" }, peer.status),
el("td", {}, age(aged("lastSeenSecsAgo", peer.lastSeenSecsAgo))),
el("td", { class: "id via", title: via }, via),
el("td", {}, rtt),
diff --git a/src/api/peers.rs b/src/api/peers.rs
index cd9381f..2bdde96 100644
--- a/src/api/peers.rs
+++ b/src/api/peers.rs
@@ -50,6 +50,9 @@ pub(crate) struct PeerStatus {
/// How long its heads have differed from ours with no sync message from it in
/// between; absent while they match.
pub(crate) diverged_for_secs: Option<u64>,
+ /// Whether we refuse it as a whole, its own change having broken the swarm's rules
+ /// (`access::screen`): then we send it nothing.
+ pub(crate) refused: Option<RefusedPeer>,
pub(crate) has_pending_delivery: bool,
/// Epoch the peer last advertised; absent until it has sent a message.
pub(crate) epoch: Option<EpochRecord>,
@@ -113,6 +116,16 @@ pub(crate) struct ThroughputInfo {
pub(crate) secs_ago: u64,
}
+/// A peer refused as a whole (`PeerStatus::refused`).
+#[derive(Debug, Clone, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub(crate) struct RefusedPeer {
+ pub(crate) secs_ago: u64,
+ /// The change of its that broke the rules, and why.
+ pub(crate) change: String,
+ pub(crate) why: String,
+}
+
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct PeerListResponse {
@@ -176,6 +189,17 @@ pub(crate) async fn peer_list(state: &AppState) -> PeerListResponse {
samples: estimate.samples,
secs_ago: now.saturating_duration_since(estimate.at).as_secs(),
});
+ let refused = {
+ state
+ .screening
+ .lock_or_recover()
+ .refusing(&peer)
+ .map(|refusing| RefusedPeer {
+ secs_ago: now.saturating_duration_since(refusing.since).as_secs(),
+ change: refusing.change.to_string(),
+ why: refusing.why.clone(),
+ })
+ };
let ps = peer_state_mutex.lock().await;
let (status, dormant_for_secs) = match ps.liveness {
@@ -223,6 +247,7 @@ pub(crate) async fn peer_list(state: &AppState) -> PeerListResponse {
diverged_for_secs: ps
.diverged_since
.map(|t| now.saturating_duration_since(t).as_secs()),
+ refused,
has_pending_delivery: ps.sync.sending.is_some() || ps.sync.retry_at.is_some(),
epoch: ps.epoch.map(EpochRecord::from),
same_epoch: ps.epoch.map(|epoch| epoch == local_epoch),
diff --git a/src/api/stats.rs b/src/api/stats.rs
index 8eaff0c..1f0701d 100644
--- a/src/api/stats.rs
+++ b/src/api/stats.rs
@@ -127,6 +127,9 @@ struct SyncStats {
last_from_neighbour_secs: Option<u64>,
/// Peers whose heads differ from ours while sync is idle (`peers.md`, healing).
diverged_peers: usize,
+ /// Peers refused as a whole, their own changes having broken the swarm's rules
+ /// (`access::screen`): sent nothing.
+ refused_peers: usize,
/// The largest sync message received since start (its Automerge part). Larger
/// transfers come as deltas (blobs).
largest_received_bytes: u64,
@@ -324,11 +327,15 @@ pub(crate) async fn collect(state: &AppState, list: &PeerListResponse) -> StatsR
let mut incidents = Incidents::default();
let mut last_from_neighbour_secs: Option<u64> = None;
let mut diverged_peers = 0;
+ let mut refused_peers = 0;
for peer in list.peers.values() {
incidents.add(&peer.incidents);
if peer.diverged_for_secs.is_some() {
diverged_peers += 1;
}
+ if peer.refused.is_some() {
+ refused_peers += 1;
+ }
if peer.neighbour
&& let Some(secs) = peer.last_sync_secs_ago
{
@@ -370,6 +377,7 @@ pub(crate) async fn collect(state: &AppState, list: &PeerListResponse) -> StatsR
sync: SyncStats {
last_from_neighbour_secs,
diverged_peers,
+ refused_peers,
largest_received_bytes: node.largest_sync_received,
},
relay: RelayStats {
diff --git a/src/api/stats/prometheus.rs b/src/api/stats/prometheus.rs
index 038f739..41d224a 100644
--- a/src/api/stats/prometheus.rs
+++ b/src/api/stats/prometheus.rs
@@ -634,6 +634,12 @@ pub(super) fn render(stats: &StatsResponse, list: &PeerListResponse) -> String {
"Peers whose heads differ from ours while sync is idle.",
stats.sync.diverged_peers as f64,
);
+ out.single(
+ "refused_peers",
+ "gauge",
+ "Peers refused as a whole, their own changes having broken the swarm's rules.",
+ stats.sync.refused_peers as f64,
+ );
let relay = &stats.relay;
out.single(
"announce_rounds_total",