Conversation
r? @afck (maidsafe_highfive has picked a reviewer for you, use r? to override) |
src/peer_manager.rs
Outdated
peer.peer_id.map_or(None, |peer_id| Some((*peer.name(), peer_id))) | ||
} else { | ||
None | ||
/// Returns the closest section to the peer and the closest section to own. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It only returns direct-connected peers. I wouldn't say "closest" either - just our section and the peer's section.
src/peer_manager.rs
Outdated
self.routing_table | ||
.our_section() | ||
.iter() | ||
.chain(self.routing_table.closest_section(name).1.iter()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use self.routing_table.get_section(name)
here to ensure we return the right section (this will return a different section if the one for name
is still empty) and to avoid exposing another RT function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but get_section will return None if the section for name is still empty.
I think here we are more interested in ensure the coverage, instead of section accurate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maqi if it returns None, that should be fine right? cos that means we dont have anyone else "yet" from the target section, which means we'll soon be having some people added and at that point we'll query them if they can tunnel to this person anyways from the add_to_routing_table msg flow. This here is going to get us someone who we may loose a lot earlier due to further future splits and that can be prevented by using the other option maybe.
src/peer_manager.rs
Outdated
.our_section() | ||
.iter() | ||
.chain(self.routing_table.closest_section(name).1.iter()) | ||
.sorted_by(|name0, name1| name.cmp_distance(name0, name1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to sort now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if not sort, the result pattern will be based on the pattern of RT, which will be consistent across the nodes in the same section, hence making certain node overloaded for the test.
the sort is just trying to share the load meanwhile maintain a deterministic behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i tested without sort (50 iterations of aggressive churn) and seems the new approach (our section + peer's section) already spread load enough and won't fail the test due to overloaded node.
I will remove the sort part then
src/routing_table/mod.rs
Outdated
@@ -989,7 +989,7 @@ impl<T: Binary + Clone + Copy + Debug + Default + Hash + Xorable> RoutingTable<T | |||
|
|||
/// Returns the prefix of the closest non-empty section to `name`, regardless of whether `name` | |||
/// belongs in that section or not, and the section itself. | |||
fn closest_section(&self, name: &T) -> (&Prefix<T>, &BTreeSet<T>) { | |||
pub fn closest_section(&self, name: &T) -> (&Prefix<T>, &BTreeSet<T>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this change is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as the comment above, get_section may return None, I think this expose is required.
src/states/node.rs
Outdated
for dst_id in peers_needing_tunnel { | ||
for (dst_id, peer_name) in self.peer_mgr.peers_needing_tunnel() { | ||
if self.peer_mgr | ||
.potential_tunnel_nodes(&peer_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call to self.peer_mgr.potential_tunnel_nodes().contains(...)
used to be outside the loop and done only if peers_needing_tunnel
was non-empty to avoid repeating the relatively costly call. Not a big deal I guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but now we have to re-calculate for each peer now. :(
the good thing is now we only looking for section, so hope it won't be too costly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But instead of computing the set of all potential tunnel nodes, we could just:
(self.peer_mgr.routing_table().our_section().contains(&peer_name) ||
self.peer_mgr.routing_table().get_section(public_id.name()).map_or(false, |section| section.contains(&peer_name)))
&& self.peer_mgr.get_state_by_name(public_id.name()).map_or(false, PeerState::can_tunnel_for)
(Or, probably cleaner, move that check into the peer manager.)
src/peer_manager.rs
Outdated
} | ||
self.peer_map.get_by_name(name).map_or(None, | ||
|peer| if peer.state.can_tunnel_for() { | ||
peer.peer_id().map_or(None, |peer_id| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and_then()
looks like a more natural choice here than map_or()
.
src/peer_manager.rs
Outdated
self.peer_map | ||
.peers() | ||
.filter_map(|peer| match peer.state { | ||
PeerState::SearchingForTunnel => peer.peer_id, | ||
PeerState::SearchingForTunnel => { | ||
peer.peer_id.map_or(None, |peer_id| Some((peer_id, *peer.name()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again - and_then()
seems better.
tests/mock_crust/churn.rs
Outdated
let index = rng.gen_range(1, len + 1); | ||
|
||
if nodes.len() > 16 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this constant 16 here? Or was it supposed to be 2 * min_section_size
like somewhere above?
tests/mock_crust/churn.rs
Outdated
let config = Config::with_contacts(&[nodes[proxy].handle.endpoint()]); | ||
|
||
nodes.insert(index, TestNode::builder(network).config(config).create()); | ||
|
||
if nodes.len() > 16 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like above.
tests/mock_crust/churn.rs
Outdated
if nodes.len() > 16 { | ||
if index <= proxy { | ||
proxy += 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the purpose of this? Some comment would be useful here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new node may got inserted into a position before the proxy, hence push proxy_node's index by one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I get it! Makes sense 👍 But a comment would still be nice ;)
tests/mock_crust/churn.rs
Outdated
@@ -213,6 +253,7 @@ impl ExpectedGets { | |||
} | |||
} | |||
} | |||
assert!(unexpected_receive <= self.sections.len()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we allow unexpected messages now and why this number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is due to the scenario :
1, A and B are tunnelling via C
2, A being the leader of a group message M (B is supposed to receive M)
3, A lost C (connection lost or C got removed), hence remove B
4, A collected enough signatures from section, hence sending M out according to its RT
the sending list will contain an out-of-range node D
5, D also lost tunnel to a close-range node due to the removal of the tunnel_node
6, D will then receive that msg and handle it as from its perspective, it is within close-range.
we cannot resolve it from routing perspective only.
And such unexpected_receive normally will be just one for each message, if happens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it's at most one for each message, let's not just count, but instead record the messages itself and assert that each of them was received in at most one unexpected node.
src/states/node.rs
Outdated
for dst_id in peers_needing_tunnel { | ||
for (dst_id, peer_name) in self.peer_mgr.peers_needing_tunnel() { | ||
if self.peer_mgr | ||
.potential_tunnel_nodes(&peer_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But instead of computing the set of all potential tunnel nodes, we could just:
(self.peer_mgr.routing_table().our_section().contains(&peer_name) ||
self.peer_mgr.routing_table().get_section(public_id.name()).map_or(false, |section| section.contains(&peer_name)))
&& self.peer_mgr.get_state_by_name(public_id.name()).map_or(false, PeerState::can_tunnel_for)
(Or, probably cleaner, move that check into the peer manager.)
tests/mock_crust/churn.rs
Outdated
let mut block_peer = gen_range_except(rng, 0, nodes.len(), Some(new_node)); | ||
while block_peer == proxy { | ||
block_peer = gen_range_except(rng, 0, nodes.len(), Some(new_node)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can avoid the potentially unlimited number of random number generations:
let mut block_peer = gen_range_except(rng, 0, nodes.len() - 1, Some(new_node));
if block_peer >= proxy {
block_peer += 1;
}
(Also below.)
tests/mock_crust/churn.rs
Outdated
node.name(), | ||
key, | ||
self.sections); | ||
unexpected_receive += 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Section
and PrefixSection
destinations, this should still be impossible, shouldn't it? In that case, let's assert that dst
is in fact a group authority here.
tests/mock_crust/churn.rs
Outdated
@@ -213,6 +253,7 @@ impl ExpectedGets { | |||
} | |||
} | |||
} | |||
assert!(unexpected_receive <= self.sections.len()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it's at most one for each message, let's not just count, but instead record the messages itself and assert that each of them was received in at most one unexpected node.
ed095ea
to
40449ba
Compare
src/peer_manager.rs
Outdated
.collect() | ||
} | ||
|
||
/// Returns true if peer is direct-connected and in our section or in tunnel_client's section. | ||
pub fn is_potential_tunnel_node(&self, peer: &PublicId, tunnel_client: &XorName) -> bool { | ||
(self.routing_table.our_section().contains(peer.name()) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe overkill, but just to err on the side of safety we could also return false if peer
is ourself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is only being called in node::add_to_routing_table
if peer is ourself, we shall already return at https://github.com/maidsafe/routing/blob/master/src/states/node.rs#L1404-L1410, because routing_table::add will return an error for that case https://github.com/maidsafe/routing/blob/master/src/routing_table/mod.rs#L502-L504 anyway (also I think peer_mgr won't have peer info
for self, right?)
so, I think we are safe to not carry out an additional check here?
src/peer_manager.rs
Outdated
(self.routing_table.our_section().contains(peer.name()) || | ||
self.routing_table | ||
.get_section(peer.name()) | ||
.map_or(false, |section| section.contains(tunnel_client))) && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're calling this function, it's implied that we don't already have a connection to tunnel_client
, so section.contains(tunnel_client)
should always return false, right? Should we just check that get_section()
is the same for peer
and tunnel_client
and not None
?
tests/mock_crust/churn.rs
Outdated
while block_peer == proxy { | ||
block_peer = gen_range_except(rng, 0, nodes.len(), Some(new_node)); | ||
let mut block_peer = gen_range_except(rng, 0, nodes.len() - 1, Some(new_node)); | ||
if block_peer == proxy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should be >= proxy
. (Also below.)
tests/mock_crust/utils.rs
Outdated
@@ -40,18 +40,15 @@ const BALANCED_POLLING: bool = true; | |||
pub fn gen_range_except<T: Rng>(rng: &mut T, | |||
low: usize, | |||
high: usize, | |||
exclude: Option<usize>) | |||
exclude: BTreeSet<usize>) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be exclude: &BTreeSet<usize>
instead, then we wouldn't need to clone the set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…or exclude: I
with I: IntoIterator<usize>
, then it would accept vectors and None
and Some
etc.
No description provided.