github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/storage/boltdb.go (about) 1 // Copyright 2017-2018 DERO Project. All rights reserved. 2 // Use of this source code in any form is governed by RESEARCH license. 3 // license can be found in the LICENSE file. 4 // GPG: 0F39 E425 8C65 3947 702A 8234 08B2 0360 A03A 9DE8 5 // 6 // 7 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 8 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 9 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 10 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 11 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 12 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 13 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 14 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 15 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 16 17 // 64 bit arch will use this DB 18 19 package storage 20 21 import "os" 22 import "fmt" 23 import "sync" 24 import "strconv" // has intsize which give whether int is 64 bits or 32 bits 25 import "runtime" 26 import "path/filepath" 27 import "encoding/binary" 28 29 import "github.com/romana/rlog" 30 import bolt "github.com/coreos/bbolt" 31 import log "github.com/sirupsen/logrus" 32 33 import "github.com/deroproject/derosuite/globals" 34 35 type BoltStore struct { 36 DB *bolt.DB 37 tx *bolt.Tx 38 sync.Mutex // lock this struct 39 rw sync.RWMutex 40 } 41 42 // this object is returned 43 type BoltTXWrapper struct { 44 bdb *BoltStore 45 tx *bolt.Tx 46 } 47 48 var Bolt_backend *BoltStore = &BoltStore{} // global variable 49 var logger *log.Entry 50 51 func (b *BoltStore) Init(params map[string]interface{}) (err error) { 52 logger = globals.Logger.WithFields(log.Fields{"com": "STORE"}) 53 current_path := filepath.Join(globals.GetDataDirectory(), "derod_database.db") 54 55 if params["--simulator"] == true { 56 current_path = filepath.Join(os.TempDir(), "derod_simulation.db") // sp 57 } 58 logger.Infof("Initializing boltdb store at path %s", current_path) 59 60 // Open the my.db data file in your current directory. 61 // It will be created if it doesn't exist. 62 63 options := &bolt.Options{InitialMmapSize :1 * 1024 * 1024 * 1024} 64 if runtime.GOOS != "windows" && strconv.IntSize == 64 { 65 options.InitialMmapSize *= 80 // default allocation 80 GB 66 }else{ 67 options.InitialMmapSize = 0 // on windows, make it 0 68 } 69 70 b.DB, err = bolt.Open(current_path, 0600, options) 71 if err != nil { 72 logger.Fatalf("Cannot open boltdb store err %s", err) 73 } 74 75 // if simulation, delete the file , so as it gets cleaned up automcatically 76 if params["--simulator"] == true { 77 os.Remove(current_path) 78 } 79 80 // place db in no sync mode 81 //b.DB.NoSync = true 82 83 return nil 84 } 85 86 func (b *BoltStore) Shutdown() (err error) { 87 logger.Infof("Shutting boltdb store") 88 if b.DB != nil { 89 b.DB.Sync() // sync the DB before closing 90 b.DB.Close() 91 } 92 93 return nil 94 } 95 96 // get a new writable tx, 97 // we will manage the writable txs manually 98 // since a block may cause changes to a number of fields which must be reflected atomically 99 // this function is always triggered while the atomic lock is taken 100 // this is done avoid a race condition in returning the tx and using it 101 func (b *BoltStore) get_new_writable_tx() (tx *bolt.Tx) { 102 if b.tx != nil { 103 tx = b.tx // use existing pending tx 104 } else { // create new pending tx 105 106 tx, err := b.DB.Begin(true) // begin a new writable tx 107 if err != nil { 108 logger.Warnf("Error while creating new writable tx, err %s", err) 109 } else { 110 b.tx = tx 111 rlog.Tracef(1, "Beginning new writable TX") 112 113 } 114 } 115 return b.tx 116 } 117 118 // Commit the pending transaction to disk 119 func (b *BoltStore) BeginTX(writable bool) (DBTX, error) { 120 121 // logger.Warnf(" new writable tx, err") 122 txwrapper := &BoltTXWrapper{} 123 tx, err := b.DB.Begin(writable) // begin a new writable tx 124 if err != nil { 125 logger.Warnf("Error while creating new writable tx, err %s", err) 126 return nil, fmt.Errorf("Error while creating new writable tx, err %s", err) 127 } 128 txwrapper.tx = tx 129 txwrapper.bdb = b // parent DB reference 130 131 //logger.Warnf(" created new writable tx, err") 132 return txwrapper, nil 133 } 134 135 func (b *BoltTXWrapper) Commit() error { 136 err := b.tx.Commit() 137 if err != nil { 138 logger.Warnf("Error while committing tx, err %s", err) 139 return err 140 } 141 //logger.Warnf(" Commiting TX") 142 143 return nil 144 } 145 146 // Commit the pending transaction to disk 147 func (b *BoltStore) Commit() { 148 b.Lock() 149 if b.tx != nil { 150 rlog.Tracef(1, "Committing writable TX") 151 err := b.tx.Commit() 152 if err != nil { 153 logger.Warnf("Error while commit tx, err %s", err) 154 } 155 b.tx = nil 156 } else { 157 logger.Warnf("Trying to Commit a NULL transaction, NOT possible") 158 } 159 b.Unlock() 160 } 161 162 // Roll back existing changes to disk 163 func (b *BoltTXWrapper) Rollback() { 164 // logger.Warnf(" Rollbacking TX") 165 b.tx.Rollback() 166 } 167 168 // Roll back existing changes to disk 169 // TODO implement this 170 func (b *BoltTXWrapper) Sync() { 171 172 } 173 174 // Roll back existing changes to disk 175 func (b *BoltStore) Rollback() { 176 b.Lock() 177 if b.tx != nil { 178 rlog.Tracef(1, "Rollbacking writable TX") 179 b.tx.Rollback() 180 b.tx = nil 181 } else { 182 //logger.Warnf("Trying to Rollback a NULL transaction, NOT possible") 183 } 184 b.Unlock() 185 } 186 187 // sync the DB to disk 188 func (b *BoltStore) Sync() { 189 b.Lock() 190 if b.DB != nil { 191 b.DB.Sync() // sync the DB 192 } 193 b.Unlock() 194 } 195 196 func (b *BoltStore) StoreObject(tx *bolt.Tx, universe_name []byte, galaxy_name []byte, solar_name []byte, key []byte, data []byte) (err error) { 197 198 rlog.Tracef(10, "Storing object %s %s %x data len %d", string(universe_name), string(galaxy_name), key, len(data)) 199 // open universe bucket 200 201 universe, err := tx.CreateBucketIfNotExists(universe_name) 202 if err != nil { 203 logger.Errorf("Error while creating universe bucket %s", err) 204 return err 205 } 206 galaxy, err := universe.CreateBucketIfNotExists(galaxy_name) 207 if err != nil { 208 logger.Errorf("Error while creating galaxy bucket %s err %s", string(galaxy_name), err) 209 return err 210 } 211 212 solar, err := galaxy.CreateBucketIfNotExists(solar_name) 213 if err != nil { 214 logger.Errorf("Error while creating solar bucket %s err %s", string(solar_name), err) 215 return err 216 } 217 // now lets update the object attribute 218 err = solar.Put(key, data) 219 220 return err 221 222 } 223 224 func (b *BoltTXWrapper) StoreObject(universe_name []byte, galaxy_name []byte, solar_name []byte, key []byte, data []byte) (err error) { 225 return b.bdb.StoreObject(b.tx, universe_name, galaxy_name, solar_name, key, data) 226 227 } 228 229 // creates an empty bucket 230 func (b *BoltStore) CreateBucket(tx *bolt.Tx, universe_name []byte, galaxy_name []byte, solar_name []byte) (err error) { 231 232 //rlog.Tracef(10, "Storing object %s %s %x data len %d", string(universe_name), string(galaxy_name), key, len(data)) 233 234 universe, err := tx.CreateBucketIfNotExists(universe_name) 235 if err != nil { 236 logger.Errorf("Error while creating universe bucket %s", err) 237 return err 238 } 239 galaxy, err := universe.CreateBucketIfNotExists(galaxy_name) 240 if err != nil { 241 logger.Errorf("Error while creating galaxy bucket %s err %s", string(galaxy_name), err) 242 return err 243 } 244 245 solar, err := galaxy.CreateBucketIfNotExists(solar_name) 246 if err != nil { 247 logger.Errorf("Error while creating solar bucket %s err %s", string(solar_name), err) 248 return err 249 } 250 _ = solar 251 252 return err 253 254 } 255 256 func (b *BoltTXWrapper) CreateBucket(universe_name []byte, galaxy_name []byte, solar_name []byte) (err error) { 257 return b.bdb.CreateBucket(b.tx, universe_name, galaxy_name, solar_name) 258 } 259 260 // a tx is shared by multiple goroutines, so they are protected by a mutex 261 func (b *BoltStore) LoadObject(tx *bolt.Tx, universe_name []byte, bucket_name []byte, solar_bucket []byte, key []byte) (data []byte, err error) { 262 //rlog.Tracef(10, "Loading object %s %s %x", string(universe_name), string(bucket_name), key) 263 264 b.Lock() 265 defer b.Unlock() 266 //b.rw.RLock() 267 //defer b.rw.RUnlock() 268 269 // open universe bucket 270 { 271 universe := tx.Bucket(universe_name) 272 if universe == nil { 273 return data, fmt.Errorf("No Such Universe %x", universe_name) 274 } 275 bucket := universe.Bucket(bucket_name) 276 if bucket == nil { 277 return data, fmt.Errorf("No Such Bucket %x", bucket_name) 278 } 279 280 solar := bucket.Bucket(solar_bucket) 281 if solar == nil { 282 return data, fmt.Errorf("No Such Bucket %x", solar_bucket) 283 } 284 285 // now lets find the object 286 value := solar.Get(key) 287 288 data = make([]byte, len(value), len(value)) 289 290 copy(data, value) // job done 291 } 292 293 return 294 295 } 296 297 func (b *BoltTXWrapper) LoadObject(universe_name []byte, bucket_name []byte, solar_bucket []byte, key []byte) (data []byte, err error) { 298 return b.bdb.LoadObject(b.tx, universe_name, bucket_name, solar_bucket, key) 299 } 300 301 func (b *BoltStore) LoadObjects(tx *bolt.Tx, universe_name []byte, bucket_name []byte, solar_bucket []byte) (keys [][]byte, values [][]byte, err error) { 302 //rlog.Tracef(10, "Loading object %s %s %x", string(universe_name), string(bucket_name), key) 303 304 b.Lock() 305 defer b.Unlock() 306 307 // open universe bucket 308 { 309 universe := tx.Bucket(universe_name) 310 if universe == nil { 311 return keys, values, fmt.Errorf("No Such Universe %x", universe_name) 312 } 313 bucket := universe.Bucket(bucket_name) 314 if bucket == nil { 315 return keys, values, fmt.Errorf("No Such Bucket %x", bucket_name) 316 } 317 318 solar := bucket.Bucket(solar_bucket) 319 if solar == nil { 320 return keys, values, fmt.Errorf("No Such Bucket %x", solar_bucket) 321 } 322 323 // Create a cursor for iteration. 324 cursor := solar.Cursor() 325 326 // Iterate over items in sorted key order. This starts from the 327 // first key/value pair and updates the k/v variables to the 328 // next key/value on each iteration. 329 // 330 // The loop finishes at the end of the cursor when a nil key is returned. 331 for k, v := cursor.First(); k != nil; k, v = cursor.Next() { 332 333 key := make([]byte, len(k), len(k)) 334 value := make([]byte, len(v), len(v)) 335 copy(key, k) // job done 336 copy(value, v) // job done 337 keys = append(keys, key) 338 values = append(values, value) 339 // fmt.Printf("A %s is %s.", k, v) 340 } 341 342 } 343 344 return 345 346 } 347 348 func (b *BoltTXWrapper) LoadObjects(universe_name []byte, galaxy_name []byte, solar_name []byte) (keys [][]byte, values [][]byte, err error) { 349 return b.bdb.LoadObjects(b.tx, universe_name, galaxy_name, solar_name) 350 351 } 352 353 // this function stores a uint64 354 // this will automcatically use the lock 355 func (b *BoltTXWrapper) StoreUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte, data uint64) error { 356 return b.bdb.StoreObject(b.tx, universe_bucket, galaxy_bucket, solar_bucket, key, itob(data)) 357 358 } 359 360 // this function loads the data as 64 byte integer 361 func (b *BoltTXWrapper) LoadUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte) (uint64, error) { 362 object_data, err := b.LoadObject(universe_bucket, galaxy_bucket, solar_bucket, key) 363 if err != nil { 364 return 0, err 365 } 366 367 if len(object_data) == 0 { 368 return 0, fmt.Errorf("No value stored here, we should look more") 369 } 370 371 if len(object_data) != 8 { 372 panic("Database corruption, invalid data ") 373 } 374 375 value := binary.BigEndian.Uint64(object_data) 376 return value, nil 377 } 378 379 // itob returns an 8-byte big endian representation of v. 380 func itob(v uint64) []byte { 381 b := make([]byte, 8) 382 binary.BigEndian.PutUint64(b, uint64(v)) 383 return b 384 }