github.com/braveheart12/just@v0.8.7/ledger/storage/db.go (about)

     1  /*
     2   *    Copyright 2019 Insolar Technologies
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package storage
    18  
    19  import (
    20  	"context"
    21  	"path/filepath"
    22  	"sync"
    23  
    24  	"github.com/dgraph-io/badger"
    25  	"github.com/insolar/insolar/configuration"
    26  	"github.com/insolar/insolar/core"
    27  	"github.com/insolar/insolar/ledger/storage/jet"
    28  	"github.com/insolar/insolar/ledger/storage/record"
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  const (
    33  	scopeIDLifeline byte = 1
    34  	scopeIDRecord   byte = 2
    35  	scopeIDJetDrop  byte = 3
    36  	scopeIDPulse    byte = 4
    37  	scopeIDSystem   byte = 5
    38  	scopeIDMessage  byte = 6
    39  	scopeIDBlob     byte = 7
    40  
    41  	sysGenesis                byte = 1
    42  	sysLatestPulse            byte = 2
    43  	sysHeavyClientState       byte = 3
    44  	sysLastSyncedPulseOnHeavy byte = 4
    45  	sysJetList                byte = 5
    46  	sysDropSizeHistory        byte = 6
    47  )
    48  
    49  // DBContext provides base db methods
    50  //go:generate minimock -i github.com/insolar/insolar/ledger/storage.DBContext -o ./ -s _mock.go
    51  type DBContext interface {
    52  	BeginTransaction(update bool) (*TransactionManager, error)
    53  	View(ctx context.Context, fn func(*TransactionManager) error) error
    54  	Update(ctx context.Context, fn func(*TransactionManager) error) error
    55  
    56  	IterateRecordsOnPulse(
    57  		ctx context.Context,
    58  		jetID core.RecordID,
    59  		pulse core.PulseNumber,
    60  		handler func(id core.RecordID, rec record.Record) error,
    61  	) error
    62  
    63  	StoreKeyValues(ctx context.Context, kvs []core.KV) error
    64  
    65  	GetBadgerDB() *badger.DB
    66  
    67  	Close() error
    68  
    69  	set(ctx context.Context, key, value []byte) error
    70  	get(ctx context.Context, key []byte) ([]byte, error)
    71  
    72  	// TODO i.markin 28.01.19: Delete after switching to conveyor architecture.
    73  	waitingFlight()
    74  
    75  	iterate(ctx context.Context,
    76  		prefix []byte,
    77  		handler func(k, v []byte) error,
    78  	) error
    79  }
    80  
    81  // DB represents BadgerDB storage implementation.
    82  type DB struct {
    83  	PlatformCryptographyScheme core.PlatformCryptographyScheme `inject:""`
    84  
    85  	db *badger.DB
    86  
    87  	// dropLock protects dropWG from concurrent calls to Add and Wait
    88  	dropLock sync.Mutex
    89  	// dropWG guards inflight updates before jet drop calculated.
    90  	dropWG sync.WaitGroup
    91  
    92  	// for BadgerDB it is normal to have transaction conflicts
    93  	// and these conflicts we should resolve by ourself
    94  	// so txretiries is our knob to tune up retry logic.
    95  	txretiries int
    96  
    97  	idlocker             *IDLocker
    98  	jetHeavyClientLocker *IDLocker
    99  
   100  	closeLock sync.RWMutex
   101  	isClosed  bool
   102  }
   103  
   104  func setOptions(o *badger.Options) *badger.Options {
   105  	newo := &badger.Options{}
   106  	if o != nil {
   107  		*newo = *o
   108  	} else {
   109  		*newo = badger.DefaultOptions
   110  	}
   111  	return newo
   112  }
   113  
   114  // NewDB returns storage.DB with BadgerDB instance initialized by opts.
   115  // Creates database in provided dir or in current directory if dir parameter is empty.
   116  func NewDB(conf configuration.Ledger, opts *badger.Options) (DBContext, error) {
   117  	opts = setOptions(opts)
   118  	dir, err := filepath.Abs(conf.Storage.DataDirectory)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	opts.Dir = dir
   124  	opts.ValueDir = dir
   125  
   126  	bdb, err := badger.Open(*opts)
   127  	if err != nil {
   128  		return nil, errors.Wrap(err, "local database open failed")
   129  	}
   130  
   131  	db := &DB{
   132  		db:                   bdb,
   133  		txretiries:           conf.Storage.TxRetriesOnConflict,
   134  		idlocker:             NewIDLocker(),
   135  		jetHeavyClientLocker: NewIDLocker(),
   136  	}
   137  	return db, nil
   138  }
   139  
   140  // Close wraps BadgerDB Close method.
   141  //
   142  // From https://godoc.org/github.com/dgraph-io/badger#DB.Close:
   143  // «It's crucial to call it to ensure all the pending updates make their way to disk.
   144  // Calling DB.Close() multiple times is not safe and wouldcause panic.»
   145  func (db *DB) Close() error {
   146  	db.closeLock.Lock()
   147  	defer db.closeLock.Unlock()
   148  	if db.isClosed {
   149  		return ErrClosed
   150  	}
   151  	db.isClosed = true
   152  
   153  	return db.db.Close()
   154  }
   155  
   156  // Stop stops DB component.
   157  func (db *DB) Stop(ctx context.Context) error {
   158  	return db.Close()
   159  }
   160  
   161  // BeginTransaction opens a new transaction.
   162  // All methods called on returned transaction manager will persist changes
   163  // only after success on "Commit" call.
   164  func (db *DB) BeginTransaction(update bool) (*TransactionManager, error) {
   165  	db.closeLock.RLock()
   166  	defer db.closeLock.RUnlock()
   167  	if db.isClosed {
   168  		return nil, ErrClosed
   169  	}
   170  
   171  	if update {
   172  		db.dropLock.Lock()
   173  		db.dropWG.Add(1)
   174  		db.dropLock.Unlock()
   175  	}
   176  	return &TransactionManager{
   177  		db:        db,
   178  		update:    update,
   179  		txupdates: make(map[string]keyval),
   180  	}, nil
   181  }
   182  
   183  // View accepts transaction function. All calls to received transaction manager will be consistent.
   184  func (db *DB) View(ctx context.Context, fn func(*TransactionManager) error) error {
   185  	tx, err := db.BeginTransaction(false)
   186  	if err != nil {
   187  		return err
   188  	}
   189  	defer tx.Discard()
   190  	return fn(tx)
   191  }
   192  
   193  // Update accepts transaction function and commits changes. All calls to received transaction manager will be
   194  // consistent and written tp disk or an error will be returned.
   195  func (db *DB) Update(ctx context.Context, fn func(*TransactionManager) error) error {
   196  	tries := db.txretiries
   197  	var tx *TransactionManager
   198  	var err error
   199  	for {
   200  		tx, err = db.BeginTransaction(true)
   201  		if err != nil {
   202  			return err
   203  		}
   204  		err = fn(tx)
   205  		if err != nil {
   206  			break
   207  		}
   208  		err = tx.Commit()
   209  		if err == nil {
   210  			break
   211  		}
   212  		if err != badger.ErrConflict {
   213  			break
   214  		}
   215  		if tries < 1 {
   216  			if db.txretiries > 0 {
   217  				err = ErrConflictRetriesOver
   218  			} else {
   219  				err = ErrConflict
   220  			}
   221  			break
   222  		}
   223  		tries--
   224  		tx.Discard()
   225  	}
   226  	tx.Discard()
   227  
   228  	return err
   229  }
   230  
   231  // GetBadgerDB return badger.DB instance (for internal usage, like tests)
   232  func (db *DB) GetBadgerDB() *badger.DB {
   233  	return db.db
   234  }
   235  
   236  // IterateRecordsOnPulse iterates over records on provided Jet ID and Pulse.
   237  func (db *DB) IterateRecordsOnPulse(
   238  	ctx context.Context,
   239  	jetID core.RecordID,
   240  	pulse core.PulseNumber,
   241  	handler func(id core.RecordID, rec record.Record) error,
   242  ) error {
   243  	_, jetPrefix := jet.Jet(jetID)
   244  	prefix := prefixkey(scopeIDRecord, jetPrefix, pulse.Bytes())
   245  
   246  	return db.iterate(ctx, prefix, func(k, v []byte) error {
   247  		id := core.NewRecordID(pulse, k)
   248  		rec := record.DeserializeRecord(v)
   249  		err := handler(*id, rec)
   250  		if err != nil {
   251  			return err
   252  		}
   253  		return nil
   254  	})
   255  }
   256  
   257  // StoreKeyValues stores provided key/value pairs.
   258  func (db *DB) StoreKeyValues(ctx context.Context, kvs []core.KV) error {
   259  	return db.Update(ctx, func(tx *TransactionManager) error {
   260  		for _, rec := range kvs {
   261  			err := tx.set(ctx, rec.K, rec.V)
   262  			if err != nil {
   263  				return err
   264  			}
   265  		}
   266  		return nil
   267  	})
   268  }
   269  
   270  func (db *DB) GetPlatformCryptographyScheme() core.PlatformCryptographyScheme {
   271  	return db.PlatformCryptographyScheme
   272  }
   273  
   274  // get wraps matching transaction manager method.
   275  func (db *DB) get(ctx context.Context, key []byte) ([]byte, error) {
   276  	tx, err := db.BeginTransaction(false)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	defer tx.Discard()
   281  	return tx.get(ctx, key)
   282  }
   283  
   284  // set wraps matching transaction manager method.
   285  func (db *DB) set(ctx context.Context, key, value []byte) error {
   286  	return db.Update(ctx, func(tx *TransactionManager) error {
   287  		return tx.set(ctx, key, value)
   288  	})
   289  }
   290  
   291  func (db *DB) waitingFlight() {
   292  	db.dropLock.Lock()
   293  	db.dropWG.Wait()
   294  	db.dropLock.Unlock()
   295  }
   296  
   297  func (db *DB) iterate(
   298  	ctx context.Context,
   299  	prefix []byte,
   300  	handler func(k, v []byte) error,
   301  ) error {
   302  	db.closeLock.RLock()
   303  	defer db.closeLock.RUnlock()
   304  	if db.isClosed {
   305  		return ErrClosed
   306  	}
   307  
   308  	return db.db.View(func(txn *badger.Txn) error {
   309  		it := txn.NewIterator(badger.DefaultIteratorOptions)
   310  		defer it.Close()
   311  
   312  		for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
   313  			key := it.Item().KeyCopy(nil)[len(prefix):]
   314  			value, err := it.Item().ValueCopy(nil)
   315  			if err != nil {
   316  				return err
   317  			}
   318  			err = handler(key, value)
   319  			if err != nil {
   320  				return err
   321  			}
   322  		}
   323  		return nil
   324  	})
   325  }