cachedsql.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. package sqlc
  2. import (
  3. "context"
  4. "database/sql"
  5. "time"
  6. "github.com/zeromicro/go-zero/core/stores/cache"
  7. "github.com/zeromicro/go-zero/core/stores/redis"
  8. "github.com/zeromicro/go-zero/core/stores/sqlx"
  9. "github.com/zeromicro/go-zero/core/syncx"
  10. )
  11. // see doc/sql-cache.md
  12. const cacheSafeGapBetweenIndexAndPrimary = time.Second * 5
  13. var (
  14. // ErrNotFound is an alias of sqlx.ErrNotFound.
  15. ErrNotFound = sqlx.ErrNotFound
  16. // can't use one SingleFlight per conn, because multiple conns may share the same cache key.
  17. singleFlights = syncx.NewSingleFlight()
  18. stats = cache.NewStat("sqlc")
  19. )
  20. type (
  21. // ExecFn defines the sql exec method.
  22. ExecFn func(conn sqlx.SqlConn) (sql.Result, error)
  23. // ExecCtxFn defines the sql exec method.
  24. ExecCtxFn func(ctx context.Context, conn sqlx.SqlConn) (sql.Result, error)
  25. // IndexQueryFn defines the query method that based on unique indexes.
  26. IndexQueryFn func(conn sqlx.SqlConn, v any) (any, error)
  27. // IndexQueryCtxFn defines the query method that based on unique indexes.
  28. IndexQueryCtxFn func(ctx context.Context, conn sqlx.SqlConn, v any) (any, error)
  29. // PrimaryQueryFn defines the query method that based on primary keys.
  30. PrimaryQueryFn func(conn sqlx.SqlConn, v, primary any) error
  31. // PrimaryQueryCtxFn defines the query method that based on primary keys.
  32. PrimaryQueryCtxFn func(ctx context.Context, conn sqlx.SqlConn, v, primary any) error
  33. // QueryFn defines the query method.
  34. QueryFn func(conn sqlx.SqlConn, v any) error
  35. // QueryCtxFn defines the query method.
  36. QueryCtxFn func(ctx context.Context, conn sqlx.SqlConn, v any) error
  37. // A CachedConn is a DB connection with cache capability.
  38. CachedConn struct {
  39. db sqlx.SqlConn
  40. cache cache.Cache
  41. }
  42. )
  43. // NewConn returns a CachedConn with a redis cluster cache.
  44. func NewConn(db sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) CachedConn {
  45. cc := cache.New(c, singleFlights, stats, sql.ErrNoRows, opts...)
  46. return NewConnWithCache(db, cc)
  47. }
  48. // NewConnWithCache returns a CachedConn with a custom cache.
  49. func NewConnWithCache(db sqlx.SqlConn, c cache.Cache) CachedConn {
  50. return CachedConn{
  51. db: db,
  52. cache: c,
  53. }
  54. }
  55. // NewNodeConn returns a CachedConn with a redis node cache.
  56. func NewNodeConn(db sqlx.SqlConn, rds *redis.Redis, opts ...cache.Option) CachedConn {
  57. c := cache.NewNode(rds, singleFlights, stats, sql.ErrNoRows, opts...)
  58. return NewConnWithCache(db, c)
  59. }
  60. // DelCache deletes cache with keys.
  61. func (cc CachedConn) DelCache(keys ...string) error {
  62. return cc.DelCacheCtx(context.Background(), keys...)
  63. }
  64. // DelCacheCtx deletes cache with keys.
  65. func (cc CachedConn) DelCacheCtx(ctx context.Context, keys ...string) error {
  66. return cc.cache.DelCtx(ctx, keys...)
  67. }
  68. // GetCache unmarshals cache with given key into v.
  69. func (cc CachedConn) GetCache(key string, v any) error {
  70. return cc.GetCacheCtx(context.Background(), key, v)
  71. }
  72. // GetCacheCtx unmarshals cache with given key into v.
  73. func (cc CachedConn) GetCacheCtx(ctx context.Context, key string, v any) error {
  74. return cc.cache.GetCtx(ctx, key, v)
  75. }
  76. // Exec runs given exec on given keys, and returns execution result.
  77. func (cc CachedConn) Exec(exec ExecFn, keys ...string) (sql.Result, error) {
  78. execCtx := func(_ context.Context, conn sqlx.SqlConn) (sql.Result, error) {
  79. return exec(conn)
  80. }
  81. return cc.ExecCtx(context.Background(), execCtx, keys...)
  82. }
  83. // ExecCtx runs given exec on given keys, and returns execution result.
  84. func (cc CachedConn) ExecCtx(ctx context.Context, exec ExecCtxFn, keys ...string) (
  85. sql.Result, error) {
  86. res, err := exec(ctx, cc.db)
  87. if err != nil {
  88. return nil, err
  89. }
  90. if err := cc.DelCacheCtx(ctx, keys...); err != nil {
  91. return nil, err
  92. }
  93. return res, nil
  94. }
  95. // ExecNoCache runs exec with given sql statement, without affecting cache.
  96. func (cc CachedConn) ExecNoCache(q string, args ...any) (sql.Result, error) {
  97. return cc.ExecNoCacheCtx(context.Background(), q, args...)
  98. }
  99. // ExecNoCacheCtx runs exec with given sql statement, without affecting cache.
  100. func (cc CachedConn) ExecNoCacheCtx(ctx context.Context, q string, args ...any) (
  101. sql.Result, error) {
  102. return cc.db.ExecCtx(ctx, q, args...)
  103. }
  104. // QueryRow unmarshals into v with given key and query func.
  105. func (cc CachedConn) QueryRow(v any, key string, query QueryFn) error {
  106. queryCtx := func(_ context.Context, conn sqlx.SqlConn, v any) error {
  107. return query(conn, v)
  108. }
  109. return cc.QueryRowCtx(context.Background(), v, key, queryCtx)
  110. }
  111. // QueryRowCtx unmarshals into v with given key and query func.
  112. func (cc CachedConn) QueryRowCtx(ctx context.Context, v any, key string, query QueryCtxFn) error {
  113. return cc.cache.TakeCtx(ctx, v, key, func(v any) error {
  114. return query(ctx, cc.db, v)
  115. })
  116. }
  117. // QueryRowIndex unmarshals into v with given key.
  118. func (cc CachedConn) QueryRowIndex(v any, key string, keyer func(primary any) string,
  119. indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
  120. indexQueryCtx := func(_ context.Context, conn sqlx.SqlConn, v any) (any, error) {
  121. return indexQuery(conn, v)
  122. }
  123. primaryQueryCtx := func(_ context.Context, conn sqlx.SqlConn, v, primary any) error {
  124. return primaryQuery(conn, v, primary)
  125. }
  126. return cc.QueryRowIndexCtx(context.Background(), v, key, keyer, indexQueryCtx, primaryQueryCtx)
  127. }
  128. // QueryRowIndexCtx unmarshals into v with given key.
  129. func (cc CachedConn) QueryRowIndexCtx(ctx context.Context, v any, key string,
  130. keyer func(primary any) string, indexQuery IndexQueryCtxFn,
  131. primaryQuery PrimaryQueryCtxFn) error {
  132. var primaryKey any
  133. var found bool
  134. if err := cc.cache.TakeWithExpireCtx(ctx, &primaryKey, key,
  135. func(val any, expire time.Duration) (err error) {
  136. primaryKey, err = indexQuery(ctx, cc.db, v)
  137. if err != nil {
  138. return
  139. }
  140. found = true
  141. return cc.cache.SetWithExpireCtx(ctx, keyer(primaryKey), v,
  142. expire+cacheSafeGapBetweenIndexAndPrimary)
  143. }); err != nil {
  144. return err
  145. }
  146. if found {
  147. return nil
  148. }
  149. return cc.cache.TakeCtx(ctx, v, keyer(primaryKey), func(v any) error {
  150. return primaryQuery(ctx, cc.db, v, primaryKey)
  151. })
  152. }
  153. // QueryRowNoCache unmarshals into v with given statement.
  154. func (cc CachedConn) QueryRowNoCache(v any, q string, args ...any) error {
  155. return cc.QueryRowNoCacheCtx(context.Background(), v, q, args...)
  156. }
  157. // QueryRowNoCacheCtx unmarshals into v with given statement.
  158. func (cc CachedConn) QueryRowNoCacheCtx(ctx context.Context, v any, q string,
  159. args ...any) error {
  160. return cc.db.QueryRowCtx(ctx, v, q, args...)
  161. }
  162. // QueryRowsNoCache unmarshals into v with given statement.
  163. // It doesn't use cache, because it might cause consistency problem.
  164. func (cc CachedConn) QueryRowsNoCache(v any, q string, args ...any) error {
  165. return cc.QueryRowsNoCacheCtx(context.Background(), v, q, args...)
  166. }
  167. // QueryRowsNoCacheCtx unmarshals into v with given statement.
  168. // It doesn't use cache, because it might cause consistency problem.
  169. func (cc CachedConn) QueryRowsNoCacheCtx(ctx context.Context, v any, q string,
  170. args ...any) error {
  171. return cc.db.QueryRowsCtx(ctx, v, q, args...)
  172. }
  173. // SetCache sets v into cache with given key.
  174. func (cc CachedConn) SetCache(key string, val any) error {
  175. return cc.SetCacheCtx(context.Background(), key, val)
  176. }
  177. // SetCacheCtx sets v into cache with given key.
  178. func (cc CachedConn) SetCacheCtx(ctx context.Context, key string, val any) error {
  179. return cc.cache.SetCtx(ctx, key, val)
  180. }
  181. // Transact runs given fn in transaction mode.
  182. func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
  183. fnCtx := func(_ context.Context, session sqlx.Session) error {
  184. return fn(session)
  185. }
  186. return cc.TransactCtx(context.Background(), fnCtx)
  187. }
  188. // TransactCtx runs given fn in transaction mode.
  189. func (cc CachedConn) TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  190. return cc.db.TransactCtx(ctx, fn)
  191. }