Skip to content

Commit

Permalink
feat: use lru cache
Browse files Browse the repository at this point in the history
  • Loading branch information
skyline93 committed Oct 7, 2022
1 parent 61bb14f commit 84a8482
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 100 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@

# Dependency directories (remove the comment below to include it)
# vendor/
.idea
52 changes: 51 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,51 @@
# gokv
# GoKV
GoKV is a key-value storage in the memory, it is simple and fast.

## Requirements
Go1.18+

## Installation
```bash
go get -u github.com/skyline93/gokv
```

## Example
```go
package main

import (
"github.com/skyline93/gokv"
)

func main(){
cache := gokv.New(5)
cache.Put("key1", "value1")
key2 := cache.PutWithKey("value2") // auto generate key

v1 := cache.Get("key1")
v2 := cache.Get(key2)
}
```

```go
package main

import (
"time"

"github.com/skyline93/gokv"
)

func main(){
cache := gokv.New(5)
cache.Put("key1", "value1", 2) // value expires after 2 second
key2 := cache.PutWithKey("value2", 5) // value expires after 5 second


time.Sleep(time.Second * 2)
v1 := cache.Get("key1") // v1 is nil

time.Sleep(time.Second * 3)
v2 := cache.Get(key2) // v2 is nil
}
```
258 changes: 198 additions & 60 deletions gokv.go
Original file line number Diff line number Diff line change
@@ -1,109 +1,247 @@
package gokv

import (
"fmt"
uuid "github.com/satori/go.uuid"
"log"
"sync"
"time"

uuid "github.com/satori/go.uuid"
)

type Node struct {
next *Node
prev *Node

key interface{}
}

type List struct {
head *Node
tail *Node
}

func (l *List) Insert(key interface{}) {
node := &Node{key: key}

if l.head == nil {
l.head = node
l.tail = node
return
}

t := l.tail
t.next = node
node.prev = t
l.tail = node
}

func (l *List) get(key interface{}) *Node {
n := l.head

if n.key == key {
return n
}

for n.next != nil {
if n.key == key {
return n
}

n = n.next
}

return nil
}

func (l *List) Delete(key interface{}) {
n := l.get(key)

if n == nil {
return
}

if n.prev == nil && n.next == nil {
l.head = nil
l.tail = nil
return
}

if n.prev == nil {
l.head = n.next
n.next.prev = nil
return
}

if n.next == nil {
l.tail = n.prev
n.prev.next = nil
return
}

next := n.next
prev := n.prev

next.prev = prev
prev.next = next
}

func (l *List) Head() (key interface{}) {
if l.head == nil {
return nil
}

return l.head.key
}

func (l *List) All() {
n := l.head

if n == nil {
return
}

fmt.Printf("n: %s ", n.key)
for n.next != nil {
n = n.next
fmt.Printf("n: %s ", n.key)
}
}

type Value struct {
V interface{}
Ttl int
v interface{}

ttl int
createTime time.Time
}

func NewValue(v interface{}) *Value {
func NewValue(v interface{}, ttl int) *Value {
return &Value{
V: v,
Ttl: 60 * 60 * 2,
v: v,
ttl: ttl,
createTime: time.Now(),
}
}

func (v *Value) expiration() time.Time {
return v.createTime.Add(time.Second * time.Duration(v.Ttl))
if v.ttl < 0 {
return v.createTime.Add(time.Second * time.Duration(999999999))
}

return v.createTime.Add(time.Second * time.Duration(v.ttl))
}

func (v *Value) IsExpired() bool {
return time.Now().After(v.expiration())
}

type KV struct {
m map[string]*Value
sync.RWMutex
type Cache struct {
sync.Mutex
index *List
kv map[string]*Value
len int

gcChan chan interface{}
}

func New() *KV {
return &KV{
m: make(map[string]*Value),
func New(len int) *Cache {
c := &Cache{
index: new(List),
kv: make(map[string]*Value, len),
len: len,

gcChan: make(chan interface{}, 100),
}

go func() {
for {
select {
case g := <-c.gcChan:
// TODO
log.Printf("gc: %v\n", g)
}
}
}()

return c
}

func (kv *KV) Get(k string) interface{} {
kv.RLock()
v, ok := kv.m[k]
kv.RUnlock()
func (c *Cache) collect() {
k := c.index.Head()
c.index.Delete(k)

if !ok {
return nil
}
g := struct {
K string
V interface{}
}{K: k.(string), V: c.kv[k.(string)]}

if v.IsExpired() {
kv.Lock()
delete(kv.m, k)
kv.Unlock()
delete(c.kv, k.(string))

return nil
}
c.gcChan <- g
}

func (c *Cache) reset(key string) {
c.index.Delete(key)
c.index.Insert(key)
}

return v.V
func (c *Cache) delete(key string) {
c.index.Delete(key)
delete(c.kv, key)
}

func (kv *KV) Put(k string, v interface{}, args ...interface{}) bool {
value := NewValue(v)
for i, v := range args {
func (c *Cache) Delete(key string) {
c.Lock()
defer c.Unlock()

c.delete(key)
}

func (c *Cache) put(key string, value interface{}, opts ...interface{}) {
ttl := -1

for i, opt := range opts {
switch i {
case 0:
ttl, ok := v.(int)
if !ok {
return false
}
value.Ttl = ttl
default:
return false
ttl = opt.(int)
}
}

kv.Lock()
defer kv.Unlock()
c.Lock()
defer c.Unlock()

kv.m[k] = value
if len(c.kv) == c.len {
c.collect()
}

return true
c.kv[key] = NewValue(value, ttl)
c.index.Insert(key)
}

func (kv *KV) PutWithUuid(v interface{}, args ...interface{}) (key string, ok bool) {
value := NewValue(v)
for i, v := range args {
switch i {
case 0:
ttl, ok := v.(int)
if !ok {
return "", false
}
value.Ttl = ttl
default:
return "", false
}
}
func (c *Cache) Put(key string, value interface{}, opts ...interface{}) {
c.put(key, value, opts...)
}

func (c *Cache) PutWithKey(value interface{}, opts ...interface{}) (key string) {
key = uuid.NewV4().String()

kv.Lock()
defer kv.Unlock()
c.put(key, value, opts...)
return
}

func (c *Cache) Get(key string) interface{} {
c.Lock()
defer c.Unlock()

value, ok := c.kv[key]
if !ok {
return nil
}

kv.m[key] = value
if value.IsExpired() {
c.delete(key)
return nil
}

return key, true
c.reset(key)
return value.v
}
Loading

0 comments on commit 84a8482

Please sign in to comment.