github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/framework/cache/ssdb/ssdb.go (about)

     1  // The package is migrated from beego, you can get from following link:
     2  // import(
     3  //
     4  //	"github.com/beego/beego/v2/client/cache"
     5  //
     6  // )
     7  // Copyright 2023. All Rights Reserved.
     8  //
     9  // Licensed under the Apache License, Version 2.0 (the "License");
    10  // you may not use this file except in compliance with the License.
    11  // You may obtain a copy of the License at
    12  //
    13  //	http://www.apache.org/licenses/LICENSE-2.0
    14  //
    15  // Unless required by applicable law or agreed to in writing, software
    16  // distributed under the License is distributed on an "AS IS" BASIS,
    17  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    18  // See the License for the specific language governing permissions and
    19  // limitations under the License.
    20  package ssdb
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"fmt"
    26  	"strconv"
    27  	"strings"
    28  	"time"
    29  
    30  	"github.com/ssdb/gossdb/ssdb"
    31  
    32  	"github.com/mdaxf/iac/framework/berror"
    33  	"github.com/mdaxf/iac/framework/cache"
    34  )
    35  
    36  // Cache SSDB adapter
    37  type Cache struct {
    38  	conn     *ssdb.Client
    39  	conninfo []string
    40  }
    41  
    42  // NewSsdbCache creates new ssdb adapter.
    43  func NewSsdbCache() cache.Cache {
    44  	return &Cache{}
    45  }
    46  
    47  // Get gets a key's value from memcache.
    48  func (rc *Cache) Get(ctx context.Context, key string) (interface{}, error) {
    49  	value, err := rc.conn.Get(key)
    50  	if err == nil {
    51  		return value, nil
    52  	}
    53  	return nil, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "could not get value, key: %s", key)
    54  }
    55  
    56  // GetMulti gets one or keys values from ssdb.
    57  func (rc *Cache) GetMulti(ctx context.Context, keys []string) ([]interface{}, error) {
    58  	size := len(keys)
    59  	values := make([]interface{}, size)
    60  
    61  	res, err := rc.conn.Do("multi_get", keys)
    62  	if err != nil {
    63  		return values, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_get failed, key: %v", keys)
    64  	}
    65  
    66  	resSize := len(res)
    67  	keyIdx := make(map[string]int)
    68  	for i := 1; i < resSize; i += 2 {
    69  		keyIdx[res[i]] = i
    70  	}
    71  
    72  	keysErr := make([]string, 0)
    73  	for i, ki := range keys {
    74  		if _, ok := keyIdx[ki]; !ok {
    75  			keysErr = append(keysErr, fmt.Sprintf("key [%s] error: %s", ki, "key not exist"))
    76  			continue
    77  		}
    78  		values[i] = res[keyIdx[ki]+1]
    79  	}
    80  
    81  	if len(keysErr) != 0 {
    82  		return values, berror.Error(cache.MultiGetFailed, strings.Join(keysErr, "; "))
    83  	}
    84  
    85  	return values, nil
    86  }
    87  
    88  // DelMulti deletes one or more keys from memcache
    89  func (rc *Cache) DelMulti(keys []string) error {
    90  	_, err := rc.conn.Do("multi_del", keys)
    91  	return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
    92  }
    93  
    94  // Put puts value into memcache.
    95  // value:  must be of type string
    96  func (rc *Cache) Put(ctx context.Context, key string, val interface{}, timeout time.Duration) error {
    97  	v, ok := val.(string)
    98  	if !ok {
    99  		return berror.Errorf(cache.InvalidSsdbCacheValue, "value must be string: %v", val)
   100  	}
   101  	var resp []string
   102  	var err error
   103  	ttl := int(timeout / time.Second)
   104  	if ttl < 0 {
   105  		resp, err = rc.conn.Do("set", key, v)
   106  	} else {
   107  		resp, err = rc.conn.Do("setx", key, v, ttl)
   108  	}
   109  	if err != nil {
   110  		return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "set or setx failed, key: %s", key)
   111  	}
   112  	if len(resp) == 2 && resp[0] == "ok" {
   113  		return nil
   114  	}
   115  	return berror.Errorf(cache.SsdbBadResponse, "the response from SSDB server is invalid: %v", resp)
   116  }
   117  
   118  // Delete deletes a value in memcache.
   119  func (rc *Cache) Delete(ctx context.Context, key string) error {
   120  	_, err := rc.conn.Del(key)
   121  	return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "del failed: %s", key)
   122  }
   123  
   124  // Incr increases a key's counter.
   125  func (rc *Cache) Incr(ctx context.Context, key string) error {
   126  	_, err := rc.conn.Do("incr", key, 1)
   127  	return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "increase failed: %s", key)
   128  }
   129  
   130  // Decr decrements a key's counter.
   131  func (rc *Cache) Decr(ctx context.Context, key string) error {
   132  	_, err := rc.conn.Do("incr", key, -1)
   133  	return berror.Wrapf(err, cache.SsdbCacheCurdFailed, "decrease failed: %s", key)
   134  }
   135  
   136  // IsExist checks if a key exists in memcache.
   137  func (rc *Cache) IsExist(ctx context.Context, key string) (bool, error) {
   138  	resp, err := rc.conn.Do("exists", key)
   139  	if err != nil {
   140  		return false, berror.Wrapf(err, cache.SsdbCacheCurdFailed, "exists failed: %s", key)
   141  	}
   142  	if len(resp) == 2 && resp[1] == "1" {
   143  		return true, nil
   144  	}
   145  	return false, nil
   146  }
   147  
   148  // ClearAll clears all cached items in ssdb.
   149  // If there are many keys, this method may spent much time.
   150  func (rc *Cache) ClearAll(context.Context) error {
   151  	keyStart, keyEnd, limit := "", "", 50
   152  	resp, err := rc.Scan(keyStart, keyEnd, limit)
   153  	for err == nil {
   154  		size := len(resp)
   155  		if size == 1 {
   156  			return nil
   157  		}
   158  		keys := []string{}
   159  		for i := 1; i < size; i += 2 {
   160  			keys = append(keys, resp[i])
   161  		}
   162  		_, e := rc.conn.Do("multi_del", keys)
   163  		if e != nil {
   164  			return berror.Wrapf(e, cache.SsdbCacheCurdFailed, "multi_del failed: %v", keys)
   165  		}
   166  		keyStart = resp[size-2]
   167  		resp, err = rc.Scan(keyStart, keyEnd, limit)
   168  	}
   169  	return berror.Wrap(err, cache.SsdbCacheCurdFailed, "scan failed")
   170  }
   171  
   172  // Scan key all cached in ssdb.
   173  func (rc *Cache) Scan(keyStart string, keyEnd string, limit int) ([]string, error) {
   174  	resp, err := rc.conn.Do("scan", keyStart, keyEnd, limit)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	return resp, nil
   179  }
   180  
   181  // StartAndGC starts the memcache adapter.
   182  // config: must be in the format {"conn":"connection info"}.
   183  // If an error occurs during connection, an error is returned
   184  func (rc *Cache) StartAndGC(config string) error {
   185  	var cf map[string]string
   186  	err := json.Unmarshal([]byte(config), &cf)
   187  	if err != nil {
   188  		return berror.Wrapf(err, cache.InvalidSsdbCacheCfg,
   189  			"unmarshal this config failed, it must be a valid json string: %s", config)
   190  	}
   191  	if _, ok := cf["conn"]; !ok {
   192  		return berror.Wrapf(err, cache.InvalidSsdbCacheCfg,
   193  			"Missing conn field: %s", config)
   194  	}
   195  	rc.conninfo = strings.Split(cf["conn"], ";")
   196  	return rc.connectInit()
   197  }
   198  
   199  // connect to memcache and keep the connection.
   200  func (rc *Cache) connectInit() error {
   201  	conninfoArray := strings.Split(rc.conninfo[0], ":")
   202  	if len(conninfoArray) < 2 {
   203  		return berror.Errorf(cache.InvalidSsdbCacheCfg, "The value of conn should be host:port: %s", rc.conninfo[0])
   204  	}
   205  	host := conninfoArray[0]
   206  	port, e := strconv.Atoi(conninfoArray[1])
   207  	if e != nil {
   208  		return berror.Errorf(cache.InvalidSsdbCacheCfg, "Port is invalid. It must be integer, %s", rc.conninfo[0])
   209  	}
   210  	var err error
   211  	if rc.conn, err = ssdb.Connect(host, port); err != nil {
   212  		return berror.Wrapf(err, cache.InvalidConnection,
   213  			"could not connect to SSDB, please check your connection info, network and firewall: %s", rc.conninfo[0])
   214  	}
   215  	return nil
   216  }
   217  
   218  func init() {
   219  	cache.Register("ssdb", NewSsdbCache)
   220  }