Skip to content

Commit 7f8db2e

Browse files
authored
fix cache (#6)
1 parent a9f691b commit 7f8db2e

File tree

10 files changed

+205
-169
lines changed

10 files changed

+205
-169
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
strategy:
1414
matrix:
15-
go: [ '1.23.8' ]
15+
go: [ '1.23.11' ]
1616
steps:
1717
- uses: actions/checkout@v3
1818

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
run:
3-
go: "1.23.8"
3+
go: "1.23.11"
44
concurrency: 4
55
timeout: 5m
66
tests: false

cache/cache_simple.go renamed to cache/cache.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,25 @@ import (
1010
)
1111

1212
type (
13-
cacheReplace[K comparable, V any] struct {
13+
_cache[K comparable, V any] struct {
1414
list map[K]V
1515
mux sync.RWMutex
1616
}
1717
)
1818

19-
func NewWithReplace[K comparable, V any]() TCacheReplace[K, V] {
20-
return &cacheReplace[K, V]{
21-
list: make(map[K]V, 1000),
19+
func New[K comparable, V any](opts ...Option[K, V]) Cache[K, V] {
20+
obj := &_cache[K, V]{
21+
list: make(map[K]V, 100),
2222
}
23+
24+
for _, opt := range opts {
25+
opt(obj)
26+
}
27+
28+
return obj
2329
}
2430

25-
func (v *cacheReplace[K, V]) Has(key K) bool {
31+
func (v *_cache[K, V]) Has(key K) bool {
2632
v.mux.RLock()
2733
defer v.mux.RUnlock()
2834

@@ -31,7 +37,7 @@ func (v *cacheReplace[K, V]) Has(key K) bool {
3137
return ok
3238
}
3339

34-
func (v *cacheReplace[K, V]) Get(key K) (V, bool) {
40+
func (v *_cache[K, V]) Get(key K) (V, bool) {
3541
v.mux.RLock()
3642
defer v.mux.RUnlock()
3743

@@ -44,28 +50,43 @@ func (v *cacheReplace[K, V]) Get(key K) (V, bool) {
4450
return item, true
4551
}
4652

47-
func (v *cacheReplace[K, V]) Set(key K, value V) {
53+
func (v *_cache[K, V]) Extract(key K) (V, bool) {
54+
v.mux.Lock()
55+
defer v.mux.Unlock()
56+
57+
item, ok := v.list[key]
58+
if !ok {
59+
var zeroValue V
60+
return zeroValue, false
61+
}
62+
63+
delete(v.list, key)
64+
65+
return item, true
66+
}
67+
68+
func (v *_cache[K, V]) Set(key K, value V) {
4869
v.mux.Lock()
4970
defer v.mux.Unlock()
5071

5172
v.list[key] = value
5273
}
5374

54-
func (v *cacheReplace[K, V]) Replace(data map[K]V) {
75+
func (v *_cache[K, V]) Replace(data map[K]V) {
5576
v.mux.Lock()
5677
defer v.mux.Unlock()
5778

5879
v.list = data
5980
}
6081

61-
func (v *cacheReplace[K, V]) Del(key K) {
82+
func (v *_cache[K, V]) Del(key K) {
6283
v.mux.Lock()
6384
defer v.mux.Unlock()
6485

6586
delete(v.list, key)
6687
}
6788

68-
func (v *cacheReplace[K, V]) Keys() []K {
89+
func (v *_cache[K, V]) Keys() []K {
6990
v.mux.RLock()
7091
defer v.mux.RUnlock()
7192

@@ -77,7 +98,7 @@ func (v *cacheReplace[K, V]) Keys() []K {
7798
return result
7899
}
79100

80-
func (v *cacheReplace[K, V]) Flush() {
101+
func (v *_cache[K, V]) Flush() {
81102
v.mux.Lock()
82103
defer v.mux.Unlock()
83104

cache/cache_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright (c) 2024-2025 Mikhail Knyazhev <[email protected]>. All rights reserved.
3+
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
4+
*/
5+
6+
package cache_test
7+
8+
import (
9+
"context"
10+
"testing"
11+
"time"
12+
13+
"go.osspkg.com/casecheck"
14+
15+
"go.osspkg.com/ioutils/cache"
16+
)
17+
18+
func TestUnit_New(t *testing.T) {
19+
c := cache.New[string, string]()
20+
21+
c.Set("foo", "bar")
22+
casecheck.True(t, c.Has("foo"))
23+
24+
casecheck.Equal(t, []string{"foo"}, c.Keys())
25+
26+
v, ok := c.Get("foo")
27+
casecheck.True(t, ok)
28+
casecheck.Equal(t, v, "bar")
29+
30+
v, ok = c.Extract("foo")
31+
casecheck.True(t, ok)
32+
casecheck.Equal(t, v, "bar")
33+
34+
v, ok = c.Extract("foo")
35+
casecheck.False(t, ok)
36+
casecheck.Equal(t, v, "")
37+
38+
v, ok = c.Get("foo")
39+
casecheck.False(t, ok)
40+
casecheck.Equal(t, v, "")
41+
42+
casecheck.False(t, c.Has("foo"))
43+
casecheck.Equal(t, []string{}, c.Keys())
44+
45+
c.Set("foo", "bar")
46+
casecheck.True(t, c.Has("foo"))
47+
48+
c.Del("foo")
49+
casecheck.False(t, c.Has("foo"))
50+
51+
c.Replace(map[string]string{"foo": "bar"})
52+
casecheck.Equal(t, []string{"foo"}, c.Keys())
53+
54+
c.Flush()
55+
casecheck.Equal(t, []string{}, c.Keys())
56+
}
57+
58+
type testValue struct {
59+
Val string
60+
TS int64
61+
}
62+
63+
func (v testValue) Timestamp() int64 { return v.TS }
64+
65+
func TestUnit_AutoClean(t *testing.T) {
66+
ctx, cancel := context.WithCancel(context.Background())
67+
defer cancel()
68+
69+
c := cache.New[string, testValue](
70+
cache.AutoClean[string, testValue](ctx, time.Millisecond*100),
71+
)
72+
73+
c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
74+
casecheck.True(t, c.Has("foo"))
75+
76+
time.Sleep(time.Second)
77+
78+
casecheck.False(t, c.Has("foo"))
79+
}
80+
81+
func Benchmark_New(b *testing.B) {
82+
ctx, cancel := context.WithCancel(context.Background())
83+
defer cancel()
84+
85+
c := cache.New[string, testValue](
86+
cache.AutoClean[string, testValue](ctx, time.Millisecond*100),
87+
)
88+
89+
b.ReportAllocs()
90+
b.ResetTimer()
91+
92+
b.RunParallel(func(pb *testing.PB) {
93+
for pb.Next() {
94+
95+
c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
96+
c.Get("foo")
97+
c.Has("foo")
98+
c.Extract("foo")
99+
c.Replace(map[string]testValue{"foo": {Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()}})
100+
c.Keys()
101+
c.Del("foo")
102+
c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
103+
c.Flush()
104+
}
105+
})
106+
}

cache/cache_time.go

Lines changed: 0 additions & 118 deletions
This file was deleted.

cache/cache_time_test.go

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)