github.com/evdatsion/aphelion-dpos-bft@v0.32.1/libs/db/boltdb.go (about) 1 // +build boltdb 2 3 package db 4 5 import ( 6 "bytes" 7 "errors" 8 "fmt" 9 "os" 10 "path/filepath" 11 12 "github.com/etcd-io/bbolt" 13 ) 14 15 var bucket = []byte("tm") 16 17 func init() { 18 registerDBCreator(BoltDBBackend, func(name, dir string) (DB, error) { 19 return NewBoltDB(name, dir) 20 }, false) 21 } 22 23 // BoltDB is a wrapper around etcd's fork of bolt 24 // (https://github.com/etcd-io/bbolt). 25 // 26 // NOTE: All operations (including Set, Delete) are synchronous by default. One 27 // can globally turn it off by using NoSync config option (not recommended). 28 // 29 // A single bucket ([]byte("tm")) is used per a database instance. This could 30 // lead to performance issues when/if there will be lots of keys. 31 type BoltDB struct { 32 db *bbolt.DB 33 } 34 35 // NewBoltDB returns a BoltDB with default options. 36 func NewBoltDB(name, dir string) (DB, error) { 37 return NewBoltDBWithOpts(name, dir, bbolt.DefaultOptions) 38 } 39 40 // NewBoltDBWithOpts allows you to supply *bbolt.Options. ReadOnly: true is not 41 // supported because NewBoltDBWithOpts creates a global bucket. 42 func NewBoltDBWithOpts(name string, dir string, opts *bbolt.Options) (DB, error) { 43 if opts.ReadOnly { 44 return nil, errors.New("ReadOnly: true is not supported") 45 } 46 47 dbPath := filepath.Join(dir, name+".db") 48 db, err := bbolt.Open(dbPath, os.ModePerm, opts) 49 if err != nil { 50 return nil, err 51 } 52 53 // create a global bucket 54 err = db.Update(func(tx *bbolt.Tx) error { 55 _, err := tx.CreateBucketIfNotExists(bucket) 56 return err 57 }) 58 if err != nil { 59 return nil, err 60 } 61 62 return &BoltDB{db: db}, nil 63 } 64 65 func (bdb *BoltDB) Get(key []byte) (value []byte) { 66 key = nonEmptyKey(nonNilBytes(key)) 67 err := bdb.db.View(func(tx *bbolt.Tx) error { 68 b := tx.Bucket(bucket) 69 if v := b.Get(key); v != nil { 70 value = append([]byte{}, v...) 71 } 72 return nil 73 }) 74 if err != nil { 75 panic(err) 76 } 77 return 78 } 79 80 func (bdb *BoltDB) Has(key []byte) bool { 81 return bdb.Get(key) != nil 82 } 83 84 func (bdb *BoltDB) Set(key, value []byte) { 85 key = nonEmptyKey(nonNilBytes(key)) 86 value = nonNilBytes(value) 87 err := bdb.db.Update(func(tx *bbolt.Tx) error { 88 b := tx.Bucket(bucket) 89 return b.Put(key, value) 90 }) 91 if err != nil { 92 panic(err) 93 } 94 } 95 96 func (bdb *BoltDB) SetSync(key, value []byte) { 97 bdb.Set(key, value) 98 } 99 100 func (bdb *BoltDB) Delete(key []byte) { 101 key = nonEmptyKey(nonNilBytes(key)) 102 err := bdb.db.Update(func(tx *bbolt.Tx) error { 103 return tx.Bucket(bucket).Delete(key) 104 }) 105 if err != nil { 106 panic(err) 107 } 108 } 109 110 func (bdb *BoltDB) DeleteSync(key []byte) { 111 bdb.Delete(key) 112 } 113 114 func (bdb *BoltDB) Close() { 115 bdb.db.Close() 116 } 117 118 func (bdb *BoltDB) Print() { 119 stats := bdb.db.Stats() 120 fmt.Printf("%v\n", stats) 121 122 err := bdb.db.View(func(tx *bbolt.Tx) error { 123 tx.Bucket(bucket).ForEach(func(k, v []byte) error { 124 fmt.Printf("[%X]:\t[%X]\n", k, v) 125 return nil 126 }) 127 return nil 128 }) 129 if err != nil { 130 panic(err) 131 } 132 } 133 134 func (bdb *BoltDB) Stats() map[string]string { 135 stats := bdb.db.Stats() 136 m := make(map[string]string) 137 138 // Freelist stats 139 m["FreePageN"] = fmt.Sprintf("%v", stats.FreePageN) 140 m["PendingPageN"] = fmt.Sprintf("%v", stats.PendingPageN) 141 m["FreeAlloc"] = fmt.Sprintf("%v", stats.FreeAlloc) 142 m["FreelistInuse"] = fmt.Sprintf("%v", stats.FreelistInuse) 143 144 // Transaction stats 145 m["TxN"] = fmt.Sprintf("%v", stats.TxN) 146 m["OpenTxN"] = fmt.Sprintf("%v", stats.OpenTxN) 147 148 return m 149 } 150 151 // boltDBBatch stores key values in sync.Map and dumps them to the underlying 152 // DB upon Write call. 153 type boltDBBatch struct { 154 db *BoltDB 155 ops []operation 156 } 157 158 // NewBatch returns a new batch. 159 func (bdb *BoltDB) NewBatch() Batch { 160 return &boltDBBatch{ 161 ops: nil, 162 db: bdb, 163 } 164 } 165 166 // It is safe to modify the contents of the argument after Set returns but not 167 // before. 168 func (bdb *boltDBBatch) Set(key, value []byte) { 169 bdb.ops = append(bdb.ops, operation{opTypeSet, key, value}) 170 } 171 172 // It is safe to modify the contents of the argument after Delete returns but 173 // not before. 174 func (bdb *boltDBBatch) Delete(key []byte) { 175 bdb.ops = append(bdb.ops, operation{opTypeDelete, key, nil}) 176 } 177 178 // NOTE: the operation is synchronous (see BoltDB for reasons) 179 func (bdb *boltDBBatch) Write() { 180 err := bdb.db.db.Batch(func(tx *bbolt.Tx) error { 181 b := tx.Bucket(bucket) 182 for _, op := range bdb.ops { 183 key := nonEmptyKey(nonNilBytes(op.key)) 184 switch op.opType { 185 case opTypeSet: 186 if putErr := b.Put(key, op.value); putErr != nil { 187 return putErr 188 } 189 case opTypeDelete: 190 if delErr := b.Delete(key); delErr != nil { 191 return delErr 192 } 193 } 194 } 195 return nil 196 }) 197 if err != nil { 198 panic(err) 199 } 200 } 201 202 func (bdb *boltDBBatch) WriteSync() { 203 bdb.Write() 204 } 205 206 func (bdb *boltDBBatch) Close() {} 207 208 // WARNING: Any concurrent writes or reads will block until the iterator is 209 // closed. 210 func (bdb *BoltDB) Iterator(start, end []byte) Iterator { 211 tx, err := bdb.db.Begin(false) 212 if err != nil { 213 panic(err) 214 } 215 return newBoltDBIterator(tx, start, end, false) 216 } 217 218 // WARNING: Any concurrent writes or reads will block until the iterator is 219 // closed. 220 func (bdb *BoltDB) ReverseIterator(start, end []byte) Iterator { 221 tx, err := bdb.db.Begin(false) 222 if err != nil { 223 panic(err) 224 } 225 return newBoltDBIterator(tx, start, end, true) 226 } 227 228 // boltDBIterator allows you to iterate on range of keys/values given some 229 // start / end keys (nil & nil will result in doing full scan). 230 type boltDBIterator struct { 231 tx *bbolt.Tx 232 233 itr *bbolt.Cursor 234 start []byte 235 end []byte 236 237 currentKey []byte 238 currentValue []byte 239 240 isInvalid bool 241 isReverse bool 242 } 243 244 func newBoltDBIterator(tx *bbolt.Tx, start, end []byte, isReverse bool) *boltDBIterator { 245 itr := tx.Bucket(bucket).Cursor() 246 247 var ck, cv []byte 248 if isReverse { 249 if end == nil { 250 ck, cv = itr.Last() 251 } else { 252 _, _ = itr.Seek(end) // after key 253 ck, cv = itr.Prev() // return to end key 254 } 255 } else { 256 if start == nil { 257 ck, cv = itr.First() 258 } else { 259 ck, cv = itr.Seek(start) 260 } 261 } 262 263 return &boltDBIterator{ 264 tx: tx, 265 itr: itr, 266 start: start, 267 end: end, 268 currentKey: ck, 269 currentValue: cv, 270 isReverse: isReverse, 271 isInvalid: false, 272 } 273 } 274 275 func (itr *boltDBIterator) Domain() ([]byte, []byte) { 276 return itr.start, itr.end 277 } 278 279 func (itr *boltDBIterator) Valid() bool { 280 if itr.isInvalid { 281 return false 282 } 283 284 // iterated to the end of the cursor 285 if len(itr.currentKey) == 0 { 286 itr.isInvalid = true 287 return false 288 } 289 290 if itr.isReverse { 291 if itr.start != nil && bytes.Compare(itr.currentKey, itr.start) < 0 { 292 itr.isInvalid = true 293 return false 294 } 295 } else { 296 if itr.end != nil && bytes.Compare(itr.end, itr.currentKey) <= 0 { 297 itr.isInvalid = true 298 return false 299 } 300 } 301 302 // Valid 303 return true 304 } 305 306 func (itr *boltDBIterator) Next() { 307 itr.assertIsValid() 308 if itr.isReverse { 309 itr.currentKey, itr.currentValue = itr.itr.Prev() 310 } else { 311 itr.currentKey, itr.currentValue = itr.itr.Next() 312 } 313 } 314 315 func (itr *boltDBIterator) Key() []byte { 316 itr.assertIsValid() 317 return append([]byte{}, itr.currentKey...) 318 } 319 320 func (itr *boltDBIterator) Value() []byte { 321 itr.assertIsValid() 322 var value []byte 323 if itr.currentValue != nil { 324 value = append([]byte{}, itr.currentValue...) 325 } 326 return value 327 } 328 329 func (itr *boltDBIterator) Close() { 330 err := itr.tx.Rollback() 331 if err != nil { 332 panic(err) 333 } 334 } 335 336 func (itr *boltDBIterator) assertIsValid() { 337 if !itr.Valid() { 338 panic("Boltdb-iterator is invalid") 339 } 340 } 341 342 // nonEmptyKey returns a []byte("nil") if key is empty. 343 // WARNING: this may collude with "nil" user key! 344 func nonEmptyKey(key []byte) []byte { 345 if len(key) == 0 { 346 return []byte("nil") 347 } 348 return key 349 }