github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/go-xorm/xorm/lru_cacher.go (about) 1 // Copyright 2015 The Xorm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package xorm 6 7 import ( 8 "container/list" 9 "fmt" 10 "sync" 11 "time" 12 13 "github.com/insionng/yougam/libraries/go-xorm/core" 14 ) 15 16 type LRUCacher struct { 17 idList *list.List 18 sqlList *list.List 19 idIndex map[string]map[string]*list.Element 20 sqlIndex map[string]map[string]*list.Element 21 store core.CacheStore 22 mutex sync.Mutex 23 // maxSize int 24 MaxElementSize int 25 Expired time.Duration 26 GcInterval time.Duration 27 } 28 29 func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher { 30 return NewLRUCacher2(store, 3600*time.Second, maxElementSize) 31 } 32 33 func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher { 34 cacher := &LRUCacher{store: store, idList: list.New(), 35 sqlList: list.New(), Expired: expired, 36 GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize, 37 sqlIndex: make(map[string]map[string]*list.Element), 38 idIndex: make(map[string]map[string]*list.Element), 39 } 40 cacher.RunGC() 41 return cacher 42 } 43 44 //func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher { 45 // return newLRUCacher(store, expired, maxSize, 0) 46 //} 47 48 // RunGC run once every m.GcInterval 49 func (m *LRUCacher) RunGC() { 50 time.AfterFunc(m.GcInterval, func() { 51 m.RunGC() 52 m.GC() 53 }) 54 } 55 56 // GC check ids lit and sql list to remove all element expired 57 func (m *LRUCacher) GC() { 58 //fmt.Println("begin gc ...") 59 //defer fmt.Println("end gc ...") 60 m.mutex.Lock() 61 defer m.mutex.Unlock() 62 var removedNum int 63 for e := m.idList.Front(); e != nil; { 64 if removedNum <= core.CacheGcMaxRemoved && 65 time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired { 66 removedNum++ 67 next := e.Next() 68 //fmt.Println("removing ...", e.Value) 69 node := e.Value.(*idNode) 70 m.delBean(node.tbName, node.id) 71 e = next 72 } else { 73 //fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.idList.Len()) 74 break 75 } 76 } 77 78 removedNum = 0 79 for e := m.sqlList.Front(); e != nil; { 80 if removedNum <= core.CacheGcMaxRemoved && 81 time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired { 82 removedNum++ 83 next := e.Next() 84 //fmt.Println("removing ...", e.Value) 85 node := e.Value.(*sqlNode) 86 m.delIds(node.tbName, node.sql) 87 e = next 88 } else { 89 //fmt.Printf("removing %d cache nodes ..., left %d\n", removedNum, m.sqlList.Len()) 90 break 91 } 92 } 93 } 94 95 // GetIds returns all bean's ids according to sql and parameter from cache 96 func (m *LRUCacher) GetIds(tableName, sql string) interface{} { 97 m.mutex.Lock() 98 defer m.mutex.Unlock() 99 if _, ok := m.sqlIndex[tableName]; !ok { 100 m.sqlIndex[tableName] = make(map[string]*list.Element) 101 } 102 if v, err := m.store.Get(sql); err == nil { 103 if el, ok := m.sqlIndex[tableName][sql]; !ok { 104 el = m.sqlList.PushBack(newSqlNode(tableName, sql)) 105 m.sqlIndex[tableName][sql] = el 106 } else { 107 lastTime := el.Value.(*sqlNode).lastVisit 108 // if expired, remove the node and return nil 109 if time.Now().Sub(lastTime) > m.Expired { 110 m.delIds(tableName, sql) 111 return nil 112 } 113 m.sqlList.MoveToBack(el) 114 el.Value.(*sqlNode).lastVisit = time.Now() 115 } 116 return v 117 } else { 118 m.delIds(tableName, sql) 119 } 120 121 return nil 122 } 123 124 // GetBean returns bean according tableName and id from cache 125 func (m *LRUCacher) GetBean(tableName string, id string) interface{} { 126 m.mutex.Lock() 127 defer m.mutex.Unlock() 128 if _, ok := m.idIndex[tableName]; !ok { 129 m.idIndex[tableName] = make(map[string]*list.Element) 130 } 131 tid := genId(tableName, id) 132 if v, err := m.store.Get(tid); err == nil { 133 if el, ok := m.idIndex[tableName][id]; ok { 134 lastTime := el.Value.(*idNode).lastVisit 135 // if expired, remove the node and return nil 136 if time.Now().Sub(lastTime) > m.Expired { 137 m.delBean(tableName, id) 138 //m.clearIds(tableName) 139 return nil 140 } 141 m.idList.MoveToBack(el) 142 el.Value.(*idNode).lastVisit = time.Now() 143 } else { 144 el = m.idList.PushBack(newIdNode(tableName, id)) 145 m.idIndex[tableName][id] = el 146 } 147 return v 148 } else { 149 // store bean is not exist, then remove memory's index 150 m.delBean(tableName, id) 151 //m.clearIds(tableName) 152 return nil 153 } 154 } 155 156 // Clear all sql-ids mapping on table tableName from cache 157 func (m *LRUCacher) clearIds(tableName string) { 158 if tis, ok := m.sqlIndex[tableName]; ok { 159 for sql, v := range tis { 160 m.sqlList.Remove(v) 161 m.store.Del(sql) 162 } 163 } 164 m.sqlIndex[tableName] = make(map[string]*list.Element) 165 } 166 167 func (m *LRUCacher) ClearIds(tableName string) { 168 m.mutex.Lock() 169 defer m.mutex.Unlock() 170 m.clearIds(tableName) 171 } 172 173 func (m *LRUCacher) clearBeans(tableName string) { 174 if tis, ok := m.idIndex[tableName]; ok { 175 for id, v := range tis { 176 m.idList.Remove(v) 177 tid := genId(tableName, id) 178 m.store.Del(tid) 179 } 180 } 181 m.idIndex[tableName] = make(map[string]*list.Element) 182 } 183 184 func (m *LRUCacher) ClearBeans(tableName string) { 185 m.mutex.Lock() 186 defer m.mutex.Unlock() 187 m.clearBeans(tableName) 188 } 189 190 func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { 191 m.mutex.Lock() 192 defer m.mutex.Unlock() 193 if _, ok := m.sqlIndex[tableName]; !ok { 194 m.sqlIndex[tableName] = make(map[string]*list.Element) 195 } 196 if el, ok := m.sqlIndex[tableName][sql]; !ok { 197 el = m.sqlList.PushBack(newSqlNode(tableName, sql)) 198 m.sqlIndex[tableName][sql] = el 199 } else { 200 el.Value.(*sqlNode).lastVisit = time.Now() 201 } 202 m.store.Put(sql, ids) 203 if m.sqlList.Len() > m.MaxElementSize { 204 e := m.sqlList.Front() 205 node := e.Value.(*sqlNode) 206 m.delIds(node.tbName, node.sql) 207 } 208 } 209 210 func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { 211 m.mutex.Lock() 212 defer m.mutex.Unlock() 213 var el *list.Element 214 var ok bool 215 216 if el, ok = m.idIndex[tableName][id]; !ok { 217 el = m.idList.PushBack(newIdNode(tableName, id)) 218 m.idIndex[tableName][id] = el 219 } else { 220 el.Value.(*idNode).lastVisit = time.Now() 221 } 222 223 m.store.Put(genId(tableName, id), obj) 224 if m.idList.Len() > m.MaxElementSize { 225 e := m.idList.Front() 226 node := e.Value.(*idNode) 227 m.delBean(node.tbName, node.id) 228 } 229 } 230 231 func (m *LRUCacher) delIds(tableName, sql string) { 232 if _, ok := m.sqlIndex[tableName]; ok { 233 if el, ok := m.sqlIndex[tableName][sql]; ok { 234 delete(m.sqlIndex[tableName], sql) 235 m.sqlList.Remove(el) 236 } 237 } 238 m.store.Del(sql) 239 } 240 241 func (m *LRUCacher) DelIds(tableName, sql string) { 242 m.mutex.Lock() 243 defer m.mutex.Unlock() 244 m.delIds(tableName, sql) 245 } 246 247 func (m *LRUCacher) delBean(tableName string, id string) { 248 tid := genId(tableName, id) 249 if el, ok := m.idIndex[tableName][id]; ok { 250 delete(m.idIndex[tableName], id) 251 m.idList.Remove(el) 252 m.clearIds(tableName) 253 } 254 m.store.Del(tid) 255 } 256 257 func (m *LRUCacher) DelBean(tableName string, id string) { 258 m.mutex.Lock() 259 defer m.mutex.Unlock() 260 m.delBean(tableName, id) 261 } 262 263 type idNode struct { 264 tbName string 265 id string 266 lastVisit time.Time 267 } 268 269 type sqlNode struct { 270 tbName string 271 sql string 272 lastVisit time.Time 273 } 274 275 func genSqlKey(sql string, args interface{}) string { 276 return fmt.Sprintf("%v-%v", sql, args) 277 } 278 279 func genId(prefix string, id string) string { 280 return fmt.Sprintf("%v-%v", prefix, id) 281 } 282 283 func newIdNode(tbName string, id string) *idNode { 284 return &idNode{tbName, id, time.Now()} 285 } 286 287 func newSqlNode(tbName, sql string) *sqlNode { 288 return &sqlNode{tbName, sql, time.Now()} 289 }