Skip to content

Commit

Permalink
Add SetWithCallback Method
Browse files Browse the repository at this point in the history
  • Loading branch information
lixizan authored and lxzan committed Nov 1, 2023
1 parent 8c25fb4 commit ddd276c
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 151 deletions.
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
[4]: https://codecov.io/gh/lxzan/memorycache

### Description

Minimalist in-memory KV storage, powered by hashmap and minimal quad heap, without optimizations for GC.
Cache deprecation policy: the set method cleans up overflowed keys; the cycle cleans up expired keys.

### Principle

- Storage Data Limit: Limited by maximum capacity
- Expiration Time: Supported
- Cache Elimination Policy: LRU-Like, Set method and Cycle Cleanup
Expand All @@ -23,6 +25,7 @@ Cache deprecation policy: the set method cleans up overflowed keys; the cycle cl
- Locking Mechanism: Slicing + Mutual Exclusion Locking

### Usage

```go
package main

Expand All @@ -34,35 +37,36 @@ import (

func main() {
mc := memorycache.New(
memorycache.WithBucketNum(16),
memorycache.WithBucketSize(1000, 100000),
memorycache.WithInterval(100*time.Millisecond),
memorycache.WithBucketNum(128),
memorycache.WithBucketSize(1000, 10000),
memorycache.WithInterval(5*time.Second, 30*time.Second),
)

mc.Set("xxx", 1, 500*time.Millisecond)
mc.Set("xxx", 1, 10*time.Second)

val, exist := mc.Get("xxx")
fmt.Printf("val=%v, exist=%v\n", val, exist)

time.Sleep(time.Second)
time.Sleep(32 * time.Second)

val, exist = mc.Get("xxx")
fmt.Printf("val=%v, exist=%v\n", val, exist)
}
```

### Benchmark

- 1,000,000 elements

```
go test -benchmem -run=^$ -bench . github.com/lxzan/memorycache/benchmark
goos: linux
goos: windows
goarch: amd64
pkg: github.com/lxzan/memorycache/benchmark
cpu: AMD Ryzen 5 PRO 4650G with Radeon Graphics
BenchmarkMemoryCache_Set-12 11499579 101.7 ns/op 16 B/op 0 allocs/op
BenchmarkMemoryCache_Get-12 26326636 45.97 ns/op 0 B/op 0 allocs/op
BenchmarkRistretto_Set-12 12341542 275.4 ns/op 119 B/op 2 allocs/op
BenchmarkRistretto_Get-12 22825676 50.12 ns/op 16 B/op 1 allocs/op
BenchmarkMemoryCache_Set-12 14058852 73.00 ns/op 14 B/op 0 allocs/op
BenchmarkMemoryCache_Get-12 30767100 34.70 ns/op 0 B/op 0 allocs/op
BenchmarkRistretto_Set-12 15583969 218.4 ns/op 114 B/op 2 allocs/op
BenchmarkRistretto_Get-12 27272788 42.05 ns/op 16 B/op 1 allocs/op
PASS
ok github.com/lxzan/memorycache/benchmark 20.107s
ok github.com/lxzan/memorycache/benchmark 17.232s
```
60 changes: 29 additions & 31 deletions internal/heap/heap.go → heap.go
Original file line number Diff line number Diff line change
@@ -1,52 +1,48 @@
package heap
package memorycache

import "github.com/lxzan/memorycache/internal/types"

