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  }