github.com/renegr87/renegr87@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package stateleveldb
     8  
     9  import (
    10  	"bytes"
    11  
    12  	"github.com/hyperledger/fabric/common/flogging"
    13  	"github.com/hyperledger/fabric/common/ledger/dataformat"
    14  	"github.com/hyperledger/fabric/common/ledger/util/leveldbhelper"
    15  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    16  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
    17  	"github.com/pkg/errors"
    18  	"github.com/syndtr/goleveldb/leveldb/iterator"
    19  )
    20  
    21  var logger = flogging.MustGetLogger("stateleveldb")
    22  
    23  var (
    24  	dataKeyPrefix    = []byte{'d'}
    25  	nsKeySep         = []byte{0x00}
    26  	lastKeyIndicator = byte(0x01)
    27  	savePointKey     = []byte{'s'}
    28  )
    29  
    30  // VersionedDBProvider implements interface VersionedDBProvider
    31  type VersionedDBProvider struct {
    32  	dbProvider *leveldbhelper.Provider
    33  }
    34  
    35  // NewVersionedDBProvider instantiates VersionedDBProvider
    36  func NewVersionedDBProvider(dbPath string) (*VersionedDBProvider, error) {
    37  	logger.Debugf("constructing VersionedDBProvider dbPath=%s", dbPath)
    38  	dbProvider, err := leveldbhelper.NewProvider(
    39  		&leveldbhelper.Conf{
    40  			DBPath:                dbPath,
    41  			ExpectedFormatVersion: dataformat.Version20,
    42  		})
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return &VersionedDBProvider{dbProvider}, nil
    47  }
    48  
    49  // GetDBHandle gets the handle to a named database
    50  func (provider *VersionedDBProvider) GetDBHandle(dbName string) (statedb.VersionedDB, error) {
    51  	return newVersionedDB(provider.dbProvider.GetDBHandle(dbName), dbName), nil
    52  }
    53  
    54  // Close closes the underlying db
    55  func (provider *VersionedDBProvider) Close() {
    56  	provider.dbProvider.Close()
    57  }
    58  
    59  // VersionedDB implements VersionedDB interface
    60  type versionedDB struct {
    61  	db     *leveldbhelper.DBHandle
    62  	dbName string
    63  }
    64  
    65  // newVersionedDB constructs an instance of VersionedDB
    66  func newVersionedDB(db *leveldbhelper.DBHandle, dbName string) *versionedDB {
    67  	return &versionedDB{db, dbName}
    68  }
    69  
    70  // Open implements method in VersionedDB interface
    71  func (vdb *versionedDB) Open() error {
    72  	// do nothing because shared db is used
    73  	return nil
    74  }
    75  
    76  // Close implements method in VersionedDB interface
    77  func (vdb *versionedDB) Close() {
    78  	// do nothing because shared db is used
    79  }
    80  
    81  // ValidateKeyValue implements method in VersionedDB interface
    82  func (vdb *versionedDB) ValidateKeyValue(key string, value []byte) error {
    83  	return nil
    84  }
    85  
    86  // BytesKeySupported implements method in VersionedDB interface
    87  func (vdb *versionedDB) BytesKeySupported() bool {
    88  	return true
    89  }
    90  
    91  // GetState implements method in VersionedDB interface
    92  func (vdb *versionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) {
    93  	logger.Debugf("GetState(). ns=%s, key=%s", namespace, key)
    94  	dbVal, err := vdb.db.Get(encodeDataKey(namespace, key))
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	if dbVal == nil {
    99  		return nil, nil
   100  	}
   101  	return decodeValue(dbVal)
   102  }
   103  
   104  // GetVersion implements method in VersionedDB interface
   105  func (vdb *versionedDB) GetVersion(namespace string, key string) (*version.Height, error) {
   106  	versionedValue, err := vdb.GetState(namespace, key)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	if versionedValue == nil {
   111  		return nil, nil
   112  	}
   113  	return versionedValue.Version, nil
   114  }
   115  
   116  // GetStateMultipleKeys implements method in VersionedDB interface
   117  func (vdb *versionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) {
   118  	vals := make([]*statedb.VersionedValue, len(keys))
   119  	for i, key := range keys {
   120  		val, err := vdb.GetState(namespace, key)
   121  		if err != nil {
   122  			return nil, err
   123  		}
   124  		vals[i] = val
   125  	}
   126  	return vals, nil
   127  }
   128  
   129  // GetStateRangeScanIterator implements method in VersionedDB interface
   130  // startKey is inclusive
   131  // endKey is exclusive
   132  func (vdb *versionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) {
   133  	return vdb.GetStateRangeScanIteratorWithMetadata(namespace, startKey, endKey, nil)
   134  }
   135  
   136  const optionLimit = "limit"
   137  
   138  // GetStateRangeScanIteratorWithMetadata implements method in VersionedDB interface
   139  func (vdb *versionedDB) GetStateRangeScanIteratorWithMetadata(namespace string, startKey string, endKey string, metadata map[string]interface{}) (statedb.QueryResultsIterator, error) {
   140  
   141  	requestedLimit := int32(0)
   142  	// if metadata is provided, validate and apply options
   143  	if metadata != nil {
   144  		//validate the metadata
   145  		err := statedb.ValidateRangeMetadata(metadata)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		if limitOption, ok := metadata[optionLimit]; ok {
   150  			requestedLimit = limitOption.(int32)
   151  		}
   152  	}
   153  
   154  	// Note:  metadata is not used for the goleveldb implementation of the range query
   155  	dataStartKey := encodeDataKey(namespace, startKey)
   156  	dataEndKey := encodeDataKey(namespace, endKey)
   157  	if endKey == "" {
   158  		dataEndKey[len(dataEndKey)-1] = lastKeyIndicator
   159  	}
   160  	dbItr := vdb.db.GetIterator(dataStartKey, dataEndKey)
   161  	return newKVScanner(namespace, dbItr, requestedLimit), nil
   162  }
   163  
   164  // ExecuteQuery implements method in VersionedDB interface
   165  func (vdb *versionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) {
   166  	return nil, errors.New("ExecuteQuery not supported for leveldb")
   167  }
   168  
   169  // ExecuteQueryWithMetadata implements method in VersionedDB interface
   170  func (vdb *versionedDB) ExecuteQueryWithMetadata(namespace, query string, metadata map[string]interface{}) (statedb.QueryResultsIterator, error) {
   171  	return nil, errors.New("ExecuteQueryWithMetadata not supported for leveldb")
   172  }
   173  
   174  // ApplyUpdates implements method in VersionedDB interface
   175  func (vdb *versionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error {
   176  	dbBatch := leveldbhelper.NewUpdateBatch()
   177  	namespaces := batch.GetUpdatedNamespaces()
   178  	for _, ns := range namespaces {
   179  		updates := batch.GetUpdates(ns)
   180  		for k, vv := range updates {
   181  			dataKey := encodeDataKey(ns, k)
   182  			logger.Debugf("Channel [%s]: Applying key(string)=[%s] key(bytes)=[%#v]", vdb.dbName, string(dataKey), dataKey)
   183  
   184  			if vv.Value == nil {
   185  				dbBatch.Delete(dataKey)
   186  			} else {
   187  				encodedVal, err := encodeValue(vv)
   188  				if err != nil {
   189  					return err
   190  				}
   191  				dbBatch.Put(dataKey, encodedVal)
   192  			}
   193  		}
   194  	}
   195  	// Record a savepoint at a given height
   196  	// If a given height is nil, it denotes that we are committing pvt data of old blocks.
   197  	// In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList
   198  	// in the pvtstore acts as a savepoint for pvt data.
   199  	if height != nil {
   200  		dbBatch.Put(savePointKey, height.ToBytes())
   201  	}
   202  	// Setting snyc to true as a precaution, false may be an ok optimization after further testing.
   203  	if err := vdb.db.WriteBatch(dbBatch, true); err != nil {
   204  		return err
   205  	}
   206  	return nil
   207  }
   208  
   209  // GetLatestSavePoint implements method in VersionedDB interface
   210  func (vdb *versionedDB) GetLatestSavePoint() (*version.Height, error) {
   211  	versionBytes, err := vdb.db.Get(savePointKey)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	if versionBytes == nil {
   216  		return nil, nil
   217  	}
   218  	version, _, err := version.NewHeightFromBytes(versionBytes)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	return version, nil
   223  }
   224  
   225  func encodeDataKey(ns, key string) []byte {
   226  	k := append(dataKeyPrefix, []byte(ns)...)
   227  	k = append(k, nsKeySep...)
   228  	return append(k, []byte(key)...)
   229  }
   230  
   231  func decodeDataKey(encodedDataKey []byte) (string, string) {
   232  	split := bytes.SplitN(encodedDataKey, nsKeySep, 2)
   233  	return string(split[0][1:]), string(split[1])
   234  }
   235  
   236  type kvScanner struct {
   237  	namespace            string
   238  	dbItr                iterator.Iterator
   239  	requestedLimit       int32
   240  	totalRecordsReturned int32
   241  }
   242  
   243  func newKVScanner(namespace string, dbItr iterator.Iterator, requestedLimit int32) *kvScanner {
   244  	return &kvScanner{namespace, dbItr, requestedLimit, 0}
   245  }
   246  
   247  func (scanner *kvScanner) Next() (statedb.QueryResult, error) {
   248  	if scanner.requestedLimit > 0 && scanner.totalRecordsReturned >= scanner.requestedLimit {
   249  		return nil, nil
   250  	}
   251  	if !scanner.dbItr.Next() {
   252  		return nil, nil
   253  	}
   254  
   255  	dbKey := scanner.dbItr.Key()
   256  	dbVal := scanner.dbItr.Value()
   257  	dbValCopy := make([]byte, len(dbVal))
   258  	copy(dbValCopy, dbVal)
   259  	_, key := decodeDataKey(dbKey)
   260  	vv, err := decodeValue(dbValCopy)
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  
   265  	scanner.totalRecordsReturned++
   266  	return &statedb.VersionedKV{
   267  		CompositeKey: statedb.CompositeKey{Namespace: scanner.namespace, Key: key},
   268  		// TODO remove dereferrencing below by changing the type of the field
   269  		// `VersionedValue` in `statedb.VersionedKV` to a pointer
   270  		VersionedValue: *vv}, nil
   271  }
   272  
   273  func (scanner *kvScanner) Close() {
   274  	scanner.dbItr.Release()
   275  }
   276  
   277  func (scanner *kvScanner) GetBookmarkAndClose() string {
   278  	retval := ""
   279  	if scanner.dbItr.Next() {
   280  		dbKey := scanner.dbItr.Key()
   281  		_, key := decodeDataKey(dbKey)
   282  		retval = key
   283  	}
   284  	scanner.Close()
   285  	return retval
   286  }