You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lru.go 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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. package caches
  5. import (
  6. "container/list"
  7. "fmt"
  8. "sync"
  9. "time"
  10. )
  11. // LRUCacher implments cache object facilities
  12. type LRUCacher struct {
  13. idList *list.List
  14. sqlList *list.List
  15. idIndex map[string]map[string]*list.Element
  16. sqlIndex map[string]map[string]*list.Element
  17. store CacheStore
  18. mutex sync.Mutex
  19. MaxElementSize int
  20. Expired time.Duration
  21. GcInterval time.Duration
  22. }
  23. // NewLRUCacher creates a cacher
  24. func NewLRUCacher(store CacheStore, maxElementSize int) *LRUCacher {
  25. return NewLRUCacher2(store, 3600*time.Second, maxElementSize)
  26. }
  27. // NewLRUCacher2 creates a cache include different params
  28. func NewLRUCacher2(store CacheStore, expired time.Duration, maxElementSize int) *LRUCacher {
  29. cacher := &LRUCacher{store: store, idList: list.New(),
  30. sqlList: list.New(), Expired: expired,
  31. GcInterval: CacheGcInterval, MaxElementSize: maxElementSize,
  32. sqlIndex: make(map[string]map[string]*list.Element),
  33. idIndex: make(map[string]map[string]*list.Element),
  34. }
  35. cacher.RunGC()
  36. return cacher
  37. }
  38. // RunGC run once every m.GcInterval
  39. func (m *LRUCacher) RunGC() {
  40. time.AfterFunc(m.GcInterval, func() {
  41. m.RunGC()
  42. m.GC()
  43. })
  44. }
  45. // GC check ids lit and sql list to remove all element expired
  46. func (m *LRUCacher) GC() {
  47. m.mutex.Lock()
  48. defer m.mutex.Unlock()
  49. var removedNum int
  50. for e := m.idList.Front(); e != nil; {
  51. if removedNum <= CacheGcMaxRemoved &&
  52. time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired {
  53. removedNum++
  54. next := e.Next()
  55. node := e.Value.(*idNode)
  56. m.delBean(node.tbName, node.id)
  57. e = next
  58. } else {
  59. break
  60. }
  61. }
  62. removedNum = 0
  63. for e := m.sqlList.Front(); e != nil; {
  64. if removedNum <= CacheGcMaxRemoved &&
  65. time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired {
  66. removedNum++
  67. next := e.Next()
  68. node := e.Value.(*sqlNode)
  69. m.delIds(node.tbName, node.sql)
  70. e = next
  71. } else {
  72. break
  73. }
  74. }
  75. }
  76. // GetIds returns all bean's ids according to sql and parameter from cache
  77. func (m *LRUCacher) GetIds(tableName, sql string) interface{} {
  78. m.mutex.Lock()
  79. defer m.mutex.Unlock()
  80. if _, ok := m.sqlIndex[tableName]; !ok {
  81. m.sqlIndex[tableName] = make(map[string]*list.Element)
  82. }
  83. if v, err := m.store.Get(sql); err == nil {
  84. if el, ok := m.sqlIndex[tableName][sql]; !ok {
  85. el = m.sqlList.PushBack(newSQLNode(tableName, sql))
  86. m.sqlIndex[tableName][sql] = el
  87. } else {
  88. lastTime := el.Value.(*sqlNode).lastVisit
  89. // if expired, remove the node and return nil
  90. if time.Now().Sub(lastTime) > m.Expired {
  91. m.delIds(tableName, sql)
  92. return nil
  93. }
  94. m.sqlList.MoveToBack(el)
  95. el.Value.(*sqlNode).lastVisit = time.Now()
  96. }
  97. return v
  98. }
  99. m.delIds(tableName, sql)
  100. return nil
  101. }
  102. // GetBean returns bean according tableName and id from cache
  103. func (m *LRUCacher) GetBean(tableName string, id string) interface{} {
  104. m.mutex.Lock()
  105. defer m.mutex.Unlock()
  106. if _, ok := m.idIndex[tableName]; !ok {
  107. m.idIndex[tableName] = make(map[string]*list.Element)
  108. }
  109. tid := genID(tableName, id)
  110. if v, err := m.store.Get(tid); err == nil {
  111. if el, ok := m.idIndex[tableName][id]; ok {
  112. lastTime := el.Value.(*idNode).lastVisit
  113. // if expired, remove the node and return nil
  114. if time.Now().Sub(lastTime) > m.Expired {
  115. m.delBean(tableName, id)
  116. return nil
  117. }
  118. m.idList.MoveToBack(el)
  119. el.Value.(*idNode).lastVisit = time.Now()
  120. } else {
  121. el = m.idList.PushBack(newIDNode(tableName, id))
  122. m.idIndex[tableName][id] = el
  123. }
  124. return v
  125. }
  126. // store bean is not exist, then remove memory's index
  127. m.delBean(tableName, id)
  128. return nil
  129. }
  130. // clearIds clears all sql-ids mapping on table tableName from cache
  131. func (m *LRUCacher) clearIds(tableName string) {
  132. if tis, ok := m.sqlIndex[tableName]; ok {
  133. for sql, v := range tis {
  134. m.sqlList.Remove(v)
  135. m.store.Del(sql)
  136. }
  137. }
  138. m.sqlIndex[tableName] = make(map[string]*list.Element)
  139. }
  140. // ClearIds clears all sql-ids mapping on table tableName from cache
  141. func (m *LRUCacher) ClearIds(tableName string) {
  142. m.mutex.Lock()
  143. m.clearIds(tableName)
  144. m.mutex.Unlock()
  145. }
  146. func (m *LRUCacher) clearBeans(tableName string) {
  147. if tis, ok := m.idIndex[tableName]; ok {
  148. for id, v := range tis {
  149. m.idList.Remove(v)
  150. tid := genID(tableName, id)
  151. m.store.Del(tid)
  152. }
  153. }
  154. m.idIndex[tableName] = make(map[string]*list.Element)
  155. }
  156. // ClearBeans clears all beans in some table
  157. func (m *LRUCacher) ClearBeans(tableName string) {
  158. m.mutex.Lock()
  159. m.clearBeans(tableName)
  160. m.mutex.Unlock()
  161. }
  162. // PutIds pus ids into table
  163. func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) {
  164. m.mutex.Lock()
  165. if _, ok := m.sqlIndex[tableName]; !ok {
  166. m.sqlIndex[tableName] = make(map[string]*list.Element)
  167. }
  168. if el, ok := m.sqlIndex[tableName][sql]; !ok {
  169. el = m.sqlList.PushBack(newSQLNode(tableName, sql))
  170. m.sqlIndex[tableName][sql] = el
  171. } else {
  172. el.Value.(*sqlNode).lastVisit = time.Now()
  173. }
  174. m.store.Put(sql, ids)
  175. if m.sqlList.Len() > m.MaxElementSize {
  176. e := m.sqlList.Front()
  177. node := e.Value.(*sqlNode)
  178. m.delIds(node.tbName, node.sql)
  179. }
  180. m.mutex.Unlock()
  181. }
  182. // PutBean puts beans into table
  183. func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) {
  184. m.mutex.Lock()
  185. var el *list.Element
  186. var ok bool
  187. if el, ok = m.idIndex[tableName][id]; !ok {
  188. el = m.idList.PushBack(newIDNode(tableName, id))
  189. m.idIndex[tableName][id] = el
  190. } else {
  191. el.Value.(*idNode).lastVisit = time.Now()
  192. }
  193. m.store.Put(genID(tableName, id), obj)
  194. if m.idList.Len() > m.MaxElementSize {
  195. e := m.idList.Front()
  196. node := e.Value.(*idNode)
  197. m.delBean(node.tbName, node.id)
  198. }
  199. m.mutex.Unlock()
  200. }
  201. func (m *LRUCacher) delIds(tableName, sql string) {
  202. if _, ok := m.sqlIndex[tableName]; ok {
  203. if el, ok := m.sqlIndex[tableName][sql]; ok {
  204. delete(m.sqlIndex[tableName], sql)
  205. m.sqlList.Remove(el)
  206. }
  207. }
  208. m.store.Del(sql)
  209. }
  210. // DelIds deletes ids
  211. func (m *LRUCacher) DelIds(tableName, sql string) {
  212. m.mutex.Lock()
  213. m.delIds(tableName, sql)
  214. m.mutex.Unlock()
  215. }
  216. func (m *LRUCacher) delBean(tableName string, id string) {
  217. tid := genID(tableName, id)
  218. if el, ok := m.idIndex[tableName][id]; ok {
  219. delete(m.idIndex[tableName], id)
  220. m.idList.Remove(el)
  221. m.clearIds(tableName)
  222. }
  223. m.store.Del(tid)
  224. }
  225. // DelBean deletes beans in some table
  226. func (m *LRUCacher) DelBean(tableName string, id string) {
  227. m.mutex.Lock()
  228. m.delBean(tableName, id)
  229. m.mutex.Unlock()
  230. }
  231. type idNode struct {
  232. tbName string
  233. id string
  234. lastVisit time.Time
  235. }
  236. type sqlNode struct {
  237. tbName string
  238. sql string
  239. lastVisit time.Time
  240. }
  241. func genSQLKey(sql string, args interface{}) string {
  242. return fmt.Sprintf("%s-%v", sql, args)
  243. }
  244. func genID(prefix string, id string) string {
  245. return fmt.Sprintf("%s-%s", prefix, id)
  246. }
  247. func newIDNode(tbName string, id string) *idNode {
  248. return &idNode{tbName, id, time.Now()}
  249. }
  250. func newSQLNode(tbName, sql string) *sqlNode {
  251. return &sqlNode{tbName, sql, time.Now()}
  252. }