// A Group is a cache namespace and associated data loaded spread over type Group struct { name string getter Getter mainCache cache }
var ( mu sync.RWMutex groups = make(map[string]*Group) )
// NewGroup create a new instance of Group funcNewGroup(name string, cacheBytes int64, getter Getter) *Group { if getter == nil { panic("nil Getter") } mu.Lock() defer mu.Unlock() g := &Group{ name: name, getter: getter, mainCache: cache{cacheBytes: cacheBytes}, } groups[name] = g return g }
// GetGroup returns the named group previously created with NewGroup, or // nil if there's no such group. funcGetGroup(name string) *Group { mu.RLock() g := groups[name] mu.RUnlock() return g }
一个 Group 可以认为是一个缓存的命名空间,每个 Group 拥有一个唯一的名称 name。比如可以创建三个 Group,缓存学生的成绩命名为 scores,缓存学生信息的命名为 info,缓存学生课程的命名为 courses。
第二个属性是 getter Getter,即缓存未命中时获取源数据的回调(callback)。
第三个属性是 mainCache cache,即一开始实现的并发缓存。
构建函数 NewGroup 用来实例化 Group,并且将 group 存储在全局变量 groups 中。
funcTestGet(t *testing.T) { loadCounts := make(map[string]int, len(db)) gee := NewGroup("scores", 2<<10, GetterFunc( func(key string)([]byte, error) { log.Println("[SlowDB] search key", key) if v, ok := db[key]; ok { if _, ok := loadCounts[key]; !ok { loadCounts[key] = 0 } loadCounts[key] += 1 return []byte(v), nil } returnnil, fmt.Errorf("%s not exist", key) }))
for k, v := range db { if view, err := gee.Get(k); err != nil || view.String() != v { t.Fatal("failed to get value of Tom") } // load from callback function if _, err := gee.Get(k); err != nil || loadCounts[k] > 1 { t.Fatalf("cache %s miss", k) } // cache hit }
if view, err := gee.Get("unknown"); err == nil { t.Fatalf("the value of unknow should be empty, but %s got", view) } }
$ go test -run TestGet 2020/02/11 22:07:31 [SlowDB] search key Sam 2020/02/11 22:07:31 [GeeCache] hit 2020/02/11 22:07:31 [SlowDB] search key Tom 2020/02/11 22:07:31 [GeeCache] hit 2020/02/11 22:07:31 [SlowDB] search key Jack 2020/02/11 22:07:31 [GeeCache] hit 2020/02/11 22:07:31 [SlowDB] search key unknown PASS ok geecache 0.008s