forked from kataras/iris
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cache.go
113 lines (101 loc) · 4.1 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright (c) 2016, Gerasimos Maropoulos
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL JULIEN SCHMIDT BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package iris
import (
"sync"
)
// IRouterCache is the interface which the MemoryRouter implements
type IRouterCache interface {
OnTick()
AddItem(method, url string, ctx *Context)
GetItem(method, url string) *Context
SetMaxItems(maxItems int)
}
// MemoryRouterCache creation done with just &MemoryRouterCache{}
type MemoryRouterCache struct {
//1. map[string] ,key is HTTP Method(GET,POST...)
//2. map[string]*Context ,key is The Request URL Path
//the map in this case is the faster way, I tried with array of structs but it's 100 times slower on > 1 core because of async goroutes on addItem I sugges, so we keep the map
items map[string]map[string]*Context
MaxItems int
//we need this mutex if we have running the iris at > 1 core, because we use map but maybe at the future I will change it.
mu *sync.Mutex
}
var _ IRouterCache = &MemoryRouterCache{}
// SetMaxItems receives int and set max cached items to this number
func (mc *MemoryRouterCache) SetMaxItems(_itemslen int) {
mc.MaxItems = _itemslen
}
// NewMemoryRouterCache returns the cache for a router, is used on the MemoryRouter
func NewMemoryRouterCache() *MemoryRouterCache {
mc := &MemoryRouterCache{mu: &sync.Mutex{}, items: make(map[string]map[string]*Context, 0)}
mc.resetBag()
return mc
}
// AddItem adds an item to the bag/cache, is a goroutine.
func (mc *MemoryRouterCache) AddItem(method, url string, ctx *Context) {
go func(method, url string, c *Context) { //for safety on multiple fast calls
mc.mu.Lock()
mc.items[method][url] = c
mc.mu.Unlock()
}(method, url, ctx)
}
// GetItem returns an item from the bag/cache, if not exists it returns just nil.
func (mc *MemoryRouterCache) GetItem(method, url string) *Context {
//Don't check for anything else, make it as fast as it can be.
mc.mu.Lock()
if ctx := mc.items[method][url]; ctx != nil {
mc.mu.Unlock()
return ctx
}
mc.mu.Unlock()
return nil
}
// OnTick is the implementation of the ITick
// it makes the MemoryRouterCache a ticker's listener
func (mc *MemoryRouterCache) OnTick() {
mc.mu.Lock()
if mc.MaxItems == 0 {
//just reset to complete new maps all methods
mc.resetBag()
} else {
//loop each method on bag and clear it if it's len is more than MaxItems
for k, v := range mc.items {
if len(v) >= mc.MaxItems {
//we just create a new map, no delete each manualy because this number maybe be very long.
mc.items[k] = make(map[string]*Context, 0)
}
}
}
mc.mu.Unlock()
}
// resetBag clears the cached items
func (mc *MemoryRouterCache) resetBag() {
for _, m := range HTTPMethods.ANY {
mc.items[m] = make(map[string]*Context, 0)
}
}