Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@ func New[K comparable, V any](opts ...Option[K, V]) Cache[K, V] {
}

for _, opt := range opts {
opt(obj)
go opt(obj)
}

return obj
}

func (v *_cache[K, V]) Size() int {
v.mux.RLock()
defer v.mux.RUnlock()

return len(v.list)
}

func (v *_cache[K, V]) Has(key K) bool {
v.mux.RLock()
defer v.mux.RUnlock()
Expand Down
24 changes: 21 additions & 3 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ type testValue struct {

func (v testValue) Timestamp() int64 { return v.TS }

func TestUnit_AutoClean(t *testing.T) {
func TestUnit_OptTimeClean(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

c := cache.New[string, testValue](
cache.AutoClean[string, testValue](ctx, time.Millisecond*100),
cache.OptTimeClean[string, testValue](ctx, time.Millisecond*100),
)

c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
Expand All @@ -78,12 +78,30 @@ func TestUnit_AutoClean(t *testing.T) {
casecheck.False(t, c.Has("foo"))
}

func TestUnit_OptCountRandomClean(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

c := cache.New[int, int](
cache.OptCountRandomClean[int, int](ctx, 100, time.Second),
)

for i := 0; i < 200; i++ {
c.Set(i, i)
}
casecheck.Equal(t, 200, c.Size())

time.Sleep(time.Second * 2)

casecheck.Equal(t, 100, c.Size())
}

func Benchmark_New(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

c := cache.New[string, testValue](
cache.AutoClean[string, testValue](ctx, time.Millisecond*100),
cache.OptTimeClean[string, testValue](ctx, time.Millisecond*100),
)

b.ReportAllocs()
Expand Down
97 changes: 73 additions & 24 deletions cache/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,79 @@ import (
"go.osspkg.com/routine"
)

func AutoClean[K comparable, V Timestamp](ctx context.Context, interval time.Duration) Option[K, V] {
func OptTimeClean[K comparable, V Timestamp](ctx context.Context, interval time.Duration) Option[K, V] {
return func(v *_cache[K, V]) {
routine.Interval(ctx, interval, func(ctx context.Context) {
curr := time.Now().Unix()
keys := make([]K, 0, 10)

v.mux.RLock()
for key, value := range v.list {
if value.Timestamp() < curr {
keys = append(keys, key)
}
}
v.mux.RUnlock()

if len(keys) == 0 {
return
}

v.mux.Lock()
defer v.mux.Unlock()

for _, key := range keys {
delete(v.list, key)
}
})

tik := routine.Ticker{
Interval: interval,
OnStart: false,
Calls: []routine.TickFunc{
func(ctx context.Context, t time.Time) {
curr := t.Unix()
keys := make([]K, 0, 10)

v.mux.RLock()
for key, value := range v.list {
if value.Timestamp() < curr {
keys = append(keys, key)
}
}
v.mux.RUnlock()

if len(keys) == 0 {
return
}

v.mux.Lock()
defer v.mux.Unlock()

for _, key := range keys {
delete(v.list, key)
}
},
},
}

tik.Run(ctx)
}
}

func OptCountRandomClean[K comparable, V any](ctx context.Context, maxCount int, interval time.Duration) Option[K, V] {
return func(v *_cache[K, V]) {

if maxCount < 0 {
panic("OptCountRandomClean: maxCount < 0")
}

tik := routine.Ticker{
Interval: interval,
OnStart: false,
Calls: []routine.TickFunc{
func(ctx context.Context, _ time.Time) {

v.mux.RLock()
removeCount := len(v.list) - maxCount
v.mux.RUnlock()

if removeCount <= 0 {
return
}

v.mux.Lock()
defer v.mux.Unlock()

for key := range v.list {
if removeCount <= 0 {
return
}

delete(v.list, key)
removeCount--
}
},
},
}

tik.Run(ctx)
}
}
1 change: 1 addition & 0 deletions cache/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Cache[K comparable, V any] interface {
Replace(data map[K]V)
Del(key K)
Keys() []K
Size() int
Flush()
}

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/BurntSushi/toml v1.5.0
go.osspkg.com/casecheck v0.3.0
go.osspkg.com/errors v0.3.1
go.osspkg.com/routine v0.3.1
go.osspkg.com/syncing v0.3.1
go.osspkg.com/routine v0.4.0
go.osspkg.com/syncing v0.4.0
gopkg.in/yaml.v3 v3.0.1
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ go.osspkg.com/casecheck v0.3.0 h1:x15blEszElbrHrEH5H02JIIhGIg/lGZzIt1kQlD3pwM=
go.osspkg.com/casecheck v0.3.0/go.mod h1:TRFXDMFJEOtnlp3ET2Hix3osbxwPWhvaiT/HfD3+gBA=
go.osspkg.com/errors v0.3.1 h1:F9m/EEd/Ot2jba/TV7tvVRIpWXzIpNLc7vRJKcBD86A=
go.osspkg.com/errors v0.3.1/go.mod h1:dKXe6Rt07nzY7OyKQNZ8HGBicZ2uQ5TKEoVFnVFOK44=
go.osspkg.com/routine v0.3.1 h1:R0o4P0Ml5eoeHc2DiHjRvHBo/XXrW5nJNqIj3ToRzjg=
go.osspkg.com/routine v0.3.1/go.mod h1:z5AvvTbB19/tt1E5JOb4POhK1tOPgmejajgao/IWn4s=
go.osspkg.com/syncing v0.3.1 h1:zt5o/X5DQ/GE5OQTKkq1nNWJMg7EcYhw0YiwMGuA0f8=
go.osspkg.com/syncing v0.3.1/go.mod h1:Dpe0ljlEG6cI2Y9PxEjKiYEX2sgs1eUjWNVjFu4/iB0=
go.osspkg.com/routine v0.4.0 h1:fEDOI3BTaM/rt5pYT+qPq4gFxvw8WJVY2jMeczB7F9A=
go.osspkg.com/routine v0.4.0/go.mod h1:HUVnPHLFzNCYEGOqUiQWL+av73aFJ6H57XutFxhA+eU=
go.osspkg.com/syncing v0.4.0 h1:9ytMfGHd6Ew69D2n/1syj0FTujNfb1KBiUSZ+KsMTqk=
go.osspkg.com/syncing v0.4.0/go.mod h1:/LBmgCAHFW6nQgVDILpEuo6eRCFK1yyFeNbDs4eVNls=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down