Example #1
0
// Returns the list of nodes that a broadcast message should be sent to. An
// optional ex node can be specified to exclude it from the list.
func (t *Topic) Broadcast(ex *big.Int) []*big.Int {
	t.lock.RLock()
	defer t.lock.RUnlock()

	// Gather all the nodes to broadcast to
	nodes := make([]*big.Int, len(t.nodes), len(t.nodes)+1)
	copy(nodes, t.nodes)
	if t.parent != nil {
		nodes = append(nodes, t.parent)
	}
	// If exclusion is needed, do it
	if ex != nil {
		// Sort the nodes and do a binary search on them
		sortext.BigInts(nodes)
		idx := sortext.SearchBigInts(nodes, ex)

		// Swap out with the last if found
		if idx < len(nodes) && ex.Cmp(nodes[idx]) == 0 {
			last := len(nodes) - 1
			nodes[idx] = nodes[last]
			nodes = nodes[:last]
		}
	}
	return nodes
}
Example #2
0
func ExampleBigInts() {
	// Define some sample big ints
	one := big.NewInt(1)
	two := big.NewInt(2)
	three := big.NewInt(3)
	four := big.NewInt(4)
	five := big.NewInt(5)
	six := big.NewInt(6)

	// Sort and print a random slice
	s := []*big.Int{five, two, six, three, one, four}
	sortext.BigInts(s)
	fmt.Println(s)

	// Output:
	// [1 2 3 4 5 6]
}
Example #3
0
// Subscribes a node to the topic and inserts it into the load balancer registry.
func (t *Topic) Subscribe(id *big.Int) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	// log.Printf("%v:%v: node subscription: %v.", t.owner, t.id, id)

	// Ensure double subscription doesn't happen
	idx := sortext.SearchBigInts(t.nodes, id)
	if idx < len(t.nodes) && id.Cmp(t.nodes[idx]) == 0 {
		return ErrSubscribed
	}
	// New entity, insert into the list
	t.nodes = append(t.nodes, id)
	sortext.BigInts(t.nodes)
	t.members[id.String()] = struct{}{}

	// log.Printf("%v:%v: subbed, state: %v.", t.owner, t.id, t.nodes)

	// Start load balancing to it too
	t.load.Register(id)
	return nil
}
Example #4
0
// Searches a potential routing table for nodes not yet connected.
func (o *Overlay) discover(t *table) []*big.Int {
	o.lock.RLock()
	defer o.lock.RUnlock()

	ids := []*big.Int{}
	for _, id := range t.leaves {
		if id.Cmp(o.nodeId) != 0 {
			if _, ok := o.livePeers[id.String()]; !ok {
				ids = append(ids, id)
			}
		}
	}
	for _, row := range t.routes {
		for _, id := range row {
			if id != nil {
				if _, ok := o.livePeers[id.String()]; !ok {
					ids = append(ids, id)
				}
			}
		}
	}
	sortext.BigInts(ids)
	return ids[:sortext.Unique(sortext.BigIntSlice(ids))]
}
Example #5
0
// Unregisters a node from the topic, removing it from the balancer's registry.
func (t *Topic) Unsubscribe(id *big.Int) error {
	t.lock.Lock()
	defer t.lock.Unlock()

	// log.Printf("%v:%v: node unsubscription: %v.", t.owner, t.id, id)

	// Ensure double unsubscription doesn't happen
	idx := sortext.SearchBigInts(t.nodes, id)
	if idx == len(t.nodes) || id.Cmp(t.nodes[idx]) != 0 {
		return ErrNotSubscribed
	}
	// Remove the node from the children
	last := len(t.nodes) - 1
	t.nodes[idx] = t.nodes[last]
	t.nodes = t.nodes[:last]
	sortext.BigInts(t.nodes)
	delete(t.members, id.String())

	// log.Printf("%v:%v: remed, state: %v.", t.owner, t.id, t.nodes)

	// Remove the node from the load balancer
	t.load.Unregister(id)
	return nil
}
Example #6
0
func TestTopic(t *testing.T) {
	// Define some setup parameters for the test
	topicId := big.NewInt(314)
	ownerId := big.NewInt(141)

	nodeIds := []int64{1, 2, 3, 4, 5}
	nodes := make([]*big.Int, len(nodeIds))
	for i, id := range nodeIds {
		nodes[i] = big.NewInt(id)
	}
	sortext.BigInts(nodes)

	// Create the topic and check internal state
	top := New(topicId, ownerId)
	if id := top.Self(); id.Cmp(topicId) != 0 {
		t.Fatalf("topic id mismatch: have %v, want %v.", id, topicId)
	}
	if id := top.owner; id.Cmp(ownerId) != 0 {
		t.Fatalf("topic owner mismatch: have %v, want %v.", id, ownerId)
	}
	// Check subscribe and unsubscribe
	for i, id := range nodes {
		top.Subscribe(id)
		if n := len(top.nodes); n != i+1 {
			t.Fatalf("topic child node count mismatch: have %v, want %v", n, i+1)
		}
	}
	for i, id := range nodes {
		top.Unsubscribe(id)
		if n := len(top.nodes); n != len(nodes)-1-i {
			t.Fatalf("topic child node count mismatch: have %v, want %v", n, len(nodes)-1-i)
		}
	}
	// Subscribe everybody back
	for _, id := range nodes {
		top.Subscribe(id)
	}
	// Check broadcasting with no exclusion
	ns := top.Broadcast(nil)
	if len(ns) != len(nodes) {
		t.Fatalf("broadcast node list length mismatch: have %v, want %v.", len(ns), len(nodes))
	}
	for i, id := range ns {
		if nodes[i].Cmp(id) != 0 {
			t.Fatalf("broadcast node %d mismatch: have %v, want %v.", i, id, nodes[i])
		}
	}
	// Check broadcasting with exclusion
	ns = top.Broadcast(nodes[0])
	if len(ns) != len(nodes)-1 {
		t.Fatalf("excluded broadcast node list length mismatch: have %v, want %v.", len(ns), len(nodes)-1)
	}
	for _, id := range ns {
		if nodes[0].Cmp(id) == 0 {
			t.Fatalf("broadcast includes excluded node: %v.", id)
		}
	}
	// Check load balancing (without one entry)
	ns = []*big.Int{}
	for i := 0; i < 1000; i++ {
		n, err := top.Balance(nodes[0])
		if err != nil {
			t.Fatalf("failed to balance: %v.", err)
		}
		ns = append(ns, n)
	}
	if len(ns) == 0 {
		t.Fatalf("no nodes have been balanced to")
	}
	for i, id := range ns {
		if id.Cmp(nodes[0]) == 0 {
			t.Fatalf("balance %d: reached excluded node %v.", i, id)
		}
		idx := sortext.SearchBigInts(nodes, id)
		if idx >= len(nodes) || nodes[idx] != id {
			t.Fatalf("balance %d: invalid node id %v.", i, id)
		}
	}
	// Add a local subscription
	if err := top.Subscribe(ownerId); err != nil {
		t.Fatalf("failed to subscribe with local node: %v.", err)
	}
	// Check load report generation
	ns, caps := top.GenerateReports()
	if len(ns) != len(nodes) || len(caps) != len(nodes) {
		t.Fatalf("report target size mismatch: have %v/%v nodes/caps, want %v.", len(ns), len(caps), len(nodes))
	}
	for i, cap := range caps {
		if cap != len(nodes) {
			t.Fatalf("capacity %d mismatch: have %v, want %v", i, cap, len(nodes))
		}
	}
	// Check load processing
	total := 1 // Local apps
	for i, id := range nodes {
		top.ProcessReport(id, 10*(i+1))
		total += 10 * (i + 1)
	}
	ns, caps = top.GenerateReports()
	for i, cap := range caps {
		if cap != total-10*(i+1) {
			t.Fatalf("capacity %d mismatch: have %v, want %v", i, cap, total-10*(i+1))
		}
	}
}