github.com/s7techlab/cckit@v0.10.5/state/state_cached.go (about)

     1  package state
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  
     7  	"github.com/hyperledger/fabric-chaincode-go/shim"
     8  	"github.com/hyperledger/fabric-protos-go/ledger/queryresult"
     9  	"github.com/pkg/errors"
    10  )
    11  
    12  type (
    13  	TxWriteSet  map[string][]byte
    14  	TxDeleteSet map[string]interface{}
    15  
    16  	Cached struct {
    17  		State
    18  		TxWriteSet  TxWriteSet
    19  		TxDeleteSet TxDeleteSet
    20  	}
    21  
    22  	CachedQueryIterator struct {
    23  		current int
    24  		closed  bool
    25  		KVs     []*queryresult.KV
    26  	}
    27  )
    28  
    29  // WithCached returns state with tx level state cache
    30  func WithCache(ss State) *Cached {
    31  	s := ss.(*Impl)
    32  	cached := &Cached{
    33  		State:       s,
    34  		TxWriteSet:  make(map[string][]byte),
    35  		TxDeleteSet: make(map[string]interface{}),
    36  	}
    37  
    38  	// PutState wrapper
    39  	s.PutState = func(key string, bb []byte) error {
    40  		cached.TxWriteSet[key] = bb
    41  		return s.stub.PutState(key, bb)
    42  	}
    43  
    44  	// GetState wrapper
    45  	s.GetState = func(key string) ([]byte, error) {
    46  		if bb, ok := cached.TxWriteSet[key]; ok {
    47  			return bb, nil
    48  		}
    49  
    50  		if _, ok := cached.TxDeleteSet[key]; ok {
    51  			return []byte{}, nil
    52  		}
    53  		return s.stub.GetState(key)
    54  	}
    55  
    56  	s.DelState = func(key string) error {
    57  		delete(cached.TxWriteSet, key)
    58  		cached.TxDeleteSet[key] = nil
    59  
    60  		return s.stub.DelState(key)
    61  	}
    62  
    63  	s.GetStateByPartialCompositeKey = func(objectType string, keys []string) (shim.StateQueryIteratorInterface, error) {
    64  		iterator, err := s.stub.GetStateByPartialCompositeKey(objectType, keys)
    65  		if err != nil {
    66  			return nil, err
    67  		}
    68  
    69  		prefix, err := s.stub.CreateCompositeKey(objectType, keys)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  
    74  		return NewCachedQueryIterator(iterator, prefix, cached.TxWriteSet, cached.TxDeleteSet)
    75  	}
    76  
    77  	return cached
    78  }
    79  
    80  func NewCachedQueryIterator(iterator shim.StateQueryIteratorInterface, prefix string, writeSet TxWriteSet, deleteSet TxDeleteSet) (*CachedQueryIterator, error) {
    81  	queryIterator := &CachedQueryIterator{
    82  		current: -1,
    83  	}
    84  	for iterator.HasNext() {
    85  		kv, err := iterator.Next()
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  
    90  		if _, ok := deleteSet[kv.Key]; ok {
    91  			continue
    92  		}
    93  
    94  		queryIterator.KVs = append(queryIterator.KVs, kv)
    95  	}
    96  
    97  	for wroteKey, wroteValue := range writeSet {
    98  		if strings.HasPrefix(wroteKey, prefix) {
    99  			queryIterator.KVs = append(queryIterator.KVs, &queryresult.KV{
   100  				Namespace: "",
   101  				Key:       wroteKey,
   102  				Value:     wroteValue,
   103  			})
   104  		}
   105  	}
   106  
   107  	sort.Slice(queryIterator.KVs, func(i, j int) bool {
   108  		return queryIterator.KVs[i].Key < queryIterator.KVs[j].Key
   109  	})
   110  
   111  	return queryIterator, nil
   112  }
   113  
   114  func (i *CachedQueryIterator) Next() (*queryresult.KV, error) {
   115  	if !i.HasNext() {
   116  		return nil, errors.New(`no next items`)
   117  	}
   118  
   119  	i.current++
   120  	return i.KVs[i.current], nil
   121  }
   122  
   123  // HasNext returns true if the range query iterator contains additional keys
   124  // and values.
   125  func (i *CachedQueryIterator) HasNext() bool {
   126  	return i.current < len(i.KVs)-1
   127  }
   128  
   129  // Close closes the iterator. This should be called when done
   130  // reading from the iterator to free up resources.
   131  func (i *CachedQueryIterator) Close() error {
   132  	i.closed = true
   133  	return nil
   134  }