github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/statedb/statedb.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statedb
     8  
     9  import (
    10  	"sort"
    11  
    12  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    13  	"github.com/osdi23p228/fabric/core/ledger/util"
    14  )
    15  
    16  //go:generate counterfeiter -o mock/results_iterator.go -fake-name ResultsIterator . ResultsIterator
    17  //go:generate counterfeiter -o mock/versioned_db.go -fake-name VersionedDB . VersionedDB
    18  //go:generate counterfeiter -o mock/namespace_provider.go -fake-name NamespaceProvider . NamespaceProvider
    19  
    20  // VersionedDBProvider provides an instance of an versioned DB
    21  type VersionedDBProvider interface {
    22  	// GetDBHandle returns a handle to a VersionedDB
    23  	GetDBHandle(id string, namespaceProvider NamespaceProvider) (VersionedDB, error)
    24  	// Close closes all the VersionedDB instances and releases any resources held by VersionedDBProvider
    25  	Close()
    26  }
    27  
    28  // VersionedDB lists methods that a db is supposed to implement
    29  type VersionedDB interface {
    30  	// GetState gets the value for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
    31  	GetState(namespace string, key string) (*VersionedValue, error)
    32  	// GetVersion gets the version for given namespace and key. For a chaincode, the namespace corresponds to the chaincodeId
    33  	GetVersion(namespace string, key string) (*version.Height, error)
    34  	// GetStateMultipleKeys gets the values for multiple keys in a single call
    35  	GetStateMultipleKeys(namespace string, keys []string) ([]*VersionedValue, error)
    36  	// GetStateRangeScanIterator returns an iterator that contains all the key-values between given key ranges.
    37  	// startKey is inclusive
    38  	// endKey is exclusive
    39  	// The returned ResultsIterator contains results of type *VersionedKV
    40  	GetStateRangeScanIterator(namespace string, startKey string, endKey string) (ResultsIterator, error)
    41  	// GetStateRangeScanIteratorWithPagination returns an iterator that contains all the key-values between given key ranges.
    42  	// startKey is inclusive
    43  	// endKey is exclusive
    44  	// pageSize parameter limits the number of returned results
    45  	// The returned ResultsIterator contains results of type *VersionedKV
    46  	GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (QueryResultsIterator, error)
    47  	// ExecuteQuery executes the given query and returns an iterator that contains results of type *VersionedKV.
    48  	ExecuteQuery(namespace, query string) (ResultsIterator, error)
    49  	// ExecuteQueryWithPagination executes the given query and
    50  	// returns an iterator that contains results of type *VersionedKV.
    51  	// The bookmark and page size parameters are associated with the pagination query.
    52  	ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (QueryResultsIterator, error)
    53  	// ApplyUpdates applies the batch to the underlying db.
    54  	// height is the height of the highest transaction in the Batch that
    55  	// a state db implementation is expected to ues as a save point
    56  	ApplyUpdates(batch *UpdateBatch, height *version.Height) error
    57  	// GetLatestSavePoint returns the height of the highest transaction upto which
    58  	// the state db is consistent
    59  	GetLatestSavePoint() (*version.Height, error)
    60  	// ValidateKeyValue tests whether the key and value is supported by the db implementation.
    61  	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
    62  	// TODO make the function ValidateKeyValue return a specific error say ErrInvalidKeyValue
    63  	// However, as of now, the both implementations of this function (leveldb and couchdb) are deterministic in returing an error
    64  	// i.e., an error is returned only if the key-value are found to be invalid for the underlying db
    65  	ValidateKeyValue(key string, value []byte) error
    66  	// BytesKeySupported returns true if the implementation (underlying db) supports the any bytes to be used as key.
    67  	// For instance, leveldb supports any bytes for the key while the couchdb supports only valid utf-8 string
    68  	BytesKeySupported() bool
    69  	// GetFullScanIterator returns a FullScanIterator that can be used to iterate over entire data in the statedb.
    70  	// `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator
    71  	// to skip one or more namespaces from the returned results. The second parameter returns the format information
    72  	// about the value bytes returned by the Next function in the returned FullScanIterator.
    73  	// The intended use of this iterator is to generate the snapshot files for the statedb.
    74  	GetFullScanIterator(skipNamespace func(string) bool) (FullScanIterator, byte, error)
    75  	// Open opens the db
    76  	Open() error
    77  	// Close closes the db
    78  	Close()
    79  }
    80  
    81  // NamespaceProvider provides a mean for statedb to get all the possible namespaces for a channel.
    82  // The intended use is for statecouchdb to retroactively build channel metadata when it is missing,
    83  // e.g., when opening a statecouchdb from v2.0/2.1 version.
    84  type NamespaceProvider interface {
    85  	// PossibleNamespaces returns all possible namespaces for the statedb. Note that it is a superset
    86  	// of the actual namespaces. Therefore, the caller should compare with the existing databases to
    87  	// filter out the namespaces that have no matched databases.
    88  	PossibleNamespaces(vdb VersionedDB) ([]string, error)
    89  }
    90  
    91  //BulkOptimizable interface provides additional functions for
    92  //databases capable of batch operations
    93  type BulkOptimizable interface {
    94  	LoadCommittedVersions(keys []*CompositeKey) error
    95  	GetCachedVersion(namespace, key string) (*version.Height, bool)
    96  	ClearCachedVersions()
    97  }
    98  
    99  //IndexCapable interface provides additional functions for
   100  //databases capable of index operations
   101  type IndexCapable interface {
   102  	GetDBType() string
   103  	ProcessIndexesForChaincodeDeploy(namespace string, indexFilesData map[string][]byte) error
   104  }
   105  
   106  // FullScanIterator provides a mean to iterate over entire statedb. The intended use of this iterator
   107  // is to generate the snapshot files for the statedb
   108  type FullScanIterator interface {
   109  	// Next returns the key-values in the lexical order of <Namespace, key>
   110  	// A particular statedb implementation is free to chose any deterministic bytes representation for the <version, value, metadata>
   111  	Next() (*CompositeKey, []byte, error)
   112  	// Close releases any resources held with the implementation
   113  	Close()
   114  }
   115  
   116  // CompositeKey encloses Namespace and Key components
   117  type CompositeKey struct {
   118  	Namespace string
   119  	Key       string
   120  }
   121  
   122  // VersionedValue encloses value and corresponding version
   123  type VersionedValue struct {
   124  	Value    []byte
   125  	Metadata []byte
   126  	Version  *version.Height
   127  }
   128  
   129  // IsDelete returns true if this update indicates delete of a key
   130  func (vv *VersionedValue) IsDelete() bool {
   131  	return vv.Value == nil
   132  }
   133  
   134  // VersionedKV encloses key and corresponding VersionedValue
   135  type VersionedKV struct {
   136  	CompositeKey
   137  	VersionedValue
   138  }
   139  
   140  // ResultsIterator iterates over query results
   141  type ResultsIterator interface {
   142  	Next() (QueryResult, error)
   143  	Close()
   144  }
   145  
   146  // QueryResultsIterator adds GetBookmarkAndClose method
   147  type QueryResultsIterator interface {
   148  	ResultsIterator
   149  	GetBookmarkAndClose() string
   150  }
   151  
   152  // QueryResult - a general interface for supporting different types of query results. Actual types differ for different queries
   153  type QueryResult interface{}
   154  
   155  type nsUpdates struct {
   156  	M map[string]*VersionedValue
   157  }
   158  
   159  func newNsUpdates() *nsUpdates {
   160  	return &nsUpdates{make(map[string]*VersionedValue)}
   161  }
   162  
   163  // UpdateBatch encloses the details of multiple `updates`
   164  type UpdateBatch struct {
   165  	ContainsPostOrderWrites bool
   166  	Updates                 map[string]*nsUpdates
   167  }
   168  
   169  // NewUpdateBatch constructs an instance of a Batch
   170  func NewUpdateBatch() *UpdateBatch {
   171  	return &UpdateBatch{false, make(map[string]*nsUpdates)}
   172  }
   173  
   174  // Get returns the VersionedValue for the given namespace and key
   175  func (batch *UpdateBatch) Get(ns string, key string) *VersionedValue {
   176  	nsUpdates, ok := batch.Updates[ns]
   177  	if !ok {
   178  		return nil
   179  	}
   180  	vv, ok := nsUpdates.M[key]
   181  	if !ok {
   182  		return nil
   183  	}
   184  	return vv
   185  }
   186  
   187  // Put adds a key with value only. The metadata is assumed to be nil
   188  func (batch *UpdateBatch) Put(ns string, key string, value []byte, version *version.Height) {
   189  	batch.PutValAndMetadata(ns, key, value, nil, version)
   190  }
   191  
   192  // PutValAndMetadata adds a key with value and metadata
   193  // TODO introducing a new function to limit the refactoring. Later in a separate CR, the 'Put' function above should be removed
   194  func (batch *UpdateBatch) PutValAndMetadata(ns string, key string, value []byte, metadata []byte, version *version.Height) {
   195  	if value == nil {
   196  		panic("Nil value not allowed. Instead call 'Delete' function")
   197  	}
   198  	batch.Update(ns, key, &VersionedValue{value, metadata, version})
   199  }
   200  
   201  // Delete deletes a Key and associated value
   202  func (batch *UpdateBatch) Delete(ns string, key string, version *version.Height) {
   203  	batch.Update(ns, key, &VersionedValue{nil, nil, version})
   204  }
   205  
   206  // Exists checks whether the given key exists in the batch
   207  func (batch *UpdateBatch) Exists(ns string, key string) bool {
   208  	nsUpdates, ok := batch.Updates[ns]
   209  	if !ok {
   210  		return false
   211  	}
   212  	_, ok = nsUpdates.M[key]
   213  	return ok
   214  }
   215  
   216  // GetUpdatedNamespaces returns the names of the namespaces that are updated
   217  func (batch *UpdateBatch) GetUpdatedNamespaces() []string {
   218  	namespaces := make([]string, len(batch.Updates))
   219  	i := 0
   220  	for ns := range batch.Updates {
   221  		namespaces[i] = ns
   222  		i++
   223  	}
   224  	return namespaces
   225  }
   226  
   227  // Update updates the batch with a latest entry for a namespace and a key
   228  func (batch *UpdateBatch) Update(ns string, key string, vv *VersionedValue) {
   229  	batch.getOrCreateNsUpdates(ns).M[key] = vv
   230  }
   231  
   232  // GetUpdates returns all the updates for a namespace
   233  func (batch *UpdateBatch) GetUpdates(ns string) map[string]*VersionedValue {
   234  	nsUpdates, ok := batch.Updates[ns]
   235  	if !ok {
   236  		return nil
   237  	}
   238  	return nsUpdates.M
   239  }
   240  
   241  // GetRangeScanIterator returns an iterator that iterates over keys of a specific namespace in sorted order
   242  // In other word this gives the same functionality over the contents in the `UpdateBatch` as
   243  // `VersionedDB.GetStateRangeScanIterator()` method gives over the contents in the statedb
   244  // This function can be used for querying the contents in the updateBatch before they are committed to the statedb.
   245  // For instance, a validator implementation can used this to verify the validity of a range query of a transaction
   246  // where the UpdateBatch represents the union of the modifications performed by the preceding valid transactions in the same block
   247  // (Assuming Group commit approach where we commit all the updates caused by a block together).
   248  func (batch *UpdateBatch) GetRangeScanIterator(ns string, startKey string, endKey string) QueryResultsIterator {
   249  	return newNsIterator(ns, startKey, endKey, batch)
   250  }
   251  
   252  // Merge merges another updates batch with this updates batch
   253  func (batch *UpdateBatch) Merge(toMerge *UpdateBatch) {
   254  	batch.ContainsPostOrderWrites = batch.ContainsPostOrderWrites || toMerge.ContainsPostOrderWrites
   255  	for ns, nsUpdates := range toMerge.Updates {
   256  		for key, vv := range nsUpdates.M {
   257  			batch.Update(ns, key, vv)
   258  		}
   259  	}
   260  }
   261  
   262  func (batch *UpdateBatch) getOrCreateNsUpdates(ns string) *nsUpdates {
   263  	nsUpdates := batch.Updates[ns]
   264  	if nsUpdates == nil {
   265  		nsUpdates = newNsUpdates()
   266  		batch.Updates[ns] = nsUpdates
   267  	}
   268  	return nsUpdates
   269  }
   270  
   271  type nsIterator struct {
   272  	ns         string
   273  	nsUpdates  *nsUpdates
   274  	sortedKeys []string
   275  	nextIndex  int
   276  	lastIndex  int
   277  }
   278  
   279  func newNsIterator(ns string, startKey string, endKey string, batch *UpdateBatch) *nsIterator {
   280  	nsUpdates, ok := batch.Updates[ns]
   281  	if !ok {
   282  		return &nsIterator{}
   283  	}
   284  	sortedKeys := util.GetSortedKeys(nsUpdates.M)
   285  	var nextIndex int
   286  	var lastIndex int
   287  	if startKey == "" {
   288  		nextIndex = 0
   289  	} else {
   290  		nextIndex = sort.SearchStrings(sortedKeys, startKey)
   291  	}
   292  	if endKey == "" {
   293  		lastIndex = len(sortedKeys)
   294  	} else {
   295  		lastIndex = sort.SearchStrings(sortedKeys, endKey)
   296  	}
   297  	return &nsIterator{ns, nsUpdates, sortedKeys, nextIndex, lastIndex}
   298  }
   299  
   300  // Next gives next key and versioned value. It returns a nil when exhausted
   301  func (itr *nsIterator) Next() (QueryResult, error) {
   302  	if itr.nextIndex >= itr.lastIndex {
   303  		return nil, nil
   304  	}
   305  	key := itr.sortedKeys[itr.nextIndex]
   306  	vv := itr.nsUpdates.M[key]
   307  	itr.nextIndex++
   308  	return &VersionedKV{CompositeKey{itr.ns, key}, VersionedValue{vv.Value, vv.Metadata, vv.Version}}, nil
   309  }
   310  
   311  // Close implements the method from QueryResult interface
   312  func (itr *nsIterator) Close() {
   313  	// do nothing
   314  }
   315  
   316  // GetBookmarkAndClose implements the method from QueryResult interface
   317  func (itr *nsIterator) GetBookmarkAndClose() string {
   318  	// do nothing
   319  	return ""
   320  }