Skip to content

Commit

Permalink
精简 loader 设计
Browse files Browse the repository at this point in the history
  • Loading branch information
FishGoddess committed Jan 12, 2024
1 parent 58665b6 commit a2453a4
Show file tree
Hide file tree
Showing 20 changed files with 249 additions and 179 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Setup
uses: actions/setup-go@v4
with:
go-version: "1.17"
go-version: "1.20"
- run: go version

- name: Checkout
Expand Down
1 change: 1 addition & 0 deletions FUTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* [ ] ~~提供一个清空并设置全量值的方法,方便定时数据的全量替换~~
目前还找不到一个合适的设计去加入这个功能,并且也不是非常刚需,通过业务手段可以处理,所以先不加
* [ ] 完善监控上报器,提供更多缓存信息查询的方法
* [ ] 梳理代码,优化代码风格

### v0.4.x

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
all: test bench

test:
go test -cover ./...
go test -cover -count=1 -test.cpu=1 ./...

bench:
go test -v -bench=. -benchtime=1s ./_examples/performance_test.go
go test -v ./_examples/performance_test.go -bench=. -benchtime=1s

fmt:
go fmt ./...
Binary file removed _icons/jetbrains.png
Binary file not shown.
91 changes: 21 additions & 70 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package cachego

import (
"context"
"sync"
"time"

"github.com/FishGoddess/cachego/pkg/task"
Expand All @@ -27,20 +26,6 @@ const (
NoTTL = 0
)

const (
// standard cache is a simple cache with locked map.
// It evicts entries randomly if cache size reaches to max entries.
standard CacheType = "standard"

// lru cache is a cache using lru to evict entries.
// More details see https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU).
lru CacheType = "lru"

// lfu cache is a cache using lfu to evict entries.
// More details see https://en.wikipedia.org/wiki/Cache_replacement_policies#Least-frequently_used_(LFU).
lfu CacheType = "lfu"
)

var (
newCaches = map[CacheType]func(conf *config) Cache{
standard: newStandardCache,
Expand All @@ -49,29 +34,6 @@ var (
}
)

// CacheType is the type of cache.
type CacheType string

// String returns the cache type in string form.
func (ct CacheType) String() string {
return string(ct)
}

// IsStandard returns if cache type is standard.
func (ct CacheType) IsStandard() bool {
return ct == standard
}

// IsLRU returns if cache type is lru.
func (ct CacheType) IsLRU() bool {
return ct == lru
}

// IsLFU returns if cache type is lfu.
func (ct CacheType) IsLFU() bool {
return ct == lfu
}

// Cache is the core interface of cachego.
// We provide some implements including standard cache and sharding cache.
type Cache interface {
Expand Down Expand Up @@ -101,38 +63,10 @@ type Cache interface {
// Reset resets cache to initial status which is like a new cache.
Reset()

// Loader loads a value to cache.
// See Loader interface.
Loader
}

type cache struct {
*config
Loader

lock sync.RWMutex
}

func (c *cache) setup(conf *config, cache Cache) {
c.config = conf
c.Loader = NewLoader(cache, conf.singleflight)
}

// RunGCTask runs a gc task in a new goroutine and returns a cancel function to cancel the task.
// However, you don't need to call it manually for most time, instead, use options is a better choice.
// Making it a public function is for more customizations in some situations.
// For example, using options to run gc task is un-cancelable, so you can use it to run gc task by your own
// and get a cancel function to cancel the gc task.
func RunGCTask(cache Cache, duration time.Duration) (cancel func()) {
fn := func(ctx context.Context) {
cache.GC()
}

ctx := context.Background()
ctx, cancel = context.WithCancel(ctx)

go task.New(fn).Context(ctx).Duration(duration).Run()
return cancel
// Load loads a key with ttl to cache and returns an error if failed.
// We recommend you use this method to load missed keys to cache,
// because it may use singleflight to reduce the times calling load function.
Load(key string, ttl time.Duration, load func() (value interface{}, err error)) (value interface{}, err error)
}

func newCache(withReport bool, opts ...Option) (cache Cache, reporter *Reporter) {
Expand Down Expand Up @@ -180,3 +114,20 @@ func NewCache(opts ...Option) (cache Cache) {
func NewCacheWithReport(opts ...Option) (cache Cache, reporter *Reporter) {
return newCache(true, opts...)
}

// RunGCTask runs a gc task in a new goroutine and returns a cancel function to cancel the task.
// However, you don't need to call it manually for most time, instead, use options is a better choice.
// Making it a public function is for more customizations in some situations.
// For example, using options to run gc task is un-cancelable, so you can use it to run gc task by your own
// and get a cancel function to cancel the gc task.
func RunGCTask(cache Cache, duration time.Duration) (cancel func()) {
fn := func(ctx context.Context) {
cache.GC()
}

ctx := context.Background()
ctx, cancel = context.WithCancel(ctx)

go task.New(fn).Context(ctx).Duration(duration).Run()
return cancel
}
35 changes: 7 additions & 28 deletions cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,10 @@ const (
maxTestEntries = 10
)

// go test -v -cover -run=^TestCacheType$
func TestCacheType(t *testing.T) {
if standard.String() != string(standard) {
t.Fatalf("standard.String() %s is wrong", standard.String())
}

if lru.String() != string(lru) {
t.Fatalf("lru.String() %s is wrong", lru.String())
}

if lfu.String() != string(lfu) {
t.Fatalf("lfu.String() %s is wrong", lfu.String())
}

if !standard.IsStandard() {
t.Fatal("!standard.IsStandard()")
}

if !lru.IsLRU() {
t.Fatal("!standard.IsLRU()")
}

if !lfu.IsLFU() {
t.Fatal("!standard.IsLFU()")
}
}

type testCache struct {
cache
*config
loader *Loader

count int32
}

Expand Down Expand Up @@ -84,6 +59,10 @@ func (tc *testCache) GC() (cleans int) {

func (tc *testCache) Reset() {}

func (tc *testCache) Load(key string, ttl time.Duration, load func() (value interface{}, err error)) (value interface{}, err error) {
return nil, nil
}

func testCacheGet(t *testing.T, cache Cache) {
value, found := cache.Get("key")
if found {
Expand Down
52 changes: 52 additions & 0 deletions cache_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2024 FishGoddess. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cachego

const (
// standard cache is a simple cache with locked map.
// It evicts entries randomly if cache size reaches to max entries.
standard CacheType = "standard"

// lru cache is a cache using lru to evict entries.
// More details see https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU).
lru CacheType = "lru"

// lfu cache is a cache using lfu to evict entries.
// More details see https://en.wikipedia.org/wiki/Cache_replacement_policies#Least-frequently_used_(LFU).
lfu CacheType = "lfu"
)

// CacheType is the type of cache.
type CacheType string

// String returns the cache type in string form.
func (ct CacheType) String() string {
return string(ct)
}

// IsStandard returns if cache type is standard.
func (ct CacheType) IsStandard() bool {
return ct == standard
}

// IsLRU returns if cache type is lru.
func (ct CacheType) IsLRU() bool {
return ct == lru
}

// IsLFU returns if cache type is lfu.
func (ct CacheType) IsLFU() bool {
return ct == lfu
}
44 changes: 44 additions & 0 deletions cache_type_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 FishGoddess. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cachego

import "testing"

// go test -v -cover -run=^TestCacheType$
func TestCacheType(t *testing.T) {
if standard.String() != string(standard) {
t.Fatalf("standard.String() %s is wrong", standard.String())
}

if lru.String() != string(lru) {
t.Fatalf("lru.String() %s is wrong", lru.String())
}

if lfu.String() != string(lfu) {
t.Fatalf("lfu.String() %s is wrong", lfu.String())
}

if !standard.IsStandard() {
t.Fatal("!standard.IsStandard()")
}

if !lru.IsLRU() {
t.Fatal("!standard.IsLRU()")
}

if !lfu.IsLFU() {
t.Fatal("!standard.IsLFU()")
}
}
2 changes: 1 addition & 1 deletion doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,4 +480,4 @@ Package cachego provides an easy way to use foundation for your caching operatio
package cachego // import "github.com/FishGoddess/cachego"

// Version is the version string representation of cachego.
const Version = "v0.5.0"
const Version = "v0.6.0-alpha"
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/FishGoddess/cachego

go 1.17
go 1.20
24 changes: 21 additions & 3 deletions lfu.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@
package cachego

import (
"sync"
"time"

"github.com/FishGoddess/cachego/pkg/heap"
)

type lfuCache struct {
cache
*config

itemMap map[string]*heap.Item
itemHeap *heap.Heap
lock sync.RWMutex

loader *Loader
}

func newLFUCache(conf *config) Cache {
Expand All @@ -33,11 +37,12 @@ func newLFUCache(conf *config) Cache {
}

cache := &lfuCache{
config: conf,
itemMap: make(map[string]*heap.Item, mapInitialCap),
itemHeap: heap.New(sliceInitialCap),
loader: NewLoader(conf.singleflight),
}

cache.setup(conf, cache)
return cache
}

Expand Down Expand Up @@ -137,7 +142,8 @@ func (lc *lfuCache) gc() (cleans int) {
func (lc *lfuCache) reset() {
lc.itemMap = make(map[string]*heap.Item, mapInitialCap)
lc.itemHeap = heap.New(sliceInitialCap)
lc.Loader.Reset()

lc.loader.Reset()
}

// Get gets the value of key from cache and returns value if found.
Expand Down Expand Up @@ -193,3 +199,15 @@ func (lc *lfuCache) Reset() {

lc.reset()
}

// Load loads a value by load function and sets it to cache.
// Returns an error if load failed.
func (lc *lfuCache) Load(key string, ttl time.Duration, load func() (value interface{}, err error)) (value interface{}, err error) {
value, err = lc.loader.Load(key, ttl, load)
if err != nil {
return value, err
}

lc.Set(key, value, ttl)
return value, nil
}
Loading

0 comments on commit a2453a4

Please sign in to comment.