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 }