github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go (about)

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