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