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  }