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 }