From 30ea17e8a481a5488073da4c0bb6c3838f2309c5 Mon Sep 17 00:00:00 2001 From: Patrick Crosby Date: Thu, 19 Sep 2019 23:12:45 -0500 Subject: [PATCH] Add option to use FNV32a hash (UseFnv) --- consistent.go | 15 ++++++++++++++ consistent_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/consistent.go b/consistent.go index 4a9e148..a1a1fa0 100644 --- a/consistent.go +++ b/consistent.go @@ -23,6 +23,7 @@ package consistent // import "stathat.com/c/consistent" import ( "errors" "hash/crc32" + "hash/fnv" "sort" "strconv" "sync" @@ -50,6 +51,7 @@ type Consistent struct { NumberOfReplicas int count int64 scratch [64]byte + UseFnv bool sync.RWMutex } @@ -236,6 +238,13 @@ func (c *Consistent) GetN(name string, n int) ([]string, error) { } func (c *Consistent) hashKey(key string) uint32 { + if c.UseFnv { + return c.hashKeyFnv(key) + } + return c.hashKeyCRC32(key) +} + +func (c *Consistent) hashKeyCRC32(key string) uint32 { if len(key) < 64 { var scratch [64]byte copy(scratch[:], key) @@ -244,6 +253,12 @@ func (c *Consistent) hashKey(key string) uint32 { return crc32.ChecksumIEEE([]byte(key)) } +func (c *Consistent) hashKeyFnv(key string) uint32 { + h := fnv.New32a() + h.Write([]byte(key)) + return h.Sum32() +} + func (c *Consistent) updateSortedHashes() { hashes := c.sortedHashes[:0] //reallocate if we're holding on to too much (1/4th) diff --git a/consistent_test.go b/consistent_test.go index f566468..7f29e17 100644 --- a/consistent_test.go +++ b/consistent_test.go @@ -6,6 +6,7 @@ package consistent import ( "bufio" + "encoding/base64" "math/rand" "os" "runtime" @@ -740,3 +741,52 @@ func TestConcurrentGetSet(t *testing.T) { } wg.Wait() } + +func TestDistributionFnv(t *testing.T) { + x := New() + x.UseFnv = true + x.Add("abcdefg") + x.Add("hijklmn") + x.Add("opqrstu") + dist := make(map[string]int) + g := make([]byte, 12) + for i := 0; i < 10000; i++ { + _, err := rand.Read(g) + if err != nil { + t.Fatal(err) + } + s := base64.StdEncoding.EncodeToString(g) + r, err := x.Get(s) + if err != nil { + t.Fatal(err) + } + dist[r] = dist[r] + 1 + } + for k, v := range dist { + t.Logf("%s: %d", k, v) + } +} + +func TestDistributionCRC(t *testing.T) { + x := New() + x.Add("abcdefg") + x.Add("hijklmn") + x.Add("opqrstu") + dist := make(map[string]int) + g := make([]byte, 12) + for i := 0; i < 10000; i++ { + _, err := rand.Read(g) + if err != nil { + t.Fatal(err) + } + s := base64.StdEncoding.EncodeToString(g) + r, err := x.Get(s) + if err != nil { + t.Fatal(err) + } + dist[r] = dist[r] + 1 + } + for k, v := range dist { + t.Logf("%s: %d", k, v) + } +}