github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/ledger/kvledger/txmgmt/queryutil/iterator_combiner.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package queryutil 8 9 import ( 10 "fmt" 11 12 commonledger "github.com/hechain20/hechain/common/ledger" 13 "github.com/hechain20/hechain/core/ledger/kvledger/txmgmt/statedb" 14 "github.com/hyperledger/fabric-protos-go/ledger/queryresult" 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}) 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 if _, err := combiner.moveItrAndRemoveIfExhausted(smallestHolderIndex); err != nil { 71 return nil, err 72 } 73 if kv.IsDelete() { 74 return combiner.Next() 75 } 76 logger.Debugf("Key [%s] selected from iterator at index [%d]", kv.Key, smallestHolderIndex) 77 logger.Debugf("Iterators position at end: %s", combiner.holders) 78 return &queryresult.KV{Namespace: combiner.namespace, Key: kv.Key, Value: kv.Value}, nil 79 } 80 81 // moveItrAndRemoveIfExhausted moves the iterator at index i to the next item. If the iterator gets exhausted 82 // then the iterator is removed from the underlying slice 83 func (combiner *itrCombiner) moveItrAndRemoveIfExhausted(i int) (removed bool, err error) { 84 holder := combiner.holders[i] 85 exhausted, err := holder.moveToNext() 86 if err != nil { 87 return false, err 88 } 89 if exhausted { 90 combiner.holders[i].itr.Close() 91 combiner.holders = append(combiner.holders[:i], combiner.holders[i+1:]...) 92 93 } 94 return exhausted, nil 95 } 96 97 // kvAt returns the kv available from iterator at index i 98 func (combiner *itrCombiner) kvAt(i int) *statedb.VersionedKV { 99 return combiner.holders[i].kv 100 } 101 102 // keyAt returns the key available from iterator at index i 103 func (combiner *itrCombiner) keyAt(i int) string { 104 return combiner.kvAt(i).Key 105 } 106 107 // Close closes all the underlying iterators 108 func (combiner *itrCombiner) Close() { 109 for _, holder := range combiner.holders { 110 holder.itr.Close() 111 } 112 } 113 114 // itrHolder encloses an iterator and keeps the next item available from the iterator in the buffer 115 type itrHolder struct { 116 itr statedb.ResultsIterator 117 kv *statedb.VersionedKV 118 } 119 120 // moveToNext fetches the next item to keep in buffer and returns true if the iterator is exhausted 121 func (holder *itrHolder) moveToNext() (exhausted bool, err error) { 122 var res *statedb.VersionedKV 123 if res, err = holder.itr.Next(); err != nil { 124 return false, err 125 } 126 if res != nil { 127 holder.kv = res 128 } 129 return res == nil, nil 130 } 131 132 // String returns the key that the holder has in the buffer for serving as a next key 133 func (holder *itrHolder) String() string { 134 return fmt.Sprintf("{%s}", holder.kv.Key) 135 }