github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/kv/kv-bunt-db.go (about) 1 package kv 2 3 import ( 4 "github.com/angenalZZZ/gofunc/f" 5 "github.com/tidwall/buntdb" 6 "strconv" 7 "strings" 8 "time" 9 ) 10 11 // BuntDB is a low-level, in-memory, key/value store. 12 // It persists to disk, is ACID compliant, and uses locking for multiple readers and a single writer. 13 // It supports custom indexes and geo-spatial data. 14 // It's ideal for projects that need a dependable database and favor speed over data size. 15 type BuntDB struct { 16 DB *buntdb.DB 17 locker *f.Locker 18 } 19 20 // Open Opens the specified path, default in memory. 21 func (db *BuntDB) Open(path ...string) error { 22 filename := ":memory:" 23 if len(path) > 0 { 24 filename = path[0] 25 } else { 26 //filename, _ = ioutil.TempDir(os.TempDir(), "") 27 } 28 var err error 29 db.DB, err = buntdb.Open(filename) 30 if err != nil { 31 return err 32 } 33 db.locker = f.NewLocker() 34 return nil 35 } 36 37 // Size gets the size of the database in bytes. 38 func (db *BuntDB) Size() int64 { 39 return 0 40 } 41 42 // Incr increment the key by the specified value. 43 func (db *BuntDB) Incr(k string, by int64) (int64, error) { 44 db.locker.Lock(k) 45 defer db.locker.Unlock(k) 46 47 val, err := db.get(k) 48 if err != nil || val == "" { 49 val = "0" 50 } 51 52 valFloat, _ := strconv.ParseInt(val, 10, 64) 53 valFloat += by 54 55 err = db.set(k, strconv.FormatInt(valFloat, 10), -1) 56 if err != nil { 57 return 0, err 58 } 59 60 return valFloat, nil 61 } 62 63 // Set sets a key with the specified value and optional ttl.seconds 64 func (db *BuntDB) Set(k, v string, ttl int) error { 65 return db.set(k, v, ttl) 66 } 67 68 // SetBytes sets a key with the specified value and optional ttl.seconds 69 func (db *BuntDB) SetBytes(k, v []byte, ttl int) error { 70 return db.set(f.String(k), f.String(v), ttl) 71 } 72 73 // MSet sets multiple key-value pairs. 74 func (db *BuntDB) MSet(data map[string]string) (err error) { 75 err = db.DB.Update(func(tx *buntdb.Tx) (err1 error) { 76 for k, v := range data { 77 _, _, err1 = tx.Set(k, v, nil) 78 } 79 return 80 }) 81 return 82 } 83 84 // Get fetches the value of the specified k. 85 func (db *BuntDB) Get(k string) (string, error) { 86 return db.get(k) 87 } 88 89 // GetBytes fetches the value of the specified k. 90 func (db *BuntDB) GetBytes(k []byte) ([]byte, error) { 91 data, err := db.get(f.String(k)) 92 if err != nil { 93 return []byte{}, err 94 } 95 return f.Bytes(data), nil 96 } 97 98 // MGet fetch multiple values of the specified keys. 99 func (db *BuntDB) MGet(keys []string) (data []string) { 100 data = make([]string, 0, len(keys)) 101 for _, key := range keys { 102 val, err := db.get(key) 103 if err != nil { 104 data = append(data, "") 105 continue 106 } 107 data = append(data, val) 108 } 109 return data 110 } 111 112 // TTL gets the time.seconds to live of the specified key's value. 113 func (db *BuntDB) TTL(key string) int64 { 114 ts := time.Nanosecond 115 _ = db.DB.View(func(tx *buntdb.Tx) (err1 error) { 116 ts, err1 = tx.TTL(key) 117 return 118 }) 119 return int64(ts.Seconds()) 120 } 121 122 // Del removes key(s) from the store. 123 func (db *BuntDB) Del(keys []string) (err error) { 124 err = db.DB.Update(func(tx *buntdb.Tx) (err1 error) { 125 for _, k := range keys { 126 _, err1 = tx.Delete(k) 127 } 128 return 129 }) 130 return 131 } 132 133 // Close ... 134 func (db *BuntDB) Close() error { 135 return db.DB.Close() 136 } 137 138 // Keys gets matched keys. 139 func (db *BuntDB) Keys(prefix ...string) []string { 140 var prefix1 string 141 var keys []string 142 l := len(prefix) 143 if l > 0 { 144 prefix1 = prefix[0] 145 l = len(prefix1) 146 } 147 _ = db.DB.View(func(tx *buntdb.Tx) (err1 error) { 148 err1 = tx.AscendKeys("", func(key, _ string) bool { 149 if l == 0 || strings.HasPrefix(key, prefix1) { 150 keys = append(keys, key) 151 } 152 return true 153 }) 154 return 155 }) 156 return keys 157 } 158 159 // GC runs the garbage collector. 160 func (db *BuntDB) GC() error { 161 return db.DB.Shrink() 162 } 163 164 func (db *BuntDB) get(k string) (data string, err error) { 165 err = db.DB.View(func(tx *buntdb.Tx) (err1 error) { 166 data, err1 = tx.Get(k) 167 return 168 }) 169 return 170 } 171 172 func (db *BuntDB) set(k, v string, ttl int) (err error) { 173 err = db.DB.Update(func(tx *buntdb.Tx) (err1 error) { 174 _, _, err1 = tx.Set(k, v, &buntdb.SetOptions{Expires: true, TTL: time.Duration(ttl) * time.Second}) 175 return 176 }) 177 return 178 }