Skip to content

Commit

Permalink
Merge pull request #2 from xiachufang/optimize
Browse files Browse the repository at this point in the history
支持跳过写缓存
  • Loading branch information
x1ah authored Jan 12, 2021
2 parents 2019b37 + 688ebfb commit e174f7a
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: lint
uses: golangci/golangci-lint-action@v1
with:
version: v1.27
version: v1.35

errcheck:
name: Errcheck
Expand Down
7 changes: 5 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ linters:
- gocritic
- gocyclo
- gofmt
- goimports
# - goimports
- golint
- gomnd
- goprintffuncname
Expand Down Expand Up @@ -112,6 +112,9 @@ issues:
- path: _test\.go
linters:
- gomnd
- goconst
- gosec
- gomnd
# https://github.com/go-critic/go-critic/issues/926
- linters:
- gocritic
Expand All @@ -125,4 +128,4 @@ run:
# golangci.com configuration
# https://github.com/golangci/golangci/wiki/Configuration
service:
golangci-lint-version: 1.28.1 # use the fixed version to not introduce new linters unexpectedly
golangci-lint-version: 1.35.2 # use the fixed version to not introduce new linters unexpectedly
9 changes: 5 additions & 4 deletions examples/hacache/basic.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"fmt"
"time"

Expand All @@ -11,12 +12,12 @@ import (
)

