Skip to content

Commit

Permalink
Merge 3717606 into 47ad7f1
Browse files Browse the repository at this point in the history
  • Loading branch information
dulumao committed Jul 13, 2020
2 parents 47ad7f1 + 3717606 commit cadc098
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 103 deletions.
141 changes: 88 additions & 53 deletions cached_routes.go
Original file line number Diff line number Diff line change
@@ -1,91 +1,126 @@
package rux

import (
"container/list"
"sync"
)

// CachedRoutes struct
type CachedRoutes struct {
m map[string]*Route
lock *sync.RWMutex
// cacheNode struct
type cacheNode struct {
Key string
Value *Route
}

// NewCachedRoutes get CachedRoutes pointer
func NewCachedRoutes(size int) *CachedRoutes {
return &CachedRoutes{
lock: new(sync.RWMutex),
m: make(map[string]*Route, size),
}
// cachedRoutes struct
type cachedRoutes struct {
size int
list *list.List
hashMap map[string]*list.Element
lock *sync.Mutex
}

// Get Router pointer
func (c *CachedRoutes) Get(k string) *Route {
c.lock.RLock()
defer c.lock.RUnlock()

if val, ok := c.m[k]; ok {
return val
// NewCachedRoutes Get Cache pointer
func NewCachedRoutes(size int) *cachedRoutes {
return &cachedRoutes{
size: size,
list: list.New(),
hashMap: make(map[string]*list.Element),
lock: new(sync.Mutex),
}
}

return nil
// Len cache len
func (c *cachedRoutes) Len() int {
c.lock.Lock()
defer c.lock.Unlock()

return c.list.Len()
}

// Set Maps the given key and value. Returns false
// if the key is already in the map and changes nothing.
func (c *CachedRoutes) Set(k string, v *Route) bool {
// Set route key and Route
func (c *cachedRoutes) Set(k string, v *Route) bool {
c.lock.Lock()
defer c.lock.Unlock()

if val, ok := c.m[k]; !ok {
c.m[k] = v
} else if val != v {
c.m[k] = v
} else {
if c.list == nil {
return false
}

if element, ok := c.hashMap[k]; ok {
c.list.MoveToFront(element)
element.Value.(*cacheNode).Value = v

return true
}

var newElement = c.list.PushFront(&cacheNode{k, v})

c.hashMap[k] = newElement

if c.list.Len() > c.size {
lastElement := c.list.Back()

if lastElement == nil {
return true
}

cacheNode := lastElement.Value.(*cacheNode)

delete(c.hashMap, cacheNode.Key)

c.list.Remove(lastElement)
}

return true
}

// Has Returns true if k is exist in the map.
func (c *CachedRoutes) Has(k string) (*Route, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
// Get Router by key
func (c *cachedRoutes) Get(k string) *Route {
c.lock.Lock()
defer c.lock.Unlock()

if _, ok := c.m[k]; ok {
return c.m[k], true
if c.hashMap == nil {
return nil
}

return nil, false
}
if element, ok := c.hashMap[k]; ok {
c.list.MoveToFront(element)

// Len the given m total.
func (c *CachedRoutes) Len() int {
c.lock.RLock()
defer c.lock.RUnlock()
return element.Value.(*cacheNode).Value
}

return len(c.m)
return nil
}

// Items the given m.
func (c *CachedRoutes) Items() map[string]*Route {
c.lock.RLock()
defer c.lock.RUnlock()
// Delete Router by key
func (c *cachedRoutes) Delete(k string) bool {
c.lock.Lock()
defer c.lock.Unlock()

if c.hashMap == nil {
return false
}

if element, ok := c.hashMap[k]; ok {
cacheNode := element.Value.(*cacheNode)

r := make(map[string]*Route)
delete(c.hashMap, cacheNode.Key)

for k, v := range c.m {
r[k] = v
c.list.Remove(element)

return true
}

return r
return false
}

// Delete the given key and value.
func (c *CachedRoutes) Delete(k string) {
c.lock.Lock()
defer c.lock.Unlock()
// Has Returns true if k is exist in the hashmap.
func (c *cachedRoutes) Has(k string) (*Route, bool) {
var r = c.Get(k)

if r != nil {
return r, true
}

c.m[k] = nil
delete(c.m, k)
return nil, false
}
83 changes: 48 additions & 35 deletions cached_routes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,65 +7,78 @@ import (
)

func TestCachedRoutes_SetAndGet(t *testing.T) {
r := New()
is := assert.New(t)
c := NewCachedRoutes(3)

test := r.GET("/users/{id}", func(c *Context) {
cache1 := c.Set("cache1", NewRoute("/cache1", nil))
is.True(cache1)

})
cache2 := c.Set("cache2", NewRoute("/cache2", nil))
is.True(cache2)

cache3 := c.Set("cache3", NewRoute("/cache3", nil))
is.True(cache3)

cache4 := c.Set("cache4", NewRoute("/cache4", nil))
is.True(cache4)

is.Equal(c.list.Front().Value.(*cacheNode).Key, "cache4")

is.NotNil(c.Get("cache3"))

is.Equal(c.list.Front().Value.(*cacheNode).Key, "cache3")
is.Equal(3, c.Len())

c2 := NewCachedRoutes(3)
c2.list = nil

c := NewCachedRoutes(10)
c.Set("test", test)
cache5 := c2.Set("cache5", NewRoute("/cache5", nil))
is.False(cache5)

is.Equal(test, c.Get("test"))
is.Nil(c.Get("not-exist"))
is.Nil(c2.Get("not-found"))
}

func TestCachedRoutes_Delete(t *testing.T) {
r := New()
is := assert.New(t)
c := NewCachedRoutes(3)

test := r.GET("/users/{id}", func(c *Context) {
c.Set("cache1", NewRoute("/cache1", nil))
c.Delete("cache1")

})
is.Equal(0, c.Len())

c := NewCachedRoutes(10)
c.Set("test", test)
c.Delete("test")
c.hashMap = nil

is.Equal(c.Len(), 0)
is.False(c.Delete("cache1"))
}

func TestCachedRoutes_Has(t *testing.T) {
r := New()
is := assert.New(t)
c := NewCachedRoutes(3)

test := r.GET("/users/{id}", func(c *Context) {

})

c := NewCachedRoutes(10)
c.Set("test", test)
c.Set("cache1", NewRoute("/cache1", nil))

_, ok := c.Has("test")
_, ok := c.Has("cache1")
is.True(ok)
}

func TestCachedRoutes_Items(t *testing.T) {
r := New()
func TestCacheRoutes(t *testing.T) {
is := assert.New(t)
r := New(EnableCaching, MaxNumCaches(3))

test := r.GET("/users/{id}", func(c *Context) {})
test1 := r.GET("/news/{id}", func(c *Context) {})

c := NewCachedRoutes(10)
c.Set("test", test)
r.GET("/cache1/{id}", func(c *Context) {})
r.GET("/cache2/{id}", func(c *Context) {})
r.GET("/cache3/{id}", func(c *Context) {
c.WriteString("cache3")
})
r.GET("/cache4/{id}", func(c *Context) {
c.WriteString("cache4")
})

for _, r := range c.Items() {
is.Equal(r, test)
}
w1 := mockRequest(r, "GET", "/cache3/1234", nil)
is.Equal("cache3", w1.Body.String())
w2 := mockRequest(r, "GET", "/cache4/1234", nil)
is.Equal("cache4", w2.Body.String())

is.False(c.Set("test", test))
is.True(c.Set("test", test1))
is.Equal(1, c.Len())
is.Equal(2, r.cachedRoutes.Len())
}
29 changes: 15 additions & 14 deletions parse_match.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,20 +228,21 @@ func (r *Router) cacheDynamicRoute(method, path string, ps Params, route *Route)
return
}

if r.cachedRoutes.Len() >= int(r.maxNumCaches) {
num := 0
maxClean := int(r.maxNumCaches / 10)

for k := range r.cachedRoutes.Items() {
if num == maxClean {
break
}

num++

r.cachedRoutes.Delete(k)
}
}
// removed
//if r.cachedRoutes.Len() >= int(r.maxNumCaches) {
// num := 0
// maxClean := int(r.maxNumCaches / 10)
//
// for k := range r.cachedRoutes.Items() {
// if num == maxClean {
// break
// }
//
// num++
//
// r.cachedRoutes.Delete(k)
// }
//}

key := method + path
// copy new route instance. Notice: cache matched Params
Expand Down
2 changes: 1 addition & 1 deletion router.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ type Router struct {
// "GET/users/12": Route,
// }
// cachedRoutes map[string]*Route
cachedRoutes *CachedRoutes
cachedRoutes *cachedRoutes

// Regular dynamic routing
// - key is METHOD + "first-node":
Expand Down

0 comments on commit cadc098

Please sign in to comment.