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  }