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.

284 lines
6.9 KiB

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