github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/gokv/sqlkv/sqlkv.go (about)

     1  package sqlkv
     2  
     3  import (
     4  	"database/sql"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/bingoohuang/gg/pkg/sqx"
    12  	"go.uber.org/multierr"
    13  )
    14  
    15  type Config struct {
    16  	DriverName     string
    17  	DataSourceName string
    18  
    19  	AllSQL    string
    20  	GetSQL    string
    21  	UpdateSQL string
    22  	InsertSQL string
    23  	DelSQL    string
    24  
    25  	// RefreshInterval will Refresh the key values from the database in every Refresh interval.
    26  	RefreshInterval time.Duration
    27  }
    28  
    29  // Client is a gokv.Store implementation for SQL databases.
    30  type Client struct {
    31  	Config
    32  
    33  	cache     map[string]string
    34  	cacheLock sync.Mutex
    35  }
    36  
    37  func DefaultDuration(s, defaultValue time.Duration) time.Duration {
    38  	if s == 0 {
    39  		return defaultValue
    40  	}
    41  	return s
    42  }
    43  
    44  func Default(s, defaultValue string) string {
    45  	if s == "" {
    46  		return defaultValue
    47  	}
    48  
    49  	return s
    50  }
    51  
    52  const (
    53  	DefaultAllSQL    = `select k,v from kv where state = 1`
    54  	DefaultGetSQL    = `select v from kv where k = :k and state = 1`
    55  	DefaultInsertSQL = `insert into kv(k, v, state, created) values(:k, :v, 1, :time)`
    56  	DefaultUpdateSQL = `update kv set v = :v, updated = :time, state = 1 where k = :k`
    57  	DefaultDelSQL    = `update kv set state = 0  where k = :k`
    58  )
    59  
    60  func NewClient(c Config) (*Client, error) {
    61  	c.RefreshInterval = DefaultDuration(c.RefreshInterval, 60*time.Second)
    62  	c.DriverName = Default(c.DriverName, "mysql")
    63  	c.AllSQL = Default(c.AllSQL, DefaultAllSQL)
    64  	c.GetSQL = Default(c.GetSQL, DefaultGetSQL)
    65  	c.UpdateSQL = Default(c.UpdateSQL, DefaultUpdateSQL)
    66  	c.InsertSQL = Default(c.InsertSQL, DefaultInsertSQL)
    67  	c.DelSQL = Default(c.DelSQL, DefaultDelSQL)
    68  
    69  	cli := &Client{
    70  		Config: c,
    71  		cache:  make(map[string]string),
    72  	}
    73  
    74  	go cli.tickerRefresh()
    75  
    76  	return cli, nil
    77  }
    78  
    79  func (c *Client) tickerRefresh() {
    80  	ticker := time.NewTicker(c.RefreshInterval)
    81  	for range ticker.C {
    82  		if _, err := c.All(); err != nil {
    83  			log.Printf("W! refersh error %v", err)
    84  		}
    85  	}
    86  }
    87  
    88  // All list the keys in the store.
    89  func (c *Client) All() (kvs map[string]string, er error) {
    90  	_, dbx, err := sqx.Open(c.DriverName, c.DataSourceName)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	defer func() { er = multierr.Append(er, dbx.Close()) }()
    96  
    97  	kvs = make(map[string]string)
    98  	sqx.SQL{Q: c.AllSQL}.QueryRaw(dbx, sqx.WithScanRow(func(_ []string, rows *sql.Rows, _ int) (bool, error) {
    99  		rowValues, err := sqx.ScanRowValues(rows)
   100  		if err != nil {
   101  			return false, err
   102  		}
   103  
   104  		k := fmt.Sprintf("%v", rowValues[0])
   105  		kvs[k] = fmt.Sprintf("%v", rowValues[1])
   106  		return true, nil
   107  	}))
   108  
   109  	c.cacheLock.Lock()
   110  	c.cache = kvs
   111  	c.cacheLock.Unlock()
   112  
   113  	return kvs, nil
   114  }
   115  
   116  // Set stores the given value for the given key.
   117  // Values are automatically marshalled to JSON or gob (depending on the configuration).
   118  // The key must not be "" and the value must not be nil.
   119  func (c *Client) Set(k, v string) (er error) {
   120  	m := map[string]string{
   121  		"k":    k,
   122  		"v":    v,
   123  		"time": time.Now().Format(`2006-01-02 15:04:05.000`),
   124  	}
   125  
   126  	_, dbx, err := sqx.Open(c.DriverName, c.DataSourceName)
   127  	if err != nil {
   128  		return err
   129  	}
   130  
   131  	defer func() { er = multierr.Append(er, dbx.Close()) }()
   132  
   133  	effectedRows, err := sqx.SQL{Q: c.UpdateSQL, Vars: sqx.Vars(m)}.Update(dbx)
   134  	if err == nil && effectedRows > 0 {
   135  		c.set(k, v)
   136  		return nil
   137  	}
   138  
   139  	_, err = sqx.SQL{Q: c.InsertSQL, Vars: sqx.Vars(m)}.Update(dbx)
   140  	return err
   141  }
   142  
   143  func (c *Client) set(k, v string) {
   144  	c.cacheLock.Lock()
   145  	c.cache[k] = v
   146  	c.cacheLock.Unlock()
   147  }
   148  
   149  // Get retrieves the stored value for the given key.
   150  // You need to pass a pointer to the value, so in case of a struct
   151  // the automatic unmarshalling can populate the fields of the object
   152  // that v points to with the values of the retrieved object's values.
   153  // If no value is found it returns (false, nil).
   154  func (c *Client) Get(k string) (found bool, v string, er error) {
   155  	c.cacheLock.Lock()
   156  	if v, ok := c.cache[k]; ok {
   157  		c.cacheLock.Unlock()
   158  		return true, v, nil
   159  	}
   160  	c.cacheLock.Unlock()
   161  
   162  	_, dbx, err := sqx.Open(c.DriverName, c.DataSourceName)
   163  	if err != nil {
   164  		return false, "", err
   165  	}
   166  
   167  	defer func() { er = multierr.Append(er, dbx.Close()) }()
   168  
   169  	m := map[string]string{"k": k}
   170  	v, err = sqx.SQL{Q: c.GetSQL, Vars: sqx.Vars(m)}.QueryAsString(dbx)
   171  	if err != nil {
   172  		if errors.Is(err, sql.ErrNoRows) {
   173  			err = nil
   174  		}
   175  		return false, "", err
   176  	}
   177  
   178  	c.set(k, v)
   179  	return true, v, nil
   180  }
   181  
   182  // Del deletes the stored value for the given key.
   183  // Deleting a non-existing key-value pair does NOT lead to an error.
   184  // The key must not be "".
   185  func (c *Client) Del(k string) (er error) {
   186  	c.cacheLock.Lock()
   187  	delete(c.cache, k)
   188  	c.cacheLock.Unlock()
   189  
   190  	defer func() {
   191  		if err := c.del(k); err != nil {
   192  			log.Printf("W! failed to del %v", err)
   193  		}
   194  	}()
   195  
   196  	return nil
   197  }
   198  
   199  func (c *Client) del(k string) (er error) {
   200  	m := map[string]string{
   201  		"k":    k,
   202  		"time": time.Now().Format(`2006-01-02 15:04:05.000`),
   203  	}
   204  	_, db, err := sqx.Open(c.DriverName, c.DataSourceName)
   205  	if err != nil {
   206  		return err
   207  	}
   208  
   209  	defer func() { er = multierr.Append(er, db.Close()) }()
   210  
   211  	if _, err := (sqx.SQL{Q: c.DelSQL, Vars: sqx.Vars(m)}).Update(db); err != nil {
   212  		return err
   213  	}
   214  
   215  	return nil
   216  }