Skip to content

Commit

Permalink
feat: optimize cache performance (#519)
Browse files Browse the repository at this point in the history
* feat: optimize cache performance

* chore: update mocks

* fix: test

* fix: test

* chore: merge master

* chore: merge master

* chore: merge master

---------

Co-authored-by: devhaozi <[email protected]>
  • Loading branch information
devhaozi and devhaozi authored Jul 2, 2024
1 parent 4651700 commit d288c4c
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 121 deletions.
4 changes: 2 additions & 2 deletions cache/driver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (r *Store) Add(key string, value any, seconds time.Duration) bool {
return true
}

func (r *Store) Decrement(key string, value ...int) (int, error) {
func (r *Store) Decrement(key string, value ...int64) (int64, error) {
return 1, nil
}

Expand Down Expand Up @@ -128,7 +128,7 @@ func (r *Store) Has(key string) bool {
return true
}

func (r *Store) Increment(key string, value ...int) (int, error) {
func (r *Store) Increment(key string, value ...int64) (int64, error) {
return 1, nil
}

Expand Down
78 changes: 53 additions & 25 deletions cache/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package cache

import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"

"github.com/patrickmn/go-cache"
"github.com/spf13/cast"

contractscache "github.com/goravel/framework/contracts/cache"
Expand All @@ -14,41 +16,52 @@ import (
type Memory struct {
ctx context.Context
prefix string
instance *cache.Cache
instance sync.Map
}

func NewMemory(config config.Config) (*Memory, error) {
memory := cache.New(5*time.Minute, 10*time.Minute)

return &Memory{
prefix: prefix(config),
instance: memory,
prefix: prefix(config),
}, nil
}

// Add Driver an item in the cache if the key does not exist.
func (r *Memory) Add(key string, value any, t time.Duration) bool {
if t == NoExpiration {
t = cache.NoExpiration
if t != NoExpiration {
time.AfterFunc(t, func() {
r.Forget(key)
})
}

err := r.instance.Add(r.key(key), value, t)

return err == nil
_, loaded := r.instance.LoadOrStore(r.key(key), value)
return !loaded
}

func (r *Memory) Decrement(key string, value ...int) (int, error) {
// Decrement Decrement the value of an item in the cache.
func (r *Memory) Decrement(key string, value ...int64) (int64, error) {
if len(value) == 0 {
value = append(value, 1)
}
r.Add(key, 0, cache.NoExpiration)

return r.instance.DecrementInt(r.key(key), value[0])
r.Add(key, new(int64), NoExpiration)
pv := r.Get(key)
switch nv := pv.(type) {
case *atomic.Int64:
return nv.Add(-value[0]), nil
case *atomic.Int32:
return int64(nv.Add(int32(-value[0]))), nil
case *int64:
return atomic.AddInt64(nv, -value[0]), nil
case *int32:
return int64(atomic.AddInt32(nv, int32(-value[0]))), nil
default:
return 0, fmt.Errorf("value type of %s is not *atomic.Int64 or *int64 or *atomic.Int32 or *int32", key)
}
}

// Forever Driver an item in the cache indefinitely.
func (r *Memory) Forever(key string, value any) bool {
if err := r.Put(key, value, cache.NoExpiration); err != nil {
if err := r.Put(key, value, NoExpiration); err != nil {
return false
}

Expand All @@ -64,14 +77,13 @@ func (r *Memory) Forget(key string) bool {

// Flush Remove all items from the cache.
func (r *Memory) Flush() bool {
r.instance.Flush()

r.instance = sync.Map{}
return true
}

// Get Retrieve an item from the cache by key.
func (r *Memory) Get(key string, def ...any) any {
val, exist := r.instance.Get(r.key(key))
val, exist := r.instance.Load(r.key(key))
if exist {
return val
}
Expand Down Expand Up @@ -122,18 +134,29 @@ func (r *Memory) GetString(key string, def ...string) string {

// Has Check an item exists in the cache.
func (r *Memory) Has(key string) bool {
_, exist := r.instance.Get(r.key(key))

_, exist := r.instance.Load(r.key(key))
return exist
}

func (r *Memory) Increment(key string, value ...int) (int, error) {
func (r *Memory) Increment(key string, value ...int64) (int64, error) {
if len(value) == 0 {
value = append(value, 1)
}
r.Add(key, 0, cache.NoExpiration)

return r.instance.IncrementInt(r.key(key), value[0])
r.Add(key, new(int64), NoExpiration)
pv := r.Get(key)
switch nv := pv.(type) {
case *atomic.Int64:
return nv.Add(value[0]), nil
case *atomic.Int32:
return int64(nv.Add(int32(value[0]))), nil
case *int64:
return atomic.AddInt64(nv, value[0]), nil
case *int32:
return int64(atomic.AddInt32(nv, int32(value[0]))), nil
default:
return 0, fmt.Errorf("value type of %s is not *atomic.Int64 or *int64 or *atomic.Int32 or *int32", key)
}
}

func (r *Memory) Lock(key string, t ...time.Duration) contractscache.Lock {
Expand All @@ -155,8 +178,13 @@ func (r *Memory) Pull(key string, def ...any) any {

// Put Driver an item in the cache for a given number of seconds.
func (r *Memory) Put(key string, value any, t time.Duration) error {
r.instance.Set(r.key(key), value, t)
if t != NoExpiration {
time.AfterFunc(t, func() {
r.Forget(key)
})
}

r.instance.Store(r.key(key), value)
return nil
}

Expand Down Expand Up @@ -193,7 +221,7 @@ func (r *Memory) RememberForever(key string, callback func() (any, error)) (any,
return nil, err
}

if err := r.Put(key, val, cache.NoExpiration); err != nil {
if err = r.Put(key, val, NoExpiration); err != nil {
return nil, err
}

Expand Down
79 changes: 63 additions & 16 deletions cache/memory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cache

import (
"errors"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -39,28 +40,51 @@ func (s *MemoryTestSuite) TestAdd() {

func (s *MemoryTestSuite) TestDecrement() {
res, err := s.memory.Decrement("decrement")
s.Equal(-1, res)
s.Equal(int64(-1), res)
s.Nil(err)

s.Equal(-1, s.memory.GetInt("decrement"))
s.Equal(int64(-1), s.memory.GetInt64("decrement"))

res, err = s.memory.Decrement("decrement", 2)
s.Equal(-3, res)
s.Equal(int64(-3), res)
s.Nil(err)

res, err = s.memory.Decrement("decrement1", 2)
s.Equal(-2, res)
s.Equal(int64(-2), res)
s.Nil(err)

s.Equal(-2, s.memory.GetInt("decrement1"))
s.Equal(int64(-2), s.memory.GetInt64("decrement1"))

s.True(s.memory.Add("decrement2", 4, 2*time.Second))
decrement2 := int64(4)
s.True(s.memory.Add("decrement2", &decrement2, 2*time.Second))
res, err = s.memory.Decrement("decrement2")
s.Equal(3, res)
s.Equal(int64(3), res)
s.Nil(err)

res, err = s.memory.Decrement("decrement2", 2)
s.Equal(1, res)
s.Equal(int64(1), res)
s.Nil(err)
}

func (s *MemoryTestSuite) TestDecrementWithConcurrent() {
res, err := s.memory.Decrement("decrement")
s.Equal(int64(-1), res)
s.Nil(err)

var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
_, err = s.memory.Decrement("decrement", 1)
s.Nil(err)
wg.Done()
}()
}

wg.Wait()

res = s.memory.GetInt64("decrement")
s.Equal(int64(-1001), res)
s.Nil(err)
}

Expand Down Expand Up @@ -124,28 +148,51 @@ func (s *MemoryTestSuite) TestHas() {

func (s *MemoryTestSuite) TestIncrement() {
res, err := s.memory.Increment("Increment")
s.Equal(1, res)
s.Equal(int64(1), res)
s.Nil(err)

s.Equal(1, s.memory.GetInt("Increment"))
s.Equal(int64(1), s.memory.GetInt64("Increment"))

res, err = s.memory.Increment("Increment", 2)
s.Equal(3, res)
s.Equal(int64(3), res)
s.Nil(err)

res, err = s.memory.Increment("Increment1", 2)
s.Equal(2, res)
s.Equal(int64(2), res)
s.Nil(err)

s.Equal(2, s.memory.GetInt("Increment1"))
s.Equal(int64(2), s.memory.GetInt64("Increment1"))

s.True(s.memory.Add("Increment2", 1, 2*time.Second))
increment2 := int64(1)
s.True(s.memory.Add("Increment2", &increment2, 2*time.Second))
res, err = s.memory.Increment("Increment2")
s.Equal(2, res)
s.Equal(int64(2), res)
s.Nil(err)

res, err = s.memory.Increment("Increment2", 2)
s.Equal(4, res)
s.Equal(int64(4), res)
s.Nil(err)
}

func (s *MemoryTestSuite) TestIncrementWithConcurrent() {
res, err := s.memory.Increment("increment")
s.Equal(int64(1), res)
s.Nil(err)

var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
_, err = s.memory.Increment("increment", 1)
s.Nil(err)
wg.Done()
}()
}

wg.Wait()

res = s.memory.GetInt64("increment")
s.Equal(int64(1001), res)
s.Nil(err)
}

Expand Down
4 changes: 2 additions & 2 deletions contracts/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Driver interface {
// Add an item in the cache if the key does not exist.
Add(key string, value any, t time.Duration) bool
// Decrement decrements the value of an item in the cache.
Decrement(key string, value ...int) (int, error)
Decrement(key string, value ...int64) (int64, error)
// Forever add an item in the cache indefinitely.
Forever(key string, value any) bool
// Forget removes an item from the cache.
Expand All @@ -34,7 +34,7 @@ type Driver interface {
// Has check an item exists in the cache.
Has(key string) bool
// Increment increments the value of an item in the cache.
Increment(key string, value ...int) (int, error)
Increment(key string, value ...int64) (int64, error)
// Lock get a lock instance.
Lock(key string, t ...time.Duration) Lock
// Put Driver an item in the cache for a given time.
Expand Down
2 changes: 1 addition & 1 deletion database/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"testing"
"time"

"github.com/brianvoe/gofakeit/v6"
"github.com/brianvoe/gofakeit/v7"
"github.com/stretchr/testify/suite"
gormio "gorm.io/gorm"

Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ toolchain go1.22.4

require (
github.com/RichardKnop/machinery/v2 v2.0.13
github.com/brianvoe/gofakeit/v6 v6.28.0
github.com/brianvoe/gofakeit/v7 v7.0.4
github.com/charmbracelet/huh v0.4.2
github.com/charmbracelet/huh/spinner v0.0.0-20240702124906-34ae8b72b63e
github.com/charmbracelet/lipgloss v0.11.0
Expand All @@ -23,9 +23,7 @@ require (
github.com/google/wire v0.6.0
github.com/gookit/validate v1.5.2
github.com/goravel/file-rotatelogs/v2 v2.4.2
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/hashicorp/go-multierror v1.1.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pterm/pterm v0.12.79
github.com/redis/go-redis/v9 v9.5.3
Expand Down
Loading

0 comments on commit d288c4c

Please sign in to comment.