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  }