// GenerateCacheKey generate cache key
func GenerateCacheKey(name string, age int) string {
func GenerateCacheKey(ctx context.Context, name string, age int) string {
return name
}

// LongTimeTask cached func.
func LongTimeTask(name string, age int) (string, error) {
func LongTimeTask(ctx context.Context, name string, age int) (string, error) {
time.Sleep(time.Second)
return fmt.Sprintf("%s is %d years old.\n", name, age), nil
}
Expand Down Expand Up @@ -46,15 +47,15 @@ func main() {
panic(err)
}

tom, err := cache.Do("tom", 10)
tom, err := cache.Do(context.Background(), "tom", 10)
if err != nil {
panic(err)
}

// tom == "tom is 10 years old"
fmt.Println(tom.(string))

tom2, err := cache.Do("tom", 20)
tom2, err := cache.Do(context.Background(), "tom", 20)
if err != nil {
panic(err)
}
Expand Down
60 changes: 60 additions & 0 deletions hacache/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package hacache

import (
"context"
"sync"
"sync/atomic"
)

const (
cacheResult = 1
ignoreResult = -1
cacheContextKey = "cacheContext"
)

type contextKey string

// CacheContext 缓存的一些上下文信息
type CacheContext struct {
sync.Mutex

// cacheResult 是否需要缓存原函数返回结果
// v < 0 表示不缓存, >= 0 表示缓存
cacheResult int64
}

// IgnoreResult 不缓存原函数返回结果
func (ctx *CacheContext) IgnoreResult() {
atomic.StoreInt64(&ctx.cacheResult, ignoreResult)
}

// CacheResult 是否缓存结果
func (ctx *CacheContext) CacheResult() bool {
v := atomic.LoadInt64(&ctx.cacheResult)
return v == cacheResult
}

// IgnoreFuncResult 不缓存原函数返回结果
func IgnoreFuncResult(ctx context.Context) {
cacheCtx, ok := ctx.Value(contextKey(cacheContextKey)).(*CacheContext)
if !ok {
return
}

cacheCtx.IgnoreResult()
}

// CacheResult 是否缓存结果, default is true
func CacheResult(ctx context.Context) bool {
cacheCtx, ok := ctx.Value(contextKey(cacheContextKey)).(*CacheContext)
if !ok {
return true
}

return cacheCtx.CacheResult()
}

// WrapCacheContext wrap new cache context
func WrapCacheContext(ctx context.Context) context.Context {
return context.WithValue(ctx, contextKey(cacheContextKey), &CacheContext{cacheResult: cacheResult})
}
9 changes: 7 additions & 2 deletions hacache/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,14 @@ func (enc *HaEncoder) Decode(b []byte) (interface{}, error) {
v := enc.NewValue()
switch msg := v.(type) {
case proto.Message:
return msg, proto.Unmarshal(b, msg)
err := proto.Unmarshal(b, msg)
return msg, err
case int64:
err := msgpack.Unmarshal(b, &msg)
return msg, err
default:
return msg, msgpack.Unmarshal(b, msg)
err := msgpack.Unmarshal(b, &msg)
return msg, err
}
}

Expand Down
32 changes: 24 additions & 8 deletions hacache/hacache.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hacache

import (
"context"
"errors"
"fmt"
"reflect"
Expand Down Expand Up @@ -49,6 +50,7 @@ type CachedValue struct {
}

// New return a new ha-cache instance
// nolint: gomnd
func New(opt *Options) (*HaCache, error) {
if opt.Storage == nil {
return nil, errors.New("no storage found")
Expand Down Expand Up @@ -123,6 +125,7 @@ func (hc *HaCache) FnRun(background bool, args ...interface{}) (interface{}, err
}

// 被缓存的函数签名为: func(args ...interface{}) (interface{}, error)
// nolint: gomnd
if len(result) != 2 {
return nil, fmt.Errorf("invalid fn: %v", hc.opt.Fn)
}
Expand Down Expand Up @@ -197,6 +200,14 @@ func (hc *HaCache) Trigger(event Event) {

// Do 取缓存结果,如果不存在,则更新缓存
func (hc *HaCache) Do(args ...interface{}) (interface{}, error) {
ctx := context.Background()
if len(args) > 0 {
if c, ok := args[0].(context.Context); ok {
ctx = WrapCacheContext(c)
args[0] = ctx
}
}

cacheKey := hc.GenCacheKey(args...)
if cacheKey == "" {
return nil, ErrorInvalidCacheKey
Expand All @@ -219,10 +230,13 @@ func (hc *HaCache) Do(args ...interface{}) (interface{}, error) {
CurrentStats.Incr(MFnRunErr, 1)
return nil, err
}
hc.Trigger(&EventCacheInvalid{
Data: res,
Key: cacheKey,
})

if CacheResult(ctx) {
hc.Trigger(&EventCacheInvalid{
Data: res,
Key: cacheKey,
})
}
return res, nil
}

Expand All @@ -245,10 +259,12 @@ func (hc *HaCache) Do(args ...interface{}) (interface{}, error) {
return hc.opt.Encoder.Decode(value.Bytes)
}

hc.Trigger(&EventCacheInvalid{
Data: copy(res),
Key: cacheKey,
})
if CacheResult(ctx) {
hc.Trigger(&EventCacheInvalid{
Data: copyVal(res),
Key: cacheKey,
})
}

return res, err
}
Expand Down
65 changes: 61 additions & 4 deletions hacache/hacache_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hacache

import (
"context"
"fmt"
"math/rand"
"strconv"
Expand Down Expand Up @@ -49,9 +50,8 @@ func (enc *MyEncoder) Encode(v interface{}) ([]byte, error) {
func (enc *MyEncoder) Decode(b []byte) (interface{}, error) {
v := enc.NewValue()
err := msgpack.Unmarshal(b, v)
switch data := v.(type) {
case *Foo:
data.Cached = true
if v, ok := v.(*Foo); ok {
v.Cached = true
}

return v, err
Expand Down Expand Up @@ -107,7 +107,7 @@ func TestHaCache_Cache_basic(t *testing.T) {
}

func TestHaCache_SkipCache(t *testing.T) {
var rd = func() (int, error) { return rand.Int(), nil }
var rd = func(name string) (int, error) { return rand.Int(), nil }
hc, err := New(&Options{
Storage: &LocalStorage{Data: make(map[string]*Value)},
GenKeyFn: func(name string) string { return SkipCache },
Expand Down Expand Up @@ -233,6 +233,9 @@ func TestHaCache_Cache_limit(t *testing.T) {
GenKeyFn: func(i int) string { return strconv.Itoa(i) + "fn2-limit" },
Fn: foo,
FnRunLimit: 2,
Encoder: NewEncoder(func() interface{} {
return Fooo{}
}),
})
if err != nil {
t.Fatal("init ha-cache error: ", err)
Expand All @@ -257,3 +260,57 @@ func TestHaCache_Cache_limit(t *testing.T) {
t.Fatal("expect success: 2, got: ", successCnt)
}
}

// 测试 context,跳过缓存
func TestHaCache_Context(t *testing.T) {
var fn = func(ctx context.Context) (int64, error) {
IgnoreFuncResult(ctx)
return time.Now().UnixNano(), nil
}

hc, err := New(&Options{
FnRunLimit: 10,
Storage: &LocalStorage{Data: make(map[string]*Value)},
GenKeyFn: func(ctx context.Context) string { return "TestHaCache_Context" },
Fn: fn,
Expiration: time.Hour,
Encoder: NewEncoder(func() interface{} {
return int64(0)
}),
})
if err != nil {
t.Fatal("init hacache error: ", err)
}

v1, _ := hc.Do(context.Background())
time.Sleep(time.Second)
v2, _ := hc.Do(context.Background())
if v1.(int64) == v2.(int64) {
t.Fatal("cache context test fail: ", v1, v2)
}

var fn2 = func() (int64, error) {
return time.Now().UnixNano(), nil
}

hc, err = New(&Options{
FnRunLimit: 10,
Storage: &LocalStorage{Data: make(map[string]*Value)},
GenKeyFn: func() string { return "TestHaCache_bbContext" },
Fn: fn2,
Expiration: time.Hour,
Encoder: NewEncoder(func() interface{} {
return int64(0)
}),
})
if err != nil {
t.Fatal("init hacache error: ", err)
}

v1, _ = hc.Do()
time.Sleep(time.Second)
v2, _ = hc.Do()
if v1.(int64) != v2.(int64) {
t.Fatal("cache context test fail: ", v1, v2)
}
}
2 changes: 1 addition & 1 deletion hacache/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func call(fn interface{}, args ...interface{}) ([]reflect.Value, error) {
}

// copy 拷贝值,返回拷贝后的指针
func copy(v interface{}) interface{} {
func copyVal(v interface{}) interface{} {
if reflect.TypeOf(v).Kind() != reflect.Ptr {
return nil
}
Expand Down
3 changes: 3 additions & 0 deletions logger/zapsyslog.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Core struct {
}

// NewCore return a new syslog zap Core
// nolint: gocritic
func NewCore(level zapcore.LevelEnabler, encoder zapcore.Encoder, writer *syslog.Writer) *Core {
return &Core{
LevelEnabler: level,
Expand All @@ -33,6 +34,7 @@ func (core *Core) With(fields []zapcore.Field) zapcore.Core {
}

// Check determines whether the supplied Entry should be logged
// nolint: gocritic
func (core *Core) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if core.Enabled(entry.Level) {
return checked.AddCore(entry, core)
Expand All @@ -42,6 +44,7 @@ func (core *Core) Check(entry zapcore.Entry, checked *zapcore.CheckedEntry) *zap

// Write serializes the Entry and any Fields supplied at the log site and
// writes them to their destination.
// nolint: gocritic
func (core *Core) Write(entry zapcore.Entry, fields []zapcore.Field) error {
// Generate the message.
buffer, err := core.encoder.EncodeEntry(entry, fields)
Expand Down

0 comments on commit e174f7a

Please sign in to comment.