Coder Social home page Coder Social logo

hashring's People

Contributors

barakmich avatar bodyno avatar charl avatar connor4312 avatar grihabor avatar lorneli avatar mjanson avatar mjuarez avatar serialx avatar unkaktus avatar xuanjiazhen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hashring's Issues

i wonder about.

could you tell me the reason?

1, why use 40 there?

factor := math.Floor(float64(40*len(h.nodes)*weight) / float64(totalWeight))

2, why use 3 there?

for i := 0; i < 3; i++ {
	key := hashVal(bKey[i*4 : i*4+4])
        h.ring[key] = node
	h.sortedKeys = append(h.sortedKeys, key)
}

thanks

`generateCircle` is using sort.Sort, which is not guaranteed to be stable

Hashring.generateCircle() is using sort.Sort(data Interface) to sort sortedkeys, but this function is not guaranteed to be stable.
Is this a potential risk that, with some very edge cases, (for example, Hashring.AddNode or Hashring.RemoveNode is called, which invokes Hashring.generateCircle,) a range of keys will be mapped to a different node?

is this thread safe?

It seems from the code that the ring is not thread safe to modify and use at the same time, right? Like one goroutine add/remove node, while other goroutines call GetNode. Is it true that I need my own locking?

Breaking change consistant hashing

Hi everyone,

the last commit broke our assumption that a randomly ordered slice of nodes will consistently return the same node for the same hash. The following test code fails on the current HEAD of master and works fine with the previous commit:

func TestStableHashring(t *testing.T) {
	nodes := []string{"NODE_1", "NODE_2", "NODE_3", "NODE_4", "NODE_5"}

	hash := "HASH"
	var constantNode string
	for i := 0; i < 100; i++ {

		rand.Shuffle(len(nodes), func(i, j int) { nodes[i], nodes[j] = nodes[j], nodes[i] })

		hr := New(nodes)
		n, ok := hr.GetNode(hash)
		if !ok {
			t.Error("error getting node")
		}

		if constantNode == "" {
			constantNode = n
		}

		if n != constantNode {
			t.Errorf("nodes are not equalt %s != %s", n, constantNode)
		}
	}
}

Is this a falsey assumption or did the latest changes introduce an unintended behaviour?

Cheers,
Dennis

control placement

Hi, does it possible to control placement (near, far) in case of using this package?
For example i have rack with servers and ring contains 40 nodes and i have 3 replica count, how can i specify that i need to place data on far servers?
Or another use-case - i have 3 racks and replica count 2, how can i place data on rack that far from another?

return value of GetNode is unbalanced in some situation

unbalanced example:

$ cat main.go
package main

import (
	"fmt"
	"github.com/google/uuid"
	"github.com/serialx/hashring"
)

func main() {
	schedulers := []string{
		"1.1.1.1",
		"2.2.2.2",
	}
	ring := hashring.New(schedulers)
	serverCount := make(map[string]int)

	for i := 0; i < 1000; i++ {
		etName := uuid.New().String()
		server, _ := ring.GetNode(etName)
		serverCount[server]++
	}
	fmt.Println(serverCount)
}

$ go run main.go
map[1.1.1.1:966 2.2.2.2:34]

❕Misusing crypto/md5

In the latest commit which “Allow passing a hash.Hash at creation time”, it allows to use custom hashers to calculate the hash value, not only crypto/md5.Sum().

However, the code doesn't run as it should be when using the default hasher md5.New().

Things are wrong

If you run following code listed at README.md, you would find that only two servers are inserted to the ring, when you print some info of the ring.

serversInRing := []string{"192.168.0.246:11212",
                          "192.168.0.247:11212",
                          "192.168.0.248:11212",
                          "192.168.0.249:11212",
                          "192.168.0.250:11212",
                          "192.168.0.251:11212",
                          "192.168.0.252:11212"}

replicaCount := 3
ring := hashring.New(serversInRing)
server, _ := ring.GetNodes("my_key", replicaCount)

There is an open issue reporting similar problems.

Inspect the code

In earlier version, it uses md5.Sum([]byte(key)) to calculate hash values.

// crypto/md5
func Sum(data []byte) [Size]byte {
	var d digest
	d.Reset()
	d.Write(data)
	return d.checkSum()
}

For now, it creates a new digest when you use a default hasher. And it calculates hash values by calling digest.Sum(in []byte). Here comes the problem. They are not the same function at all!

// crypto/md5
func (d *digest) Sum(in []byte) []byte {
	// Make a copy of d so that caller can keep writing and summing.
	d0 := *d
	hash := d0.checkSum()
	return append(in, hash[:]...)
}

digest.Sum() simply returns what it receives following the check sum of the same old data. This is not what we expect!

Try to solve it

Maybe the correct way:

func (h *HashRing) hashDigest(key string) []byte {
	h.hasher.Write([]byte(key))
	return h.hasher.Sum(nil)
}

I'm not familiar with hash things, and I'm not sure whether it's ok for other hashers. But at least, it works for the default hasher.

Bug in algorithm for weighted nodes

I am trying to iterate over all nodes of the hash ring, and there are missing nodes.

Here is a sample:

package main

import (
	"github.com/serialx/hashring"
	"fmt"
	"sort"
)

func generate(n int) map[string]int {
	result := make(map[string]int)
	for i := 0; i < n; i++ {
		result[fmt.Sprintf("node%02d", i)] = i
	}
	return result
}

func main() {
	weights := generate(1000)
	ring := hashring.NewWithWeights(weights)
	fmt.Printf("size %d\n", ring.Size())

	var nodes []string
	var ok bool
	for i := 0; i < ring.Size(); i++ {
		nodes, ok = ring.GetNodes("1", ring.Size()-i)
		if ok {
			break
		}
	}
	sort.Strings(nodes)
	fmt.Printf("actual %d\n", len(nodes))
}

Result:

size 1000
actual 987

Always returns the same node.

I am trying this library, but I consistently get the same node. I am trying to hash on stock ticker symbols, here is an example:

package main

import (
	"log"

	"github.com/serialx/hashring"
)

func main() {
	memcacheServers := []string{
		"192.168.0.246:11212",
		"192.168.0.247:11212",
		"192.168.0.249:11212",
	}

	tickers := []string{
		"MSFT",
		"AAPL",
		"AMZN",
		"AAPL",
		"X",
		"NFLX",
		"AMD",
		"APO",
		"V",
		"SPY",
		"TSLA",
	}

	ring := hashring.New(memcacheServers)
	for _, ticker := range tickers {
		server, _ := ring.GetNode(ticker)
		log.Println("Node:", server)
	}
}

Results:

$ go run ring-experiment.go 
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212
2019/10/16 15:14:59 Node: 192.168.0.249:11212

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.