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 }