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

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package statecouchdb
     8  
     9  import (
    10  	"bytes"
    11  	"context"
    12  	"encoding/json"
    13  	"sort"
    14  	"sync"
    15  
    16  	"github.com/osdi23p228/fabric/common/flogging"
    17  	"github.com/osdi23p228/fabric/common/ledger/dataformat"
    18  	"github.com/osdi23p228/fabric/common/metrics"
    19  	"github.com/osdi23p228/fabric/core/ledger"
    20  	"github.com/osdi23p228/fabric/core/ledger/internal/version"
    21  	"github.com/osdi23p228/fabric/core/ledger/kvledger/txmgmt/statedb"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  var logger = flogging.MustGetLogger("statecouchdb")
    26  
    27  const (
    28  	// savepointDocID is used as a key for maintaining savepoint (maintained in metadatadb for a channel)
    29  	savepointDocID = "statedb_savepoint"
    30  	// channelMetadataDocID is used as a key to store the channel metadata for a channel (maintained in the channel's metadatadb).
    31  	// Due to CouchDB's length restriction on db names, channel names and namepsaces may be truncated in db names.
    32  	// The metadata is used for dropping channel-specific databases and snapshot support.
    33  	channelMetadataDocID = "channel_metadata"
    34  	// fabricInternalDBName is used to create a db in couch that would be used for internal data such as the version of the data format
    35  	// a double underscore ensures that the dbname does not clash with the dbnames created for the chaincodes
    36  	fabricInternalDBName = "fabric__internal"
    37  	// dataformatVersionDocID is used as a key for maintaining version of the data format (maintained in fabric internal db)
    38  	dataformatVersionDocID      = "dataformatVersion"
    39  	fullScanIteratorValueFormat = byte(1)
    40  )
    41  
    42  // VersionedDBProvider implements interface VersionedDBProvider
    43  type VersionedDBProvider struct {
    44  	couchInstance      *couchInstance
    45  	databases          map[string]*VersionedDB
    46  	mux                sync.Mutex
    47  	openCounts         uint64
    48  	redoLoggerProvider *redoLoggerProvider
    49  	cache              *cache
    50  }
    51  
    52  // NewVersionedDBProvider instantiates VersionedDBProvider
    53  func NewVersionedDBProvider(config *ledger.CouchDBConfig, metricsProvider metrics.Provider, sysNamespaces []string) (*VersionedDBProvider, error) {
    54  	logger.Debugf("constructing CouchDB VersionedDBProvider")
    55  	couchInstance, err := createCouchInstance(config, metricsProvider)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  	if err := checkExpectedDataformatVersion(couchInstance); err != nil {
    60  		return nil, err
    61  	}
    62  	p, err := newRedoLoggerProvider(config.RedoLogPath)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	cache := newCache(config.UserCacheSizeMBs, sysNamespaces)
    68  	return &VersionedDBProvider{
    69  			couchInstance:      couchInstance,
    70  			databases:          make(map[string]*VersionedDB),
    71  			mux:                sync.Mutex{},
    72  			openCounts:         0,
    73  			redoLoggerProvider: p,
    74  			cache:              cache,
    75  		},
    76  		nil
    77  }
    78  
    79  func checkExpectedDataformatVersion(couchInstance *couchInstance) error {
    80  	databasesToIgnore := []string{fabricInternalDBName}
    81  	isEmpty, err := couchInstance.isEmpty(databasesToIgnore)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	if isEmpty {
    86  		logger.Debugf("couch instance is empty. Setting dataformat version to %s", dataformat.CurrentFormat)
    87  		return writeDataFormatVersion(couchInstance, dataformat.CurrentFormat)
    88  	}
    89  	dataformatVersion, err := readDataformatVersion(couchInstance)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	if dataformatVersion != dataformat.CurrentFormat {
    94  		return &dataformat.ErrFormatMismatch{
    95  			DBInfo:         "CouchDB for state database",
    96  			ExpectedFormat: dataformat.CurrentFormat,
    97  			Format:         dataformatVersion,
    98  		}
    99  	}
   100  	return nil
   101  }
   102  
   103  func readDataformatVersion(couchInstance *couchInstance) (string, error) {
   104  	db, err := createCouchDatabase(couchInstance, fabricInternalDBName)
   105  	if err != nil {
   106  		return "", err
   107  	}
   108  	doc, _, err := db.readDoc(dataformatVersionDocID)
   109  	logger.Debugf("dataformatVersionDoc = %s", doc)
   110  	if err != nil || doc == nil {
   111  		return "", err
   112  	}
   113  	return decodeDataformatInfo(doc)
   114  }
   115  
   116  func writeDataFormatVersion(couchInstance *couchInstance, dataformatVersion string) error {
   117  	db, err := createCouchDatabase(couchInstance, fabricInternalDBName)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	doc, err := encodeDataformatInfo(dataformatVersion)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	_, err = db.saveDoc(dataformatVersionDocID, "", doc)
   126  	return err
   127  }
   128  
   129  // GetDBHandle gets the handle to a named database
   130  func (provider *VersionedDBProvider) GetDBHandle(dbName string, nsProvider statedb.NamespaceProvider) (statedb.VersionedDB, error) {
   131  	provider.mux.Lock()
   132  	defer provider.mux.Unlock()
   133  	vdb := provider.databases[dbName]
   134  	if vdb == nil {
   135  		var err error
   136  		vdb, err = newVersionedDB(
   137  			provider.couchInstance,
   138  			provider.redoLoggerProvider.newRedoLogger(dbName),
   139  			dbName,
   140  			provider.cache,
   141  			nsProvider,
   142  		)
   143  		if err != nil {
   144  			return nil, err
   145  		}
   146  		provider.databases[dbName] = vdb
   147  	}
   148  	return vdb, nil
   149  }
   150  
   151  // Close closes the underlying db instance
   152  func (provider *VersionedDBProvider) Close() {
   153  	// No close needed on Couch
   154  	provider.redoLoggerProvider.close()
   155  }
   156  
   157  // HealthCheck checks to see if the couch instance of the peer is healthy
   158  func (provider *VersionedDBProvider) HealthCheck(ctx context.Context) error {
   159  	return provider.couchInstance.healthCheck(ctx)
   160  }
   161  
   162  // VersionedDB implements VersionedDB interface
   163  type VersionedDB struct {
   164  	couchInstance      *couchInstance
   165  	metadataDB         *couchDatabase            // A database per channel to store metadata such as savepoint.
   166  	chainName          string                    // The name of the chain/channel.
   167  	namespaceDBs       map[string]*couchDatabase // One database per namespace.
   168  	channelMetadata    *channelMetadata          // Store channel name and namespaceDBInfo
   169  	committedDataCache *versionsCache            // Used as a local cache during bulk processing of a block.
   170  	verCacheLock       sync.RWMutex
   171  	mux                sync.RWMutex
   172  	redoLogger         *redoLogger
   173  	cache              *cache
   174  }
   175  
   176  // newVersionedDB constructs an instance of VersionedDB
   177  func newVersionedDB(couchInstance *couchInstance, redoLogger *redoLogger, dbName string, cache *cache, nsProvider statedb.NamespaceProvider) (*VersionedDB, error) {
   178  	// CreateCouchDatabase creates a CouchDB database object, as well as the underlying database if it does not exist
   179  	chainName := dbName
   180  	dbName = constructMetadataDBName(dbName)
   181  
   182  	metadataDB, err := createCouchDatabase(couchInstance, dbName)
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	namespaceDBMap := make(map[string]*couchDatabase)
   187  	vdb := &VersionedDB{
   188  		couchInstance:      couchInstance,
   189  		metadataDB:         metadataDB,
   190  		chainName:          chainName,
   191  		namespaceDBs:       namespaceDBMap,
   192  		committedDataCache: newVersionCache(),
   193  		redoLogger:         redoLogger,
   194  		cache:              cache,
   195  	}
   196  
   197  	logger.Debugf("chain [%s]: checking for redolog record", chainName)
   198  	redologRecord, err := redoLogger.load()
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	savepoint, err := vdb.GetLatestSavePoint()
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	isNewDB := savepoint == nil
   208  	if err = vdb.initChannelMetadata(isNewDB, nsProvider); err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	// in normal circumstances, redolog is expected to be either equal to the last block
   213  	// committed to the statedb or one ahead (in the event of a crash). However, either of
   214  	// these or both could be nil on first time start (fresh start/rebuild)
   215  	if redologRecord == nil || savepoint == nil {
   216  		logger.Debugf("chain [%s]: No redo-record or save point present", chainName)
   217  		return vdb, nil
   218  	}
   219  
   220  	logger.Debugf("chain [%s]: save point = %#v, version of redolog record = %#v",
   221  		chainName, savepoint, redologRecord.Version)
   222  
   223  	if redologRecord.Version.BlockNum-savepoint.BlockNum == 1 {
   224  		logger.Debugf("chain [%s]: Re-applying last batch", chainName)
   225  		if err := vdb.applyUpdates(redologRecord.UpdateBatch, redologRecord.Version); err != nil {
   226  			return nil, err
   227  		}
   228  	}
   229  	return vdb, nil
   230  }
   231  
   232  // getNamespaceDBHandle gets the handle to a named chaincode database
   233  func (vdb *VersionedDB) getNamespaceDBHandle(namespace string) (*couchDatabase, error) {
   234  	vdb.mux.RLock()
   235  	db := vdb.namespaceDBs[namespace]
   236  	vdb.mux.RUnlock()
   237  	if db != nil {
   238  		return db, nil
   239  	}
   240  	namespaceDBName := constructNamespaceDBName(vdb.chainName, namespace)
   241  	vdb.mux.Lock()
   242  	defer vdb.mux.Unlock()
   243  	db = vdb.namespaceDBs[namespace]
   244  	if db == nil {
   245  		var err error
   246  		if _, ok := vdb.channelMetadata.NamespaceDBsInfo[namespace]; !ok {
   247  			logger.Debugf("[%s] add namespaceDBInfo for namespace %s", vdb.chainName, namespace)
   248  			vdb.channelMetadata.NamespaceDBsInfo[namespace] = &namespaceDBInfo{
   249  				Namespace: namespace,
   250  				DBName:    namespaceDBName,
   251  			}
   252  			if err = vdb.writeChannelMetadata(); err != nil {
   253  				return nil, err
   254  			}
   255  		}
   256  		db, err = createCouchDatabase(vdb.couchInstance, namespaceDBName)
   257  		if err != nil {
   258  			return nil, err
   259  		}
   260  		vdb.namespaceDBs[namespace] = db
   261  	}
   262  	return db, nil
   263  }
   264  
   265  // ProcessIndexesForChaincodeDeploy creates indexes for a specified namespace
   266  func (vdb *VersionedDB) ProcessIndexesForChaincodeDeploy(namespace string, indexFilesData map[string][]byte) error {
   267  	db, err := vdb.getNamespaceDBHandle(namespace)
   268  	if err != nil {
   269  		return err
   270  	}
   271  	// We need to satisfy two requirements while processing the index files.
   272  	// R1: all valid indexes should be processed.
   273  	// R2: the order of index creation must be the same in all peers. For example, if user
   274  	// passes two index files with the same index name but different index fields and we
   275  	// process these files in different orders in different peers, each peer would
   276  	// have different indexes (as one index definion could replace another if the index names
   277  	// are the same).
   278  	// To satisfy R1, we log the error and continue to process the next index file.
   279  	// To satisfy R2, we sort the indexFilesData map based on the filenames and process
   280  	// each index as per the sorted order.
   281  	var indexFilesName []string
   282  	for fileName := range indexFilesData {
   283  		indexFilesName = append(indexFilesName, fileName)
   284  	}
   285  	sort.Strings(indexFilesName)
   286  	for _, fileName := range indexFilesName {
   287  		_, err = db.createIndex(string(indexFilesData[fileName]))
   288  		switch {
   289  		case err != nil:
   290  			logger.Errorf("error creating index from file [%s] for chaincode [%s] on channel [%s]: %+v",
   291  				fileName, namespace, vdb.chainName, err)
   292  		default:
   293  			logger.Infof("successfully submitted index creation request present in the file [%s] for chaincode [%s] on channel [%s]",
   294  				fileName, namespace, vdb.chainName)
   295  		}
   296  	}
   297  	return nil
   298  }
   299  
   300  // GetDBType returns the hosted stateDB
   301  func (vdb *VersionedDB) GetDBType() string {
   302  	return "couchdb"
   303  }
   304  
   305  // LoadCommittedVersions populates committedVersions and revisionNumbers into cache.
   306  // A bulk retrieve from couchdb is used to populate the cache.
   307  // committedVersions cache will be used for state validation of readsets
   308  // revisionNumbers cache will be used during commit phase for couchdb bulk updates
   309  func (vdb *VersionedDB) LoadCommittedVersions(keys []*statedb.CompositeKey) error {
   310  	missingKeys := map[string][]string{}
   311  	committedDataCache := newVersionCache()
   312  	for _, compositeKey := range keys {
   313  		ns, key := compositeKey.Namespace, compositeKey.Key
   314  		committedDataCache.setVerAndRev(ns, key, nil, "")
   315  		logger.Debugf("Load into version cache: %s~%s", ns, key)
   316  
   317  		if !vdb.cache.enabled(ns) {
   318  			missingKeys[ns] = append(missingKeys[ns], key)
   319  			continue
   320  		}
   321  		cv, err := vdb.cache.getState(vdb.chainName, ns, key)
   322  		if err != nil {
   323  			return err
   324  		}
   325  		if cv == nil {
   326  			missingKeys[ns] = append(missingKeys[ns], key)
   327  			continue
   328  		}
   329  		vv, err := constructVersionedValue(cv)
   330  		if err != nil {
   331  			return err
   332  		}
   333  		rev := string(cv.AdditionalInfo)
   334  		committedDataCache.setVerAndRev(ns, key, vv.Version, rev)
   335  	}
   336  
   337  	nsMetadataMap, err := vdb.retrieveMetadata(missingKeys)
   338  	logger.Debugf("missingKeys=%s", missingKeys)
   339  	logger.Debugf("nsMetadataMap=%s", nsMetadataMap)
   340  	if err != nil {
   341  		return err
   342  	}
   343  	for ns, nsMetadata := range nsMetadataMap {
   344  		for _, keyMetadata := range nsMetadata {
   345  			// TODO - why would version be ever zero if loaded from db?
   346  			if len(keyMetadata.Version) != 0 {
   347  				version, _, err := decodeVersionAndMetadata(keyMetadata.Version)
   348  				if err != nil {
   349  					return err
   350  				}
   351  				committedDataCache.setVerAndRev(ns, keyMetadata.ID, version, keyMetadata.Rev)
   352  			}
   353  		}
   354  	}
   355  	vdb.verCacheLock.Lock()
   356  	defer vdb.verCacheLock.Unlock()
   357  	vdb.committedDataCache = committedDataCache
   358  	return nil
   359  }
   360  
   361  // GetVersion implements method in VersionedDB interface
   362  func (vdb *VersionedDB) GetVersion(namespace string, key string) (*version.Height, error) {
   363  	version, keyFound := vdb.GetCachedVersion(namespace, key)
   364  	if !keyFound {
   365  		// This if block get executed only during simulation because during commit
   366  		// we always call `LoadCommittedVersions` before calling `GetVersion`
   367  		vv, err := vdb.GetState(namespace, key)
   368  		if err != nil || vv == nil {
   369  			return nil, err
   370  		}
   371  		version = vv.Version
   372  	}
   373  	return version, nil
   374  }
   375  
   376  // GetCachedVersion returns version from cache. `LoadCommittedVersions` function populates the cache
   377  func (vdb *VersionedDB) GetCachedVersion(namespace string, key string) (*version.Height, bool) {
   378  	logger.Debugf("Retrieving cached version: %s~%s", key, namespace)
   379  	vdb.verCacheLock.RLock()
   380  	defer vdb.verCacheLock.RUnlock()
   381  	return vdb.committedDataCache.getVersion(namespace, key)
   382  }
   383  
   384  // ValidateKeyValue implements method in VersionedDB interface
   385  func (vdb *VersionedDB) ValidateKeyValue(key string, value []byte) error {
   386  	err := validateKey(key)
   387  	if err != nil {
   388  		return err
   389  	}
   390  	return validateValue(value)
   391  }
   392  
   393  // BytesKeySupported implements method in VersionvdbedDB interface
   394  func (vdb *VersionedDB) BytesKeySupported() bool {
   395  	return false
   396  }
   397  
   398  // GetState implements method in VersionedDB interface
   399  func (vdb *VersionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) {
   400  	logger.Debugf("GetState(). ns=%s, key=%s", namespace, key)
   401  
   402  	// (1) read the KV from the cache if available
   403  	cacheEnabled := vdb.cache.enabled(namespace)
   404  	if cacheEnabled {
   405  		cv, err := vdb.cache.getState(vdb.chainName, namespace, key)
   406  		if err != nil {
   407  			return nil, err
   408  		}
   409  		if cv != nil {
   410  			vv, err := constructVersionedValue(cv)
   411  			if err != nil {
   412  				return nil, err
   413  			}
   414  			return vv, nil
   415  		}
   416  	}
   417  
   418  	// (2) read from the database if cache miss occurs
   419  	kv, err := vdb.readFromDB(namespace, key)
   420  	if err != nil {
   421  		return nil, err
   422  	}
   423  	if kv == nil {
   424  		return nil, nil
   425  	}
   426  
   427  	// (3) if the value is not nil, store in the cache
   428  	if cacheEnabled {
   429  		cacheValue := constructCacheValue(kv.VersionedValue, kv.revision)
   430  		if err := vdb.cache.putState(vdb.chainName, namespace, key, cacheValue); err != nil {
   431  			return nil, err
   432  		}
   433  	}
   434  
   435  	return kv.VersionedValue, nil
   436  }
   437  
   438  func (vdb *VersionedDB) readFromDB(namespace, key string) (*keyValue, error) {
   439  	db, err := vdb.getNamespaceDBHandle(namespace)
   440  	if err != nil {
   441  		return nil, err
   442  	}
   443  	if err := validateKey(key); err != nil {
   444  		return nil, err
   445  	}
   446  	couchDoc, _, err := db.readDoc(key)
   447  	if err != nil {
   448  		return nil, err
   449  	}
   450  	if couchDoc == nil {
   451  		return nil, nil
   452  	}
   453  	kv, err := couchDocToKeyValue(couchDoc)
   454  	if err != nil {
   455  		return nil, err
   456  	}
   457  	return kv, nil
   458  }
   459  
   460  // GetStateMultipleKeys implements method in VersionedDB interface
   461  func (vdb *VersionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) {
   462  	vals := make([]*statedb.VersionedValue, len(keys))
   463  	for i, key := range keys {
   464  		val, err := vdb.GetState(namespace, key)
   465  		if err != nil {
   466  			return nil, err
   467  		}
   468  		vals[i] = val
   469  	}
   470  	return vals, nil
   471  }
   472  
   473  // GetStateRangeScanIterator implements method in VersionedDB interface
   474  // startKey is inclusive
   475  // endKey is exclusive
   476  func (vdb *VersionedDB) GetStateRangeScanIterator(namespace string, startKey string, endKey string) (statedb.ResultsIterator, error) {
   477  	return vdb.GetStateRangeScanIteratorWithPagination(namespace, startKey, endKey, 0)
   478  }
   479  
   480  // GetStateRangeScanIteratorWithPagination implements method in VersionedDB interface
   481  // startKey is inclusive
   482  // endKey is exclusive
   483  // pageSize limits the number of results returned
   484  func (vdb *VersionedDB) GetStateRangeScanIteratorWithPagination(namespace string, startKey string, endKey string, pageSize int32) (statedb.QueryResultsIterator, error) {
   485  	logger.Debugf("Entering GetStateRangeScanIteratorWithPagination namespace: %s  startKey: %s  endKey: %s  pageSize: %d", namespace, startKey, endKey, pageSize)
   486  	internalQueryLimit := vdb.couchInstance.internalQueryLimit()
   487  	db, err := vdb.getNamespaceDBHandle(namespace)
   488  	if err != nil {
   489  		return nil, err
   490  	}
   491  	return newQueryScanner(namespace, db, "", internalQueryLimit, pageSize, "", startKey, endKey)
   492  }
   493  
   494  func (scanner *queryScanner) getNextStateRangeScanResults() error {
   495  	queryLimit := scanner.queryDefinition.internalQueryLimit
   496  	if scanner.paginationInfo.requestedLimit > 0 {
   497  		moreResultsNeeded := scanner.paginationInfo.requestedLimit - scanner.resultsInfo.totalRecordsReturned
   498  		if moreResultsNeeded < scanner.queryDefinition.internalQueryLimit {
   499  			queryLimit = moreResultsNeeded
   500  		}
   501  	}
   502  	queryResult, nextStartKey, err := rangeScanFilterCouchInternalDocs(scanner.db,
   503  		scanner.queryDefinition.startKey, scanner.queryDefinition.endKey, queryLimit)
   504  	if err != nil {
   505  		return err
   506  	}
   507  	scanner.resultsInfo.results = queryResult
   508  	scanner.paginationInfo.cursor = 0
   509  	if scanner.queryDefinition.endKey == nextStartKey {
   510  		// as we always set inclusive_end=false to match the behavior of
   511  		// goleveldb iterator, it is safe to mark the scanner as exhausted
   512  		scanner.exhausted = true
   513  		// we still need to update the startKey as it is returned as bookmark
   514  	}
   515  	scanner.queryDefinition.startKey = nextStartKey
   516  	return nil
   517  }
   518  
   519  func rangeScanFilterCouchInternalDocs(db *couchDatabase,
   520  	startKey, endKey string, queryLimit int32,
   521  ) ([]*queryResult, string, error) {
   522  	var finalResults []*queryResult
   523  	var finalNextStartKey string
   524  	for {
   525  		results, nextStartKey, err := db.readDocRange(startKey, endKey, queryLimit)
   526  		if err != nil {
   527  			logger.Debugf("Error calling ReadDocRange(): %s\n", err.Error())
   528  			return nil, "", err
   529  		}
   530  		var filteredResults []*queryResult
   531  		for _, doc := range results {
   532  			if !isCouchInternalKey(doc.id) {
   533  				filteredResults = append(filteredResults, doc)
   534  			}
   535  		}
   536  
   537  		finalResults = append(finalResults, filteredResults...)
   538  		finalNextStartKey = nextStartKey
   539  		queryLimit = int32(len(results) - len(filteredResults))
   540  		if queryLimit == 0 || finalNextStartKey == "" {
   541  			break
   542  		}
   543  		startKey = finalNextStartKey
   544  	}
   545  	var err error
   546  	for i := 0; isCouchInternalKey(finalNextStartKey); i++ {
   547  		_, finalNextStartKey, err = db.readDocRange(finalNextStartKey, endKey, 1)
   548  		logger.Debugf("i=%d, finalNextStartKey=%s", i, finalNextStartKey)
   549  		if err != nil {
   550  			return nil, "", err
   551  		}
   552  	}
   553  	return finalResults, finalNextStartKey, nil
   554  }
   555  
   556  func isCouchInternalKey(key string) bool {
   557  	return len(key) != 0 && key[0] == '_'
   558  }
   559  
   560  // ExecuteQuery implements method in VersionedDB interface
   561  func (vdb *VersionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) {
   562  	queryResult, err := vdb.ExecuteQueryWithPagination(namespace, query, "", 0)
   563  	if err != nil {
   564  		return nil, err
   565  	}
   566  	return queryResult, nil
   567  }
   568  
   569  // ExecuteQueryWithPagination implements method in VersionedDB interface
   570  func (vdb *VersionedDB) ExecuteQueryWithPagination(namespace, query, bookmark string, pageSize int32) (statedb.QueryResultsIterator, error) {
   571  	logger.Debugf("Entering ExecuteQueryWithPagination namespace: %s,  query: %s,  bookmark: %s, pageSize: %d", namespace, query, bookmark, pageSize)
   572  	internalQueryLimit := vdb.couchInstance.internalQueryLimit()
   573  	queryString, err := applyAdditionalQueryOptions(query, internalQueryLimit, bookmark)
   574  	if err != nil {
   575  		logger.Errorf("Error calling applyAdditionalQueryOptions(): %s", err.Error())
   576  		return nil, err
   577  	}
   578  	db, err := vdb.getNamespaceDBHandle(namespace)
   579  	if err != nil {
   580  		return nil, err
   581  	}
   582  	return newQueryScanner(namespace, db, queryString, internalQueryLimit, pageSize, bookmark, "", "")
   583  }
   584  
   585  // executeQueryWithBookmark executes a "paging" query with a bookmark, this method allows a
   586  // paged query without returning a new query iterator
   587  func (scanner *queryScanner) executeQueryWithBookmark() error {
   588  	queryLimit := scanner.queryDefinition.internalQueryLimit
   589  	if scanner.paginationInfo.requestedLimit > 0 {
   590  		if scanner.paginationInfo.requestedLimit-scanner.resultsInfo.totalRecordsReturned < scanner.queryDefinition.internalQueryLimit {
   591  			queryLimit = scanner.paginationInfo.requestedLimit - scanner.resultsInfo.totalRecordsReturned
   592  		}
   593  	}
   594  	queryString, err := applyAdditionalQueryOptions(scanner.queryDefinition.query,
   595  		queryLimit, scanner.paginationInfo.bookmark)
   596  	if err != nil {
   597  		logger.Debugf("Error calling applyAdditionalQueryOptions(): %s\n", err.Error())
   598  		return err
   599  	}
   600  	queryResult, bookmark, err := scanner.db.queryDocuments(queryString)
   601  	if err != nil {
   602  		logger.Debugf("Error calling QueryDocuments(): %s\n", err.Error())
   603  		return err
   604  	}
   605  	scanner.resultsInfo.results = queryResult
   606  	scanner.paginationInfo.bookmark = bookmark
   607  	scanner.paginationInfo.cursor = 0
   608  	return nil
   609  }
   610  
   611  // ApplyUpdates implements method in VersionedDB interface
   612  func (vdb *VersionedDB) ApplyUpdates(updates *statedb.UpdateBatch, height *version.Height) error {
   613  	if height != nil && updates.ContainsPostOrderWrites {
   614  		// height is passed nil when committing missing private data for previously committed blocks
   615  		r := &redoRecord{
   616  			UpdateBatch: updates,
   617  			Version:     height,
   618  		}
   619  		if err := vdb.redoLogger.persist(r); err != nil {
   620  			return err
   621  		}
   622  	}
   623  	return vdb.applyUpdates(updates, height)
   624  }
   625  
   626  func (vdb *VersionedDB) applyUpdates(updates *statedb.UpdateBatch, height *version.Height) error {
   627  	// TODO a note about https://jira.hyperledger.org/browse/FAB-8622
   628  	// The write lock is needed only for the stage 2.
   629  
   630  	// stage 1 - buildCommitters builds committers per namespace (per DB). Each committer transforms the
   631  	// given batch in the form of underlying db and keep it in memory.
   632  	committers, err := vdb.buildCommitters(updates)
   633  	if err != nil {
   634  		return err
   635  	}
   636  
   637  	// stage 2 -- executeCommitter executes each committer to push the changes to the DB
   638  	if err = vdb.executeCommitter(committers); err != nil {
   639  		return err
   640  	}
   641  
   642  	// Stgae 3 - postCommitProcessing - flush and record savepoint.
   643  	namespaces := updates.GetUpdatedNamespaces()
   644  	if err := vdb.postCommitProcessing(committers, namespaces, height); err != nil {
   645  		return err
   646  	}
   647  
   648  	return nil
   649  }
   650  
   651  func (vdb *VersionedDB) postCommitProcessing(committers []*committer, namespaces []string, height *version.Height) error {
   652  	var wg sync.WaitGroup
   653  
   654  	wg.Add(1)
   655  	errChan := make(chan error, 1)
   656  	defer close(errChan)
   657  	go func() {
   658  		defer wg.Done()
   659  
   660  		cacheUpdates := make(cacheUpdates)
   661  		for _, c := range committers {
   662  			if !c.cacheEnabled {
   663  				continue
   664  			}
   665  			cacheUpdates.add(c.namespace, c.cacheKVs)
   666  		}
   667  
   668  		if len(cacheUpdates) == 0 {
   669  			return
   670  		}
   671  
   672  		// update the cache
   673  		if err := vdb.cache.UpdateStates(vdb.chainName, cacheUpdates); err != nil {
   674  			vdb.cache.Reset()
   675  			errChan <- err
   676  		}
   677  
   678  	}()
   679  
   680  	for _, ns := range namespaces {
   681  		db, err := vdb.getNamespaceDBHandle(ns)
   682  		if err != nil {
   683  			return err
   684  		}
   685  		if db.couchInstance.conf.WarmIndexesAfterNBlocks > 0 {
   686  			if db.indexWarmCounter >= db.couchInstance.conf.WarmIndexesAfterNBlocks {
   687  				go db.runWarmIndexAllIndexes()
   688  				db.indexWarmCounter = 0
   689  			}
   690  			db.indexWarmCounter++
   691  		}
   692  	}
   693  
   694  	// Record a savepoint at a given height
   695  	if err := vdb.recordSavepoint(height); err != nil {
   696  		logger.Errorf("Error during recordSavepoint: %s", err.Error())
   697  		return err
   698  	}
   699  
   700  	wg.Wait()
   701  	select {
   702  	case err := <-errChan:
   703  		return errors.WithStack(err)
   704  	default:
   705  		return nil
   706  	}
   707  }
   708  
   709  // ClearCachedVersions clears committedVersions and revisionNumbers
   710  func (vdb *VersionedDB) ClearCachedVersions() {
   711  	logger.Debugf("Clear Cache")
   712  	vdb.verCacheLock.Lock()
   713  	defer vdb.verCacheLock.Unlock()
   714  	vdb.committedDataCache = newVersionCache()
   715  }
   716  
   717  // Open implements method in VersionedDB interface
   718  func (vdb *VersionedDB) Open() error {
   719  	// no need to open db since a shared couch instance is used
   720  	return nil
   721  }
   722  
   723  // Close implements method in VersionedDB interface
   724  func (vdb *VersionedDB) Close() {
   725  	// no need to close db since a shared couch instance is used
   726  }
   727  
   728  // recordSavepoint records a savepoint in the metadata db for the channel.
   729  func (vdb *VersionedDB) recordSavepoint(height *version.Height) error {
   730  	// If a given height is nil, it denotes that we are committing pvt data of old blocks.
   731  	// In this case, we should not store a savepoint for recovery. The lastUpdatedOldBlockList
   732  	// in the pvtstore acts as a savepoint for pvt data.
   733  	if height == nil {
   734  		return nil
   735  	}
   736  	savepointCouchDoc, err := encodeSavepoint(height)
   737  	if err != nil {
   738  		return err
   739  	}
   740  	_, err = vdb.metadataDB.saveDoc(savepointDocID, "", savepointCouchDoc)
   741  	if err != nil {
   742  		logger.Errorf("Failed to save the savepoint to DB %s", err.Error())
   743  		return err
   744  	}
   745  	return nil
   746  }
   747  
   748  // GetLatestSavePoint implements method in VersionedDB interface
   749  func (vdb *VersionedDB) GetLatestSavePoint() (*version.Height, error) {
   750  	var err error
   751  	couchDoc, _, err := vdb.metadataDB.readDoc(savepointDocID)
   752  	if err != nil {
   753  		logger.Errorf("Failed to read savepoint data %s", err.Error())
   754  		return nil, err
   755  	}
   756  	// ReadDoc() not found (404) will result in nil response, in these cases return height nil
   757  	if couchDoc == nil || couchDoc.jsonValue == nil {
   758  		return nil, nil
   759  	}
   760  	return decodeSavepoint(couchDoc)
   761  }
   762  
   763  // initChannelMetadata initizlizes channelMetadata and build NamespaceDBInfo mapping if not present
   764  func (vdb *VersionedDB) initChannelMetadata(isNewDB bool, namespaceProvider statedb.NamespaceProvider) error {
   765  	// create channelMetadata with empty NamespaceDBInfo mapping for a new DB
   766  	if isNewDB {
   767  		vdb.channelMetadata = &channelMetadata{
   768  			ChannelName:      vdb.chainName,
   769  			NamespaceDBsInfo: make(map[string]*namespaceDBInfo),
   770  		}
   771  		return vdb.writeChannelMetadata()
   772  	}
   773  
   774  	// read stored channelMetadata from an existing DB
   775  	var err error
   776  	vdb.channelMetadata, err = vdb.readChannelMetadata()
   777  	if vdb.channelMetadata != nil || err != nil {
   778  		return err
   779  	}
   780  
   781  	// channelMetadata is not present - this is the case when opening older dbs (e.g., v2.0/v2.1) for the first time
   782  	// create channelMetadata and build NamespaceDBInfo mapping retroactively
   783  	vdb.channelMetadata = &channelMetadata{
   784  		ChannelName:      vdb.chainName,
   785  		NamespaceDBsInfo: make(map[string]*namespaceDBInfo),
   786  	}
   787  	// retrieve existing DB names
   788  	dbNames, err := vdb.couchInstance.retrieveApplicationDBNames()
   789  	if err != nil {
   790  		return err
   791  	}
   792  	existingDBNames := make(map[string]struct{}, len(dbNames))
   793  	for _, dbName := range dbNames {
   794  		existingDBNames[dbName] = struct{}{}
   795  	}
   796  	// get namespaces and add a namespace to channelMetadata only if its DB name already exists
   797  	namespaces, err := namespaceProvider.PossibleNamespaces(vdb)
   798  	if err != nil {
   799  		return err
   800  	}
   801  	for _, ns := range namespaces {
   802  		dbName := constructNamespaceDBName(vdb.chainName, ns)
   803  		if _, ok := existingDBNames[dbName]; ok {
   804  			vdb.channelMetadata.NamespaceDBsInfo[ns] = &namespaceDBInfo{
   805  				Namespace: ns,
   806  				DBName:    dbName,
   807  			}
   808  		}
   809  	}
   810  	return vdb.writeChannelMetadata()
   811  }
   812  
   813  // readChannelMetadata returns channel metadata stored in metadataDB
   814  func (vdb *VersionedDB) readChannelMetadata() (*channelMetadata, error) {
   815  	var err error
   816  	couchDoc, _, err := vdb.metadataDB.readDoc(channelMetadataDocID)
   817  	if err != nil {
   818  		logger.Errorf("Failed to read db name mapping data %s", err.Error())
   819  		return nil, err
   820  	}
   821  	// ReadDoc() not found (404) will result in nil response, in these cases return nil
   822  	if couchDoc == nil || couchDoc.jsonValue == nil {
   823  		return nil, nil
   824  	}
   825  	return decodeChannelMetadata(couchDoc)
   826  }
   827  
   828  // writeChannelMetadata saves channel metadata to metadataDB
   829  func (vdb *VersionedDB) writeChannelMetadata() error {
   830  	couchDoc, err := encodeChannelMetadata(vdb.channelMetadata)
   831  	if err != nil {
   832  		return err
   833  	}
   834  	_, err = vdb.metadataDB.saveDoc(channelMetadataDocID, "", couchDoc)
   835  	return err
   836  }
   837  
   838  // GetFullScanIterator implements method in VersionedDB interface. This function returns a
   839  // FullScanIterator that can be used to iterate over entire data in the statedb for a channel.
   840  // `skipNamespace` parameter can be used to control if the consumer wants the FullScanIterator
   841  // to skip one or more namespaces from the returned results.
   842  func (vdb *VersionedDB) GetFullScanIterator(skipNamespace func(string) bool) (statedb.FullScanIterator, byte, error) {
   843  	namespacesToScan := []string{}
   844  	for ns := range vdb.channelMetadata.NamespaceDBsInfo {
   845  		if skipNamespace(ns) {
   846  			continue
   847  		}
   848  		namespacesToScan = append(namespacesToScan, ns)
   849  	}
   850  	sort.Strings(namespacesToScan)
   851  
   852  	// if namespacesToScan is empty, we can return early with a nil FullScanIterator. However,
   853  	// the implementation of this method needs be consistent with the same method implemented in
   854  	// the stateleveldb pkg. Hence, we don't return a nil FullScanIterator by checking the length
   855  	// of the namespacesToScan.
   856  
   857  	dbsToScan := []*namespaceDB{}
   858  	for _, ns := range namespacesToScan {
   859  		db, err := vdb.getNamespaceDBHandle(ns)
   860  		if err != nil {
   861  			return nil, byte(0), errors.WithMessagef(err, "failed to get database handle for the namespace %s", ns)
   862  		}
   863  		dbsToScan = append(dbsToScan, &namespaceDB{ns, db})
   864  	}
   865  
   866  	// the database which belong to an empty namespace contains
   867  	// internal keys. The scanner must skip these keys.
   868  	toSkipKeysFromEmptyNs := map[string]bool{
   869  		savepointDocID:       true,
   870  		channelMetadataDocID: true,
   871  	}
   872  	return newDBsScanner(dbsToScan, vdb.couchInstance.internalQueryLimit(), toSkipKeysFromEmptyNs)
   873  }
   874  
   875  // applyAdditionalQueryOptions will add additional fields to the query required for query processing
   876  func applyAdditionalQueryOptions(queryString string, queryLimit int32, queryBookmark string) (string, error) {
   877  	const jsonQueryFields = "fields"
   878  	const jsonQueryLimit = "limit"
   879  	const jsonQueryBookmark = "bookmark"
   880  	//create a generic map for the query json
   881  	jsonQueryMap := make(map[string]interface{})
   882  	//unmarshal the selector json into the generic map
   883  	decoder := json.NewDecoder(bytes.NewBuffer([]byte(queryString)))
   884  	decoder.UseNumber()
   885  	err := decoder.Decode(&jsonQueryMap)
   886  	if err != nil {
   887  		return "", err
   888  	}
   889  	if fieldsJSONArray, ok := jsonQueryMap[jsonQueryFields]; ok {
   890  		switch fieldsJSONArray := fieldsJSONArray.(type) {
   891  		case []interface{}:
   892  			//Add the "_id", and "version" fields,  these are needed by default
   893  			jsonQueryMap[jsonQueryFields] = append(fieldsJSONArray, idField, versionField)
   894  		default:
   895  			return "", errors.New("fields definition must be an array")
   896  		}
   897  	}
   898  	// Add limit
   899  	// This will override any limit passed in the query.
   900  	// Explicit paging not yet supported.
   901  	jsonQueryMap[jsonQueryLimit] = queryLimit
   902  	// Add the bookmark if provided
   903  	if queryBookmark != "" {
   904  		jsonQueryMap[jsonQueryBookmark] = queryBookmark
   905  	}
   906  	//Marshal the updated json query
   907  	editedQuery, err := json.Marshal(jsonQueryMap)
   908  	if err != nil {
   909  		return "", err
   910  	}
   911  	logger.Debugf("Rewritten query: %s", editedQuery)
   912  	return string(editedQuery), nil
   913  }
   914  
   915  type queryScanner struct {
   916  	namespace       string
   917  	db              *couchDatabase
   918  	queryDefinition *queryDefinition
   919  	paginationInfo  *paginationInfo
   920  	resultsInfo     *resultsInfo
   921  	exhausted       bool
   922  }
   923  
   924  type queryDefinition struct {
   925  	startKey           string
   926  	endKey             string
   927  	query              string
   928  	internalQueryLimit int32
   929  }
   930  
   931  type paginationInfo struct {
   932  	cursor         int32
   933  	requestedLimit int32
   934  	bookmark       string
   935  }
   936  
   937  type resultsInfo struct {
   938  	totalRecordsReturned int32
   939  	results              []*queryResult
   940  }
   941  
   942  func newQueryScanner(namespace string, db *couchDatabase, query string, internalQueryLimit,
   943  	limit int32, bookmark, startKey, endKey string) (*queryScanner, error) {
   944  	scanner := &queryScanner{namespace, db, &queryDefinition{startKey, endKey, query, internalQueryLimit}, &paginationInfo{-1, limit, bookmark}, &resultsInfo{0, nil}, false}
   945  	var err error
   946  	// query is defined, then execute the query and return the records and bookmark
   947  	if scanner.queryDefinition.query != "" {
   948  		err = scanner.executeQueryWithBookmark()
   949  	} else {
   950  		err = scanner.getNextStateRangeScanResults()
   951  	}
   952  	if err != nil {
   953  		return nil, err
   954  	}
   955  	scanner.paginationInfo.cursor = -1
   956  	return scanner, nil
   957  }
   958  
   959  func (scanner *queryScanner) Next() (statedb.QueryResult, error) {
   960  	doc, err := scanner.next()
   961  	if err != nil {
   962  		return nil, err
   963  	}
   964  	if doc == nil {
   965  		return nil, nil
   966  	}
   967  	kv, err := couchDocToKeyValue(doc)
   968  	if err != nil {
   969  		return nil, err
   970  	}
   971  	scanner.resultsInfo.totalRecordsReturned++
   972  	return &statedb.VersionedKV{
   973  		CompositeKey: statedb.CompositeKey{
   974  			Namespace: scanner.namespace,
   975  			Key:       kv.key,
   976  		},
   977  		VersionedValue: *kv.VersionedValue,
   978  	}, nil
   979  }
   980  
   981  func (scanner *queryScanner) next() (*couchDoc, error) {
   982  	if len(scanner.resultsInfo.results) == 0 {
   983  		return nil, nil
   984  	}
   985  	scanner.paginationInfo.cursor++
   986  	if scanner.paginationInfo.cursor >= scanner.queryDefinition.internalQueryLimit {
   987  		if scanner.exhausted {
   988  			return nil, nil
   989  		}
   990  		var err error
   991  		if scanner.queryDefinition.query != "" {
   992  			err = scanner.executeQueryWithBookmark()
   993  		} else {
   994  			err = scanner.getNextStateRangeScanResults()
   995  		}
   996  		if err != nil {
   997  			return nil, err
   998  		}
   999  		if len(scanner.resultsInfo.results) == 0 {
  1000  			return nil, nil
  1001  		}
  1002  	}
  1003  	if scanner.paginationInfo.cursor >= int32(len(scanner.resultsInfo.results)) {
  1004  		return nil, nil
  1005  	}
  1006  	result := scanner.resultsInfo.results[scanner.paginationInfo.cursor]
  1007  	return &couchDoc{
  1008  		jsonValue:   result.value,
  1009  		attachments: result.attachments,
  1010  	}, nil
  1011  }
  1012  
  1013  func (scanner *queryScanner) Close() {}
  1014  
  1015  func (scanner *queryScanner) GetBookmarkAndClose() string {
  1016  	retval := ""
  1017  	if scanner.queryDefinition.query != "" {
  1018  		retval = scanner.paginationInfo.bookmark
  1019  	} else {
  1020  		retval = scanner.queryDefinition.startKey
  1021  	}
  1022  	scanner.Close()
  1023  	return retval
  1024  }
  1025  
  1026  func constructCacheValue(v *statedb.VersionedValue, rev string) *CacheValue {
  1027  	return &CacheValue{
  1028  		Version:        v.Version.ToBytes(),
  1029  		Value:          v.Value,
  1030  		Metadata:       v.Metadata,
  1031  		AdditionalInfo: []byte(rev),
  1032  	}
  1033  }
  1034  
  1035  func constructVersionedValue(cv *CacheValue) (*statedb.VersionedValue, error) {
  1036  	height, _, err := version.NewHeightFromBytes(cv.Version)
  1037  	if err != nil {
  1038  		return nil, err
  1039  	}
  1040  
  1041  	return &statedb.VersionedValue{
  1042  		Value:    cv.Value,
  1043  		Version:  height,
  1044  		Metadata: cv.Metadata,
  1045  	}, nil
  1046  }
  1047  
  1048  type dbsScanner struct {
  1049  	dbs                   []*namespaceDB
  1050  	nextDBToScanIndex     int
  1051  	resultItr             *queryScanner
  1052  	currentNamespace      string
  1053  	prefetchLimit         int32
  1054  	toSkipKeysFromEmptyNs map[string]bool
  1055  }
  1056  
  1057  type namespaceDB struct {
  1058  	ns string
  1059  	db *couchDatabase
  1060  }
  1061  
  1062  func newDBsScanner(dbsToScan []*namespaceDB, prefetchLimit int32, toSkipKeysFromEmptyNs map[string]bool) (*dbsScanner, byte, error) {
  1063  	if len(dbsToScan) == 0 {
  1064  		return nil, fullScanIteratorValueFormat, nil
  1065  	}
  1066  	s := &dbsScanner{
  1067  		dbs:                   dbsToScan,
  1068  		prefetchLimit:         prefetchLimit,
  1069  		toSkipKeysFromEmptyNs: toSkipKeysFromEmptyNs,
  1070  	}
  1071  	if err := s.beginNextDBScan(); err != nil {
  1072  		return nil, byte(0), err
  1073  	}
  1074  	return s, fullScanIteratorValueFormat, nil
  1075  }
  1076  
  1077  func (s *dbsScanner) beginNextDBScan() error {
  1078  	dbUnderScan := s.dbs[s.nextDBToScanIndex]
  1079  	queryScanner, err := newQueryScanner(dbUnderScan.ns, dbUnderScan.db, "", s.prefetchLimit, 0, "", "", "")
  1080  	if err != nil {
  1081  		return errors.WithMessagef(
  1082  			err,
  1083  			"failed to create a query scanner for the database %s associated with the namespace %s",
  1084  			dbUnderScan.db.dbName,
  1085  			dbUnderScan.ns,
  1086  		)
  1087  	}
  1088  	s.resultItr = queryScanner
  1089  	s.currentNamespace = dbUnderScan.ns
  1090  	s.nextDBToScanIndex++
  1091  	return nil
  1092  }
  1093  
  1094  // Next returns the key-values present in the namespaceDB. Once a namespaceDB
  1095  // is processed, it moves to the next namespaceDB till all are processed.
  1096  // The <version, value, metadata> is converted to []byte using a proto.
  1097  func (s *dbsScanner) Next() (*statedb.CompositeKey, []byte, error) {
  1098  	if s == nil {
  1099  		return nil, nil, nil
  1100  	}
  1101  	for {
  1102  		couchDoc, err := s.resultItr.next()
  1103  		if err != nil {
  1104  			return nil, nil, errors.WithMessagef(
  1105  				err,
  1106  				"failed to retrieve the next entry from scanner associated with namespace %s",
  1107  				s.currentNamespace,
  1108  			)
  1109  		}
  1110  		if couchDoc == nil {
  1111  			s.resultItr.Close()
  1112  			if len(s.dbs) <= s.nextDBToScanIndex {
  1113  				break
  1114  			}
  1115  			if err := s.beginNextDBScan(); err != nil {
  1116  				return nil, nil, err
  1117  			}
  1118  			continue
  1119  		}
  1120  		if s.currentNamespace == "" {
  1121  			key, err := couchDoc.key()
  1122  			if err != nil {
  1123  				return nil, nil, errors.WithMessagef(
  1124  					err,
  1125  					"failed to retrieve key from the couchdoc present in the empty namespace",
  1126  				)
  1127  			}
  1128  			if s.toSkipKeysFromEmptyNs[key] {
  1129  				continue
  1130  			}
  1131  		}
  1132  		fields, err := validateAndRetrieveFields(couchDoc)
  1133  		if err != nil {
  1134  			return nil, nil, errors.WithMessagef(
  1135  				err,
  1136  				"failed to validate and retrieve fields from couch doc with id %s",
  1137  				fields.id,
  1138  			)
  1139  		}
  1140  		dbval, err := encodeValueVersionMetadata(fields.value, []byte(fields.versionAndMetadata))
  1141  		if err != nil {
  1142  			return nil, nil, errors.WithMessagef(
  1143  				err,
  1144  				"failed to encode value [%v] version and metadata [%v]",
  1145  				fields.value,
  1146  				fields.versionAndMetadata,
  1147  			)
  1148  		}
  1149  		return &statedb.CompositeKey{
  1150  			Namespace: s.currentNamespace,
  1151  			Key:       fields.id,
  1152  		}, dbval, nil
  1153  	}
  1154  	return nil, nil, nil
  1155  }
  1156  
  1157  func (s *dbsScanner) Close() {
  1158  	if s == nil {
  1159  		return
  1160  	}
  1161  	s.resultItr.Close()
  1162  }