github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/ledger/util/leveldbhelper/leveldb_provider.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package leveldbhelper
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"sync"
    13  
    14  	"github.com/hyperledger/fabric/common/ledger/dataformat"
    15  	"github.com/syndtr/goleveldb/leveldb"
    16  	"github.com/syndtr/goleveldb/leveldb/iterator"
    17  )
    18  
    19  // internalDBName is used to keep track of data related to internals such as data format
    20  // _ is used as name because this is not allowed as a channelname
    21  const internalDBName = "_"
    22  
    23  var (
    24  	dbNameKeySep     = []byte{0x00}
    25  	lastKeyIndicator = byte(0x01)
    26  	formatVersionKey = []byte{'f'} // a single key in db whose value indicates the version of the data format
    27  )
    28  
    29  // Conf configuration for `Provider`
    30  //
    31  // `ExpectedFormatVersion` is the expected value of the format key in the internal database.
    32  // At the time of opening the db, A check is performed that
    33  // either the db is empty (i.e., opening for the first time) or the value
    34  // of the formatVersionKey is equal to `ExpectedFormatVersion`. Otherwise, an error is returned.
    35  // A nil value for ExpectedFormatVersion indicates that the format is never set and hence there is no such record
    36  type Conf struct {
    37  	DBPath                string
    38  	ExpectedFormatVersion string
    39  }
    40  
    41  // Provider enables to use a single leveldb as multiple logical leveldbs
    42  type Provider struct {
    43  	db *DB
    44  
    45  	mux       sync.Mutex
    46  	dbHandles map[string]*DBHandle
    47  }
    48  
    49  // NewProvider constructs a Provider
    50  func NewProvider(conf *Conf) (*Provider, error) {
    51  	db, err := openDBAndCheckFormat(conf)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	return &Provider{
    56  		db:        db,
    57  		dbHandles: make(map[string]*DBHandle),
    58  	}, nil
    59  }
    60  
    61  func openDBAndCheckFormat(conf *Conf) (d *DB, e error) {
    62  	db := CreateDB(conf)
    63  	db.Open()
    64  
    65  	defer func() {
    66  		if e != nil {
    67  			db.Close()
    68  		}
    69  	}()
    70  
    71  	internalDB := &DBHandle{
    72  		db:     db,
    73  		dbName: internalDBName,
    74  	}
    75  
    76  	dbEmpty, err := db.IsEmpty()
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	if dbEmpty && conf.ExpectedFormatVersion != "" {
    82  		logger.Infof("DB is empty Setting db format as %s", conf.ExpectedFormatVersion)
    83  		if err := internalDB.Put(formatVersionKey, []byte(conf.ExpectedFormatVersion), true); err != nil {
    84  			return nil, err
    85  		}
    86  		return db, nil
    87  	}
    88  
    89  	formatVersion, err := internalDB.Get(formatVersionKey)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	logger.Debugf("Checking for db format at path [%s]", conf.DBPath)
    94  
    95  	if !bytes.Equal(formatVersion, []byte(conf.ExpectedFormatVersion)) {
    96  		logger.Errorf("The db at path [%s] contains data in unexpected format. expected data format = [%s] (%#v), data format = [%s] (%#v).",
    97  			conf.DBPath, conf.ExpectedFormatVersion, []byte(conf.ExpectedFormatVersion), formatVersion, formatVersion)
    98  		return nil, &dataformat.ErrVersionMismatch{
    99  			ExpectedVersion: conf.ExpectedFormatVersion,
   100  			Version:         string(formatVersion),
   101  			DBInfo:          fmt.Sprintf("leveldb at [%s]", conf.DBPath),
   102  		}
   103  	}
   104  	logger.Debug("format is latest, nothing to do")
   105  	return db, nil
   106  }
   107  
   108  // GetDataFormat returns the format of the data
   109  func (p *Provider) GetDataFormat() (string, error) {
   110  	f, err := p.GetDBHandle(internalDBName).Get(formatVersionKey)
   111  	return string(f), err
   112  }
   113  
   114  // GetDBHandle returns a handle to a named db
   115  func (p *Provider) GetDBHandle(dbName string) *DBHandle {
   116  	p.mux.Lock()
   117  	defer p.mux.Unlock()
   118  	dbHandle := p.dbHandles[dbName]
   119  	if dbHandle == nil {
   120  		dbHandle = &DBHandle{dbName, p.db}
   121  		p.dbHandles[dbName] = dbHandle
   122  	}
   123  	return dbHandle
   124  }
   125  
   126  // Close closes the underlying leveldb
   127  func (p *Provider) Close() {
   128  	p.db.Close()
   129  }
   130  
   131  // DBHandle is an handle to a named db
   132  type DBHandle struct {
   133  	dbName string
   134  	db     *DB
   135  }
   136  
   137  // Get returns the value for the given key
   138  func (h *DBHandle) Get(key []byte) ([]byte, error) {
   139  	return h.db.Get(constructLevelKey(h.dbName, key))
   140  }
   141  
   142  // Put saves the key/value
   143  func (h *DBHandle) Put(key []byte, value []byte, sync bool) error {
   144  	return h.db.Put(constructLevelKey(h.dbName, key), value, sync)
   145  }
   146  
   147  // Delete deletes the given key
   148  func (h *DBHandle) Delete(key []byte, sync bool) error {
   149  	return h.db.Delete(constructLevelKey(h.dbName, key), sync)
   150  }
   151  
   152  // WriteBatch writes a batch in an atomic way
   153  func (h *DBHandle) WriteBatch(batch *UpdateBatch, sync bool) error {
   154  	if len(batch.KVs) == 0 {
   155  		return nil
   156  	}
   157  	levelBatch := &leveldb.Batch{}
   158  	for k, v := range batch.KVs {
   159  		key := constructLevelKey(h.dbName, []byte(k))
   160  		if v == nil {
   161  			levelBatch.Delete(key)
   162  		} else {
   163  			levelBatch.Put(key, v)
   164  		}
   165  	}
   166  	if err := h.db.WriteBatch(levelBatch, sync); err != nil {
   167  		return err
   168  	}
   169  	return nil
   170  }
   171  
   172  // GetIterator gets an handle to iterator. The iterator should be released after the use.
   173  // The resultset contains all the keys that are present in the db between the startKey (inclusive) and the endKey (exclusive).
   174  // A nil startKey represents the first available key and a nil endKey represent a logical key after the last available key
   175  func (h *DBHandle) GetIterator(startKey []byte, endKey []byte) *Iterator {
   176  	sKey := constructLevelKey(h.dbName, startKey)
   177  	eKey := constructLevelKey(h.dbName, endKey)
   178  	if endKey == nil {
   179  		// replace the last byte 'dbNameKeySep' by 'lastKeyIndicator'
   180  		eKey[len(eKey)-1] = lastKeyIndicator
   181  	}
   182  	logger.Debugf("Getting iterator for range [%#v] - [%#v]", sKey, eKey)
   183  	return &Iterator{h.db.GetIterator(sKey, eKey)}
   184  }
   185  
   186  // UpdateBatch encloses the details of multiple `updates`
   187  type UpdateBatch struct {
   188  	KVs map[string][]byte
   189  }
   190  
   191  // NewUpdateBatch constructs an instance of a Batch
   192  func NewUpdateBatch() *UpdateBatch {
   193  	return &UpdateBatch{make(map[string][]byte)}
   194  }
   195  
   196  // Put adds a KV
   197  func (batch *UpdateBatch) Put(key []byte, value []byte) {
   198  	if value == nil {
   199  		panic("Nil value not allowed")
   200  	}
   201  	batch.KVs[string(key)] = value
   202  }
   203  
   204  // Delete deletes a Key and associated value
   205  func (batch *UpdateBatch) Delete(key []byte) {
   206  	batch.KVs[string(key)] = nil
   207  }
   208  
   209  // Len returns the number of entries in the batch
   210  func (batch *UpdateBatch) Len() int {
   211  	return len(batch.KVs)
   212  }
   213  
   214  // Iterator extends actual leveldb iterator
   215  type Iterator struct {
   216  	iterator.Iterator
   217  }
   218  
   219  // Key wraps actual leveldb iterator method
   220  func (itr *Iterator) Key() []byte {
   221  	return retrieveAppKey(itr.Iterator.Key())
   222  }
   223  
   224  func constructLevelKey(dbName string, key []byte) []byte {
   225  	return append(append([]byte(dbName), dbNameKeySep...), key...)
   226  }
   227  
   228  func retrieveAppKey(levelKey []byte) []byte {
   229  	return bytes.SplitN(levelKey, dbNameKeySep, 2)[1]
   230  }