From 65801dd4194bfa0c775deb40f9d2a82e931131f1 Mon Sep 17 00:00:00 2001 From: searKing <471030698@qq.com> Date: Mon, 4 Nov 2024 16:16:15 +0800 Subject: [PATCH] refactor(exp/container/hashring): rename NodeLocator to HashRing. --- go/exp/container/hashring/example_test.go | 4 +- go/exp/container/hashring/hashring.go | 64 +++---- go/exp/container/hashring/hashring.key.go | 8 +- .../hashring/hashring.nodekeyformater.go | 10 +- go/exp/container/hashring/hashring_options.go | 180 ++++++++++++++++++ go/exp/container/hashring/hashring_test.go | 16 +- .../container/hashring/nodelocator_options.go | 180 ------------------ 7 files changed, 231 insertions(+), 231 deletions(-) create mode 100644 go/exp/container/hashring/hashring_options.go delete mode 100644 go/exp/container/hashring/nodelocator_options.go diff --git a/go/exp/container/hashring/example_test.go b/go/exp/container/hashring/example_test.go index 44e55bb8..08c2c438 100644 --- a/go/exp/container/hashring/example_test.go +++ b/go/exp/container/hashring/example_test.go @@ -44,7 +44,7 @@ func ExampleNew() { // Dave => NodeA } -func ExampleAdd() { +func ExampleHashRing_AddNodes() { c := hashring.New[string]() c.AddNodes("NodeA") c.AddNodes("NodeB") @@ -84,7 +84,7 @@ func ExampleAdd() { // Dave => NodeA } -func ExampleRemove() { +func ExampleHashRing_RemoveNodes() { c := hashring.New[string]() c.AddNodes("NodeA") c.AddNodes("NodeB") diff --git a/go/exp/container/hashring/hashring.go b/go/exp/container/hashring/hashring.go index aec11ec5..fd9c21a6 100644 --- a/go/exp/container/hashring/hashring.go +++ b/go/exp/container/hashring/hashring.go @@ -4,7 +4,7 @@ // Package hashring provides a consistent hashing function. // -// NodeLocator hashing is often used to distribute requests to a changing set of servers. For example, +// HashRing hashing is often used to distribute requests to a changing set of servers. For example, // say you have some cache servers cacheA, cacheB, and cacheC. You want to decide which cache server // to use to look up information on a user. // @@ -28,14 +28,14 @@ import ( const defaultNumReps = 160 -// NodeLocator holds the information about the allNodes of the consistent hash nodes. +// HashRing holds the information about the allNodes of the consistent hash nodes. // // Node represents a node in the consistent hash ring. // {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-0 -> 1234 // Node -> Key -> IterateKey -> HashKey // -//go:generate go-option -type "NodeLocator" -type NodeLocator[Node comparable] struct { +//go:generate go-option -type "HashRing" +type HashRing[Node comparable] struct { // The List of nodes to use in the Ketama consistent hash continuum // // This simulates the structure of keys used in the Ketama consistent hash ring, @@ -66,8 +66,8 @@ type NodeLocator[Node comparable] struct { } // New creates a hash ring of n replicas for each entry. -func New[Node comparable](opts ...NodeLocatorOption[Node]) *NodeLocator[Node] { - r := &NodeLocator[Node]{ +func New[Node comparable](opts ...HashRingOption[Node]) *HashRing[Node] { + r := &HashRing[Node]{ nodeByKey: make(map[uint32]Node), allNodes: make(map[Node]struct{}), hashAlg: KetamaHash, @@ -84,7 +84,7 @@ func New[Node comparable](opts ...NodeLocatorOption[Node]) *NodeLocator[Node] { } // AddNodes inserts nodes into the consistent hash cycle. -func (c *NodeLocator[Node]) AddNodes(nodes ...Node) { +func (c *HashRing[Node]) AddNodes(nodes ...Node) { if c.isWeighted { c.addWeightNodes(nodes...) return @@ -92,11 +92,11 @@ func (c *NodeLocator[Node]) AddNodes(nodes ...Node) { c.addNoWeightNodes(nodes...) } -// SetNodes setups the NodeLocator with the list of nodes it should use. +// SetNodes setups the HashRing with the list of nodes it should use. // If there are existing nodes not present in nodes, they will be removed. -// @param nodes a List of Nodes for this NodeLocator to use in +// @param nodes a List of Nodes for this HashRing to use in // its continuum -func (c *NodeLocator[Node]) SetNodes(nodes ...Node) { +func (c *HashRing[Node]) SetNodes(nodes ...Node) { if c.isWeighted { c.setWeightNodes(nodes...) return @@ -105,14 +105,14 @@ func (c *NodeLocator[Node]) SetNodes(nodes ...Node) { } // RemoveAllNodes removes all nodes in the continuum. -func (c *NodeLocator[Node]) RemoveAllNodes() { +func (c *HashRing[Node]) RemoveAllNodes() { c.sortedKeys = nil c.nodeByKey = make(map[uint32]Node) c.allNodes = make(map[Node]struct{}) } // Get returns an element close to where name hashes to in the nodes. -func (c *NodeLocator[Node]) Get(name string) (Node, bool) { +func (c *HashRing[Node]) Get(name string) (Node, bool) { if len(c.nodeByKey) == 0 { var zeroN Node return zeroN, false @@ -121,7 +121,7 @@ func (c *NodeLocator[Node]) Get(name string) (Node, bool) { } // GetSince returns an iterator over distinct nodes in hashring, start from where name hashes to in the nodes. -func (c *NodeLocator[Node]) GetSince(name string) iter.Seq[Node] { +func (c *HashRing[Node]) GetSince(name string) iter.Seq[Node] { return func(yield func(Node) bool) { if len(c.nodeByKey) == 0 { return @@ -162,23 +162,23 @@ func (c *NodeLocator[Node]) GetSince(name string) iter.Seq[Node] { // All returns an iterator over all nodes in hashring. // If c is empty, the sequence is empty: there is no empty element in the sequence. -func (c *NodeLocator[Node]) All() iter.Seq[Node] { +func (c *HashRing[Node]) All() iter.Seq[Node] { return maps.Keys(c.allNodes) } // getAllNodes returns all available nodes -func (c *NodeLocator[Node]) getAllNodes() []Node { +func (c *HashRing[Node]) getAllNodes() []Node { return slices.Collect(maps.Keys(c.allNodes)) } // getPrimaryNode returns the first available node for a name, such as “127.0.0.1:11311-0” for "Alice" -func (c *NodeLocator[Node]) getPrimaryNode(name string) (Node, bool) { +func (c *HashRing[Node]) getPrimaryNode(name string) (Node, bool) { return c.getNodeByHashKey(c.getHashKey(name)) } // getMaxHashKey returns the last available node's HashKey // that is, Maximum HashKey in the Hash Cycle -func (c *NodeLocator[Node]) getMaxHashKey() (key uint32, ok bool) { +func (c *HashRing[Node]) getMaxHashKey() (key uint32, ok bool) { if len(c.sortedKeys) == 0 { return 0, false } @@ -186,7 +186,7 @@ func (c *NodeLocator[Node]) getMaxHashKey() (key uint32, ok bool) { } // getNodeByHashKey returns the first available node since iterateHashKey, such as HASH(“127.0.0.1:11311-0”) -func (c *NodeLocator[Node]) getNodeByHashKey(hash uint32) (Node, bool) { +func (c *HashRing[Node]) getNodeByHashKey(hash uint32) (Node, bool) { if len(c.sortedKeys) == 0 { var zeroN Node return zeroN, false @@ -204,23 +204,23 @@ func (c *NodeLocator[Node]) getNodeByHashKey(hash uint32) (Node, bool) { } // getNodeByHashKeyIndex returns the node by index of sorted hash keys. -func (c *NodeLocator[Node]) getNodeByHashKeyIndex(keyIndex int) (node Node) { +func (c *HashRing[Node]) getNodeByHashKeyIndex(keyIndex int) (node Node) { return c.nodeByKey[c.sortedKeys[keyIndex]] } // updateLocator reconstructs the hash ring with the input nodes -func (c *NodeLocator[Node]) updateLocator(nodes ...Node) { +func (c *HashRing[Node]) updateLocator(nodes ...Node) { c.SetNodes(nodes...) } // getNodeRepetitions returns the number of discrete hashes that should be defined for each node // in the continuum. -func (c *NodeLocator[Node]) getNodeRepetitions() int { +func (c *HashRing[Node]) getNodeRepetitions() int { return c.numReps } // setNoWeightNodes sets all the elements in the hash. -func (c *NodeLocator[Node]) setNoWeightNodes(nodes ...Node) { +func (c *HashRing[Node]) setNoWeightNodes(nodes ...Node) { // Set sets all the elements in the hash. // If there are existing elements not present in nodes, they will be removed. var nodesToBeRemoved []Node @@ -261,7 +261,7 @@ func (c *NodeLocator[Node]) setNoWeightNodes(nodes ...Node) { } // setWeightNodes sets all the elements in the hash. -func (c *NodeLocator[Node]) setWeightNodes(nodes ...Node) { +func (c *HashRing[Node]) setWeightNodes(nodes ...Node) { c.RemoveAllNodes() numReps := c.getNodeRepetitions() nodeCount := len(nodes) @@ -285,12 +285,12 @@ func (c *NodeLocator[Node]) setWeightNodes(nodes ...Node) { } // addWeightNodes adds a node to the hash without sorting the keys. -func (c *NodeLocator[Node]) addWeightNodes(nodes ...Node) { +func (c *HashRing[Node]) addWeightNodes(nodes ...Node) { c.setWeightNodes(append(c.getAllNodes(), nodes...)...) } // addNoWeightNodes adds a node to the hash without sorting the keys. -func (c *NodeLocator[Node]) addNoWeightNodes(nodes ...Node) { +func (c *HashRing[Node]) addNoWeightNodes(nodes ...Node) { numReps := c.getNodeRepetitions() for _, node := range nodes { @@ -301,7 +301,7 @@ func (c *NodeLocator[Node]) addNoWeightNodes(nodes ...Node) { } // addNodeWithoutSort adds a node to the hash without sorting the keys. -func (c *NodeLocator[Node]) addNodeWithoutSort(node Node, numReps int) { +func (c *HashRing[Node]) addNodeWithoutSort(node Node, numReps int) { // Ketama does some special work with md5 where it reuses chunks. // Check to be backwards compatible, the hash algorithm does not // matter for Ketama, just the placement should always be done using @@ -335,7 +335,7 @@ func (c *NodeLocator[Node]) addNodeWithoutSort(node Node, numReps int) { } // RemoveNodes removes nodes from the consistent hash cycle -func (c *NodeLocator[Node]) RemoveNodes(nodes ...Node) { +func (c *HashRing[Node]) RemoveNodes(nodes ...Node) { if c.isWeighted { c.removeWeightNodes(nodes...) return @@ -344,14 +344,14 @@ func (c *NodeLocator[Node]) RemoveNodes(nodes ...Node) { } // removeWeightNodes removes nodes from the consistent hash cycle -func (c *NodeLocator[Node]) removeWeightNodes(nodes ...Node) { +func (c *HashRing[Node]) removeWeightNodes(nodes ...Node) { for _, node := range nodes { delete(c.allNodes, node) } c.setWeightNodes(c.getAllNodes()...) } -func (c *NodeLocator[Node]) removeNoWeightNodes(nodes ...Node) { +func (c *HashRing[Node]) removeNoWeightNodes(nodes ...Node) { numReps := c.getNodeRepetitions() for _, node := range nodes { @@ -384,7 +384,7 @@ func (c *NodeLocator[Node]) removeNoWeightNodes(nodes ...Node) { } // tailSearch returns the first available node since iterateHashKey's Index, such as Index(HASH(“127.0.0.1:11311-0”)) -func (c *NodeLocator[Node]) tailSearch(key uint32) (i int, found bool) { +func (c *HashRing[Node]) tailSearch(key uint32) (i int, found bool) { // Search uses binary search to find and return the smallest index since iterateHashKey's Index return slices.BinarySearchFunc(c.sortedKeys, key, func(v uint32, key uint32) int { if v >= key { @@ -395,7 +395,7 @@ func (c *NodeLocator[Node]) tailSearch(key uint32) (i int, found bool) { } // updateSortedNodes sorts the keys in ascending order. -func (c *NodeLocator[Node]) updateSortedNodes() { +func (c *HashRing[Node]) updateSortedNodes() { hashes := c.sortedKeys[:0] // reallocate if we're holding on to too much (1/4th) // len(nodes) * replicas < cap / 4 @@ -411,6 +411,6 @@ func (c *NodeLocator[Node]) updateSortedNodes() { } // isSameNode checks if two nodes are the same by the key. -func (c *NodeLocator[Node]) isSameNode(n1, n2 Node) bool { +func (c *HashRing[Node]) isSameNode(n1, n2 Node) bool { return c.nodeKeyFormatter.FormatNodeKey(n1, 0) == c.nodeKeyFormatter.FormatNodeKey(n2, 0) } diff --git a/go/exp/container/hashring/hashring.key.go b/go/exp/container/hashring/hashring.key.go index 9bd0384e..63b87de4 100644 --- a/go/exp/container/hashring/hashring.key.go +++ b/go/exp/container/hashring/hashring.key.go @@ -5,23 +5,23 @@ package hashring // Returns a uniquely identifying key, suitable for hashing by the -// NodeLocator algorithm. +// HashRing algorithm. // @param node The Node to use to form the unique identifier // @param repetition The repetition number for the particular node in question // // (0 is the first repetition) // // @return The key that represents the specific repetition of the node, such as “127.0.0.1:11311-0” -func (c *NodeLocator[Node]) getIterateKeyForNode(node Node, repetition int) string { +func (c *HashRing[Node]) getIterateKeyForNode(node Node, repetition int) string { return c.nodeKeyFormatter.FormatNodeKey(node, repetition) } -func (c *NodeLocator[Node]) getIterateHashKeyForNode(node Node, repetition int) []uint32 { +func (c *HashRing[Node]) getIterateHashKeyForNode(node Node, repetition int) []uint32 { return c.hashAlg.Hash(c.getIterateKeyForNode(node, repetition)) } // 127.0.0.1:11311-0 -> 1122334455 // IterateKey -> IterateHashKey -func (c *NodeLocator[Node]) getHashKey(iterateKey string) uint32 { +func (c *HashRing[Node]) getHashKey(iterateKey string) uint32 { return c.hashAlg.Hash(iterateKey)[0] } diff --git a/go/exp/container/hashring/hashring.nodekeyformater.go b/go/exp/container/hashring/hashring.nodekeyformater.go index 118b5bac..181da6aa 100644 --- a/go/exp/container/hashring/hashring.nodekeyformater.go +++ b/go/exp/container/hashring/hashring.nodekeyformater.go @@ -20,7 +20,7 @@ func (f FormatterFunc[Node]) FormatNodeKey(node Node, repetition int) string { // Formatter is used to format node for assigning nodes around the ring type Formatter[Node comparable] interface { // FormatNodeKey returns a uniquely identifying key, suitable for hashing by the - // NodeLocator algorithm. + // HashRing algorithm. FormatNodeKey(node Node, repetition int) string } @@ -51,9 +51,9 @@ const ( type KetamaNodeKeyFormatter[Node comparable] struct { format Format - // Carried over from the DefaultKetamaNodeLocatorConfiguration: + // Carried over from the DefaultKetamaHashRingConfiguration: // Internal lookup map to try to carry forward the optimisation that was - // previously in NodeLocator + // previously in HashRing keyByNode map[Node]string } @@ -69,7 +69,7 @@ func NewKetamaNodeKeyFormatter[Node comparable](format Format) *KetamaNodeKeyFor } // FormatNodeKey returns a uniquely identifying key, suitable for hashing by the -// NodeLocator algorithm. +// HashRing algorithm. // // @param node The Node to use to form the unique identifier // @param repetition The repetition number for the particular node in question @@ -78,7 +78,7 @@ func NewKetamaNodeKeyFormatter[Node comparable](format Format) *KetamaNodeKeyFor // // @return The key that represents the specific repetition of the node func (f KetamaNodeKeyFormatter[Node]) FormatNodeKey(node Node, repetition int) string { - // Carried over from the DefaultKetamaNodeLocatorConfiguration: + // Carried over from the DefaultKetamaHashRingConfiguration: // Internal Using the internal map retrieve the socket addresses // for given nodes. // I'm aware that this code is inherently thread-unsafe as diff --git a/go/exp/container/hashring/hashring_options.go b/go/exp/container/hashring/hashring_options.go new file mode 100644 index 00000000..6e490f9e --- /dev/null +++ b/go/exp/container/hashring/hashring_options.go @@ -0,0 +1,180 @@ +// Code generated by "go-option -type HashRing"; DO NOT EDIT. +// Install go-option by "go get install github.com/searKing/golang/tools/go-option" + +package hashring + +// A HashRingOption sets options. +type HashRingOption[Node comparable] interface { + apply(*HashRing[Node]) +} + +// EmptyHashRingOption does not alter the configuration. It can be embedded +// in another structure to build custom options. +// +// This API is EXPERIMENTAL. +type EmptyHashRingOption[Node comparable] struct{} + +func (EmptyHashRingOption[Node]) apply(*HashRing[Node]) {} + +// HashRingOptionFunc wraps a function that modifies HashRing[Node] into an +// implementation of the HashRingOption[Node comparable] interface. +type HashRingOptionFunc[Node comparable] func(*HashRing[Node]) + +func (f HashRingOptionFunc[Node]) apply(do *HashRing[Node]) { + f(do) +} + +// ApplyOptions call apply() for all options one by one +func (o *HashRing[Node]) ApplyOptions(options ...HashRingOption[Node]) *HashRing[Node] { + for _, opt := range options { + if opt == nil { + continue + } + opt.apply(o) + } + return o +} + +// WithHashRing sets HashRing. +func WithHashRing[Node comparable](v HashRing[Node]) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + *o = v + }) +} + +// WithHashRingSortedKeys appends sortedKeys in HashRing[Node]. +// The List of nodes to use in the Ketama consistent hash continuum +// +// This simulates the structure of keys used in the Ketama consistent hash ring, +// which stores the virtual node HashKeys on the physical nodes. +// All nodes in the cluster are topped by virtual nodes. +// In principle, it is a brute-force search to find the first complete HashKey +// +// For example, +// Node -> Key -> IterateKey -> HashKey +// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-0 -> 1234 +// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-160 -> 256 +// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-320 -> 692 +// []HashKey, Index for nodes binary search +func WithHashRingSortedKeys[Node comparable](v ...uint32) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.sortedKeys = append(o.sortedKeys, v...) + }) +} + +// WithHashRingSortedKeysReplace sets sortedKeys in HashRing[Node]. +// The List of nodes to use in the Ketama consistent hash continuum +// +// This simulates the structure of keys used in the Ketama consistent hash ring, +// which stores the virtual node HashKeys on the physical nodes. +// All nodes in the cluster are topped by virtual nodes. +// In principle, it is a brute-force search to find the first complete HashKey +// +// For example, +// Node -> Key -> IterateKey -> HashKey +// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-0 -> 1234 +// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-160 -> 256 +// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-320 -> 692 +// []HashKey, Index for nodes binary search +func WithHashRingSortedKeysReplace[Node comparable](v ...uint32) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.sortedKeys = v + }) +} + +// WithHashRingNodeByKey appends nodeByKey in HashRing[Node]. +// +func WithHashRingNodeByKey[Node comparable](m map[uint32]Node) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + if o.nodeByKey == nil { + o.nodeByKey = m + return + } + for k, v := range m { + o.nodeByKey[k] = v + } + }) +} + +// WithHashRingNodeByKeyReplace sets nodeByKey in HashRing[Node]. +// +func WithHashRingNodeByKeyReplace[Node comparable](v map[uint32]Node) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.nodeByKey = v + }) +} + +// WithHashRingAllNodes appends allNodes in HashRing[Node]. +// +func WithHashRingAllNodes[Node comparable](m map[Node]struct{}) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + if o.allNodes == nil { + o.allNodes = m + return + } + for k, v := range m { + o.allNodes[k] = v + } + }) +} + +// WithHashRingAllNodesReplace sets allNodes in HashRing[Node]. +// +func WithHashRingAllNodesReplace[Node comparable](v map[Node]struct{}) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.allNodes = v + }) +} + +// WithHashRingHashAlg sets hashAlg in HashRing[Node]. +// The hash algorithm to use when choosing a node in the Ketama consistent hash continuum +func WithHashRingHashAlg[Node comparable](v HashAlgorithm) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.hashAlg = v + }) +} + +// WithHashRingWeightByNode appends weightByNode in HashRing[Node]. +// node weights for ketama, a map from InetSocketAddress to weight as Integer +func WithHashRingWeightByNode[Node comparable](m map[Node]int) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + if o.weightByNode == nil { + o.weightByNode = m + return + } + for k, v := range m { + o.weightByNode[k] = v + } + }) +} + +// WithHashRingWeightByNodeReplace sets weightByNode in HashRing[Node]. +// node weights for ketama, a map from InetSocketAddress to weight as Integer +func WithHashRingWeightByNodeReplace[Node comparable](v map[Node]int) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.weightByNode = v + }) +} + +// WithHashRingIsWeighted sets isWeighted in HashRing[Node]. +func WithHashRingIsWeighted[Node comparable](v bool) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.isWeighted = v + }) +} + +// WithHashRingNumReps sets numReps in HashRing[Node]. +// the number of discrete hashes that should be defined for each node in the continuum. +func WithHashRingNumReps[Node comparable](v int) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.numReps = v + }) +} + +// WithHashRingNodeKeyFormatter sets nodeKeyFormatter in HashRing[Node]. +// the format used to name the nodes in Ketama, either SpyMemcached or LibMemcached +func WithHashRingNodeKeyFormatter[Node comparable](v Formatter[Node]) HashRingOption[Node] { + return HashRingOptionFunc[Node](func(o *HashRing[Node]) { + o.nodeKeyFormatter = v + }) +} diff --git a/go/exp/container/hashring/hashring_test.go b/go/exp/container/hashring/hashring_test.go index 7cb8b2f4..1177b543 100644 --- a/go/exp/container/hashring/hashring_test.go +++ b/go/exp/container/hashring/hashring_test.go @@ -16,7 +16,7 @@ import ( func TestNew(t *testing.T) { numReps := 160 - x := New[string](WithNodeLocatorNumReps[string](numReps)) + x := New[string](WithHashRingNumReps[string](numReps)) if x == nil { t.Errorf("expected obj") return @@ -29,7 +29,7 @@ func TestNew(t *testing.T) { func TestAdd(t *testing.T) { numReps := 160 - x := New[string](WithNodeLocatorNumReps[string](numReps)) + x := New[string](WithHashRingNumReps[string](numReps)) x.AddNodes("abcdefg") if len(x.nodeByKey) != numReps { @@ -56,7 +56,7 @@ func TestAdd(t *testing.T) { func TestRemove(t *testing.T) { numReps := 160 - x := New[string](WithNodeLocatorNumReps[string](numReps)) + x := New[string](WithHashRingNumReps[string](numReps)) x.AddNodes("abcdefg") x.RemoveNodes("abcdefg") if len(x.nodeByKey) != 0 { @@ -69,7 +69,7 @@ func TestRemove(t *testing.T) { func TestRemoveNonExisting(t *testing.T) { numReps := 160 - x := New[string](WithNodeLocatorNumReps[string](numReps)) + x := New[string](WithHashRingNumReps[string](numReps)) x.AddNodes("abcdefg") x.RemoveNodes("abcdefghijk") if len(x.nodeByKey) != numReps { @@ -79,7 +79,7 @@ func TestRemoveNonExisting(t *testing.T) { func TestGetEmpty(t *testing.T) { numReps := 160 - x := New[string](WithNodeLocatorNumReps[string](numReps)) + x := New[string](WithHashRingNumReps[string](numReps)) _, has := x.Get("asdfsadfsadf") if has { t.Errorf("expected error") @@ -88,7 +88,7 @@ func TestGetEmpty(t *testing.T) { func TestGetSingle(t *testing.T) { numReps := 160 - x := New[string](WithNodeLocatorNumReps[string](numReps)) + x := New[string](WithHashRingNumReps[string](numReps)) x.AddNodes("abcdefg") f := func(s string) bool { y, has := x.Get(s) @@ -700,7 +700,7 @@ func BenchmarkGetTwoLarge(b *testing.B) { // from @edsrzf on github: func TestAddCollision(t *testing.T) { // These two strings produce several crc32 collisions after "|i" is - // appended added by NodeLocator.virtualNode. + // appended added by HashRing.virtualNode. const s1 = "abear" const s2 = "solidiform" x := New[string]() @@ -725,6 +725,6 @@ func TestAddCollision(t *testing.T) { } } -func getN[Node comparable](x *NodeLocator[Node], name string, n int) []Node { +func getN[Node comparable](x *HashRing[Node], name string, n int) []Node { return slices.Collect(iter_.FilterN(x.GetSince(name), n)) } diff --git a/go/exp/container/hashring/nodelocator_options.go b/go/exp/container/hashring/nodelocator_options.go deleted file mode 100644 index 77916adf..00000000 --- a/go/exp/container/hashring/nodelocator_options.go +++ /dev/null @@ -1,180 +0,0 @@ -// Code generated by "go-option -type NodeLocator"; DO NOT EDIT. -// Install go-option by "go get install github.com/searKing/golang/tools/go-option" - -package hashring - -// A NodeLocatorOption sets options. -type NodeLocatorOption[Node comparable] interface { - apply(*NodeLocator[Node]) -} - -// EmptyNodeLocatorOption does not alter the configuration. It can be embedded -// in another structure to build custom options. -// -// This API is EXPERIMENTAL. -type EmptyNodeLocatorOption[Node comparable] struct{} - -func (EmptyNodeLocatorOption[Node]) apply(*NodeLocator[Node]) {} - -// NodeLocatorOptionFunc wraps a function that modifies NodeLocator[Node] into an -// implementation of the NodeLocatorOption[Node comparable] interface. -type NodeLocatorOptionFunc[Node comparable] func(*NodeLocator[Node]) - -func (f NodeLocatorOptionFunc[Node]) apply(do *NodeLocator[Node]) { - f(do) -} - -// ApplyOptions call apply() for all options one by one -func (o *NodeLocator[Node]) ApplyOptions(options ...NodeLocatorOption[Node]) *NodeLocator[Node] { - for _, opt := range options { - if opt == nil { - continue - } - opt.apply(o) - } - return o -} - -// WithNodeLocator sets NodeLocator. -func WithNodeLocator[Node comparable](v NodeLocator[Node]) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - *o = v - }) -} - -// WithNodeLocatorSortedKeys appends sortedKeys in NodeLocator[Node]. -// The List of nodes to use in the Ketama consistent hash continuum -// -// This simulates the structure of keys used in the Ketama consistent hash ring, -// which stores the virtual node HashKeys on the physical nodes. -// All nodes in the cluster are topped by virtual nodes. -// In principle, it is a brute-force search to find the first complete HashKey -// -// For example, -// Node -> Key -> IterateKey -> HashKey -// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-0 -> 1234 -// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-160 -> 256 -// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-320 -> 692 -// []HashKey, Index for nodes binary search -func WithNodeLocatorSortedKeys[Node comparable](v ...uint32) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.sortedKeys = append(o.sortedKeys, v...) - }) -} - -// WithNodeLocatorSortedKeysReplace sets sortedKeys in NodeLocator[Node]. -// The List of nodes to use in the Ketama consistent hash continuum -// -// This simulates the structure of keys used in the Ketama consistent hash ring, -// which stores the virtual node HashKeys on the physical nodes. -// All nodes in the cluster are topped by virtual nodes. -// In principle, it is a brute-force search to find the first complete HashKey -// -// For example, -// Node -> Key -> IterateKey -> HashKey -// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-0 -> 1234 -// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-160 -> 256 -// {} -> 127.0.0.1:11311 -> 127.0.0.1:11311-320 -> 692 -// []HashKey, Index for nodes binary search -func WithNodeLocatorSortedKeysReplace[Node comparable](v ...uint32) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.sortedKeys = v - }) -} - -// WithNodeLocatorNodeByKey appends nodeByKey in NodeLocator[Node]. -// -func WithNodeLocatorNodeByKey[Node comparable](m map[uint32]Node) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - if o.nodeByKey == nil { - o.nodeByKey = m - return - } - for k, v := range m { - o.nodeByKey[k] = v - } - }) -} - -// WithNodeLocatorNodeByKeyReplace sets nodeByKey in NodeLocator[Node]. -// -func WithNodeLocatorNodeByKeyReplace[Node comparable](v map[uint32]Node) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.nodeByKey = v - }) -} - -// WithNodeLocatorAllNodes appends allNodes in NodeLocator[Node]. -// -func WithNodeLocatorAllNodes[Node comparable](m map[Node]struct{}) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - if o.allNodes == nil { - o.allNodes = m - return - } - for k, v := range m { - o.allNodes[k] = v - } - }) -} - -// WithNodeLocatorAllNodesReplace sets allNodes in NodeLocator[Node]. -// -func WithNodeLocatorAllNodesReplace[Node comparable](v map[Node]struct{}) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.allNodes = v - }) -} - -// WithNodeLocatorHashAlg sets hashAlg in NodeLocator[Node]. -// The hash algorithm to use when choosing a node in the Ketama consistent hash continuum -func WithNodeLocatorHashAlg[Node comparable](v HashAlgorithm) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.hashAlg = v - }) -} - -// WithNodeLocatorWeightByNode appends weightByNode in NodeLocator[Node]. -// node weights for ketama, a map from InetSocketAddress to weight as Integer -func WithNodeLocatorWeightByNode[Node comparable](m map[Node]int) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - if o.weightByNode == nil { - o.weightByNode = m - return - } - for k, v := range m { - o.weightByNode[k] = v - } - }) -} - -// WithNodeLocatorWeightByNodeReplace sets weightByNode in NodeLocator[Node]. -// node weights for ketama, a map from InetSocketAddress to weight as Integer -func WithNodeLocatorWeightByNodeReplace[Node comparable](v map[Node]int) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.weightByNode = v - }) -} - -// WithNodeLocatorIsWeighted sets isWeighted in NodeLocator[Node]. -func WithNodeLocatorIsWeighted[Node comparable](v bool) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.isWeighted = v - }) -} - -// WithNodeLocatorNumReps sets numReps in NodeLocator[Node]. -// the number of discrete hashes that should be defined for each node in the continuum. -func WithNodeLocatorNumReps[Node comparable](v int) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.numReps = v - }) -} - -// WithNodeLocatorNodeKeyFormatter sets nodeKeyFormatter in NodeLocator[Node]. -// the format used to name the nodes in Ketama, either SpyMemcached or LibMemcached -func WithNodeLocatorNodeKeyFormatter[Node comparable](v Formatter[Node]) NodeLocatorOption[Node] { - return NodeLocatorOptionFunc[Node](func(o *NodeLocator[Node]) { - o.nodeKeyFormatter = v - }) -}