utils/consistenthasing/consistenthashing.go (63 lines of code) (raw):
// Copyright (c) 2017-2018 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package consistenthasing
import (
"errors"
"hash/crc32"
"sort"
)
var (
// ErrNodeIDExists indicates a duplicated node was added to the ring
ErrNodeIDExists = errors.New("Node with same id already exists")
)
// Node is a node in hash ring
type Node struct {
ID string
HashID uint32
}
// Nodes is a slice of nodes
type Nodes []Node
// Len is for sorting nodes
func (n Nodes) Len() int { return len(n) }
// Less is for sorting nodes
func (n Nodes) Less(i, j int) bool {
if n[i].HashID == n[j].HashID {
return n[i].ID < n[j].ID
}
return n[i].HashID < n[j].HashID
}
// Swap is for sorting nodes
func (n Nodes) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
// Ring is a hashring
type Ring struct {
Nodes Nodes
idSet map[string]bool
}
// NewNode returns a new node
func NewNode(id string) *Node {
return &Node{
ID: id,
HashID: hashKey(id),
}
}
// NewRing returns a new ring
func NewRing() *Ring {
return &Ring{Nodes: Nodes{}, idSet: map[string]bool{}}
}
// AddNode adds a new node
func (r *Ring) AddNode(id string) error {
if r.idSet[id] {
return ErrNodeIDExists
}
node := NewNode(id)
r.Nodes = append(r.Nodes, *node)
sort.Sort(r.Nodes)
r.idSet[id] = true
return nil
}
// Get node id given key
func (r *Ring) Get(key string) (int, string) {
searchfn := func(i int) bool {
return r.Nodes[i].HashID >= hashKey(key)
}
i := sort.Search(r.Nodes.Len(), searchfn)
if i >= r.Nodes.Len() {
i = 0
}
return i, r.Nodes[i].ID
}
func hashKey(key string) uint32 {
if len(key) < 64 {
var scratch [64]byte
copy(scratch[:], key)
return crc32.ChecksumIEEE(scratch[:len(key)])
}
return crc32.ChecksumIEEE([]byte(key))
}