cshash(consistent hashing)
Thread-safe consistent hashing implementation with virtual nodes support, ideal for distributed systems and load balancing scenarios.
The cshash module provides a thread-safe consistent hashing implementation with virtual nodes support, ideal for distributed systems and load balancing scenarios.
Features
- Thread-safe operations using read-write mutex for concurrent access
- Virtual nodes support for better load distribution
- Custom hash functions with default FNV-1a implementation
- Dynamic node management with batch update operations
- Efficient key lookup using binary search on sorted hash ring
Basic Usage
1package main
2
3import (
4 "fmt"
5 "hash/fnv"
6
7 "github.com/crazyfrankie/frx/cshash"
8)
9
10func main() {
11 // Create a new consistent hash map with 3 virtual nodes per physical node
12 hashMap := cshash.NewMap(3, nil) // nil uses default FNV-1a hash function
13
14 // Add initial nodes
15 nodes := []string{"server1", "server2", "server3"}
16 hashMap.Update(nil, nodes)
17
18 // Get node for a key
19 node := hashMap.Get("user123")
20 fmt.Printf("Key 'user123' maps to: %s\n", node)
21
22 node = hashMap.Get("session456")
23 fmt.Printf("Key 'session456' maps to: %s\n", node)
24}
Custom Hash Function
1// Using a custom hash function
2func customHash(data []byte) uint64 {
3 h := fnv.New64()
4 h.Write(data)
5 return h.Sum64()
6}
7
8func main() {
9 // Create hash map with custom hash function
10 hashMap := cshash.NewMap(5, customHash)
11
12 // Add nodes
13 hashMap.Update(nil, []string{"node1", "node2", "node3"})
14
15 // Test key distribution
16 keys := []string{"key1", "key2", "key3", "key4", "key5"}
17 for _, key := range keys {
18 node := hashMap.Get(key)
19 fmt.Printf("Key '%s' -> Node '%s'\n", key, node)
20 }
21}
Dynamic Node Management
1func main() {
2 hashMap := cshash.NewMap(3, nil)
3
4 // Initial setup
5 initialNodes := []string{"server1", "server2", "server3"}
6 hashMap.Update(nil, initialNodes)
7
8 fmt.Println("Initial mapping:")
9 testKey := "user123"
10 fmt.Printf("Key '%s' -> %s\n", testKey, hashMap.Get(testKey))
11
12 // Add new nodes and remove old ones
13 toRemove := []string{"server1"}
14 toAdd := []string{"server4", "server5"}
15
16 hashMap.Update(toRemove, toAdd)
17
18 fmt.Println("After update:")
19 fmt.Printf("Key '%s' -> %s\n", testKey, hashMap.Get(testKey))
20
21 // Add more nodes
22 hashMap.Update(nil, []string{"server6", "server7"})
23
24 fmt.Println("After adding more nodes:")
25 fmt.Printf("Key '%s' -> %s\n", testKey, hashMap.Get(testKey))
26}
Load Balancing Example
1package main
2
3import (
4 "fmt"
5 "math/rand"
6 "time"
7
8 "github.com/crazyfrankie/frx/cshash"
9)
10
11func main() {
12 // Create hash map for load balancing
13 hashMap := cshash.NewMap(10, nil) // More virtual nodes for better distribution
14
15 // Available servers
16 servers := []string{
17 "web-server-1:8080",
18 "web-server-2:8080",
19 "web-server-3:8080",
20 "web-server-4:8080",
21 }
22
23 hashMap.Update(nil, servers)
24
25 // Simulate client requests
26 rand.Seed(time.Now().UnixNano())
27
28 serverCounts := make(map[string]int)
29
30 // Generate 1000 random client IDs and see distribution
31 for i := 0; i < 1000; i++ {
32 clientID := fmt.Sprintf("client_%d", rand.Intn(10000))
33 server := hashMap.Get(clientID)
34 serverCounts[server]++
35 }
36
37 fmt.Println("Request distribution:")
38 for server, count := range serverCounts {
39 fmt.Printf("%s: %d requests (%.1f%%)\n",
40 server, count, float64(count)/10.0)
41 }
42
43 // Simulate server failure and recovery
44 fmt.Println("\nSimulating server failure...")
45 hashMap.Update([]string{"web-server-2:8080"}, nil)
46
47 // Test same keys after server removal
48 fmt.Println("Key mapping after server failure:")
49 testKeys := []string{"client_1234", "client_5678", "client_9999"}
50 for _, key := range testKeys {
51 server := hashMap.Get(key)
52 fmt.Printf("Key '%s' -> %s\n", key, server)
53 }
54}
Cache Sharding Example
1package main
2
3import (
4 "fmt"
5 "strconv"
6
7 "github.com/crazyfrankie/frx/cshash"
8)
9
10type CacheCluster struct {
11 hashMap *cshash.HashMap
12 caches map[string]*Cache
13}
14
15type Cache struct {
16 name string
17 data map[string]interface{}
18}
19
20func NewCacheCluster(cacheNodes []string) *CacheCluster {
21 cluster := &CacheCluster{
22 hashMap: cshash.NewMap(5, nil),
23 caches: make(map[string]*Cache),
24 }
25
26 // Initialize cache instances
27 for _, node := range cacheNodes {
28 cluster.caches[node] = &Cache{
29 name: node,
30 data: make(map[string]interface{}),
31 }
32 }
33
34 // Update hash map
35 cluster.hashMap.Update(nil, cacheNodes)
36
37 return cluster
38}
39
40func (cc *CacheCluster) Set(key string, value interface{}) {
41 node := cc.hashMap.Get(key)
42 if cache, exists := cc.caches[node]; exists {
43 cache.data[key] = value
44 fmt.Printf("Stored '%s' in cache '%s'\n", key, node)
45 }
46}
47
48func (cc *CacheCluster) Get(key string) (interface{}, bool) {
49 node := cc.hashMap.Get(key)
50 if cache, exists := cc.caches[node]; exists {
51 value, found := cache.data[key]
52 return value, found
53 }
54 return nil, false
55}
56
57func main() {
58 // Create cache cluster
59 cacheNodes := []string{"cache-1", "cache-2", "cache-3"}
60 cluster := NewCacheCluster(cacheNodes)
61
62 // Store some data
63 for i := 0; i < 10; i++ {
64 key := "key" + strconv.Itoa(i)
65 value := "value" + strconv.Itoa(i)
66 cluster.Set(key, value)
67 }
68
69 // Retrieve data
70 fmt.Println("\nRetrieving data:")
71 for i := 0; i < 10; i++ {
72 key := "key" + strconv.Itoa(i)
73 if value, found := cluster.Get(key); found {
74 fmt.Printf("Found '%s' = '%s'\n", key, value)
75 }
76 }
77}