database_storage.go 2.6 KB

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