Skip to content
This repository has been archived by the owner on Jun 25, 2021. It is now read-only.

Maid-1977 churn test with tunnel #1396

Merged
merged 7 commits into from Mar 20, 2017
Merged

Conversation

maqi
Copy link
Member

@maqi maqi commented Mar 17, 2017

No description provided.

@maidsafe-highfive
Copy link

r? @afck

(maidsafe_highfive has picked a reviewer for you, use r? to override)

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.
Copy link
Contributor

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.

self.routing_table
.our_section()
.iter()
.chain(self.routing_table.closest_section(name).1.iter())
Copy link
Contributor

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.

Copy link
Member Author

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

Copy link
Contributor

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.

.our_section()
.iter()
.chain(self.routing_table.closest_section(name).1.iter())
.sorted_by(|name0, name1| name.cmp_distance(name0, name1))
Copy link
Contributor

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?

Copy link
Member Author

@maqi maqi Mar 17, 2017

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.

Copy link
Member Author

@maqi maqi Mar 18, 2017

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

@@ -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>) {
Copy link
Contributor

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.

Copy link
Member Author

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.

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)
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.)

}
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| {
Copy link
Contributor

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().

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())))
Copy link
Contributor

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.

let index = rng.gen_range(1, len + 1);

if nodes.len() > 16 {
Copy link
Contributor

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?

let config = Config::with_contacts(&[nodes[proxy].handle.endpoint()]);

nodes.insert(index, TestNode::builder(network).config(config).create());

if nodes.len() > 16 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like above.

if nodes.len() > 16 {
if index <= proxy {
proxy += 1;
}
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

@fizyk20 fizyk20 Mar 17, 2017

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 ;)

@@ -213,6 +253,7 @@ impl ExpectedGets {
}
}
}
assert!(unexpected_receive <= self.sections.len());
Copy link
Contributor

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?

Copy link
Member Author

@maqi maqi Mar 17, 2017

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.

Copy link
Contributor

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.

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)
Copy link
Contributor

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.)

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));
}
Copy link
Contributor

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.)

node.name(),
key,
self.sections);
unexpected_receive += 1;
Copy link
Contributor

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.

@@ -213,6 +253,7 @@ impl ExpectedGets {
}
}
}
assert!(unexpected_receive <= self.sections.len());
Copy link
Contributor

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.

@maqi maqi force-pushed the MAID-1977 branch 4 times, most recently from ed095ea to 40449ba Compare March 19, 2017 07:34
.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()) ||
Copy link
Contributor

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.

Copy link
Member Author

@maqi maqi Mar 20, 2017

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?

(self.routing_table.our_section().contains(peer.name()) ||
self.routing_table
.get_section(peer.name())
.map_or(false, |section| section.contains(tunnel_client))) &&
Copy link
Contributor

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?

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 {
Copy link
Contributor

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.)

@@ -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>)
Copy link
Contributor

@afck afck Mar 20, 2017

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.

Copy link
Contributor

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.

@Viv-Rajkumar Viv-Rajkumar merged commit 89aa9ad into maidsafe:master Mar 20, 2017
@maqi maqi deleted the MAID-1977 branch August 1, 2017 08:30
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants