mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			294 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			294 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package hashring
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/md5"
 | 
						|
	"fmt"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
)
 | 
						|
 | 
						|
var defaultHashFunc = func() HashFunc {
 | 
						|
	hashFunc, err := NewHash(md5.New).Use(NewInt64PairHashKey)
 | 
						|
	if err != nil {
 | 
						|
		panic(fmt.Sprintf("failed to create defaultHashFunc: %s", err.Error()))
 | 
						|
	}
 | 
						|
	return hashFunc
 | 
						|
}()
 | 
						|
 | 
						|
type HashKey interface {
 | 
						|
	Less(other HashKey) bool
 | 
						|
}
 | 
						|
type HashKeyOrder []HashKey
 | 
						|
 | 
						|
func (h HashKeyOrder) Len() int      { return len(h) }
 | 
						|
func (h HashKeyOrder) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
 | 
						|
func (h HashKeyOrder) Less(i, j int) bool {
 | 
						|
	return h[i].Less(h[j])
 | 
						|
}
 | 
						|
 | 
						|
type HashFunc func([]byte) HashKey
 | 
						|
 | 
						|
type HashRing struct {
 | 
						|
	ring       map[HashKey]string
 | 
						|
	sortedKeys []HashKey
 | 
						|
	nodes      []string
 | 
						|
	weights    map[string]int
 | 
						|
	hashFunc   HashFunc
 | 
						|
}
 | 
						|
 | 
						|
type Uint32HashKey uint32
 | 
						|
 | 
						|
func (k Uint32HashKey) Less(other HashKey) bool {
 | 
						|
	return k < other.(Uint32HashKey)
 | 
						|
}
 | 
						|
 | 
						|
func New(nodes []string) *HashRing {
 | 
						|
	return NewWithHash(nodes, defaultHashFunc)
 | 
						|
}
 | 
						|
 | 
						|
func NewWithHash(
 | 
						|
	nodes []string,
 | 
						|
	hashKey HashFunc,
 | 
						|
) *HashRing {
 | 
						|
	hashRing := &HashRing{
 | 
						|
		ring:       make(map[HashKey]string),
 | 
						|
		sortedKeys: make([]HashKey, 0),
 | 
						|
		nodes:      nodes,
 | 
						|
		weights:    make(map[string]int),
 | 
						|
		hashFunc:   hashKey,
 | 
						|
	}
 | 
						|
	hashRing.generateCircle()
 | 
						|
	return hashRing
 | 
						|
}
 | 
						|
 | 
						|
func NewWithWeights(weights map[string]int) *HashRing {
 | 
						|
	return NewWithHashAndWeights(weights, defaultHashFunc)
 | 
						|
}
 | 
						|
 | 
						|
