aboutsummaryrefslogtreecommitdiffstats
path: root/src/transport
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/identity.rs32
1 files changed, 30 insertions, 2 deletions
diff --git a/src/transport/identity.rs b/src/transport/identity.rs
index 1131a11..b61fb0f 100644
--- a/src/transport/identity.rs
+++ b/src/transport/identity.rs
@@ -121,7 +121,8 @@ struct Shared {
/// Who is where, by address.
#[derive(Default)]
struct Book {
- /// The node that proved itself at an address, and when.
+ /// The node that proved itself at an address, and when it was last heard from there
+ /// (its proof, or a message since: `MAX_ENTRIES` evicts the longest silent).
at: HashMap<String, (String, Instant)>,
/// The addresses each node last proved itself at, one per scheme.
proven: HashMap<String, Vec<String>>,
@@ -173,6 +174,14 @@ impl Book {
news
}
+ /// The node proven at `address`, which was heard from there `now`.
+ fn heard(&mut self, address: &str, now: Instant) -> Option<String> {
+ self.at.get_mut(address).map(|(node, heard)| {
+ *heard = now;
+ node.clone()
+ })
+ }
+
fn forget_proof(&mut self, node: &str, address: &str) {
if let Some(addresses) = self.proven.get_mut(node) {
addresses.retain(|at| at != address);
@@ -466,7 +475,9 @@ impl Shared {
Ok(())
}
message => {
- let node = self.lock().at.get(&address).map(|(node, _)| node.clone());
+ // Heard from: a node that talks isn't the one evicted when strangers'
+ // proofs fill the book (`Book::bind`).
+ let node = self.lock().heard(&address, Instant::now());
match node {
Some(node) => handler(node, message, payload_len).await,
None => {
@@ -801,6 +812,23 @@ mod tests {
assert_ne!(signed(WHO_LABEL, "a", "b"), signed(WHO_LABEL, "b", "a"));
}
+ /// When the book is full, the address longest silent goes, not the one proven
+ /// first: a member that talks stays however many strangers prove themselves.
+ #[test]
+ fn a_full_book_evicts_the_longest_silent() {
+ let start = Instant::now();
+ let mut book = Book::default();
+ book.bind("s:member", "member", start);
+ for i in 0..MAX_ENTRIES {
+ let now = start + Duration::from_millis(i as u64 + 1);
+ assert_eq!(book.heard("s:member", now).as_deref(), Some("member"));
+ book.bind(&format!("s:{i}"), &format!("stranger{i}"), now);
+ }
+ assert!(book.at.contains_key("s:member"));
+ assert!(!book.at.contains_key("s:0"));
+ assert_eq!(book.at.len(), MAX_ENTRIES);
+ }
+
#[test]
fn one_address_per_node_and_scheme_and_one_node_per_address() {
let now = Instant::now();