forked from extern/smegmesh
Added clustering and clean up
This commit is contained in:
parent
e2c6db3a4f
commit
d17dce3b1e
73
pkg/conn/cluster.go
Normal file
73
pkg/conn/cluster.go
Normal file
@ -0,0 +1,73 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"math/rand"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// ConnCluster splits nodes into clusters where nodes in a cluster communicate
|
||||
// frequently and nodes outside of a cluster communicate infrequently
|
||||
type ConnCluster interface {
|
||||
GetNeighbours(global []string, selfId string) []string
|
||||
GetInterCluster(global []string, selfId string) string
|
||||
}
|
||||
|
||||
type ConnClusterImpl struct {
|
||||
clusterSize int
|
||||
}
|
||||
|
||||
func binarySearch(global []string, selfId string, groupSize int) (int, int) {
|
||||
slices.Sort(global)
|
||||
|
||||
lower := 0
|
||||
higher := len(global) - 1
|
||||
mid := lower + (lower+higher)/2
|
||||
|
||||
for (higher+1)-lower > groupSize {
|
||||
if global[mid] <= selfId {
|
||||
lower = mid
|
||||
} else {
|
||||
higher = mid
|
||||
}
|
||||
|
||||
mid = lower + (lower+higher)/2
|
||||
}
|
||||
|
||||
return lower, higher + 1
|
||||
}
|
||||
|
||||
// GetNeighbours return the neighbours 'nearest' to you. In this implementation the
|
||||
// neighbours aren't actually the ones nearest to you but just the ones nearest
|
||||
// to you alphabetically. Perform binary search to get the total group
|
||||
func (i *ConnClusterImpl) GetNeighbours(global []string, selfId string) []string {
|
||||
slices.Sort(global)
|
||||
|
||||
lower, higher := binarySearch(global, selfId, i.clusterSize)
|
||||
// slice the list to get the neighbours
|
||||
return global[lower:higher]
|
||||
}
|
||||
|
||||
// GetInterCluster get nodes not in your cluster. Every round there is a given chance
|
||||
// you will communicate with a random node that is not in your cluster.
|
||||
func (i *ConnClusterImpl) GetInterCluster(global []string, selfId string) string {
|
||||
// Doesn't matter if not in it. Get index of where the node 'should' be
|
||||
index, _ := binarySearch(global, selfId, 1)
|
||||
numClusters := math.Ceil(float64(len(global)) / float64(i.clusterSize))
|
||||
|
||||
randomCluster := rand.Intn(int(numClusters)-1) + 1
|
||||
|
||||
neighbourIndex := (index + randomCluster) % len(global)
|
||||
return global[neighbourIndex]
|
||||
}
|
||||
|
||||
func NewConnCluster(clusterSize int) (ConnCluster, error) {
|
||||
log2Cluster := math.Log2(float64(clusterSize))
|
||||
|
||||
if float64((log2Cluster))-log2Cluster != 0 {
|
||||
return nil, errors.New("cluster must be a power of 2")
|
||||
}
|
||||
|
||||
return &ConnClusterImpl{clusterSize: clusterSize}, nil
|
||||
}
|
84
pkg/conn/window.go
Normal file
84
pkg/conn/window.go
Normal file
@ -0,0 +1,84 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"github.com/tim-beatham/wgmesh/pkg/lib"
|
||||
)
|
||||
|
||||
// ConnectionWindow maintains a sliding window of connections between users
|
||||
type ConnectionWindow interface {
|
||||
// GetWindow is a list of connections to choose from
|
||||
GetWindow() []string
|
||||
// SlideConnection removes a node from the window and adds a random node
|
||||
// not already in the window. connList represents the list of possible
|
||||
// connections to choose from
|
||||
SlideConnection(connList []string) error
|
||||
// PushConneciton is used when connection list less than window size.
|
||||
PutConnection(conn []string) error
|
||||
// IsFull returns true if the window is full. In which case we must slide the window
|
||||
IsFull() bool
|
||||
}
|
||||
|
||||
type ConnectionWindowImpl struct {
|
||||
window []string
|
||||
windowSize int
|
||||
}
|
||||
|
||||
// GetWindow gets the current list of active connections in
|
||||
// the window
|
||||
func (c *ConnectionWindowImpl) GetWindow() []string {
|
||||
return c.window
|
||||
}
|
||||
|
||||
// SlideConnection slides the connection window by one shuffling items
|
||||
// in the windows
|
||||
func (c *ConnectionWindowImpl) SlideConnection(connList []string) error {
|
||||
// If the number of peer connections is less than the length of the window
|
||||
// then exit early. Can't slide the window it should contain all nodes!
|
||||
if len(c.window) < c.windowSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
filter := func(node string) bool {
|
||||
return !slices.Contains(c.window, node)
|
||||
}
|
||||
|
||||
pool := lib.Filter(connList, filter)
|
||||
newNode := lib.RandomSubsetOfLength(pool, 1)
|
||||
|
||||
if len(newNode) == 0 {
|
||||
return errors.New("could not slide window")
|
||||
}
|
||||
|
||||
for i := len(c.window) - 1; i >= 1; i-- {
|
||||
c.window[i] = c.window[i-1]
|
||||
}
|
||||
|
||||
c.window[0] = newNode[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutConnection put random connections in the connection
|
||||
func (c *ConnectionWindowImpl) PutConnection(connList []string) error {
|
||||
if len(c.window) >= c.windowSize {
|
||||
return errors.New("cannot place connection. Window full need to slide")
|
||||
}
|
||||
|
||||
c.window = lib.RandomSubsetOfLength(connList, c.windowSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConnectionWindowImpl) IsFull() bool {
|
||||
return len(c.window) >= c.windowSize
|
||||
}
|
||||
|
||||
func NewConnectionWindow(windowLength int) ConnectionWindow {
|
||||
window := &ConnectionWindowImpl{
|
||||
window: make([]string, 0),
|
||||
windowSize: windowLength,
|
||||
}
|
||||
|
||||
return window
|
||||
}
|
Loading…
Reference in New Issue
Block a user