github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/fabric/common/flogging"
    13  	"github.com/osdi23p228/fabric/common/ledger/dataformat"
    14  	"github.com/osdi23p228/fabric/common/ledger/util/leveldbhelper"
    15  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    16  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb"
    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  	dataKeyStopper              = []byte{'e'}
    26  	nsKeySep                    = []byte{0x00}
    27  	lastKeyIndicator            = byte(0x01)
    28  	savePointKey                = []byte{'s'}
    29  	fullScanIteratorValueFormat = byte(1)
    30  )
    31  
    32  // VersionedDBProvider implements interface VersionedDBProvider
    33  type VersionedDBProvider struct {
    34  	dbProvider *leveldbhelper.Provider
    35  }
    36  
    37  // NewVersionedDBProvider instantiates VersionedDBProvider
    38  func NewVersionedDBProvider(dbPath string) (*VersionedDBProvider, error) {
    39  	logger.Debugf("constructing VersionedDBProvider dbPath=%s", dbPath)
    40  	dbProvider, err := leveldbhelper.NewProvider(
    41  		&leveldbhelper.Conf{
    42  			DBPath:         dbPath,
    43  			ExpectedFormat: dataformat.CurrentFormat,
    44  		})
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	return &VersionedDBProvider{dbProvider}, nil
    49  }
    50  
    51  // GetDBHandle gets the handle to a named database
    52  func (provider *VersionedDBProvider) GetDBHandle(dbName string, namespaceProvider statedb.NamespaceProvider) (statedb.VersionedDB, error) {
    53  	return newVersionedDB(provider.dbProvider.GetDBHandle(dbName), dbName), nil
    54  }
    55  
    56  // Close closes the underlying db
    57  func (provider *VersionedDBProvider) Close() {
    58  	provider.dbProvider.Close()
    59  }
    60  
    61  // VersionedDB implements VersionedDB interface
    62  type versionedDB struct {
    63  	db     *leveldbhelper.DBHandle
    64  	dbName string
    65  }
    66  
    67  // newVersionedDB constructs an instance of VersionedDB
    68  func newVersionedDB(db *leveldbhelper.DBHandle, dbName string) *versionedDB {
    69  	return &versionedDB{db, dbName}
    70  }
    71  
    72  // Open implements method in VersionedDB interface
    73  func (vdb *versionedDB) Open() error {
    74  	// do nothing because shared db is used
    75  	return nil
    76  }
    77  
    78  // Close implements method in VersionedDB interface
    79  func (vdb *versionedDB) Close() {
    80  	// do nothing because shared db is used
    81  }
    82  
    83  // ValidateKeyValue implements method in VersionedDB interface
    84  func (vdb *versionedDB) ValidateKeyValue(key string, value []byte) error {
    85  	return nil
    86  }
    87  
    88  // BytesKeySupported implements method in VersionedDB interface
    89  func (vdb *versionedDB) BytesKeySupported() bool {
    90  	return true
    91  }
    92  
    93  // GetState implements method in VersionedDB interface
    94  func (vdb *versionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) {
    95  	logger.Debugf("GetState(). ns=%s, key=%s", namespace, key)
    96  	dbVal, err := vdb.db.Get(encodeDataKey(namespace, key))
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	if dbVal == nil {
   101  		return nil, nil
   102  	}
   103  	return decodeValue(dbVal)
   104  }
   105  
   106  // GetVersion implements method in VersionedDB interface
   107  func (vdb *versionedDB) GetVersion(namespace string, key string) (*version.Height, error) {
   108  	versionedValue, err := vdb.GetState(namespace, key)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	if versionedValue == nil {
   113  		return nil, nil
   114  	}
   115  	return versionedValue.Version, nil
   116  }
   117  
   118  // GetStateMultipleKeys implements method in VersionedDB interface
   119  func (vdb *versionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) {
   120  	vals := make([]*statedb.VersionedValue, len(keys))
   121  	for i, key := range keys {
   122  		val, err := vdb.GetState(namespace, key)
   123  		if err != nil {
   124  			return nil, err
   125  		}
   126  		vals[i] = val
   127  	}
   128  	return vals, nil
   129  }
   130  
   131  // GetStateRangeScanIterator implements method in VersionedDB interface
   132  // startKey is inclusive
   133  // endKey is exclusive
   134  func (vdb *versionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) {
   135  	// pageSize = 0 denotes unlimited page size
   136  	return vdb.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, 0)
   137  }
   138  
   139  // GetStateRangeScanIteratorWithPagination implements method in VersionedDB interface
   140  func (vdb *versionedDB) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (statedb.QueryResultsIterator, error) {
   141  	dataStartKey := encodeDataKey(namespace, startKey)
   142  	dataEndKey := encodeDataKey(namespace, endKey)
   143  	if endKey == "" {
   144  		dataEndKey[len(dataEndKey)-1] = lastKeyIndicator
   145  	}
   146  	dbItr, err := vdb.db.GetIterator(dataStartKey, dataEndKey)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	return newKVScanner(namespace, dbItr, pageSize), nil
   151  }
   152  
   153  // ExecuteQuery implements method in VersionedDB interface
   154  func (vdb *versionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) {
   155  	return nil, errors.New("ExecuteQuery not supported for leveldb")
   156  }
   157  
   158  // ExecuteQueryWithPagination implements method in VersionedDB interface
   159  func (vdb *versionedDB) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (statedb.QueryResultsIterator, error) {
   160  	return nil, errors.New("ExecuteQueryWithMetadata not supported for leveldb")
   161  }
   162  
   163  // ApplyUpdates implements method in VersionedDB interface
   164  func (vdb *versionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error {
   165  	dbBatch := vdb.db.NewUpdateBatch()
   166  	namespaces := batch.GetUpdatedNamespaces()
   167  	for _, ns := range namespaces {
   168  		updates := batch.GetUpdates(ns)
   169  		for k, vv := range updates {
   170  			dataKey := encodeDataKey(ns, k)
   171  			logger.Debugf("Channel [%s]: Applying key(string)=[%s] key(bytes)=[%#v]", vdb.dbName, string(dataKey), dataKey)
   172  
   173  			if vv.Value == nil {
   174  				dbBatch.Delete(dataKey)
   175  			} else {
   176  				encodedVal, err := encodeValue(vv)
   177  				if err != nil {
   178  					return err
   179  				}
   180  				dbBatch.Put(dataKey, encodedVal)
   181  			}
   182  		}
   183  	}
   184  	// Record a savepoint at a given height
   185  	// If a given height is nil, it denotes that we are committing pvt data of old blocks.
   186  	// In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList
   187  	// in the pvtstore acts as a savepoint for pvt data.
   188  	if height != nil {
   189  		dbBatch.Put(savePointKey, height.ToBytes())
   190  	}
   191  	// Setting snyc to true as a precaution, false may be an ok optimization after further testing.
   192  	if err := vdb.db.WriteBatch(dbBatch, true); err != nil {
   193  		return err
   194  	}
   195  	return nil
   196  }
   197  
   198  // GetLatestSavePoint implements method in VersionedDB interface
   199  func (vdb *versionedDB) GetLatestSavePoint() (*version.Height, error) {
   200  	versionBytes, err := vdb.db.Get(savePointKey)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	if versionBytes == nil {
   205  		return nil, nil
   206  	}
   207  	version, _, err := version.NewHeightFromBytes(versionBytes)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	return version, nil
   212  }
   213  
   214  // GetFullScanIterator implements method in VersionedDB interface. 	This function returns a
   215  // FullScanIterator that can be used to iterate over entire data in the statedb for a channel.
   216  // `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator
   217  // to skip one or more namespaces from the returned results. The intended use of this iterator
   218  // is to generate the snapshot files for the stateleveldb
   219  func (vdb *versionedDB) GetFullScanIterator(skipNamespace func(string) bool) (statedb.FullScanIterator, byte, error) {
   220  	return newFullDBScanner(vdb.db, skipNamespace)
   221  }
   222  
   223  func encodeDataKey(ns, key string) []byte {
   224  	k := append(dataKeyPrefix, []byte(ns)...)
   225  	k = append(k, nsKeySep...)
   226  	return append(k, []byte(key)...)
   227  }
   228  
   229  func decodeDataKey(encodedDataKey []byte) (string, string) {
   230  	split := bytes.SplitN(encodedDataKey, nsKeySep, 2)
   231  	return string(split[0][1:]), string(split[1])
   232  }
   233  
   234  func dataKeyStarterForNextNamespace(ns string) []byte {
   235  	k := append(dataKeyPrefix, []byte(ns)...)
   236  	return append(k, lastKeyIndicator)
   237  }
   238  
   239  type kvScanner struct {
   240  	namespace            string
   241  	dbItr                iterator.Iterator
   242  	requestedLimit       int32
   243  	totalRecordsReturned int32
   244  }
   245  
   246  func newKVScanner(namespace string, dbItr iterator.Iterator, requestedLimit int32) *kvScanner {
   247  	return &kvScanner{namespace, dbItr, requestedLimit, 0}
   248  }
   249  
   250  func (scanner *kvScanner) Next() (statedb.QueryResult, error) {
   251  	if scanner.requestedLimit > 0 && scanner.totalRecordsReturned >= scanner.requestedLimit {
   252  		return nil, nil
   253  	}
   254  	if !scanner.dbItr.Next() {
   255  		return nil, nil
   256  	}
   257  
   258  	dbKey := scanner.dbItr.Key()
   259  	dbVal := scanner.dbItr.Value()
   260  	dbValCopy := make([]byte, len(dbVal))
   261  	copy(dbValCopy, dbVal)
   262  	_, key := decodeDataKey(dbKey)
   263  	vv, err := decodeValue(dbValCopy)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	scanner.totalRecordsReturned++
   269  	return &statedb.VersionedKV{
   270  		CompositeKey: statedb.CompositeKey{Namespace: scanner.namespace, Key: key},
   271  		// TODO remove dereferrencing below by changing the type of the field
   272  		// `VersionedValue` in `statedb.VersionedKV` to a pointer
   273  		VersionedValue: *vv}, nil
   274  }
   275  
   276  func (scanner *kvScanner) Close() {
   277  	scanner.dbItr.Release()
   278  }
   279  
   280  func (scanner *kvScanner) GetBookmarkAndClose() string {
   281  	retval := ""
   282  	if scanner.dbItr.Next() {
   283  		dbKey := scanner.dbItr.Key()
   284  		_, key := decodeDataKey(dbKey)
   285  		retval = key
   286  	}
   287  	scanner.Close()
   288  	return retval
   289  }
   290  
   291  type fullDBScanner struct {
   292  	db     *leveldbhelper.DBHandle
   293  	dbItr  iterator.Iterator
   294  	toSkip func(namespace string) bool
   295  }
   296  
   297  func newFullDBScanner(db *leveldbhelper.DBHandle, skipNamespace func(namespace string) bool) (*fullDBScanner, byte, error) {
   298  	dbItr, err := db.GetIterator(dataKeyPrefix, dataKeyStopper)
   299  	if err != nil {
   300  		return nil, byte(0), err
   301  	}
   302  	return &fullDBScanner{
   303  			db:     db,
   304  			dbItr:  dbItr,
   305  			toSkip: skipNamespace,
   306  		},
   307  		fullScanIteratorValueFormat,
   308  		nil
   309  }
   310  
   311  // Next returns the key-values in the lexical order of <Namespace, key>
   312  // The bytes returned for the <version, value, metadata> are the same as they are stored in the leveldb.
   313  // Since, the primary intended use of this function is to generate the snapshot files for the statedb, the same
   314  // bytes can be consumed back as is. Hence, we do not decode or transform these bytes for the efficiency
   315  func (s *fullDBScanner) Next() (*statedb.CompositeKey, []byte, error) {
   316  	for s.dbItr.Next() {
   317  		dbKey := s.dbItr.Key()
   318  		dbVal := s.dbItr.Value()
   319  		ns, key := decodeDataKey(dbKey)
   320  		compositeKey := &statedb.CompositeKey{
   321  			Namespace: ns,
   322  			Key:       key,
   323  		}
   324  
   325  		switch {
   326  		case !s.toSkip(ns):
   327  			return compositeKey, dbVal, nil
   328  		default:
   329  			s.dbItr.Seek(dataKeyStarterForNextNamespace(ns))
   330  			s.dbItr.Prev()
   331  		}
   332  	}
   333  	return nil, nil, errors.Wrap(s.dbItr.Error(), "internal leveldb error while retrieving data from db iterator")
   334  }
   335  
   336  func (s *fullDBScanner) Close() {
   337  	if s == nil {
   338  		return
   339  	}
   340  	s.dbItr.Release()
   341  }