func (c *Connectivity) connectivityChange() { // reachability prune g := graph.ReachableGraph(c.id, func(node NodeID) []NodeID { return c.connProp.Get(node, []NodeID(nil)).([]NodeID) }) // The graph g might not be symmetric, as we might hear that // one side of a link was dropped or established // before/without hearing about the other side. // // We can't simply make it symmetric by adding edges to make // an undirected graph, because that might result in an edge in // the spanning tree that doesn't correspond to a working // link. // // Alteratively, we can make an undirected graph by removing // edges that don't have a counterpart reverse edge. This is // better, but introduces a bootstrapping problem: When we add // a link to another node, we don't know that it is linked to // us, and so the edge won't feature in the graph. Which // would mean that we can never learn anything from other // nodes. // // So we use the intersected graph, but enhance it with the // local graph whcih reflects links from this node to // neighbouring nodes. local := graph.Graph{ Nodes: graph.SortNodeIDs(append(c.linkNodeIDs(), c.id)), Edges: func(node NodeID) []NodeID { if node == c.id { return c.linkNodeIDs() } else { return nil } }, } local = local.Union(local.Transpose()) g = g.Intersect(g.Transpose()).Union(local) // XXX Prune the propagation according to g // recompute spanning tree pcn := graph.FindPseudoCentralNode(g, 10) t := graph.MakeBushySpanningTree(g, pcn, 4) for _, link := range c.links { link.pendingProps = nil } for _, b := range t.Undirected().Edges(c.id) { c.links[b].pendingProps = make(map[*Propagation]*Neighbor) } c.checkPending(c.connProp) }
func (p *Propagation) prune(g graph.Graph) { var removed []*nodeState for node, ns := range p.nodes { if g.Edges(node) == nil { delete(p.nodes, node) removed = append(removed, ns) } } if len(removed) != 0 { for _, n := range p.neighbors { if n.undelivered != nil { for _, ns := range removed { delete(n.undelivered, ns) } } } } }