github.com/ethersphere/bee/v2@v2.2.0/pkg/shed/db.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package shed provides a simple abstraction components to compose 18 // more complex operations on storage data organized in fields and indexes. 19 // 20 // Only type which holds logical information about swarm storage chunks data 21 // and metadata is Item. This part is not generalized mostly for 22 // performance reasons. 23 package shed 24 25 import ( 26 "errors" 27 28 "github.com/syndtr/goleveldb/leveldb" 29 "github.com/syndtr/goleveldb/leveldb/iterator" 30 "github.com/syndtr/goleveldb/leveldb/opt" 31 "github.com/syndtr/goleveldb/leveldb/storage" 32 "github.com/syndtr/goleveldb/leveldb/util" 33 ) 34 35 var ( 36 defaultOpenFilesLimit = uint64(256) 37 defaultBlockCacheCapacity = uint64(1 * 1024 * 1024) 38 defaultWriteBufferSize = uint64(1 * 1024 * 1024) 39 defaultDisableSeeksCompaction = false 40 ) 41 42 type Options struct { 43 BlockCacheCapacity uint64 44 WriteBufferSize uint64 45 OpenFilesLimit uint64 46 DisableSeeksCompaction bool 47 } 48 49 // DB provides abstractions over LevelDB in order to 50 // implement complex structures using fields and ordered indexes. 51 // It provides a schema functionality to store fields and indexes 52 // information about naming and types. 53 type DB struct { 54 ldb *leveldb.DB 55 metrics metrics 56 quit chan struct{} // Quit channel to stop the metrics collection before closing the database 57 } 58 59 // NewDB constructs a new DB and validates the schema 60 // if it exists in database on the given path. 61 // metricsPrefix is used for metrics collection for the given DB. 62 func NewDB(path string, o *Options) (db *DB, err error) { 63 if o == nil { 64 o = &Options{ 65 OpenFilesLimit: defaultOpenFilesLimit, 66 BlockCacheCapacity: defaultBlockCacheCapacity, 67 WriteBufferSize: defaultWriteBufferSize, 68 DisableSeeksCompaction: defaultDisableSeeksCompaction, 69 } 70 } 71 var ldb *leveldb.DB 72 if path == "" { 73 ldb, err = leveldb.Open(storage.NewMemStorage(), nil) 74 } else { 75 ldb, err = leveldb.OpenFile(path, &opt.Options{ 76 OpenFilesCacheCapacity: int(o.OpenFilesLimit), 77 BlockCacheCapacity: int(o.BlockCacheCapacity), 78 WriteBuffer: int(o.WriteBufferSize), 79 DisableSeeksCompaction: o.DisableSeeksCompaction, 80 }) 81 } 82 83 if err != nil { 84 return nil, err 85 } 86 87 return NewDBWrap(ldb) 88 } 89 90 // NewDBWrap returns new DB which uses the given ldb as its underlying storage. 91 // The function will panics if the given ldb is nil. 92 func NewDBWrap(ldb *leveldb.DB) (db *DB, err error) { 93 if ldb == nil { 94 panic(errors.New("shed: NewDBWrap: nil ldb")) 95 } 96 97 db = &DB{ 98 ldb: ldb, 99 metrics: newMetrics(), 100 } 101 102 if _, err = db.getSchema(); err != nil { 103 if errors.Is(err, leveldb.ErrNotFound) { 104 // Save schema with initialized default fields. 105 if err = db.putSchema(schema{ 106 Fields: make(map[string]fieldSpec), 107 Indexes: make(map[byte]indexSpec), 108 }); err != nil { 109 return nil, err 110 } 111 } else { 112 return nil, err 113 } 114 } 115 116 // Create a quit channel for the periodic metrics collector and run it. 117 db.quit = make(chan struct{}) 118 119 return db, nil 120 } 121 122 // Put wraps LevelDB Put method to increment metrics counter. 123 func (db *DB) Put(key, value []byte) (err error) { 124 err = db.ldb.Put(key, value, nil) 125 if err != nil { 126 db.metrics.PutFailCounter.Inc() 127 return err 128 } 129 db.metrics.PutCounter.Inc() 130 return nil 131 } 132 133 // Get wraps LevelDB Get method to increment metrics counter. 134 func (db *DB) Get(key []byte) (value []byte, err error) { 135 value, err = db.ldb.Get(key, nil) 136 if err != nil { 137 if errors.Is(err, leveldb.ErrNotFound) { 138 db.metrics.GetNotFoundCounter.Inc() 139 } else { 140 db.metrics.GetFailCounter.Inc() 141 } 142 return nil, err 143 } 144 db.metrics.GetCounter.Inc() 145 return value, nil 146 } 147 148 // Has wraps LevelDB Has method to increment metrics counter. 149 func (db *DB) Has(key []byte) (yes bool, err error) { 150 yes, err = db.ldb.Has(key, nil) 151 if err != nil { 152 db.metrics.HasFailCounter.Inc() 153 return false, err 154 } 155 db.metrics.HasCounter.Inc() 156 return yes, nil 157 } 158 159 // Delete wraps LevelDB Delete method to increment metrics counter. 160 func (db *DB) Delete(key []byte) (err error) { 161 err = db.ldb.Delete(key, nil) 162 if err != nil { 163 db.metrics.DeleteFailCounter.Inc() 164 return err 165 } 166 db.metrics.DeleteCounter.Inc() 167 return nil 168 } 169 170 // NewIterator wraps LevelDB NewIterator method to increment metrics counter. 171 func (db *DB) NewIterator() iterator.Iterator { 172 db.metrics.IteratorCounter.Inc() 173 return db.ldb.NewIterator(nil, nil) 174 } 175 176 // WriteBatch wraps LevelDB Write method to increment metrics counter. 177 func (db *DB) WriteBatch(batch *leveldb.Batch) (err error) { 178 err = db.ldb.Write(batch, nil) 179 if err != nil { 180 db.metrics.WriteBatchFailCounter.Inc() 181 return err 182 } 183 db.metrics.WriteBatchCounter.Inc() 184 return nil 185 } 186 187 // Compact triggers a full database compaction on the underlying 188 // LevelDB instance. Use with care! This can be very expensive! 189 func (db *DB) Compact(start, end []byte) error { 190 r := util.Range{Start: start, Limit: end} 191 return db.ldb.CompactRange(r) 192 } 193 194 // Close closes LevelDB database. 195 func (db *DB) Close() (err error) { 196 close(db.quit) 197 return db.ldb.Close() 198 }