github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/kv/kv-level-db.go (about) 1 package kv 2 3 import ( 4 "errors" 5 "github.com/angenalZZZ/gofunc/f" 6 "github.com/syndtr/goleveldb/leveldb" 7 "github.com/syndtr/goleveldb/leveldb/iterator" 8 "github.com/syndtr/goleveldb/leveldb/opt" 9 "github.com/syndtr/goleveldb/leveldb/util" 10 "io/ioutil" 11 "os" 12 "strconv" 13 "strings" 14 "time" 15 ) 16 17 // LevelDB represents a leveldb db implementation. 18 type LevelDB struct { 19 DB *leveldb.DB 20 locker *f.Locker 21 } 22 23 // Open Opens the specified path. 24 func (db *LevelDB) Open(path ...string) error { 25 filename := "" 26 if len(path) > 0 { 27 filename = path[0] 28 } else { 29 filename, _ = ioutil.TempDir(os.TempDir(), "") 30 } 31 var err error 32 db.DB, err = leveldb.OpenFile(filename, nil) 33 if err != nil { 34 return err 35 } 36 db.locker = f.NewLocker() 37 return nil 38 } 39 40 // Size gets the size of the database in bytes. 41 func (db *LevelDB) Size() int64 { 42 var stats leveldb.DBStats 43 if nil != db.DB.Stats(&stats) { 44 return -1 45 } 46 size := int64(0) 47 for _, v := range stats.LevelSizes { 48 size += v 49 } 50 return size 51 } 52 53 // Incr increment the key by the specified value. 54 func (db *LevelDB) Incr(k string, by int64) (int64, error) { 55 db.locker.Lock(k) 56 defer db.locker.Unlock(k) 57 58 val, err := db.get(k) 59 if err != nil || val == "" { 60 val = "0" 61 } 62 63 valFloat, _ := strconv.ParseInt(val, 10, 64) 64 valFloat += by 65 66 err = db.set(k, strconv.FormatInt(valFloat, 10), -1) 67 if err != nil { 68 return 0, err 69 } 70 71 return valFloat, nil 72 } 73 74 // Set sets a key with the specified value and optional ttl.seconds 75 func (db *LevelDB) Set(k, v string, ttl int) error { 76 return db.set(k, v, ttl) 77 } 78 79 // SetBytes sets a key with the specified value and optional ttl.seconds 80 func (db *LevelDB) SetBytes(k, v []byte, ttl int) error { 81 return db.set(f.String(k), f.String(v), ttl) 82 } 83 84 // MSet sets multiple key-value pairs. 85 func (db *LevelDB) MSet(data map[string]string) error { 86 batch := new(leveldb.Batch) 87 for k, v := range data { 88 v = "0;" + v 89 batch.Put(f.Bytes(k), f.Bytes(v)) 90 } 91 return db.DB.Write(batch, nil) 92 } 93 94 // Get fetches the value of the specified k. 95 func (db *LevelDB) Get(k string) (string, error) { 96 return db.get(k) 97 } 98 99 // GetBytes fetches the value of the specified k. 100 func (db *LevelDB) GetBytes(k []byte) ([]byte, error) { 101 data, err := db.get(f.String(k)) 102 if err != nil { 103 return []byte{}, err 104 } 105 return f.Bytes(data), nil 106 } 107 108 // MGet fetch multiple values of the specified keys. 109 func (db *LevelDB) MGet(keys []string) (data []string) { 110 data = make([]string, 0, len(keys)) 111 for _, key := range keys { 112 val, err := db.get(key) 113 if err != nil { 114 data = append(data, "") 115 continue 116 } 117 data = append(data, val) 118 } 119 return data 120 } 121 122 // TTL gets the time.seconds to live of the specified key's value. 123 func (db *LevelDB) TTL(key string) int64 { 124 item, err := db.DB.Get(f.Bytes(key), nil) 125 if err != nil { 126 return -2 127 } 128 129 parts := strings.SplitN(f.String(item), ";", 2) 130 exp, _ := strconv.ParseInt(parts[0], 10, 0) 131 if exp == 0 { 132 return -1 133 } 134 135 now := time.Now().Unix() 136 if now >= exp { 137 return -2 138 } 139 140 return (exp - now) / int64(time.Second) 141 } 142 143 // Del removes key(s) from the store. 144 func (db *LevelDB) Del(keys []string) error { 145 batch := new(leveldb.Batch) 146 for _, key := range keys { 147 batch.Delete(f.Bytes(key)) 148 } 149 return db.DB.Write(batch, nil) 150 } 151 152 // Close ... 153 func (db *LevelDB) Close() error { 154 return db.DB.Close() 155 } 156 157 // Keys gets matched keys. 158 func (db *LevelDB) Keys(prefix ...string) []string { 159 ro := &opt.ReadOptions{DontFillCache: true} 160 var prefixBytes []byte 161 var keys []string 162 l := len(prefix) 163 if l > 0 { 164 prefixBytes = f.Bytes(prefix[0]) 165 } 166 var iter iterator.Iterator 167 if l == 0 { 168 iter = db.DB.NewIterator(nil, ro) 169 } else { 170 iter = db.DB.NewIterator(util.BytesPrefix(prefixBytes), ro) 171 } 172 defer iter.Release() 173 for iter.Next() { 174 if iter.Error() != nil { 175 continue 176 } 177 keys = append(keys, f.String(iter.Key())) 178 } 179 return keys 180 } 181 182 // GC runs the garbage collector. 183 func (db *LevelDB) GC() error { 184 return db.DB.CompactRange(util.Range{}) 185 } 186 187 func (db *LevelDB) get(k string) (string, error) { 188 var data string 189 var err error 190 var del bool 191 192 item, err := db.DB.Get(f.Bytes(k), nil) 193 if err != nil { 194 return "", err 195 } 196 197 parts := strings.SplitN(f.String(item), ";", 2) 198 expires, actual := parts[0], parts[1] 199 200 if exp, _ := strconv.ParseInt(expires, 10, 0); exp > 0 && time.Now().Unix() >= exp { 201 del = true 202 err = errors.New("key not found") 203 } else { 204 data = actual 205 } 206 207 if del { 208 _ = db.DB.Delete(f.Bytes(k), nil) 209 return data, err 210 } 211 212 return data, nil 213 } 214 215 func (db *LevelDB) set(k, v string, ttl int) error { 216 var expires int64 217 if ttl > 0 { 218 expires = time.Now().Add(time.Duration(ttl) * time.Second).Unix() 219 } 220 v = strconv.FormatInt(expires, 10) + ";" + v 221 return db.DB.Put(f.Bytes(k), f.Bytes(v), nil) 222 }