github.com/glide-im/glide@v1.6.0/pkg/hash/consisten_hash.go (about)

     1  package hash
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  )
     8  
     9  const (
    10  	duplicateVirtual = 100 // 1_000_000
    11  	seed             = 0xabcd1234
    12  )
    13  
    14  var (
    15  	errNodeExist = errors.New("node already exist")
    16  )
    17  
    18  type Node struct {
    19  	Val     string
    20  	hash    uint32
    21  	virtual bool
    22  	real    *Node
    23  }
    24  
    25  type Nodes struct {
    26  	nd      Node
    27  	virtual []Node
    28  	hit     int64
    29  }
    30  
    31  func (n *Nodes) appendVirtual(node Node) {
    32  	n.virtual = append(n.virtual, node)
    33  }
    34  
    35  type ConsistentHash struct {
    36  	nodes   []Node
    37  	nodeMap map[string]*Nodes
    38  	virtual int
    39  
    40  	mu sync.RWMutex
    41  }
    42  
    43  func NewConsistentHash() *ConsistentHash {
    44  	return NewConsistentHash2(duplicateVirtual)
    45  }
    46  
    47  func NewConsistentHash2(virtual int) *ConsistentHash {
    48  	hash := &ConsistentHash{
    49  		nodes:   []Node{},
    50  		nodeMap: map[string]*Nodes{},
    51  		virtual: virtual,
    52  		mu:      sync.RWMutex{},
    53  	}
    54  	return hash
    55  }
    56  
    57  // Remove node by id, include virtual node.
    58  func (c *ConsistentHash) Remove(id string) error {
    59  	nodes, ok := c.nodeMap[id]
    60  	if !ok {
    61  		return errors.New("node does not exist, id:" + id)
    62  	}
    63  	for _, vNd := range nodes.virtual {
    64  		ndIndex, exist := c.findIndex(vNd.hash)
    65  		if exist {
    66  			ndIndex--
    67  		} else {
    68  			return errors.New("virtual node does not exist, id:" + vNd.Val)
    69  		}
    70  		c.mu.RLock()
    71  		nd := c.nodes[ndIndex]
    72  		c.mu.RUnlock()
    73  		if nd.hash != vNd.hash {
    74  			return errors.New("could not find virtual node, id:" + vNd.Val)
    75  		} else {
    76  			c.removeIndex(ndIndex)
    77  		}
    78  	}
    79  	index, exist := c.findIndex(nodes.nd.hash)
    80  	if !exist {
    81  		return errors.New("real node not fund")
    82  	}
    83  	index--
    84  	c.removeIndex(index)
    85  	delete(c.nodeMap, id)
    86  	return nil
    87  }
    88  
    89  func (c *ConsistentHash) Get(data string) (*Node, error) {
    90  	hash := Hash([]byte(data), seed)
    91  	index, _ := c.findIndex(hash)
    92  	return c.get(index)
    93  }
    94  
    95  func (c *ConsistentHash) Add(id string) error {
    96  	_, ok := c.nodeMap[id]
    97  	if ok {
    98  		return errors.New("node already exist, id=" + id)
    99  	}
   100  	hash := Hash([]byte(id), seed)
   101  	nd := Node{
   102  		Val:     id,
   103  		hash:    hash,
   104  		virtual: false,
   105  		real:    nil,
   106  	}
   107  	c.nodeMap[id] = &Nodes{
   108  		nd:      nd,
   109  		virtual: []Node{},
   110  	}
   111  	c.addNode(nd)
   112  	c.addVirtual(&nd, c.virtual)
   113  	return nil
   114  }
   115  
   116  func (c *ConsistentHash) get(index int) (*Node, error) {
   117  	c.mu.RLock()
   118  	defer c.mu.RUnlock()
   119  
   120  	if len(c.nodes) == 0 {
   121  		return nil, errNodeExist
   122  	}
   123  	if index == len(c.nodes) {
   124  		index = len(c.nodes) - 1
   125  	}
   126  	n := c.nodes[index]
   127  	if n.virtual {
   128  		return n.real, nil
   129  	}
   130  	return &n, nil
   131  }
   132  
   133  func (c *ConsistentHash) addVirtual(real *Node, duplicate int) {
   134  	for i := 0; i < duplicate; i++ {
   135  		vNodeID := fmt.Sprintf("%s_#%d", real.Val, i)
   136  		hash := Hash([]byte(vNodeID), seed)
   137  		vNode := Node{
   138  			Val:     vNodeID,
   139  			hash:    hash,
   140  			virtual: true,
   141  			real:    real,
   142  		}
   143  		c.addNode(vNode)
   144  		nds := c.nodeMap[real.Val]
   145  		nds.appendVirtual(vNode)
   146  	}
   147  }
   148  
   149  func (c *ConsistentHash) addNode(nd Node) {
   150  
   151  	index, _ := c.findIndex(nd.hash)
   152  
   153  	c.mu.Lock()
   154  	defer c.mu.Unlock()
   155  
   156  	p1 := c.nodes[:index]
   157  	p2 := c.nodes[index:]
   158  	n := make([]Node, len(p1))
   159  	copy(n, p1)
   160  	n = append(n, nd)
   161  	for _, i := range p2 {
   162  		n = append(n, i)
   163  	}
   164  	c.nodes = n
   165  }
   166  
   167  func (c *ConsistentHash) removeIndex(index int) {
   168  	c.mu.Lock()
   169  	defer c.mu.Unlock()
   170  
   171  	if index == len(c.nodes)-1 {
   172  		c.nodes = c.nodes[:len(c.nodes)-1]
   173  		return
   174  	}
   175  
   176  	p2 := c.nodes[index+1:]
   177  	c.nodes = c.nodes[:index]
   178  	for _, n := range p2 {
   179  		c.nodes = append(c.nodes, n)
   180  	}
   181  }
   182  
   183  func (c *ConsistentHash) findIndex(h uint32) (int, bool) {
   184  	c.mu.RLock()
   185  	defer c.mu.RUnlock()
   186  
   187  	left := 0
   188  	right := len(c.nodes)
   189  	exist := false
   190  
   191  LOOP:
   192  	if left < right {
   193  		middle := (left + right) / 2
   194  		hash := c.nodes[middle].hash
   195  		if hash < h {
   196  			left = middle + 1
   197  		} else if hash == h {
   198  			left = middle + 1
   199  			exist = true
   200  		} else {
   201  			right = middle
   202  		}
   203  		goto LOOP
   204  	}
   205  	return left, exist
   206  }