func NewWithHashAndWeights(
 | 
						|
	weights map[string]int,
 | 
						|
	hashFunc HashFunc,
 | 
						|
) *HashRing {
 | 
						|
	nodes := make([]string, 0, len(weights))
 | 
						|
	for node := range weights {
 | 
						|
		nodes = append(nodes, node)
 | 
						|
	}
 | 
						|
	hashRing := &HashRing{
 | 
						|
		ring:       make(map[HashKey]string),
 | 
						|
		sortedKeys: make([]HashKey, 0),
 | 
						|
		nodes:      nodes,
 | 
						|
		weights:    weights,
 | 
						|
		hashFunc:   hashFunc,
 | 
						|
	}
 | 
						|
	hashRing.generateCircle()
 | 
						|
	return hashRing
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) Size() int {
 | 
						|
	return len(h.nodes)
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) UpdateWithWeights(weights map[string]int) {
 | 
						|
	nodesChgFlg := false
 | 
						|
	if len(weights) != len(h.weights) {
 | 
						|
		nodesChgFlg = true
 | 
						|
	} else {
 | 
						|
		for node, newWeight := range weights {
 | 
						|
			oldWeight, ok := h.weights[node]
 | 
						|
			if !ok || oldWeight != newWeight {
 | 
						|
				nodesChgFlg = true
 | 
						|
				break
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if nodesChgFlg {
 | 
						|
		newhring := NewWithHashAndWeights(weights, h.hashFunc)
 | 
						|
		h.weights = newhring.weights
 | 
						|
		h.nodes = newhring.nodes
 | 
						|
		h.ring = newhring.ring
 | 
						|
		h.sortedKeys = newhring.sortedKeys
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) generateCircle() {
 | 
						|
	totalWeight := 0
 | 
						|
	for _, node := range h.nodes {
 | 
						|
		if weight, ok := h.weights[node]; ok {
 | 
						|
			totalWeight += weight
 | 
						|
		} else {
 | 
						|
			totalWeight += 1
 | 
						|
			h.weights[node] = 1
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	for _, node := range h.nodes {
 | 
						|
		weight := h.weights[node]
 | 
						|
 | 
						|
		for j := 0; j < weight; j++ {
 | 
						|
			nodeKey := node + "-" + strconv.FormatInt(int64(j), 10)
 | 
						|
			key := h.hashFunc([]byte(nodeKey))
 | 
						|
			h.ring[key] = node
 | 
						|
			h.sortedKeys = append(h.sortedKeys, key)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	sort.Sort(HashKeyOrder(h.sortedKeys))
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) GetNode(stringKey string) (node string, ok bool) {
 | 
						|
	pos, ok := h.GetNodePos(stringKey)
 | 
						|
	if !ok {
 | 
						|
		return "", false
 | 
						|
	}
 | 
						|
	return h.ring[h.sortedKeys[pos]], true
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) GetNodePos(stringKey string) (pos int, ok bool) {
 | 
						|
	if len(h.ring) == 0 {
 | 
						|
		return 0, false
 | 
						|
	}
 | 
						|
 | 
						|
	key := h.GenKey(stringKey)
 | 
						|
 | 
						|
	nodes := h.sortedKeys
 | 
						|
	pos = sort.Search(len(nodes), func(i int) bool { return key.Less(nodes[i]) })
 | 
						|
 | 
						|
	if pos == len(nodes) {
 | 
						|
		// Wrap the search, should return First node
 | 
						|
		return 0, true
 | 
						|
	} else {
 | 
						|
		return pos, true
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) GenKey(key string) HashKey {
 | 
						|
	return h.hashFunc([]byte(key))
 | 
						|
}
 | 
						|
 | 
						|
// GetNodes iterates over the hash ring and returns the nodes in the order
 | 
						|
// which is determined by the key. GetNodes is thread safe if the hash
 | 
						|
// which was used to configure the hash ring is thread safe.
 | 
						|
func (h *HashRing) GetNodes(stringKey string, size int) (nodes []string, ok bool) {
 | 
						|
	pos, ok := h.GetNodePos(stringKey)
 | 
						|
	if !ok {
 | 
						|
		return nil, false
 | 
						|
	}
 | 
						|
 | 
						|
	if size > len(h.nodes) {
 | 
						|
		return nil, false
 | 
						|
	}
 | 
						|
 | 
						|
	returnedValues := make(map[string]bool, size)
 | 
						|
	//mergedSortedKeys := append(h.sortedKeys[pos:], h.sortedKeys[:pos]...)
 | 
						|
	resultSlice := make([]string, 0, size)
 | 
						|
 | 
						|
	for i := pos; i < pos+len(h.sortedKeys); i++ {
 | 
						|
		key := h.sortedKeys[i%len(h.sortedKeys)]
 | 
						|
		val := h.ring[key]
 | 
						|
		if !returnedValues[val] {
 | 
						|
			returnedValues[val] = true
 | 
						|
			resultSlice = append(resultSlice, val)
 | 
						|
		}
 | 
						|
		if len(returnedValues) == size {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return resultSlice, len(resultSlice) == size
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) AddNode(node string) *HashRing {
 | 
						|
	return h.AddWeightedNode(node, 1)
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) AddWeightedNode(node string, weight int) *HashRing {
 | 
						|
	if weight <= 0 {
 | 
						|
		return h
 | 
						|
	}
 | 
						|
 | 
						|
	if _, ok := h.weights[node]; ok {
 | 
						|
		return h
 | 
						|
	}
 | 
						|
 | 
						|
	nodes := make([]string, len(h.nodes), len(h.nodes)+1)
 | 
						|
	copy(nodes, h.nodes)
 | 
						|
	nodes = append(nodes, node)
 | 
						|
 | 
						|
	weights := make(map[string]int)
 | 
						|
	for eNode, eWeight := range h.weights {
 | 
						|
		weights[eNode] = eWeight
 | 
						|
	}
 | 
						|
	weights[node] = weight
 | 
						|
 | 
						|
	hashRing := &HashRing{
 | 
						|
		ring:       make(map[HashKey]string),
 | 
						|
		sortedKeys: make([]HashKey, 0),
 | 
						|
		nodes:      nodes,
 | 
						|
		weights:    weights,
 | 
						|
		hashFunc:   h.hashFunc,
 | 
						|
	}
 | 
						|
	hashRing.generateCircle()
 | 
						|
	return hashRing
 | 
						|
}
 | 
						|
 | 
						|
func (h *HashRing) UpdateWeightedNode(node string, weight int) *HashRing {
 | 
						|
	if weight <= 0 {
 | 
						|
		return h
 | 
						|
	}
 | 
						|
 | 
						|
	/* node is not need to update for node is not existed or weight is not changed */
 | 
						|
	if oldWeight, ok := h.weights[node]; (!ok) || (ok && oldWeight == weight) {
 | 
						|
		return h
 | 
						|
	}
 | 
						|
 | 
						|
	nodes := make([]string, len(h.nodes))
 | 
						|
	copy(nodes, h.nodes)
 | 
						|
 | 
						|
	weights := make(map[string]int)
 | 
						|
	for eNode, eWeight := range h.weights {
 | 
						|
		weights[eNode] = eWeight
 | 
						|
	}
 | 
						|
	weights[node] = weight
 | 
						|
 | 
						|
	hashRing := &HashRing{
 | 
						|
		ring:       make(map[HashKey]string),
 | 
						|
		sortedKeys: make([]HashKey, 0),
 | 
						|
		nodes:      nodes,
 | 
						|
		weights:    weights,
 | 
						|
		hashFunc:   h.hashFunc,
 | 
						|
	}
 | 
						|
	hashRing.generateCircle()
 | 
						|
	return hashRing
 | 
						|
}
 | 
						|
func (h *HashRing) RemoveNode(node string) *HashRing {
 | 
						|
	/* if node isn't exist in hashring, don't refresh hashring */
 | 
						|
	if _, ok := h.weights[node]; !ok {
 | 
						|
		return h
 | 
						|
	}
 | 
						|
 | 
						|
	nodes := make([]string, 0)
 | 
						|
	for _, eNode := range h.nodes {
 | 
						|
		if eNode != node {
 | 
						|
			nodes = append(nodes, eNode)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	weights := make(map[string]int)
 | 
						|
	for eNode, eWeight := range h.weights {
 | 
						|
		if eNode != node {
 | 
						|
			weights[eNode] = eWeight
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	hashRing := &HashRing{
 | 
						|
		ring:       make(map[HashKey]string),
 | 
						|
		sortedKeys: make([]HashKey, 0),
 | 
						|
		nodes:      nodes,
 | 
						|
		weights:    weights,
 | 
						|
		hashFunc:   h.hashFunc,
 | 
						|
	}
 | 
						|
	hashRing.generateCircle()
 | 
						|
	return hashRing
 | 
						|
}
 |