github.com/renegr87/renegr87@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/validator/statebasedval/combined_iterator.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package statebasedval
    18  
    19  import (
    20  	"strings"
    21  
    22  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
    23  )
    24  
    25  // combinedIterator implements the interface statedb.ResultsIterator.
    26  // Internally, it maintains two iterators
    27  // - (1) dbItr - an iterator that iterates over keys present in the db
    28  // - (2) updatesItr - an iterator that iterates over keys present in the update batch
    29  //       (i.e, the keys that are inserted/updated/deleted by preceding valid transactions
    30  //        in the block and to be committed to the db as a part of block commit operation)
    31  //
    32  // This can be used where the caller wants to see what would be the final results of
    33  // iterating over a key range if the modifications of the preceding valid transactions
    34  // were to be applied to the db
    35  //
    36  // This can be used to perform validation for phantom reads in a transactions rwset
    37  type combinedIterator struct {
    38  	// input
    39  	ns            string
    40  	db            statedb.VersionedDB
    41  	updates       *statedb.UpdateBatch
    42  	endKey        string
    43  	includeEndKey bool
    44  
    45  	// internal state
    46  	dbItr        statedb.ResultsIterator
    47  	updatesItr   statedb.ResultsIterator
    48  	dbItem       statedb.QueryResult
    49  	updatesItem  statedb.QueryResult
    50  	endKeyServed bool
    51  }
    52  
    53  func newCombinedIterator(db statedb.VersionedDB, updates *statedb.UpdateBatch,
    54  	ns string, startKey string, endKey string, includeEndKey bool) (*combinedIterator, error) {
    55  
    56  	var dbItr statedb.ResultsIterator
    57  	var updatesItr statedb.ResultsIterator
    58  	var err error
    59  	if dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey); err != nil {
    60  		return nil, err
    61  	}
    62  	updatesItr = updates.GetRangeScanIterator(ns, startKey, endKey)
    63  	var dbItem, updatesItem statedb.QueryResult
    64  	if dbItem, err = dbItr.Next(); err != nil {
    65  		return nil, err
    66  	}
    67  	if updatesItem, err = updatesItr.Next(); err != nil {
    68  		return nil, err
    69  	}
    70  	logger.Debugf("Combined iterator initialized. dbItem=%#v, updatesItem=%#v", dbItem, updatesItem)
    71  	return &combinedIterator{ns, db, updates, endKey, includeEndKey,
    72  		dbItr, updatesItr, dbItem, updatesItem, false}, nil
    73  }
    74  
    75  // Next returns the KV from either dbItr or updatesItr that gives the next smaller key
    76  // If both gives the same keys, then it returns the KV from updatesItr.
    77  func (itr *combinedIterator) Next() (statedb.QueryResult, error) {
    78  	if itr.dbItem == nil && itr.updatesItem == nil {
    79  		logger.Debugf("dbItem and updatesItem both are nil.")
    80  		return itr.serveEndKeyIfNeeded()
    81  	}
    82  	var moveDBItr bool
    83  	var moveUpdatesItr bool
    84  	var selectedItem statedb.QueryResult
    85  	compResult := compareKeys(itr.dbItem, itr.updatesItem)
    86  	logger.Debugf("compResult=%d", compResult)
    87  	switch compResult {
    88  	case -1:
    89  		// dbItem is smaller
    90  		selectedItem = itr.dbItem
    91  		moveDBItr = true
    92  	case 0:
    93  		//both items are same so, choose the updatesItem (latest)
    94  		selectedItem = itr.updatesItem
    95  		moveUpdatesItr = true
    96  		moveDBItr = true
    97  	case 1:
    98  		// updatesItem is smaller
    99  		selectedItem = itr.updatesItem
   100  		moveUpdatesItr = true
   101  	}
   102  	var err error
   103  	if moveDBItr {
   104  		if itr.dbItem, err = itr.dbItr.Next(); err != nil {
   105  			return nil, err
   106  		}
   107  	}
   108  
   109  	if moveUpdatesItr {
   110  		if itr.updatesItem, err = itr.updatesItr.Next(); err != nil {
   111  			return nil, err
   112  		}
   113  	}
   114  	if isDelete(selectedItem) {
   115  		return itr.Next()
   116  	}
   117  	logger.Debugf("Returning item=%#v. Next dbItem=%#v, Next updatesItem=%#v", selectedItem, itr.dbItem, itr.updatesItem)
   118  	return selectedItem, nil
   119  }
   120  
   121  func (itr *combinedIterator) Close() {
   122  	itr.dbItr.Close()
   123  }
   124  
   125  func (itr *combinedIterator) GetBookmarkAndClose() string {
   126  	itr.Close()
   127  	return ""
   128  }
   129  
   130  // serveEndKeyIfNeeded returns the endKey only once and only if includeEndKey was set to true
   131  // in the constructor of combinedIterator.
   132  func (itr *combinedIterator) serveEndKeyIfNeeded() (statedb.QueryResult, error) {
   133  	if !itr.includeEndKey || itr.endKeyServed {
   134  		logger.Debugf("Endkey not to be served. Returning nil... [toInclude=%t, alreadyServed=%t]",
   135  			itr.includeEndKey, itr.endKeyServed)
   136  		return nil, nil
   137  	}
   138  	logger.Debug("Serving the endKey")
   139  	var vv *statedb.VersionedValue
   140  	var err error
   141  	vv = itr.updates.Get(itr.ns, itr.endKey)
   142  	logger.Debugf("endKey value from updates:%s", vv)
   143  	if vv == nil {
   144  		if vv, err = itr.db.GetState(itr.ns, itr.endKey); err != nil {
   145  			return nil, err
   146  		}
   147  		logger.Debugf("endKey value from stateDB:%s", vv)
   148  	}
   149  	itr.endKeyServed = true
   150  	if vv == nil {
   151  		return nil, nil
   152  	}
   153  	vkv := &statedb.VersionedKV{
   154  		CompositeKey:   statedb.CompositeKey{Namespace: itr.ns, Key: itr.endKey},
   155  		VersionedValue: statedb.VersionedValue{Value: vv.Value, Version: vv.Version}}
   156  
   157  	if isDelete(vkv) {
   158  		return nil, nil
   159  	}
   160  	return vkv, nil
   161  }
   162  
   163  func compareKeys(item1 statedb.QueryResult, item2 statedb.QueryResult) int {
   164  	if item1 == nil {
   165  		if item2 == nil {
   166  			return 0
   167  		}
   168  		return 1
   169  	}
   170  	if item2 == nil {
   171  		return -1
   172  	}
   173  	// at this stage both items are not nil
   174  	return strings.Compare(item1.(*statedb.VersionedKV).Key, item2.(*statedb.VersionedKV).Key)
   175  }
   176  
   177  func isDelete(item statedb.QueryResult) bool {
   178  	return item.(*statedb.VersionedKV).Value == nil
   179  }