github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/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 perfrom 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  // serveEndKeyIfNeeded returns the endKey only once and only if includeEndKey was set to true
   126  // in the constructor of combinedIterator.
   127  func (itr *combinedIterator) serveEndKeyIfNeeded() (statedb.QueryResult, error) {
   128  	if !itr.includeEndKey || itr.endKeyServed {
   129  		logger.Debugf("Endkey not to be served. Returning nil... [toInclude=%t, alreadyServed=%t]",
   130  			itr.includeEndKey, itr.endKeyServed)
   131  		return nil, nil
   132  	}
   133  	logger.Debug("Serving the endKey")
   134  	var vv *statedb.VersionedValue
   135  	var err error
   136  	vv = itr.updates.Get(itr.ns, itr.endKey)
   137  	logger.Debugf("endKey value from updates:%s", vv)
   138  	if vv == nil {
   139  		if vv, err = itr.db.GetState(itr.ns, itr.endKey); err != nil {
   140  			return nil, err
   141  		}
   142  		logger.Debugf("endKey value from stateDB:%s", vv)
   143  	}
   144  	itr.endKeyServed = true
   145  	if vv == nil {
   146  		return nil, nil
   147  	}
   148  	vkv := &statedb.VersionedKV{
   149  		CompositeKey:   statedb.CompositeKey{Namespace: itr.ns, Key: itr.endKey},
   150  		VersionedValue: statedb.VersionedValue{Value: vv.Value, Version: vv.Version}}
   151  
   152  	if isDelete(vkv) {
   153  		return nil, nil
   154  	}
   155  	return vkv, nil
   156  }
   157  
   158  func compareKeys(item1 statedb.QueryResult, item2 statedb.QueryResult) int {
   159  	if item1 == nil {
   160  		if item2 == nil {
   161  			return 0
   162  		}
   163  		return 1
   164  	}
   165  	if item2 == nil {
   166  		return -1
   167  	}
   168  	// at this stage both items are not nil
   169  	return strings.Compare(item1.(*statedb.VersionedKV).Key, item2.(*statedb.VersionedKV).Key)
   170  }
   171  
   172  func isDelete(item statedb.QueryResult) bool {
   173  	return item.(*statedb.VersionedKV).Value == nil
   174  }