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