summaryrefslogtreecommitdiffstats
path: root/src/swarm/members.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/swarm/members.rs')
-rw-r--r--src/swarm/members.rs66
1 files changed, 60 insertions, 6 deletions
diff --git a/src/swarm/members.rs b/src/swarm/members.rs
index cb8a558..c2edd01 100644
--- a/src/swarm/members.rs
+++ b/src/swarm/members.rs
@@ -10,8 +10,10 @@
//!
//! Besides its tier, an entry holds the alias a node gives itself (`--alias`), a name
//! for people reading /peers and metrics (not unique and never an identity: the public
-//! key stays that), and an `alive` stamp the node renews now and then: the members
-//! renewed within `--electorate-window` are the electorate of a remake.
+//! key stays that), where it can be reached (`addresses`, as its transport gives them:
+//! hints for the others' transports), and an `alive` stamp the node renews now and
+//! then: the members renewed within `--electorate-window` are the electorate of a
+//! remake.
use std::collections::HashMap;
use std::time::Duration;
@@ -108,6 +110,42 @@ fn set_alias(doc: &mut AutoCommit, pk: &str, alias: Option<&str>) -> Result<(),
}
}
+fn addresses_path(pk: &str) -> Vec<String> {
+ let mut path = member_path(pk);
+ path.push("addresses".to_owned());
+ path
+}
+
+/// Addresses a member most likely has, at most: one per transport.
+const MAX_ADDRESSES: usize = 8;
+
+/// Where `pk` says it can be reached (at most `MAX_ADDRESSES`); none if its entry
+/// doesn't say.
+pub(crate) fn addresses(doc: &AutoCommit, pk: &str) -> Vec<String> {
+ match store::read_path(doc, &addresses_path(pk)) {
+ Ok(Some(JsonValue::Array(items))) => items
+ .into_iter()
+ .filter_map(|item| match item {
+ JsonValue::String(address) => Some(address),
+ _ => None,
+ })
+ .take(MAX_ADDRESSES)
+ .collect(),
+ _ => Vec::new(),
+ }
+}
+
+/// Hands every other member's addresses to the transport (`Transport::learn_addresses`):
+/// the member list is how a node finds where members it never met are.
+pub(crate) fn learn_addresses(state: &AppState, doc: &AutoCommit) {
+ let me = state.local_id();
+ for pk in members(doc) {
+ if pk != me {
+ state.transport.learn_addresses(&pk, &addresses(doc, &pk));
+ }
+ }
+}
+
fn alive_path(pk: &str) -> Vec<String> {
let mut path = member_path(pk);
path.push("alive".to_owned());
@@ -146,15 +184,16 @@ fn alive_renewal(window: Duration) -> Duration {
}
/// Registers this node if it is ready and its entry is missing, keeps its alias as
-/// configured (set, changed or removed after a restart with another `--alias`), and
-/// renews its `alive` stamp now and then (see `electorate`).
+/// configured (set, changed or removed after a restart with another `--alias`) and its
+/// addresses as the transport gives them, and renews its `alive` stamp now and then
+/// (see `electorate`).
pub(crate) async fn ensure_registered(state: &AppState) {
if !state.is_ready().await {
return;
}
let local = state.local_id().to_owned();
let own = member_path(&local);
- let (registered, recorded_alias, alive) = {
+ let (registered, recorded_alias, recorded_addresses, alive) = {
let replica = state.doc.read().await;
let registered = store::read_path(&replica.doc, &own)
.ok()
@@ -163,14 +202,17 @@ pub(crate) async fn ensure_registered(state: &AppState) {
(
registered,
alias(&replica.doc, &local),
+ addresses(&replica.doc, &local),
millis_at(&replica.doc, &alive_path(&local)),
)
};
let wanted = state.alias.clone();
+ let mut own_addresses = state.transport.local_addresses();
+ own_addresses.truncate(MAX_ADDRESSES);
let now = state.clock.now_millis().as_u64();
if !registered {
- let mut entry = json!({ "since": now, "alive": now });
+ let mut entry = json!({ "since": now, "alive": now, "addresses": own_addresses });
if let Some(alias) = &wanted {
entry["alias"] = json!(alias);
}
@@ -199,6 +241,18 @@ pub(crate) async fn ensure_registered(state: &AppState) {
}
}
+ if recorded_addresses != own_addresses {
+ let path = addresses_path(&local);
+ let value = json!(own_addresses);
+ match state
+ .mutate_doc(move |doc| store::write_path(doc, &path, &value))
+ .await
+ {
+ Ok(()) => info!(addresses = ?own_addresses, "Addresses updated in the member list"),
+ Err(error) => warn!(error = %error, "Failed to record our addresses"),
+ }
+ }
+
if recorded_alias == wanted {
return;
}