github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/core/ledger/kvledger/txmgmt/validation/combined_iterator.go (about) 1 /* 2 Copyright IBM Corp. 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/osdi23p228/fabric/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.QueryResult 39 updatesItem statedb.QueryResult 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 46 var dbItr statedb.ResultsIterator 47 var updatesItr statedb.ResultsIterator 48 var err error 49 if dbItr, err = db.GetStateRangeScanIterator(ns, startKey, endKey); err != nil { 50 return nil, err 51 } 52 updatesItr = updates.GetRangeScanIterator(ns, startKey, endKey) 53 var dbItem, updatesItem statedb.QueryResult 54 if dbItem, err = dbItr.Next(); err != nil { 55 return nil, err 56 } 57 if updatesItem, err = updatesItr.Next(); err != nil { 58 return nil, err 59 } 60 logger.Debugf("Combined iterator initialized. dbItem=%#v, updatesItem=%#v", dbItem, updatesItem) 61 return &combinedIterator{ns, db, updates, endKey, includeEndKey, 62 dbItr, updatesItr, dbItem, updatesItem, false}, nil 63 } 64 65 // Next returns the KV from either dbItr or updatesItr that gives the next smaller key 66 // If both gives the same keys, then it returns the KV from updatesItr. 67 func (itr *combinedIterator) Next() (statedb.QueryResult, error) { 68 if itr.dbItem == nil && itr.updatesItem == nil { 69 logger.Debugf("dbItem and updatesItem both are nil.") 70 return itr.serveEndKeyIfNeeded() 71 } 72 var moveDBItr bool 73 var moveUpdatesItr bool 74 var selectedItem statedb.QueryResult 75 compResult := compareKeys(itr.dbItem, itr.updatesItem) 76 logger.Debugf("compResult=%d", compResult) 77 switch compResult { 78 case -1: 79 // dbItem is smaller 80 selectedItem = itr.dbItem 81 moveDBItr = true 82 case 0: 83 //both items are same so, choose the updatesItem (latest) 84 selectedItem = itr.updatesItem 85 moveUpdatesItr = true 86 moveDBItr = true 87 case 1: 88 // updatesItem is smaller 89 selectedItem = itr.updatesItem 90 moveUpdatesItr = true 91 } 92 var err error 93 if moveDBItr { 94 if itr.dbItem, err = itr.dbItr.Next(); err != nil { 95 return nil, err 96 } 97 } 98 99 if moveUpdatesItr { 100 if itr.updatesItem, err = itr.updatesItr.Next(); err != nil { 101 return nil, err 102 } 103 } 104 if isDelete(selectedItem) { 105 return itr.Next() 106 } 107 logger.Debugf("Returning item=%#v. Next dbItem=%#v, Next updatesItem=%#v", selectedItem, itr.dbItem, itr.updatesItem) 108 return selectedItem, nil 109 } 110 111 func (itr *combinedIterator) Close() { 112 itr.dbItr.Close() 113 } 114 115 func (itr *combinedIterator) GetBookmarkAndClose() string { 116 itr.Close() 117 return "" 118 } 119 120 // serveEndKeyIfNeeded returns the endKey only once and only if includeEndKey was set to true 121 // in the constructor of combinedIterator. 122 func (itr *combinedIterator) serveEndKeyIfNeeded() (statedb.QueryResult, error) { 123 if !itr.includeEndKey || itr.endKeyServed { 124 logger.Debugf("Endkey not to be served. Returning nil... [toInclude=%t, alreadyServed=%t]", 125 itr.includeEndKey, itr.endKeyServed) 126 return nil, nil 127 } 128 logger.Debug("Serving the endKey") 129 var vv *statedb.VersionedValue 130 var err error 131 vv = itr.updates.Get(itr.ns, itr.endKey) 132 logger.Debugf("endKey value from updates:%s", vv) 133 if vv == nil { 134 if vv, err = itr.db.GetState(itr.ns, itr.endKey); err != nil { 135 return nil, err 136 } 137 logger.Debugf("endKey value from stateDB:%s", vv) 138 } 139 itr.endKeyServed = true 140 if vv == nil { 141 return nil, nil 142 } 143 vkv := &statedb.VersionedKV{ 144 CompositeKey: statedb.CompositeKey{Namespace: itr.ns, Key: itr.endKey}, 145 VersionedValue: statedb.VersionedValue{Value: vv.Value, Version: vv.Version}} 146 147 if isDelete(vkv) { 148 return nil, nil 149 } 150 return vkv, nil 151 } 152 153 func compareKeys(item1 statedb.QueryResult, item2 statedb.QueryResult) int { 154 if item1 == nil { 155 if item2 == nil { 156 return 0 157 } 158 return 1 159 } 160 if item2 == nil { 161 return -1 162 } 163 // at this stage both items are not nil 164 return strings.Compare(item1.(*statedb.VersionedKV).Key, item2.(*statedb.VersionedKV).Key) 165 } 166 167 func isDelete(item statedb.QueryResult) bool { 168 return item.(*statedb.VersionedKV).Value == nil 169 }