// New 新建一个堆
// newHeap 新建一个堆
// Create a new heap
func New(cap int) *Heap {
return &Heap{Data: make([]*types.Element, 0, cap)}
func newHeap(cap int) *heap {
return &heap{Data: make([]*Element, 0, cap)}
}

type Heap struct {
Data []*types.Element
type heap struct {
Data []*Element
}

func (c *Heap) Less(i, j int) bool { return c.Data[i].ExpireAt < c.Data[j].ExpireAt }
func (c *heap) Less(i, j int) bool { return c.Data[i].ExpireAt < c.Data[j].ExpireAt }

func (c *Heap) min(i, j int) int {
func (c *heap) min(i, j int) int {
if c.Data[i].ExpireAt < c.Data[j].ExpireAt {
return i
}
return j
}

func (c *Heap) Len() int {
func (c *heap) Len() int {
return len(c.Data)
}

func (c *Heap) Swap(i, j int) {
c.Data[i].Index, c.Data[j].Index = c.Data[j].Index, c.Data[i].Index
func (c *heap) Swap(i, j int) {
c.Data[i].index, c.Data[j].index = c.Data[j].index, c.Data[i].index
c.Data[i], c.Data[j] = c.Data[j], c.Data[i]
}

func (c *Heap) Push(ele *types.Element) {
ele.Index = c.Len()
func (c *heap) Push(ele *Element) {
ele.index = c.Len()
c.Data = append(c.Data, ele)
c.Up(c.Len() - 1)
}

func (c *Heap) Up(i int) {
if i > 0 {
var j = (i - 1) >> 2
if c.Less(i, j) {
c.Swap(i, j)
c.Up(j)
}
func (c *heap) Up(i int) {
var j = (i - 1) >> 2
if i >= 1 && c.Less(i, j) {
c.Swap(i, j)
c.Up(j)
}
}

func (c *Heap) Pop() (ele *types.Element) {
func (c *heap) Pop() (ele *Element) {
var n = c.Len()
switch n {
case 0:
Expand All @@ -62,23 +58,25 @@ func (c *Heap) Pop() (ele *types.Element) {
return
}

func (c *Heap) Delete(i int) {
func (c *heap) Delete(i int) {
n := c.Len()
c.Swap(i, n-1)
c.Data = c.Data[:n-1]
c.Down(i, n-1)
}

func (c *Heap) Down(i, n int) {
var j = -1
func (c *heap) Down(i, n int) {
var index1 = i<<2 + 1
if index1 >= n {
return
}

var index2 = i<<2 + 2
var index3 = i<<2 + 3
var index4 = i<<2 + 4
var j = -1

if index1 >= n {
return
} else if index4 < n {
if index4 < n {
j = c.min(c.min(index1, index2), c.min(index3, index4))
} else if index3 < n {
j = c.min(c.min(index1, index2), index3)
Expand All @@ -95,7 +93,7 @@ func (c *Heap) Down(i, n int) {
}

// Front 访问堆顶元素
// Accessing the top element of the heap
func (c *Heap) Front() *types.Element {
// Accessing the top Element of the heap
func (c *heap) Front() *Element {
return c.Data[0]
}
29 changes: 14 additions & 15 deletions internal/heap/heap_test.go → heap_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package heap
package memorycache

import (
"github.com/lxzan/memorycache/internal/types"
"github.com/stretchr/testify/assert"
"math/rand"
"sort"
Expand All @@ -10,10 +9,10 @@ import (

func TestHeap_Sort(t *testing.T) {
var as = assert.New(t)
var h = New(0)
var h = newHeap(0)
for i := 0; i < 1000; i++ {
num := rand.Int63n(1000)
h.Push(&types.Element{ExpireAt: num})
h.Push(&Element{ExpireAt: num})
}

as.LessOrEqual(h.Front().ExpireAt, h.Data[1].ExpireAt)
Expand All @@ -34,17 +33,17 @@ func TestHeap_Sort(t *testing.T) {

func TestHeap_Delete(t *testing.T) {
var as = assert.New(t)
var h = New(0)
h.Push(&types.Element{ExpireAt: 1})
h.Push(&types.Element{ExpireAt: 2})
h.Push(&types.Element{ExpireAt: 3})
h.Push(&types.Element{ExpireAt: 4})
h.Push(&types.Element{ExpireAt: 5})
h.Push(&types.Element{ExpireAt: 6})
h.Push(&types.Element{ExpireAt: 7})
h.Push(&types.Element{ExpireAt: 8})
h.Push(&types.Element{ExpireAt: 9})
h.Push(&types.Element{ExpireAt: 10})
var h = newHeap(0)
h.Push(&Element{ExpireAt: 1})
h.Push(&Element{ExpireAt: 2})
h.Push(&Element{ExpireAt: 3})
h.Push(&Element{ExpireAt: 4})
h.Push(&Element{ExpireAt: 5})
h.Push(&Element{ExpireAt: 6})
h.Push(&Element{ExpireAt: 7})
h.Push(&Element{ExpireAt: 8})
h.Push(&Element{ExpireAt: 9})
h.Push(&Element{ExpireAt: 10})
h.Delete(3)
h.Delete(5)

Expand Down
Loading

0 comments on commit ddd276c

Please sign in to comment.