github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/storage/badgerdb.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 "runtime"
    25  import "path/filepath"
    26  import "encoding/binary"
    27  
    28  //import "github.com/romana/rlog"
    29  //import bolt "github.com/coreos/bbolt"
    30  import "github.com/dgraph-io/badger"
    31  import log "github.com/sirupsen/logrus"
    32  import "github.com/dgraph-io/badger/options"
    33  
    34  import "github.com/deroproject/derosuite/globals"
    35  
    36  type BadgerDBStore struct {
    37  	DB         *badger.DB
    38  	sync.Mutex // lock this struct TODO we must user a reader lock
    39  }
    40  
    41  // this object is returned
    42  type BadgerTXWrapper struct {
    43  	bdb *BadgerDBStore
    44  	tx  *badger.Txn
    45  }
    46  
    47  var Badger_backend *BadgerDBStore = &BadgerDBStore{} // global variable
    48  
    49  func (b *BadgerDBStore) Init(params map[string]interface{}) (err error) {
    50  	logger = globals.Logger.WithFields(log.Fields{"com": "STORE"})
    51  	current_path := filepath.Join(globals.GetDataDirectory(), "derod_badger_database")
    52  
    53  	if params["--simulator"] == true {
    54  		current_path = filepath.Join(os.TempDir(), "derod_simulation_badger_database") // sp
    55  	}
    56  	logger.Infof("Initializing badger store at path %s", current_path)
    57  
    58  	// Open the my.db data file in your current directory.
    59  	// It will be created if it doesn't exist.
    60  
    61  	opts := badger.DefaultOptions
    62  	opts.Dir = current_path
    63  	opts.ValueDir = current_path
    64  
    65  	// tune to different RAM requirements based on arch
    66  	if runtime.GOARCH == "amd64" {
    67  		opts.TableLoadingMode = options.MemoryMap
    68  		opts.ValueLogLoadingMode = options.MemoryMap
    69  	} else { // 32 bit systems/arm etc use raw file IO, no memory mapping
    70  		opts.TableLoadingMode = options.FileIO
    71  		opts.ValueLogLoadingMode = options.FileIO
    72  		logger.Infof("Running in low RAM mode")
    73  	}
    74  
    75  	db, err := badger.Open(opts)
    76  	if err != nil {
    77  		logger.Fatalf("Cannot open badgerdb store err %s", err)
    78  	}
    79  	b.DB = db
    80  
    81  	// if simulation, delete the file , so as it gets cleaned up automcatically
    82  	if params["--simulator"] == true {
    83  		os.RemoveAll(current_path)
    84  	}
    85  
    86  	// place db in no sync mode
    87  	//b.DB.NoSync = true
    88  
    89  	return nil
    90  }
    91  
    92  func (b *BadgerDBStore) Shutdown() (err error) {
    93  	logger.Infof("Shutting badgerdb store")
    94  	if b.DB != nil {
    95  		b.DB.Close()
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  // sync the DB to disk
   102  func (b *BadgerDBStore) Sync() {
   103  
   104  }
   105  
   106  // get a new writable/readable tx,
   107  // we will manage the writable txs manually
   108  // since a block may cause changes to a number of fields which must be reflected atomically
   109  func (b *BadgerDBStore) BeginTX(writable bool) (DBTX, error) {
   110  
   111  	txwrapper := &BadgerTXWrapper{}
   112  	tx := b.DB.NewTransaction(writable) // begin a new writable tx
   113  	txwrapper.tx = tx
   114  	txwrapper.bdb = b // parent DB reference
   115  
   116  	return txwrapper, nil
   117  }
   118  
   119  func (b *BadgerTXWrapper) Commit() error {
   120  	err := b.tx.Commit(nil)
   121  	if err != nil {
   122  		logger.Warnf("Error while committing tx, err %s", err)
   123  		return err
   124  	}
   125  	b.tx.Discard() // tx must always be discarded (both if commited/rollback as per documentation)
   126  
   127  	return nil
   128  }
   129  
   130  // Roll back existing changes to  disk
   131  func (b *BadgerTXWrapper) Rollback() {
   132  
   133  	b.tx.Discard()
   134  }
   135  
   136  // TODO implement this
   137  func (b *BadgerTXWrapper) Sync() {
   138  
   139  }
   140  
   141  // duplicates a byte array as badger db needs objects which are unmodifieable during the transaction
   142  func Duplicate(input []byte) []byte {
   143  	dup := make([]byte, len(input), len(input))
   144  	copy(dup, input)
   145  	return dup
   146  }
   147  
   148  func (b *BadgerTXWrapper) StoreObject(universe_name []byte, galaxy_name []byte, solar_name []byte, key []byte, data []byte) (err error) {
   149  	fullkey := make([]byte, 0, len(universe_name)+len(galaxy_name)+len(solar_name)+len(key))
   150  
   151  	fullkey = append(fullkey, universe_name...)
   152  	fullkey = append(fullkey, galaxy_name...)
   153  	fullkey = append(fullkey, solar_name...)
   154  	fullkey = append(fullkey, key...)
   155  	return b.tx.Set(fullkey, Duplicate(data))
   156  
   157  }
   158  
   159  func (b *BadgerTXWrapper) LoadObject(universe_name []byte, galaxy_name []byte, solar_name []byte, key []byte) (data []byte, err error) {
   160  
   161  	fullkey := make([]byte, 0, len(universe_name)+len(galaxy_name)+len(solar_name)+len(key))
   162  
   163  	fullkey = append(fullkey, universe_name...)
   164  	fullkey = append(fullkey, galaxy_name...)
   165  	fullkey = append(fullkey, solar_name...)
   166  	fullkey = append(fullkey, key...)
   167  
   168  	item, err := b.tx.Get(fullkey)
   169  	if err == badger.ErrKeyNotFound {
   170  		return data, badger.ErrKeyNotFound
   171  	}
   172  	data, err = item.ValueCopy(nil)
   173  	if err != nil {
   174  		return data, badger.ErrKeyNotFound
   175  	}
   176  	return data, nil
   177  }
   178  
   179  // this function stores a uint64
   180  // this will automcatically use the transaction
   181  func (b *BadgerTXWrapper) StoreUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte, data uint64) error {
   182  	return b.StoreObject(universe_bucket, galaxy_bucket, solar_bucket, key, itob(data))
   183  
   184  }
   185  
   186  //  this function loads the data as 64 byte integer
   187  func (b *BadgerTXWrapper) LoadUint64(universe_bucket []byte, galaxy_bucket []byte, solar_bucket []byte, key []byte) (uint64, error) {
   188  	object_data, err := b.LoadObject(universe_bucket, galaxy_bucket, solar_bucket, key)
   189  	if err != nil {
   190  		return 0, err
   191  	}
   192  
   193  	if len(object_data) == 0 {
   194  		return 0, fmt.Errorf("No value stored here, we should look more")
   195  	}
   196  
   197  	if len(object_data) != 8 {
   198  		panic("Database corruption, invalid data ")
   199  	}
   200  
   201  	value := binary.BigEndian.Uint64(object_data)
   202  	return value, nil
   203  }