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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.23.8' ]
go: [ '1.23.11' ]
steps:
- uses: actions/checkout@v3

Expand Down
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

run:
go: "1.23.8"
go: "1.23.11"
concurrency: 4
timeout: 5m
tests: false
Expand Down
43 changes: 32 additions & 11 deletions cache/cache_simple.go → cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@ import (
)

type (
cacheReplace[K comparable, V any] struct {
_cache[K comparable, V any] struct {
list map[K]V
mux sync.RWMutex
}
)

func NewWithReplace[K comparable, V any]() TCacheReplace[K, V] {
return &cacheReplace[K, V]{
list: make(map[K]V, 1000),
func New[K comparable, V any](opts ...Option[K, V]) Cache[K, V] {
obj := &_cache[K, V]{
list: make(map[K]V, 100),
}

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

return obj
}

func (v *cacheReplace[K, V]) Has(key K) bool {
func (v *_cache[K, V]) Has(key K) bool {
v.mux.RLock()
defer v.mux.RUnlock()

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

func (v *cacheReplace[K, V]) Get(key K) (V, bool) {
func (v *_cache[K, V]) Get(key K) (V, bool) {
v.mux.RLock()
defer v.mux.RUnlock()

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

func (v *cacheReplace[K, V]) Set(key K, value V) {
func (v *_cache[K, V]) Extract(key K) (V, bool) {
v.mux.Lock()
defer v.mux.Unlock()

item, ok := v.list[key]
if !ok {
var zeroValue V
return zeroValue, false
}

delete(v.list, key)

return item, true
}

func (v *_cache[K, V]) Set(key K, value V) {
v.mux.Lock()
defer v.mux.Unlock()

v.list[key] = value
}

func (v *cacheReplace[K, V]) Replace(data map[K]V) {
func (v *_cache[K, V]) Replace(data map[K]V) {
v.mux.Lock()
defer v.mux.Unlock()

v.list = data
}

func (v *cacheReplace[K, V]) Del(key K) {
func (v *_cache[K, V]) Del(key K) {
v.mux.Lock()
defer v.mux.Unlock()

delete(v.list, key)
}

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

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

func (v *cacheReplace[K, V]) Flush() {
func (v *_cache[K, V]) Flush() {
v.mux.Lock()
defer v.mux.Unlock()

Expand Down
106 changes: 106 additions & 0 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright (c) 2024-2025 Mikhail Knyazhev <markus621@yandex.com>. All rights reserved.
* Use of this source code is governed by a BSD 3-Clause license that can be found in the LICENSE file.
*/

package cache_test

import (
"context"
"testing"
"time"

"go.osspkg.com/casecheck"

"go.osspkg.com/ioutils/cache"
)

func TestUnit_New(t *testing.T) {
c := cache.New[string, string]()

c.Set("foo", "bar")
casecheck.True(t, c.Has("foo"))

casecheck.Equal(t, []string{"foo"}, c.Keys())

v, ok := c.Get("foo")
casecheck.True(t, ok)
casecheck.Equal(t, v, "bar")

v, ok = c.Extract("foo")
casecheck.True(t, ok)
casecheck.Equal(t, v, "bar")

v, ok = c.Extract("foo")
casecheck.False(t, ok)
casecheck.Equal(t, v, "")

v, ok = c.Get("foo")
casecheck.False(t, ok)
casecheck.Equal(t, v, "")

casecheck.False(t, c.Has("foo"))
casecheck.Equal(t, []string{}, c.Keys())

c.Set("foo", "bar")
casecheck.True(t, c.Has("foo"))

c.Del("foo")
casecheck.False(t, c.Has("foo"))

c.Replace(map[string]string{"foo": "bar"})
casecheck.Equal(t, []string{"foo"}, c.Keys())

c.Flush()
casecheck.Equal(t, []string{}, c.Keys())
}

type testValue struct {
Val string
TS int64
}

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

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

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

c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
casecheck.True(t, c.Has("foo"))

time.Sleep(time.Second)

casecheck.False(t, c.Has("foo"))
}

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),
)

b.ReportAllocs()
b.ResetTimer()

b.RunParallel(func(pb *testing.PB) {
for pb.Next() {

c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
c.Get("foo")
c.Has("foo")
c.Extract("foo")
c.Replace(map[string]testValue{"foo": {Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()}})
c.Keys()
c.Del("foo")
c.Set("foo", testValue{Val: "bar", TS: time.Now().Add(time.Millisecond * 200).Unix()})
c.Flush()
}
})
}
118 changes: 0 additions & 118 deletions cache/cache_time.go

This file was deleted.

27 changes: 0 additions & 27 deletions cache/cache_time_test.go

This file was deleted.

Loading