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  }