cachedsql.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package sqlc
  2. import (
  3. "database/sql"
  4. "time"
  5. "github.com/tal-tech/go-zero/core/stores/cache"
  6. "github.com/tal-tech/go-zero/core/stores/internal"
  7. "github.com/tal-tech/go-zero/core/stores/redis"
  8. "github.com/tal-tech/go-zero/core/stores/sqlx"
  9. "github.com/tal-tech/go-zero/core/syncx"
  10. )
  11. // see doc/sql-cache.md
  12. const cacheSafeGapBetweenIndexAndPrimary = time.Second * 5
  13. var (
  14. ErrNotFound = sqlx.ErrNotFound
  15. // can't use one SharedCalls per conn, because multiple conns may share the same cache key.
  16. exclusiveCalls = syncx.NewSharedCalls()
  17. stats = internal.NewCacheStat("sqlc")
  18. )
  19. type (
  20. ExecFn func(conn sqlx.SqlConn) (sql.Result, error)
  21. IndexQueryFn func(conn sqlx.SqlConn, v interface{}) (interface{}, error)
  22. PrimaryQueryFn func(conn sqlx.SqlConn, v, primary interface{}) error
  23. QueryFn func(conn sqlx.SqlConn, v interface{}) error
  24. CachedConn struct {
  25. db sqlx.SqlConn
  26. cache internal.Cache
  27. }
  28. )
  29. func NewNodeConn(db sqlx.SqlConn, rds *redis.Redis, opts ...cache.Option) CachedConn {
  30. return CachedConn{
  31. db: db,
  32. cache: internal.NewCacheNode(rds, exclusiveCalls, stats, sql.ErrNoRows, opts...),
  33. }
  34. }
  35. func NewConn(db sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) CachedConn {
  36. return CachedConn{
  37. db: db,
  38. cache: internal.NewCache(c, exclusiveCalls, stats, sql.ErrNoRows, opts...),
  39. }
  40. }
  41. func (cc CachedConn) DelCache(keys ...string) error {
  42. return cc.cache.DelCache(keys...)
  43. }
  44. func (cc CachedConn) GetCache(key string, v interface{}) error {
  45. return cc.cache.GetCache(key, v)
  46. }
  47. func (cc CachedConn) Exec(exec ExecFn, keys ...string) (sql.Result, error) {
  48. res, err := exec(cc.db)
  49. if err != nil {
  50. return nil, err
  51. }
  52. if err := cc.DelCache(keys...); err != nil {
  53. return nil, err
  54. }
  55. return res, nil
  56. }
  57. func (cc CachedConn) ExecNoCache(q string, args ...interface{}) (sql.Result, error) {
  58. return cc.db.Exec(q, args...)
  59. }
  60. func (cc CachedConn) QueryRow(v interface{}, key string, query QueryFn) error {
  61. return cc.cache.Take(v, key, func(v interface{}) error {
  62. return query(cc.db, v)
  63. })
  64. }
  65. func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string,
  66. indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
  67. var primaryKey interface{}
  68. var found bool
  69. if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) {
  70. primaryKey, err = indexQuery(cc.db, v)
  71. if err != nil {
  72. return
  73. }
  74. found = true
  75. return cc.cache.SetCacheWithExpire(keyer(primaryKey), v, expire+cacheSafeGapBetweenIndexAndPrimary)
  76. }); err != nil {
  77. return err
  78. }
  79. if found {
  80. return nil
  81. }
  82. return cc.cache.Take(v, keyer(primaryKey), func(v interface{}) error {
  83. return primaryQuery(cc.db, v, primaryKey)
  84. })
  85. }
  86. func (cc CachedConn) QueryRowNoCache(v interface{}, q string, args ...interface{}) error {
  87. return cc.db.QueryRow(v, q, args...)
  88. }
  89. // QueryRowsNoCache doesn't use cache, because it might cause consistency problem.
  90. func (cc CachedConn) QueryRowsNoCache(v interface{}, q string, args ...interface{}) error {
  91. return cc.db.QueryRows(v, q, args...)
  92. }
  93. func (cc CachedConn) SetCache(key string, v interface{}) error {
  94. return cc.cache.SetCache(key, v)
  95. }
  96. func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
  97. return cc.db.Transact(fn)
  98. }