github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/core/ledger/kvledger/txmgmt/txmgr/lockbasedtxmgr/helper.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 lockbasedtxmgr 18 19 import ( 20 commonledger "github.com/hyperledger/fabric/common/ledger" 21 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil" 22 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 23 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version" 24 "github.com/hyperledger/fabric/core/ledger/ledgerconfig" 25 "github.com/hyperledger/fabric/protos/ledger/queryresult" 26 "github.com/hyperledger/fabric/protos/ledger/rwset/kvrwset" 27 ) 28 29 type queryHelper struct { 30 txmgr *LockBasedTxMgr 31 rwsetBuilder *rwsetutil.RWSetBuilder 32 itrs []*resultsItr 33 err error 34 doneInvoked bool 35 } 36 37 func (h *queryHelper) getState(ns string, key string) ([]byte, error) { 38 h.checkDone() 39 versionedValue, err := h.txmgr.db.GetState(ns, key) 40 if err != nil { 41 return nil, err 42 } 43 val, ver := decomposeVersionedValue(versionedValue) 44 if h.rwsetBuilder != nil { 45 h.rwsetBuilder.AddToReadSet(ns, key, ver) 46 } 47 return val, nil 48 } 49 50 func (h *queryHelper) getStateMultipleKeys(namespace string, keys []string) ([][]byte, error) { 51 h.checkDone() 52 versionedValues, err := h.txmgr.db.GetStateMultipleKeys(namespace, keys) 53 if err != nil { 54 return nil, nil 55 } 56 values := make([][]byte, len(versionedValues)) 57 for i, versionedValue := range versionedValues { 58 val, ver := decomposeVersionedValue(versionedValue) 59 if h.rwsetBuilder != nil { 60 h.rwsetBuilder.AddToReadSet(namespace, keys[i], ver) 61 } 62 values[i] = val 63 } 64 return values, nil 65 } 66 67 func (h *queryHelper) getStateRangeScanIterator(namespace string, startKey string, endKey string) (commonledger.ResultsIterator, error) { 68 h.checkDone() 69 itr, err := newResultsItr(namespace, startKey, endKey, h.txmgr.db, h.rwsetBuilder, 70 ledgerconfig.IsQueryReadsHashingEnabled(), ledgerconfig.GetMaxDegreeQueryReadsHashing()) 71 if err != nil { 72 return nil, err 73 } 74 h.itrs = append(h.itrs, itr) 75 return itr, nil 76 } 77 78 func (h *queryHelper) executeQuery(namespace, query string) (commonledger.ResultsIterator, error) { 79 dbItr, err := h.txmgr.db.ExecuteQuery(namespace, query) 80 if err != nil { 81 return nil, err 82 } 83 return &queryResultsItr{DBItr: dbItr, RWSetBuilder: h.rwsetBuilder}, nil 84 } 85 86 func (h *queryHelper) done() { 87 if h.doneInvoked { 88 return 89 } 90 91 defer func() { 92 h.txmgr.commitRWLock.RUnlock() 93 h.doneInvoked = true 94 for _, itr := range h.itrs { 95 itr.Close() 96 } 97 }() 98 99 for _, itr := range h.itrs { 100 if h.rwsetBuilder != nil { 101 results, hash, err := itr.rangeQueryResultsHelper.Done() 102 if err != nil { 103 h.err = err 104 return 105 } 106 if results != nil { 107 itr.rangeQueryInfo.SetRawReads(results) 108 } 109 if hash != nil { 110 itr.rangeQueryInfo.SetMerkelSummary(hash) 111 } 112 h.rwsetBuilder.AddToRangeQuerySet(itr.ns, itr.rangeQueryInfo) 113 } 114 } 115 } 116 117 func (h *queryHelper) checkDone() { 118 if h.doneInvoked { 119 panic("This instance should not be used after calling Done()") 120 } 121 } 122 123 // resultsItr implements interface ledger.ResultsIterator 124 // this wraps the actual db iterator and intercept the calls 125 // to build rangeQueryInfo in the ReadWriteSet that is used 126 // for performing phantom read validation during commit 127 type resultsItr struct { 128 ns string 129 endKey string 130 dbItr statedb.ResultsIterator 131 rwSetBuilder *rwsetutil.RWSetBuilder 132 rangeQueryInfo *kvrwset.RangeQueryInfo 133 rangeQueryResultsHelper *rwsetutil.RangeQueryResultsHelper 134 } 135 136 func newResultsItr(ns string, startKey string, endKey string, 137 db statedb.VersionedDB, rwsetBuilder *rwsetutil.RWSetBuilder, enableHashing bool, maxDegree uint32) (*resultsItr, error) { 138 dbItr, err := db.GetStateRangeScanIterator(ns, startKey, endKey) 139 if err != nil { 140 return nil, err 141 } 142 itr := &resultsItr{ns: ns, dbItr: dbItr} 143 // it's a simulation request so, enable capture of range query info 144 if rwsetBuilder != nil { 145 itr.rwSetBuilder = rwsetBuilder 146 itr.endKey = endKey 147 // just set the StartKey... set the EndKey later below in the Next() method. 148 itr.rangeQueryInfo = &kvrwset.RangeQueryInfo{StartKey: startKey} 149 resultsHelper, err := rwsetutil.NewRangeQueryResultsHelper(enableHashing, maxDegree) 150 if err != nil { 151 return nil, err 152 } 153 itr.rangeQueryResultsHelper = resultsHelper 154 } 155 return itr, nil 156 } 157 158 // Next implements method in interface ledger.ResultsIterator 159 // Before returning the next result, update the EndKey and ItrExhausted in rangeQueryInfo 160 // If we set the EndKey in the constructor (as we do for the StartKey) to what is 161 // supplied in the original query, we may be capturing the unnecessary longer range if the 162 // caller decides to stop iterating at some intermediate point. Alternatively, we could have 163 // set the EndKey and ItrExhausted in the Close() function but it may not be desirable to change 164 // transactional behaviour based on whether the Close() was invoked or not 165 func (itr *resultsItr) Next() (commonledger.QueryResult, error) { 166 queryResult, err := itr.dbItr.Next() 167 if err != nil { 168 return nil, err 169 } 170 itr.updateRangeQueryInfo(queryResult) 171 if queryResult == nil { 172 return nil, nil 173 } 174 versionedKV := queryResult.(*statedb.VersionedKV) 175 return &queryresult.KV{Namespace: versionedKV.Namespace, Key: versionedKV.Key, Value: versionedKV.Value}, nil 176 } 177 178 // updateRangeQueryInfo updates two attributes of the rangeQueryInfo 179 // 1) The EndKey - set to either a) latest key that is to be returned to the caller (if the iterator is not exhausted) 180 // because, we do not know if the caller is again going to invoke Next() or not. 181 // or b) the last key that was supplied in the original query (if the iterator is exhausted) 182 // 2) The ItrExhausted - set to true if the iterator is going to return nil as a result of the Next() call 183 func (itr *resultsItr) updateRangeQueryInfo(queryResult statedb.QueryResult) { 184 if itr.rwSetBuilder == nil { 185 return 186 } 187 188 if queryResult == nil { 189 // caller scanned till the iterator got exhausted. 190 // So, set the endKey to the actual endKey supplied in the query 191 itr.rangeQueryInfo.ItrExhausted = true 192 itr.rangeQueryInfo.EndKey = itr.endKey 193 return 194 } 195 versionedKV := queryResult.(*statedb.VersionedKV) 196 itr.rangeQueryResultsHelper.AddResult(rwsetutil.NewKVRead(versionedKV.Key, versionedKV.Version)) 197 // Set the end key to the latest key retrieved by the caller. 198 // Because, the caller may actually not invoke the Next() function again 199 itr.rangeQueryInfo.EndKey = versionedKV.Key 200 } 201 202 // Close implements method in interface ledger.ResultsIterator 203 func (itr *resultsItr) Close() { 204 itr.dbItr.Close() 205 } 206 207 type queryResultsItr struct { 208 DBItr statedb.ResultsIterator 209 RWSetBuilder *rwsetutil.RWSetBuilder 210 } 211 212 // Next implements method in interface ledger.ResultsIterator 213 func (itr *queryResultsItr) Next() (commonledger.QueryResult, error) { 214 215 queryResult, err := itr.DBItr.Next() 216 if err != nil { 217 return nil, err 218 } 219 if queryResult == nil { 220 return nil, nil 221 } 222 versionedQueryRecord := queryResult.(*statedb.VersionedKV) 223 logger.Debugf("queryResultsItr.Next() returned a record:%s", string(versionedQueryRecord.Value)) 224 225 if itr.RWSetBuilder != nil { 226 itr.RWSetBuilder.AddToReadSet(versionedQueryRecord.Namespace, versionedQueryRecord.Key, versionedQueryRecord.Version) 227 } 228 return &queryresult.KV{Namespace: versionedQueryRecord.Namespace, Key: versionedQueryRecord.Key, Value: versionedQueryRecord.Value}, nil 229 } 230 231 // Close implements method in interface ledger.ResultsIterator 232 func (itr *queryResultsItr) Close() { 233 itr.DBItr.Close() 234 } 235 236 func decomposeVersionedValue(versionedValue *statedb.VersionedValue) ([]byte, *version.Height) { 237 var value []byte 238 var ver *version.Height 239 if versionedValue != nil { 240 value = versionedValue.Value 241 ver = versionedValue.Version 242 } 243 return value, ver 244 }