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.

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