github.com/Hnampk/fabric@v2.1.1+incompatible/core/ledger/kvledger/txmgmt/queryutil/iterator_combiner.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package queryutil 8 9 import ( 10 "fmt" 11 12 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 13 commonledger "github.com/hyperledger/fabric/common/ledger" 14 "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb" 15 ) 16 17 type itrCombiner struct { 18 namespace string 19 holders []*itrHolder 20 } 21 22 func newItrCombiner(namespace string, baseIterators []statedb.ResultsIterator) (*itrCombiner, error) { 23 var holders []*itrHolder 24 for _, itr := range baseIterators { 25 res, err := itr.Next() 26 if err != nil { 27 for _, holder := range holders { 28 holder.itr.Close() 29 } 30 return nil, err 31 } 32 if res != nil { 33 holders = append(holders, &itrHolder{itr, res.(*statedb.VersionedKV)}) 34 } 35 } 36 return &itrCombiner{namespace, holders}, nil 37 } 38 39 // Next returns the next eligible item from the underlying iterators. 40 // This function evaluates the underlying iterators, and picks the one which is 41 // gives the lexicographically smallest key. Then, it saves that value, and advances the chosen iterator. 42 // If the chosen iterator is out of elements, then that iterator is closed, and removed from the list of iterators. 43 func (combiner *itrCombiner) Next() (commonledger.QueryResult, error) { 44 logger.Debugf("Iterators position at beginning: %s", combiner.holders) 45 if len(combiner.holders) == 0 { 46 return nil, nil 47 } 48 smallestHolderIndex := 0 49 for i := 1; i < len(combiner.holders); i++ { 50 smallestKey, holderKey := combiner.keyAt(smallestHolderIndex), combiner.keyAt(i) 51 switch { 52 case holderKey == smallestKey: // we found the same key in the lower order iterator (stale value of the key); 53 // we already have the latest value for this key (in smallestHolder). Ignore this value and move the iterator 54 // to next item (to a greater key) so that for next round of key selection, we do not consider this key again 55 removed, err := combiner.moveItrAndRemoveIfExhausted(i) 56 if err != nil { 57 return nil, err 58 } 59 if removed { // if the current iterator is exhausted and hence removed, decrement the index 60 // because indexes of the remaining iterators are decremented by one 61 i-- 62 } 63 case holderKey < smallestKey: 64 smallestHolderIndex = i 65 default: 66 // the current key under evaluation is greater than the smallestKey - do nothing 67 } 68 } 69 kv := combiner.kvAt(smallestHolderIndex) 70 combiner.moveItrAndRemoveIfExhausted(smallestHolderIndex) 71 if kv.IsDelete() { 72 return combiner.Next() 73 } 74 logger.Debugf("Key [%s] selected from iterator at index [%d]", kv.Key, smallestHolderIndex) 75 logger.Debugf("Iterators position at end: %s", combiner.holders) 76 return &queryresult.KV{Namespace: combiner.namespace, Key: kv.Key, Value: kv.Value}, nil 77 } 78 79 // moveItrAndRemoveIfExhausted moves the iterator at index i to the next item. If the iterator gets exhausted 80 // then the iterator is removed from the underlying slice 81 func (combiner *itrCombiner) moveItrAndRemoveIfExhausted(i int) (removed bool, err error) { 82 holder := combiner.holders[i] 83 exhausted, err := holder.moveToNext() 84 if err != nil { 85 return false, err 86 } 87 if exhausted { 88 combiner.holders[i].itr.Close() 89 combiner.holders = append(combiner.holders[:i], combiner.holders[i+1:]...) 90 91 } 92 return exhausted, nil 93 } 94 95 // kvAt returns the kv available from iterator at index i 96 func (combiner *itrCombiner) kvAt(i int) *statedb.VersionedKV { 97 return combiner.holders[i].kv 98 } 99 100 // keyAt returns the key available from iterator at index i 101 func (combiner *itrCombiner) keyAt(i int) string { 102 return combiner.kvAt(i).Key 103 } 104 105 // Close closes all the underlying iterators 106 func (combiner *itrCombiner) Close() { 107 for _, holder := range combiner.holders { 108 holder.itr.Close() 109 } 110 } 111 112 // itrHolder encloses an iterator and keeps the next item available from the iterator in the buffer 113 type itrHolder struct { 114 itr statedb.ResultsIterator 115 kv *statedb.VersionedKV 116 } 117 118 // moveToNext fetches the next item to keep in buffer and returns true if the iterator is exhausted 119 func (holder *itrHolder) moveToNext() (exhausted bool, err error) { 120 var res statedb.QueryResult 121 if res, err = holder.itr.Next(); err != nil { 122 return false, err 123 } 124 if res != nil { 125 holder.kv = res.(*statedb.VersionedKV) 126 } 127 return res == nil, nil 128 } 129 130 // String returns the key that the holder has in the buffer for serving as a next key 131 func (holder *itrHolder) String() string { 132 return fmt.Sprintf("{%s}", holder.kv.Key) 133 }