Skip to content

Commit 7292c4a

Browse files
committed
day5 multi node
1 parent 5c93223 commit 7292c4a

File tree

16 files changed

+692
-12
lines changed

16 files changed

+692
-12
lines changed

gee-cache/day2-single-node/geecache/geecache.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,18 @@ func cloneBytes(b []byte) []byte {
6868
}
6969

7070
func (g *Group) load(key string) (value ByteView, err error) {
71+
return g.getLocally(key)
72+
}
73+
74+
func (g *Group) getLocally(key string) (ByteView, error) {
7175
bytes, err := g.getter.Get(key)
72-
if err == nil {
73-
value = ByteView{cloneBytes(bytes)}
74-
g.populateCache(key, value)
76+
if err != nil {
77+
return ByteView{}, err
78+
7579
}
76-
return
80+
value := ByteView{b: cloneBytes(bytes)}
81+
g.populateCache(key, value)
82+
return value, nil
7783
}
7884

7985
func (g *Group) populateCache(key string, value ByteView) {

gee-cache/day3-http-server/geecache/geecache.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,18 @@ func cloneBytes(b []byte) []byte {
6868
}
6969

7070
func (g *Group) load(key string) (value ByteView, err error) {
71+
return g.getLocally(key)
72+
}
73+
74+
func (g *Group) getLocally(key string) (ByteView, error) {
7175
bytes, err := g.getter.Get(key)
72-
if err == nil {
73-
value = ByteView{cloneBytes(bytes)}
74-
g.populateCache(key, value)
76+
if err != nil {
77+
return ByteView{}, err
78+
7579
}
76-
return
80+
value := ByteView{b: cloneBytes(bytes)}
81+
g.populateCache(key, value)
82+
return value, nil
7783
}
7884

7985
func (g *Group) populateCache(key string, value ByteView) {

gee-cache/day4-consistent-hash/geecache/geecache.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,18 @@ func cloneBytes(b []byte) []byte {
6868
}
6969

7070
func (g *Group) load(key string) (value ByteView, err error) {
71+
return g.getLocally(key)
72+
}
73+
74+
func (g *Group) getLocally(key string) (ByteView, error) {
7175
bytes, err := g.getter.Get(key)
72-
if err == nil {
73-
value = ByteView{cloneBytes(bytes)}
74-
g.populateCache(key, value)
76+
if err != nil {
77+
return ByteView{}, err
78+
7579
}
76-
return
80+
value := ByteView{b: cloneBytes(bytes)}
81+
g.populateCache(key, value)
82+
return value, nil
7783
}
7884

7985
func (g *Group) populateCache(key string, value ByteView) {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package geecache
2+
3+
// A ByteView holds an immutable view of bytes.
4+
type ByteView struct {
5+
b []byte
6+
}
7+
8+
// Len returns the view's length
9+
func (v ByteView) Len() int {
10+
return len(v.b)
11+
}
12+
13+
// ByteSlice returns a copy of the data as a byte slice.
14+
func (v ByteView) ByteSlice() []byte {
15+
return cloneBytes(v.b)
16+
}
17+
18+
// String returns the data as a string, making a copy if necessary.
19+
func (v ByteView) String() string {
20+
return string(v.b)
21+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package geecache
2+
3+
import (
4+
"geecache/lru"
5+
"sync"
6+
)
7+
8+
type cache struct {
9+
mu sync.RWMutex
10+
lru *lru.Cache
11+
cacheBytes int64
12+
}
13+
14+
func (c *cache) add(key string, value ByteView) {
15+
c.mu.RLock()
16+
defer c.mu.RUnlock()
17+
if c.lru == nil {
18+
c.lru = lru.New(c.cacheBytes, nil)
19+
}
20+
c.lru.Add(key, value)
21+
}
22+
23+
func (c *cache) get(key string) (value ByteView, ok bool) {
24+
c.mu.RLock()
25+
defer c.mu.RUnlock()
26+
if c.lru == nil {
27+
return
28+
}
29+
30+
if v, ok := c.lru.Get(key); ok {
31+
return v.(ByteView), ok
32+
}
33+
34+
return
35+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package consistenthash
2+
3+
import (
4+
"hash/crc32"
5+
"sort"
6+
"strconv"
7+
)
8+
9+
// Hash maps bytes to uint32
10+
type Hash func(data []byte) uint32
11+
12+
// Map constains all hashed keys
13+
type Map struct {
14+
hash Hash
15+
replicas int
16+
keys []int // Sorted
17+
hashMap map[int]string
18+
}
19+
20+
// New creates a Map instance
21+
func New(replicas int, fn Hash) *Map {
22+
m := &Map{
23+
replicas: replicas,
24+
hash: fn,
25+
hashMap: make(map[int]string),
26+
}
27+
if m.hash == nil {
28+
m.hash = crc32.ChecksumIEEE
29+
}
30+
return m
31+
}
32+
33+
// Add adds some keys to the hash.
34+
func (m *Map) Add(keys ...string) {
35+
for _, key := range keys {
36+
for i := 0; i < m.replicas; i++ {
37+
hash := int(m.hash([]byte(strconv.Itoa(i) + key)))
38+
m.keys = append(m.keys, hash)
39+
m.hashMap[hash] = key
40+
}
41+
}
42+
sort.Ints(m.keys)
43+
}
44+
45+
// Get gets the closest item in the hash to the provided key.
46+
func (m *Map) Get(key string) string {
47+
if len(m.keys) == 0 {
48+
return ""
49+
}
50+
51+
hash := int(m.hash([]byte(key)))
52+
// Binary search for appropriate replica.
53+
idx := sort.Search(len(m.keys), func(i int) bool {
54+
return m.keys[i] >= hash
55+
})
56+
57+
return m.hashMap[m.keys[idx%len(m.keys)]]
58+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package consistenthash
2+
3+
import (
4+
"strconv"
5+
"testing"
6+
)
7+
8+
func TestHashing(t *testing.T) {
9+
hash := New(3, func(key []byte) uint32 {
10+
i, _ := strconv.Atoi(string(key))
11+
return uint32(i)
12+
})
13+
14+
// Given the above hash function, this will give replicas with "hashes":
15+
// 2, 4, 6, 12, 14, 16, 22, 24, 26
16+
hash.Add("6", "4", "2")
17+
18+
testCases := map[string]string{
19+
"2": "2",
20+
"11": "2",
21+
"23": "4",
22+
"27": "2",
23+
}
24+
25+
for k, v := range testCases {
26+
if hash.Get(k) != v {
27+
t.Errorf("Asking for %s, should have yielded %s", k, v)
28+
}
29+
}
30+
31+
// Adds 8, 18, 28
32+
hash.Add("8")
33+
34+
// 27 should now map to 8.
35+
testCases["27"] = "8"
36+
37+
for k, v := range testCases {
38+
if hash.Get(k) != v {
39+
t.Errorf("Asking for %s, should have yielded %s", k, v)
40+
}
41+
}
42+
43+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package geecache
2+
3+
import (
4+
"sync"
5+
)
6+
7+
// A Group is a cache namespace and associated data loaded spread over
8+
type Group struct {
9+
name string
10+
getter Getter
11+
mainCache cache
12+
peers PeerPicker
13+
peersOnce sync.Once
14+
}
15+
16+
// A Getter loads data for a key.
17+
type Getter interface {
18+
Get(key string) ([]byte, error)
19+
}
20+
21+
// A GetterFunc implements Getter with a function.
22+
type GetterFunc func(key string) ([]byte, error)
23+
24+
// Get implements Getter interface function
25+
func (f GetterFunc) Get(key string) ([]byte, error) {
26+
return f(key)
27+
}
28+
29+
var (
30+
mu sync.RWMutex
31+
groups = make(map[string]*Group)
32+
)
33+
34+
// NewGroup create a new instance of Group
35+
func NewGroup(name string, cacheBytes int64, getter Getter) *Group {
36+
if getter == nil {
37+
panic("nil Getter")
38+
}
39+
mu.Lock()
40+
defer mu.Unlock()
41+
g := &Group{
42+
name: name,
43+
getter: getter,
44+
mainCache: cache{cacheBytes: cacheBytes},
45+
}
46+
groups[name] = g
47+
return g
48+
}
49+
50+
// GetGroup returns the named group previously created with NewGroup, or
51+
// nil if there's no such group.
52+
func GetGroup(name string) *Group {
53+
mu.RLock()
54+
g := groups[name]
55+
mu.RUnlock()
56+
return g
57+
}
58+
59+
// Get value for a key from cache
60+
func (g *Group) Get(key string) (ByteView, error) {
61+
g.peersOnce.Do(func() {
62+
g.peers = getPeers()
63+
})
64+
if v, ok := g.mainCache.get(key); ok {
65+
return v, nil
66+
}
67+
68+
return g.load(key)
69+
}
70+
71+
func cloneBytes(b []byte) []byte {
72+
c := make([]byte, len(b))
73+
copy(c, b)
74+
return c
75+
}
76+
77+
func (g *Group) load(key string) (value ByteView, err error) {
78+
if peer, ok := g.peers.PickPeer(key); ok {
79+
value, err = g.getFromPeer(peer, key)
80+
if err == nil {
81+
return value, nil
82+
}
83+
}
84+
85+
return g.getLocally(key)
86+
}
87+
88+
func (g *Group) populateCache(key string, value ByteView) {
89+
g.mainCache.add(key, value)
90+
}
91+
92+
func (g *Group) getLocally(key string) (ByteView, error) {
93+
bytes, err := g.getter.Get(key)
94+
if err != nil {
95+
return ByteView{}, err
96+
97+
}
98+
value := ByteView{b: cloneBytes(bytes)}
99+
g.populateCache(key, value)
100+
return value, nil
101+
}
102+
103+
func (g *Group) getFromPeer(peer PeerGetter, key string) (ByteView, error) {
104+
bytes, err := peer.Get(g.name, key)
105+
if err != nil {
106+
return ByteView{}, err
107+
}
108+
return ByteView{b: bytes}, nil
109+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package geecache
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"testing"
7+
)
8+
9+
var db = map[string]string{
10+
"Tom": "630",
11+
"Jack": "589",
12+
"Sam": "567",
13+
}
14+
15+
func TestGet(t *testing.T) {
16+
gee := NewGroup("scores", 2<<10, GetterFunc(
17+
func(key string) ([]byte, error) {
18+
log.Println("[group scores] search key", key)
19+
if v, ok := db[key]; ok {
20+
return []byte(v), nil
21+
}
22+
return nil, fmt.Errorf("%s not exist", key)
23+
}))
24+
25+
for k, v := range db {
26+
view, err := gee.Get(k)
27+
if err != nil || view.String() != v {
28+
t.Fatal("failed to get value of Tom")
29+
}
30+
}
31+
32+
if view, err := gee.Get("unknown"); err == nil {
33+
t.Fatalf("the value of unknow should be empty, but %s got", view)
34+
}
35+
}
36+
37+
func TestGetGroup(t *testing.T) {
38+
groupName := "scores"
39+
NewGroup(groupName, 2<<10, GetterFunc(
40+
func(key string) (bytes []byte, err error) { return }))
41+
if group := GetGroup(groupName); group == nil || group.name != groupName {
42+
t.Fatalf("group %s not exist", groupName)
43+
}
44+
45+
if group := GetGroup(groupName + "111"); group != nil {
46+
t.Fatalf("expect nil, but %s got", group.name)
47+
}
48+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module geecache
2+
3+
go 1.13

0 commit comments

Comments
 (0)