database_storage.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package remotecache
  2. import (
  3. "context"
  4. "time"
  5. "github.com/grafana/grafana/pkg/log"
  6. "github.com/grafana/grafana/pkg/services/sqlstore"
  7. )
  8. var getTime = time.Now
  9. type databaseCache struct {
  10. SQLStore *sqlstore.SqlStore
  11. log log.Logger
  12. }
  13. func newDatabaseCache(sqlstore *sqlstore.SqlStore) *databaseCache {
  14. dc := &databaseCache{
  15. SQLStore: sqlstore,
  16. log: log.New("distcache.database"),
  17. }
  18. return dc
  19. }
  20. func (dc *databaseCache) Run(ctx context.Context) error {
  21. ticker := time.NewTicker(time.Minute * 10)
  22. for {
  23. select {
  24. case <-ctx.Done():
  25. return ctx.Err()
  26. case <-ticker.C:
  27. dc.internalRunGC()
  28. }
  29. }
  30. }
  31. func (dc *databaseCache) internalRunGC() {
  32. now := getTime().Unix()
  33. sql := `DELETE FROM cache_data WHERE (? - created_at) >= expires AND expires <> 0`
  34. _, err := dc.SQLStore.NewSession().Exec(sql, now)
  35. if err != nil {
  36. dc.log.Error("failed to run garbage collect", "error", err)
  37. }
  38. }
  39. func (dc *databaseCache) Get(key string) (interface{}, error) {
  40. cacheHit := CacheData{}
  41. session := dc.SQLStore.NewSession()
  42. defer session.Close()
  43. exist, err := session.Where("cache_key= ?", key).Get(&cacheHit)
  44. if err != nil {
  45. return nil, err
  46. }
  47. if !exist {
  48. return nil, ErrCacheItemNotFound
  49. }
  50. if cacheHit.Expires > 0 {
  51. existedButExpired := getTime().Unix()-cacheHit.CreatedAt >= cacheHit.Expires
  52. if existedButExpired {
  53. _ = dc.Delete(key) //ignore this error since we will return `ErrCacheItemNotFound` anyway
  54. return nil, ErrCacheItemNotFound
  55. }
  56. }
  57. item := &cachedItem{}
  58. if err = decodeGob(cacheHit.Data, item); err != nil {
  59. return nil, err
  60. }
  61. return item.Val, nil
  62. }
  63. func (dc *databaseCache) Set(key string, value interface{}, expire time.Duration) error {
  64. item := &cachedItem{Val: value}
  65. data, err := encodeGob(item)
  66. if err != nil {
  67. return err
  68. }
  69. session := dc.SQLStore.NewSession()
  70. var cacheHit CacheData
  71. has, err := session.Where("cache_key = ?", key).Get(&cacheHit)
  72. if err != nil {
  73. return err
  74. }
  75. var expiresAtEpoch int64
  76. if expire != 0 {
  77. expiresAtEpoch = int64(expire) / int64(time.Second)
  78. }
  79. // insert or update depending on if item already exist
  80. if has {
  81. sql := `UPDATE cache_data SET data=?, created=?, expire=? WHERE cache_key='?'`
  82. _, err = session.Exec(sql, data, getTime().Unix(), expiresAtEpoch, key)
  83. } else {
  84. sql := `INSERT INTO cache_data (cache_key,data,created_at,expires) VALUES(?,?,?,?)`
  85. _, err = session.Exec(sql, key, data, getTime().Unix(), expiresAtEpoch)
  86. }
  87. return err
  88. }
  89. func (dc *databaseCache) Delete(key string) error {
  90. sql := "DELETE FROM cache_data WHERE cache_key=?"
  91. _, err := dc.SQLStore.NewSession().Exec(sql, key)
  92. return err
  93. }
  94. type CacheData struct {
  95. CacheKey string
  96. Data []byte
  97. Expires int64
  98. CreatedAt int64
  99. }